├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── src ├── assets │ ├── css │ │ └── tailwind.css │ └── img │ │ ├── login-office.jpeg │ │ ├── login-office-dark.jpeg │ │ ├── create-account-office.jpeg │ │ ├── forgot-password-office.jpeg │ │ ├── create-account-office-dark.jpeg │ │ └── forgot-password-office-dark.jpeg ├── icons │ ├── moon.svg │ ├── edit.svg │ ├── bell.svg │ ├── tables.svg │ ├── chat.svg │ ├── dropdown.svg │ ├── forbidden.svg │ ├── search.svg │ ├── outlinePerson.svg │ ├── menu.svg │ ├── money.svg │ ├── mail.svg │ ├── heart.svg │ ├── outlineLogout.svg │ ├── charts.svg │ ├── modals.svg │ ├── people.svg │ ├── buttons.svg │ ├── cart.svg │ ├── cards.svg │ ├── home.svg │ ├── forms.svg │ ├── trash.svg │ ├── pages.svg │ ├── sun.svg │ ├── twitter.svg │ ├── outlineCog.svg │ ├── github.svg │ └── index.js ├── components │ ├── Typography │ │ ├── SectionTitle.js │ │ └── PageTitle.js │ ├── ThemedSuspense.js │ ├── Sidebar │ │ ├── index.js │ │ ├── DesktopSidebar.js │ │ ├── MobileSidebar.js │ │ ├── SidebarContent.js │ │ └── SidebarSubmenu.js │ ├── Chart │ │ ├── ChartCard.js │ │ └── ChartLegend.js │ ├── RoundIcon.js │ ├── Cards │ │ └── InfoCard.js │ ├── AccessibleNavigationAnnouncer.js │ ├── CTA.js │ └── Header.js ├── pages │ ├── Blank.js │ ├── 404.js │ ├── Charts.js │ ├── ForgotPassword.js │ ├── Modals.js │ ├── Login.js │ ├── CreateAccount.js │ ├── Cards.js │ ├── Buttons.js │ ├── Dashboard.js │ ├── Tables.js │ └── Forms.js ├── setupTests.js ├── containers │ ├── Main.js │ └── Layout.js ├── context │ ├── SidebarContext.js │ └── ThemeContext.js ├── index.js ├── App.js ├── routes │ ├── sidebar.js │ └── index.js ├── utils │ └── demo │ │ ├── chartsData.js │ │ └── tableData.js └── serviceWorker.js ├── .github ├── windmill-dashboard-react.png └── ISSUE_TEMPLATE │ ├── ---feature-request.md │ └── ---bug-report.md ├── postcss.config.js ├── .gitignore ├── tailwind.config.js ├── LICENSE ├── package.json ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md └── README.md /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/assets/img/login-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/src/assets/img/login-office.jpeg -------------------------------------------------------------------------------- /.github/windmill-dashboard-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/.github/windmill-dashboard-react.png -------------------------------------------------------------------------------- /src/assets/img/login-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/src/assets/img/login-office-dark.jpeg -------------------------------------------------------------------------------- /src/assets/img/create-account-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/src/assets/img/create-account-office.jpeg -------------------------------------------------------------------------------- /src/assets/img/forgot-password-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/src/assets/img/forgot-password-office.jpeg -------------------------------------------------------------------------------- /src/assets/img/create-account-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/src/assets/img/create-account-office-dark.jpeg -------------------------------------------------------------------------------- /src/assets/img/forgot-password-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/estevanmaito/windmill-dashboard-react/HEAD/src/assets/img/forgot-password-office-dark.jpeg -------------------------------------------------------------------------------- /src/icons/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss'), 4 | require('autoprefixer'), 5 | require('cssnano')({ 6 | preset: 'default', 7 | }), 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /src/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/tables.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/Typography/SectionTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function SectionTitle({ children }) { 4 | return

{children}

5 | } 6 | 7 | export default SectionTitle 8 | -------------------------------------------------------------------------------- /src/pages/Blank.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import PageTitle from '../components/Typography/PageTitle' 4 | 5 | function Blank() { 6 | return ( 7 | <> 8 | Blank 9 | 10 | ) 11 | } 12 | 13 | export default Blank 14 | -------------------------------------------------------------------------------- /src/components/Typography/PageTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function PageTitle({ children }) { 4 | return ( 5 |

{children}

6 | ) 7 | } 8 | 9 | export default PageTitle 10 | -------------------------------------------------------------------------------- /src/icons/chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /src/icons/forbidden.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/containers/Main.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Main({ children }) { 4 | return ( 5 |
6 |
{children}
7 |
8 | ) 9 | } 10 | 11 | export default Main 12 | -------------------------------------------------------------------------------- /src/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/outlinePerson.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/money.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/mail.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/ThemedSuspense.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function ThemedSuspense() { 4 | return ( 5 |
6 | Loading... 7 |
8 | ) 9 | } 10 | 11 | export default ThemedSuspense 12 | -------------------------------------------------------------------------------- /src/icons/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/outlineLogout.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/Sidebar/index.js: -------------------------------------------------------------------------------- 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/icons/charts.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/icons/modals.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/people.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/buttons.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/cart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/cards.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/home.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/forms.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/Chart/ChartCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Chart({ children, title }) { 4 | return ( 5 |
6 |

{title}

7 | {children} 8 |
9 | ) 10 | } 11 | 12 | export default Chart 13 | -------------------------------------------------------------------------------- /src/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 5 | 10 | -------------------------------------------------------------------------------- /src/components/Sidebar/DesktopSidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import SidebarContent from './SidebarContent' 4 | 5 | function DesktopSidebar(props) { 6 | return ( 7 | 10 | ) 11 | } 12 | 13 | export default DesktopSidebar 14 | -------------------------------------------------------------------------------- /src/icons/pages.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const defaultTheme = require('tailwindcss/defaultTheme') 2 | const windmill = require('@windmill/react-ui/config') 3 | 4 | module.exports = windmill({ 5 | purge: ['src/**/*.js'], 6 | theme: { 7 | extend: { 8 | fontFamily: { 9 | sans: ['Inter', ...defaultTheme.fontFamily.sans], 10 | }, 11 | boxShadow: { 12 | bottom: '0 5px 6px -7px rgba(0, 0, 0, 0.6), 0 2px 4px -5px rgba(0, 0, 0, 0.06)', 13 | }, 14 | }, 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /src/components/Chart/ChartLegend.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function ChartLegend({ legends }) { 4 | return ( 5 |
6 | {legends.map((legend) => ( 7 |
8 | 9 | {legend.title} 10 |
11 | ))} 12 |
13 | ) 14 | } 15 | 16 | export default ChartLegend 17 | -------------------------------------------------------------------------------- /src/components/RoundIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classNames from 'classnames' 3 | 4 | function RoundIcon({ 5 | icon: Icon, 6 | iconColorClass = 'text-purple-600 dark:text-purple-100', 7 | bgColorClass = 'bg-purple-100 dark:bg-purple-600', 8 | className, 9 | }) { 10 | const baseStyle = 'p-3 rounded-full' 11 | 12 | const cls = classNames(baseStyle, iconColorClass, bgColorClass, className) 13 | return ( 14 |
15 | 16 |
17 | ) 18 | } 19 | 20 | export default RoundIcon 21 | -------------------------------------------------------------------------------- /src/components/Cards/InfoCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Card, CardBody } from '@windmill/react-ui' 3 | 4 | function InfoCard({ title, value, children: icon }) { 5 | return ( 6 | 7 | 8 | {icon} 9 |
10 |

{title}

11 |

{value}

12 |
13 |
14 |
15 | ) 16 | } 17 | 18 | export default InfoCard 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/icons/sun.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { ForbiddenIcon } from '../icons' 4 | 5 | function Page404() { 6 | return ( 7 |
8 |
18 | ) 19 | } 20 | 21 | export default Page404 22 | -------------------------------------------------------------------------------- /src/context/SidebarContext.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useMemo } from 'react' 2 | 3 | // create context 4 | export const SidebarContext = React.createContext() 5 | 6 | export const SidebarProvider = ({ children }) => { 7 | const [isSidebarOpen, setIsSidebarOpen] = useState(false) 8 | 9 | function toggleSidebar() { 10 | setIsSidebarOpen(!isSidebarOpen) 11 | } 12 | 13 | function closeSidebar() { 14 | setIsSidebarOpen(false) 15 | } 16 | 17 | const value = useMemo( 18 | () => ({ 19 | isSidebarOpen, 20 | toggleSidebar, 21 | closeSidebar, 22 | }), 23 | [isSidebarOpen] 24 | ) 25 | 26 | return {children} 27 | } 28 | -------------------------------------------------------------------------------- /src/icons/outlineCog.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/AccessibleNavigationAnnouncer.js: -------------------------------------------------------------------------------- 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/index.js: -------------------------------------------------------------------------------- 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/components/CTA.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { 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/pages/Charts.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import ChartCard from '../components/Chart/ChartCard' 4 | import { Doughnut, Line, Bar } from 'react-chartjs-2' 5 | import ChartLegend from '../components/Chart/ChartLegend' 6 | import PageTitle from '../components/Typography/PageTitle' 7 | import { 8 | doughnutOptions, 9 | lineOptions, 10 | barOptions, 11 | doughnutLegends, 12 | lineLegends, 13 | barLegends, 14 | } from '../utils/demo/chartsData' 15 | 16 | function Charts() { 17 | return ( 18 | <> 19 | Charts 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 | ) 39 | } 40 | 41 | export default Charts 42 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/components/Sidebar/MobileSidebar.js: -------------------------------------------------------------------------------- 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/routes/sidebar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ⚠ These are used just to render the Sidebar! 3 | * You can include any link here, local or external. 4 | * 5 | * If you're looking to actual Router routes, go to 6 | * `routes/index.js` 7 | */ 8 | const routes = [ 9 | { 10 | path: '/app/dashboard', // the url 11 | icon: 'HomeIcon', // the component being exported from icons/index.js 12 | name: 'Dashboard', // name that appear in Sidebar 13 | }, 14 | { 15 | path: '/app/forms', 16 | icon: 'FormsIcon', 17 | name: 'Forms', 18 | }, 19 | { 20 | path: '/app/cards', 21 | icon: 'CardsIcon', 22 | name: 'Cards', 23 | }, 24 | { 25 | path: '/app/charts', 26 | icon: 'ChartsIcon', 27 | name: 'Charts', 28 | }, 29 | { 30 | path: '/app/buttons', 31 | icon: 'ButtonsIcon', 32 | name: 'Buttons', 33 | }, 34 | { 35 | path: '/app/modals', 36 | icon: 'ModalsIcon', 37 | name: 'Modals', 38 | }, 39 | { 40 | path: '/app/tables', 41 | icon: 'TablesIcon', 42 | name: 'Tables', 43 | }, 44 | { 45 | icon: 'PagesIcon', 46 | name: 'Pages', 47 | routes: [ 48 | // submenu 49 | { 50 | path: '/login', 51 | name: 'Login', 52 | }, 53 | { 54 | path: '/create-account', 55 | name: 'Create account', 56 | }, 57 | { 58 | path: '/forgot-password', 59 | name: 'Forgot password', 60 | }, 61 | { 62 | path: '/app/404', 63 | name: '404', 64 | }, 65 | { 66 | path: '/app/blank', 67 | name: 'Blank', 68 | }, 69 | ], 70 | }, 71 | ] 72 | 73 | export default routes 74 | -------------------------------------------------------------------------------- /src/containers/Layout.js: -------------------------------------------------------------------------------- 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 '../containers/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]) 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/routes/index.js: -------------------------------------------------------------------------------- 1 | import { lazy } from 'react' 2 | 3 | // use lazy for better code splitting, a.k.a. load faster 4 | const Dashboard = lazy(() => import('../pages/Dashboard')) 5 | const Forms = lazy(() => import('../pages/Forms')) 6 | const Cards = lazy(() => import('../pages/Cards')) 7 | const Charts = lazy(() => import('../pages/Charts')) 8 | const Buttons = lazy(() => import('../pages/Buttons')) 9 | const Modals = lazy(() => import('../pages/Modals')) 10 | const Tables = lazy(() => import('../pages/Tables')) 11 | const Page404 = lazy(() => import('../pages/404')) 12 | const Blank = lazy(() => import('../pages/Blank')) 13 | 14 | /** 15 | * ⚠ These are internal routes! 16 | * They will be rendered inside the app, using the default `containers/Layout`. 17 | * If you want to add a route to, let's say, a landing page, you should add 18 | * it to the `App`'s router, exactly like `Login`, `CreateAccount` and other pages 19 | * are routed. 20 | * 21 | * If you're looking for the links rendered in the SidebarContent, go to 22 | * `routes/sidebar.js` 23 | */ 24 | const routes = [ 25 | { 26 | path: '/dashboard', // the url 27 | component: Dashboard, // view rendered 28 | }, 29 | { 30 | path: '/forms', 31 | component: Forms, 32 | }, 33 | { 34 | path: '/cards', 35 | component: Cards, 36 | }, 37 | { 38 | path: '/charts', 39 | component: Charts, 40 | }, 41 | { 42 | path: '/buttons', 43 | component: Buttons, 44 | }, 45 | { 46 | path: '/modals', 47 | component: Modals, 48 | }, 49 | { 50 | path: '/tables', 51 | component: Tables, 52 | }, 53 | { 54 | path: '/404', 55 | component: Page404, 56 | }, 57 | { 58 | path: '/blank', 59 | component: Blank, 60 | }, 61 | ] 62 | 63 | export default routes 64 | -------------------------------------------------------------------------------- /src/pages/ForgotPassword.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | import ImageLight from '../assets/img/forgot-password-office.jpeg' 5 | import ImageDark from '../assets/img/forgot-password-office-dark.jpeg' 6 | import { Label, Input, Button } from '@windmill/react-ui' 7 | 8 | function ForgotPassword() { 9 | return ( 10 |
11 |
12 |
13 |
14 | 20 | 26 |
27 |
28 |
29 |

30 | Forgot password 31 |

32 | 33 | 37 | 38 | 41 |
42 |
43 |
44 |
45 |
46 | ) 47 | } 48 | 49 | export default ForgotPassword 50 | -------------------------------------------------------------------------------- /src/context/ThemeContext.js: -------------------------------------------------------------------------------- 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) { 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) { 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, theme) 33 | }, [theme, key]) 34 | 35 | return [theme, setTheme] 36 | } 37 | 38 | // create context 39 | export const ThemeContext = React.createContext() 40 | 41 | // create context provider 42 | export const ThemeProvider = ({ children }) => { 43 | const [theme, setTheme] = useStorageTheme('theme') 44 | 45 | // update root element class on theme change 46 | const oldTheme = usePrevious(theme) 47 | useLayoutEffect(() => { 48 | document.documentElement.classList.remove(`theme-${oldTheme}`) 49 | document.documentElement.classList.add(`theme-${theme}`) 50 | }, [theme, oldTheme]) 51 | 52 | function toggleTheme() { 53 | if (theme === 'light') setTheme('dark') 54 | else setTheme('light') 55 | } 56 | 57 | const value = useMemo( 58 | () => ({ 59 | theme, 60 | toggleTheme, 61 | }), 62 | [theme] 63 | ) 64 | 65 | return {children} 66 | } 67 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/Sidebar/SidebarContent.js: -------------------------------------------------------------------------------- 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 | 8 | function Icon({ icon, ...props }) { 9 | const Icon = Icons[icon] 10 | return 11 | } 12 | 13 | function SidebarContent() { 14 | return ( 15 |
16 | 17 | Windmill 18 | 19 |
    20 | {routes.map((route) => 21 | route.routes ? ( 22 | 23 | ) : ( 24 |
  • 25 | 31 | 32 | 36 | 37 | 40 |
  • 41 | ) 42 | )} 43 |
44 |
45 | 51 |
52 |
53 | ) 54 | } 55 | 56 | export default SidebarContent 57 | -------------------------------------------------------------------------------- /src/pages/Modals.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | import PageTitle from '../components/Typography/PageTitle' 4 | import CTA from '../components/CTA' 5 | import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from '@windmill/react-ui' 6 | 7 | function Modals() { 8 | const [isModalOpen, setIsModalOpen] = useState(false) 9 | 10 | function openModal() { 11 | setIsModalOpen(true) 12 | } 13 | 14 | function closeModal() { 15 | setIsModalOpen(false) 16 | } 17 | 18 | return ( 19 | <> 20 | Modals 21 | 22 | 23 |
24 | 25 |
26 | 27 | 28 | Modal header 29 | 30 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nostrum et eligendi repudiandae 31 | voluptatem tempore! 32 | 33 | 34 | {/* I don't like this approach. Consider passing a prop to ModalFooter 35 | * that if present, would duplicate the buttons in a way similar to this. 36 | * Or, maybe find some way to pass something like size="large md:regular" 37 | * to Button 38 | */} 39 |
40 | 43 |
44 |
45 | 46 |
47 |
48 | 51 |
52 |
53 | 56 |
57 |
58 |
59 | 60 | ) 61 | } 62 | 63 | export default Modals 64 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 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 | export { 31 | ButtonsIcon, 32 | CardsIcon, 33 | ChartsIcon, 34 | FormsIcon, 35 | HomeIcon, 36 | ModalsIcon, 37 | PagesIcon, 38 | TablesIcon, 39 | HeartIcon, 40 | EditIcon, 41 | TrashIcon, 42 | ForbiddenIcon, 43 | GithubIcon, 44 | TwitterIcon, 45 | MailIcon, 46 | CartIcon, 47 | ChatIcon, 48 | MoneyIcon, 49 | PeopleIcon, 50 | SearchIcon, 51 | MoonIcon, 52 | SunIcon, 53 | BellIcon, 54 | MenuIcon, 55 | DropdownIcon, 56 | OutlinePersonIcon, 57 | OutlineCogIcon, 58 | OutlineLogoutIcon, 59 | } 60 | -------------------------------------------------------------------------------- /src/components/Sidebar/SidebarSubmenu.js: -------------------------------------------------------------------------------- 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 | 7 | function Icon({ icon, ...props }) { 8 | const Icon = Icons[icon] 9 | return 10 | } 11 | 12 | function SidebarSubmenu({ route }) { 13 | const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false) 14 | 15 | function handleDropdownMenuClick() { 16 | setIsDropdownMenuOpen(!isDropdownMenuOpen) 17 | } 18 | 19 | return ( 20 |
  • 21 | 32 | 41 |
      45 | {route.routes.map((r) => ( 46 |
    • 50 | 51 | {r.name} 52 | 53 |
    • 54 | ))} 55 |
    56 |
    57 |
  • 58 | ) 59 | } 60 | 61 | export default SidebarSubmenu 62 | -------------------------------------------------------------------------------- /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 | }, 18 | "dependencies": { 19 | "@testing-library/jest-dom": "4.2.4", 20 | "@testing-library/react": "9.5.0", 21 | "@testing-library/user-event": "7.2.1", 22 | "@windmill/react-ui": "0.3.1", 23 | "chart.js": "2.9.3", 24 | "classnames": "2.2.6", 25 | "faker": "4.1.0", 26 | "react": "^16.13.1", 27 | "react-chartjs-2": "2.9.0", 28 | "react-dom": "^16.13.1", 29 | "react-focus-lock": "2.4.0", 30 | "react-router-dom": "5.2.0", 31 | "react-scripts": "3.4.1", 32 | "react-transition-group": "4.4.1" 33 | }, 34 | "devDependencies": { 35 | "@release-it/conventional-changelog": "1.1.4", 36 | "@svgr/webpack": "5.4.0", 37 | "autoprefixer": "9.8.0", 38 | "commitizen": "4.1.2", 39 | "cross-env": "7.0.2", 40 | "cssnano": "4.1.10", 41 | "cz-conventional-changelog": "3.2.0", 42 | "postcss-cli": "7.1.1", 43 | "react-axe": "3.5.2", 44 | "release-it": "13.6.4", 45 | "tailwindcss": "1.4.6" 46 | }, 47 | "keywords": [ 48 | "windmill", 49 | "dashboard", 50 | "admin", 51 | "tailwind", 52 | "react" 53 | ], 54 | "release-it": { 55 | "github": { 56 | "release": true 57 | }, 58 | "npm": { 59 | "publish": false 60 | }, 61 | "plugins": { 62 | "@release-it/conventional-changelog": { 63 | "preset": "angular", 64 | "infile": "CHANGELOG.md" 65 | } 66 | } 67 | }, 68 | "config": { 69 | "commitizen": { 70 | "path": "./node_modules/cz-conventional-changelog" 71 | } 72 | }, 73 | "eslintConfig": { 74 | "extends": "react-app" 75 | }, 76 | "browserslist": { 77 | "production": [ 78 | ">0.2%", 79 | "not dead", 80 | "not op_mini all" 81 | ], 82 | "development": [ 83 | "last 1 chrome version", 84 | "last 1 firefox version", 85 | "last 1 safari version" 86 | ] 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | import ImageLight from '../assets/img/login-office.jpeg' 5 | import ImageDark from '../assets/img/login-office-dark.jpeg' 6 | import { GithubIcon, TwitterIcon } from '../icons' 7 | import { Label, Input, Button } from '@windmill/react-ui' 8 | 9 | function Login() { 10 | return ( 11 |
    12 |
    13 |
    14 |
    15 | 21 | 27 |
    28 |
    29 |
    30 |

    Login

    31 | 35 | 36 | 40 | 41 | 44 | 45 |
    46 | 47 | 51 | 55 | 56 |

    57 | 61 | Forgot your password? 62 | 63 |

    64 |

    65 | 69 | Create account 70 | 71 |

    72 |
    73 |
    74 |
    75 |
    76 |
    77 | ) 78 | } 79 | 80 | export default Login 81 | -------------------------------------------------------------------------------- /src/pages/CreateAccount.js: -------------------------------------------------------------------------------- 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 | 57 |
    58 | 59 | 63 | 67 | 68 |

    69 | 73 | Already have an account? Login 74 | 75 |

    76 |
    77 |
    78 |
    79 |
    80 |
    81 | ) 82 | } 83 | 84 | export default Login 85 | -------------------------------------------------------------------------------- /src/utils/demo/chartsData.js: -------------------------------------------------------------------------------- 1 | export const doughnutLegends = [ 2 | { title: 'Shirts', color: 'bg-blue-500' }, 3 | { title: 'Shoes', color: 'bg-teal-600' }, 4 | { title: 'Bags', color: 'bg-purple-600' }, 5 | ] 6 | 7 | export const lineLegends = [ 8 | { title: 'Organic', color: 'bg-teal-600' }, 9 | { title: 'Paid', color: 'bg-purple-600' }, 10 | ] 11 | 12 | export const barLegends = [ 13 | { title: 'Shoes', color: 'bg-teal-600' }, 14 | { title: 'Bags', color: 'bg-purple-600' }, 15 | ] 16 | 17 | export const doughnutOptions = { 18 | data: { 19 | datasets: [ 20 | { 21 | data: [33, 33, 33], 22 | /** 23 | * These colors come from Tailwind CSS palette 24 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette 25 | */ 26 | backgroundColor: ['#0694a2', '#1c64f2', '#7e3af2'], 27 | label: 'Dataset 1', 28 | }, 29 | ], 30 | labels: ['Shoes', 'Shirts', 'Bags'], 31 | }, 32 | options: { 33 | responsive: true, 34 | cutoutPercentage: 80, 35 | }, 36 | legend: { 37 | display: false, 38 | }, 39 | } 40 | 41 | export const lineOptions = { 42 | data: { 43 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], 44 | datasets: [ 45 | { 46 | label: 'Organic', 47 | /** 48 | * These colors come from Tailwind CSS palette 49 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette 50 | */ 51 | backgroundColor: '#0694a2', 52 | borderColor: '#0694a2', 53 | data: [43, 48, 40, 54, 67, 73, 70], 54 | fill: false, 55 | }, 56 | { 57 | label: 'Paid', 58 | fill: false, 59 | /** 60 | * These colors come from Tailwind CSS palette 61 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette 62 | */ 63 | backgroundColor: '#7e3af2', 64 | borderColor: '#7e3af2', 65 | data: [24, 50, 64, 74, 52, 51, 65], 66 | }, 67 | ], 68 | }, 69 | options: { 70 | responsive: true, 71 | tooltips: { 72 | mode: 'index', 73 | intersect: false, 74 | }, 75 | hover: { 76 | mode: 'nearest', 77 | intersect: true, 78 | }, 79 | scales: { 80 | x: { 81 | display: true, 82 | scaleLabel: { 83 | display: true, 84 | labelString: 'Month', 85 | }, 86 | }, 87 | y: { 88 | display: true, 89 | scaleLabel: { 90 | display: true, 91 | labelString: 'Value', 92 | }, 93 | }, 94 | }, 95 | }, 96 | legend: { 97 | display: false, 98 | }, 99 | } 100 | 101 | export const barOptions = { 102 | data: { 103 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], 104 | datasets: [ 105 | { 106 | label: 'Shoes', 107 | backgroundColor: '#0694a2', 108 | // borderColor: window.chartColors.red, 109 | borderWidth: 1, 110 | data: [-3, 14, 52, 74, 33, 90, 70], 111 | }, 112 | { 113 | label: 'Bags', 114 | backgroundColor: '#7e3af2', 115 | // borderColor: window.chartColors.blue, 116 | borderWidth: 1, 117 | data: [66, 33, 43, 12, 54, 62, 84], 118 | }, 119 | ], 120 | }, 121 | options: { 122 | responsive: true, 123 | }, 124 | legend: { 125 | display: false, 126 | }, 127 | } 128 | -------------------------------------------------------------------------------- /src/pages/Cards.js: -------------------------------------------------------------------------------- 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 | 38 | 39 | 40 | 41 | 47 | 48 | 49 | 50 | 56 | 57 | 58 | 59 | 65 | 66 |
    67 | 68 | Cards with title 69 | 70 |
    71 | 72 | 73 |

    Revenue

    74 |

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

    79 |
    80 |
    81 | 82 | 83 | 84 |

    Colored card

    85 |

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

    90 |
    91 |
    92 |
    93 | 94 | ) 95 | } 96 | 97 | export default Cards 98 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/pages/Buttons.js: -------------------------------------------------------------------------------- 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 | 127 |
    128 | 129 |
    130 | 133 |
    134 | 135 |
    136 |
    138 | 139 |
    140 |
    142 | 143 |
    144 |
    146 |
    147 |
    149 |
    150 | 151 | ) 152 | } 153 | 154 | export default Buttons 155 | -------------------------------------------------------------------------------- /src/pages/Dashboard.js: -------------------------------------------------------------------------------- 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 { 13 | TableBody, 14 | TableContainer, 15 | Table, 16 | TableHeader, 17 | TableCell, 18 | TableRow, 19 | TableFooter, 20 | Avatar, 21 | Badge, 22 | Pagination, 23 | } from '@windmill/react-ui' 24 | 25 | import { 26 | doughnutOptions, 27 | lineOptions, 28 | doughnutLegends, 29 | lineLegends, 30 | } from '../utils/demo/chartsData' 31 | 32 | function Dashboard() { 33 | const [page, setPage] = useState(1) 34 | const [data, setData] = useState([]) 35 | 36 | // pagination setup 37 | const resultsPerPage = 10 38 | const totalResults = response.length 39 | 40 | // pagination change control 41 | function onPageChange(p) { 42 | setPage(p) 43 | } 44 | 45 | // on page change, load new sliced data 46 | // here you would make another server request for new data 47 | useEffect(() => { 48 | setData(response.slice((page - 1) * resultsPerPage, page * resultsPerPage)) 49 | }, [page]) 50 | 51 | return ( 52 | <> 53 | Dashboard 54 | 55 | 56 | 57 | {/* */} 58 |
    59 | 60 | 66 | 67 | 68 | 69 | 75 | 76 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 94 |
    95 | 96 | 97 | 98 | 99 | 100 | Client 101 | Amount 102 | Status 103 | Date 104 | 105 | 106 | 107 | {data.map((user, i) => ( 108 | 109 | 110 |
    111 | 112 |
    113 |

    {user.name}

    114 |

    {user.job}

    115 |
    116 |
    117 |
    118 | 119 | $ {user.amount} 120 | 121 | 122 | {user.status} 123 | 124 | 125 | {new Date(user.date).toLocaleDateString()} 126 | 127 |
    128 | ))} 129 |
    130 |
    131 | 132 | 138 | 139 |
    140 | 141 | Charts 142 |
    143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
    153 | 154 | ) 155 | } 156 | 157 | export default Dashboard 158 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 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.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { 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 | 52 |
    53 |
    54 |
      55 | {/* */} 56 |
    • 57 | 68 |
    • 69 | {/* */} 70 |
    • 71 | 84 | 85 | setIsNotificationsMenuOpen(false)} 89 | > 90 | 91 | Messages 92 | 13 93 | 94 | 95 | Sales 96 | 2 97 | 98 | alert('Alerts!')}> 99 | Alerts 100 | 101 | 102 |
    • 103 | {/* */} 104 |
    • 105 | 118 | setIsProfileMenuOpen(false)} 122 | > 123 | 124 | 127 | 128 | 131 | alert('Log out!')}> 132 | 135 | 136 |
    • 137 |
    138 |
    139 |
    140 | ) 141 | } 142 | 143 | export default Header 144 | -------------------------------------------------------------------------------- /src/pages/Tables.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } 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 { 7 | Table, 8 | TableHeader, 9 | TableCell, 10 | TableBody, 11 | TableRow, 12 | TableFooter, 13 | TableContainer, 14 | Badge, 15 | Avatar, 16 | Button, 17 | Pagination, 18 | } from '@windmill/react-ui' 19 | import { EditIcon, TrashIcon } from '../icons' 20 | 21 | import response from '../utils/demo/tableData' 22 | // make a copy of the data, for the second table 23 | const response2 = response.concat([]) 24 | 25 | function Tables() { 26 | /** 27 | * DISCLAIMER: This code could be badly improved, but for the sake of the example 28 | * and readability, all the logic for both table are here. 29 | * You would be better served by dividing each table in its own 30 | * component, like Table(?) and TableWithActions(?) hiding the 31 | * presentation details away from the page view. 32 | */ 33 | 34 | // setup pages control for every table 35 | const [pageTable1, setPageTable1] = useState(1) 36 | const [pageTable2, setPageTable2] = useState(1) 37 | 38 | // setup data for every table 39 | const [dataTable1, setDataTable1] = useState([]) 40 | const [dataTable2, setDataTable2] = useState([]) 41 | 42 | // pagination setup 43 | const resultsPerPage = 10 44 | const totalResults = response.length 45 | 46 | // pagination change control 47 | function onPageChangeTable1(p) { 48 | setPageTable1(p) 49 | } 50 | 51 | // pagination change control 52 | function onPageChangeTable2(p) { 53 | setPageTable2(p) 54 | } 55 | 56 | // on page change, load new sliced data 57 | // here you would make another server request for new data 58 | useEffect(() => { 59 | setDataTable1(response.slice((pageTable1 - 1) * resultsPerPage, pageTable1 * resultsPerPage)) 60 | }, [pageTable1]) 61 | 62 | // on page change, load new sliced data 63 | // here you would make another server request for new data 64 | useEffect(() => { 65 | setDataTable2(response2.slice((pageTable2 - 1) * resultsPerPage, pageTable2 * resultsPerPage)) 66 | }, [pageTable2]) 67 | 68 | return ( 69 | <> 70 | Tables 71 | 72 | 73 | 74 | Simple table 75 | 76 | 77 | 78 | 79 | Client 80 | Amount 81 | Status 82 | Date 83 | 84 | 85 | 86 | {dataTable1.map((user, i) => ( 87 | 88 | 89 |
    90 | 91 |
    92 |

    {user.name}

    93 |

    {user.job}

    94 |
    95 |
    96 |
    97 | 98 | $ {user.amount} 99 | 100 | 101 | {user.status} 102 | 103 | 104 | {new Date(user.date).toLocaleDateString()} 105 | 106 |
    107 | ))} 108 |
    109 |
    110 | 111 | 117 | 118 |
    119 | 120 | Table with actions 121 | 122 | 123 | 124 | 125 | Client 126 | Amount 127 | Status 128 | Date 129 | Actions 130 | 131 | 132 | 133 | {dataTable2.map((user, i) => ( 134 | 135 | 136 |
    137 | 138 |
    139 |

    {user.name}

    140 |

    {user.job}

    141 |
    142 |
    143 |
    144 | 145 | $ {user.amount} 146 | 147 | 148 | {user.status} 149 | 150 | 151 | {new Date(user.date).toLocaleDateString()} 152 | 153 | 154 |
    155 | 158 | 161 |
    162 |
    163 |
    164 | ))} 165 |
    166 |
    167 | 168 | 174 | 175 |
    176 | 177 | ) 178 | } 179 | 180 | export default Tables 181 | -------------------------------------------------------------------------------- /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 | ## 📦 Features 15 | 16 | - 🦮 Throughly accessible (developed using screen readers) 17 | - 🌗 Dark theme enabled (load even different images based on theme) 18 | - 🧩 Multiple (custom) components 19 | - ⚡ Code splitting 20 | - Tailwind CSS 21 | - [Windmill React UI](https://windmillui.com/react-ui) 22 | - React Router 23 | - Heroicons 24 | - Chart.js 25 | - PWA delivering offline-first and app-like experience 26 | 27 | ## 📚 Docs 28 | 29 | ### General components 30 | 31 | 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. 32 | 33 | ### Routing 34 | 35 | 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)). 36 | 37 | #### Sidebar routes 38 | 39 | These are the routes that will show in the sidebar. They expect three properties: 40 | 41 | - `path`: the destination; 42 | - `name`: the name to be shown; 43 | - `icon`: an icon to illustrate the item 44 | 45 | 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`: 46 | 47 | ```js 48 | // sidebar.js 49 | { 50 | path: '/app/tables', 51 | icon: 'TablesIcon', 52 | name: 'Tables', 53 | }, 54 | { 55 | icon: 'PagesIcon', // <-- this is used as a submenu, so no path 56 | name: 'Pages', 57 | routes: [ 58 | // submenu 59 | { 60 | path: '/login', 61 | name: 'Login', // <-- these don't have icons 62 | }, 63 | { 64 | path: '/create-account', 65 | name: 'Create account', 66 | }, 67 | ``` 68 | 69 | #### General (Router) routes 70 | 71 | These are **internal** (private) routes. They will be rendered inside the app, using the default `containers/Layout`. 72 | 73 | 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. 74 | 75 | #### How to add a new page to router? 76 | 77 | 1. Create your page inside `src/pages`, say `MyPage.js`; 78 | 2. Add it to the global router (`src/routes/index.js`) 79 | 80 | ```js 81 | const MyPage = lazy(() => import('../pages/MyPage')) 82 | ``` 83 | 84 | Then add it to the `routes` array: 85 | 86 | ```js 87 | { 88 | path: '/my-page', // the url that will be added to /app/ 89 | component: MyPage, // the page component you jsut imported 90 | } 91 | ``` 92 | 93 | 3. If you want to make this page accessible from the sidebar, you have to options: 94 | 95 | - add it to the root `routes` array 96 | 97 | ```js 98 | { 99 | path: '/app/my-page', // /app + the url you added in routes/index.js 100 | icon: 'HomeIcon', // the component being exported from src/icons/index.js 101 | name: 'My Page', // name that appear in Sidebar 102 | }, 103 | ``` 104 | 105 | - add it as an option under a dropdown 106 | 107 | ```js 108 | { 109 | icon: 'PagesIcon', 110 | name: 'Pages', 111 | routes: [ 112 | // submenu 113 | { 114 | path: '/app/my-page', 115 | name: 'My Page', 116 | }, 117 | ``` 118 | 119 | If you're asking where does this `/app` come from, it is from this line inside `src/App.js`, that renders the app: 120 | 121 | ```jsx 122 | 123 | ``` 124 | 125 | --- 126 | 127 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 128 | 129 | ## Available Scripts 130 | 131 | In the project directory, you can run: 132 | 133 | ### `npm start` 134 | 135 | Runs the app in the development mode.
    136 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 137 | 138 | The page will reload if you make edits.
    139 | You will also see any lint errors in the console. 140 | 141 | ### `npm test` 142 | 143 | Launches the test runner in the interactive watch mode.
    144 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 145 | 146 | ### `npm run build` 147 | 148 | Builds the app for production to the `build` folder.
    149 | It correctly bundles React in production mode and optimizes the build for the best performance. 150 | 151 | The build is minified and the filenames include the hashes.
    152 | Your app is ready to be deployed! 153 | 154 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 155 | 156 | ### `npm run eject` 157 | 158 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 159 | 160 | 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. 161 | 162 | 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. 163 | 164 | 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. 165 | 166 | ## Learn More 167 | 168 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 169 | 170 | To learn React, check out the [React documentation](https://reactjs.org/). 171 | 172 | ### Code Splitting 173 | 174 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 175 | 176 | ### Analyzing the Bundle Size 177 | 178 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 179 | 180 | ### Making a Progressive Web App 181 | 182 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 183 | 184 | ### Advanced Configuration 185 | 186 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 187 | 188 | ### Deployment 189 | 190 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 191 | 192 | ### `npm run build` fails to minify 193 | 194 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 195 | -------------------------------------------------------------------------------- /src/pages/Forms.js: -------------------------------------------------------------------------------- 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 |