├── 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 |
2 |
7 |
--------------------------------------------------------------------------------
/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 |
6 |
11 |
--------------------------------------------------------------------------------
/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 |
9 |
404
10 |
11 | Page not found. Check the address or{' '}
12 |
13 | go back
14 |
15 | .
16 |
17 |
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 |
34 | Email
35 |
36 |
37 |
38 |
39 | Recover password
40 |
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 | You need to enable JavaScript to run this app.
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 |
38 | {route.name}
39 |
40 |
41 | )
42 | )}
43 |
44 |
45 |
46 | Create account
47 |
48 | +
49 |
50 |
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 | Open modal
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 |
41 | Cancel
42 |
43 |
44 |
45 | Accept
46 |
47 |
48 |
49 | Cancel
50 |
51 |
52 |
53 |
54 | Accept
55 |
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 |
26 |
27 |
28 | {route.name}
29 |
30 |
31 |
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 |
32 | Email
33 |
34 |
35 |
36 |
37 | Password
38 |
39 |
40 |
41 |
42 | Log in
43 |
44 |
45 |
46 |
47 |
48 |
49 | Github
50 |
51 |
52 |
53 | Twitter
54 |
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 |
34 | Email
35 |
36 |
37 |
38 | Password
39 |
40 |
41 |
42 | Confirm password
43 |
44 |
45 |
46 |
47 |
48 |
49 | I agree to the privacy policy
50 |
51 |
52 |
53 |
54 | Create account
55 |
56 |
57 |
58 |
59 |
60 |
61 | Github
62 |
63 |
64 |
65 | Twitter
66 |
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 | Larger Button
22 |
23 |
24 |
25 | Large Button
26 |
27 |
28 |
29 | Regular
30 |
31 |
32 |
33 |
34 | Router Link
35 |
36 |
37 |
38 |
39 | Disabled
40 |
41 |
42 |
43 | Small
44 |
45 |
46 |
47 | Outline
48 |
49 |
50 |
51 | Larger Button
52 |
53 |
54 |
55 |
56 |
57 | Large Button
58 |
59 |
60 |
61 |
62 | Regular
63 |
64 |
65 |
66 |
67 | Router Link
68 |
69 |
70 |
71 |
72 |
73 | Disabled
74 |
75 |
76 |
77 |
78 |
79 | Small
80 |
81 |
82 |
83 |
84 | Link
85 |
86 |
87 |
88 | Larger Button
89 |
90 |
91 |
92 |
93 |
94 | Large Button
95 |
96 |
97 |
98 |
99 | Regular
100 |
101 |
102 |
103 |
104 | Router Link
105 |
106 |
107 |
108 |
109 |
110 | Disabled
111 |
112 |
113 |
114 |
115 |
116 | Small
117 |
118 |
119 |
120 |
121 | Icons
122 |
123 |
124 |
125 | Icon right
126 |
127 |
128 |
129 |
130 |
131 | Icon Left
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
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 |
39 |
40 |
41 | {/* */}
42 |
54 |
55 | {/* */}
56 |
57 |
62 | {mode === 'dark' ? (
63 |
64 | ) : (
65 |
66 | )}
67 |
68 |
69 | {/* */}
70 |
71 |
77 |
78 | {/* */}
79 |
83 |
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 |
111 |
117 |
118 | setIsProfileMenuOpen(false)}
122 | >
123 |
124 |
125 | Profile
126 |
127 |
128 |
129 | Settings
130 |
131 | alert('Log out!')}>
132 |
133 | Log out
134 |
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 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 | ))}
165 |
166 |
167 |
168 |
174 |
175 |
176 | >
177 | )
178 | }
179 |
180 | export default Tables
181 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
81 |
82 | Validation
83 |
84 |
85 |
86 | Invalid input
87 |
88 | Your password is too short.
89 |
90 |
91 |
92 | Valid input
93 |
94 | Your password is strong.
95 |
96 |
97 |
98 | Helper text
99 |
100 | Your password must be at least 6 characters long.
101 |
102 |
103 |
104 | {/* */}
105 | Icons
106 |
107 |
108 |
109 | Icon left
110 | {/* */}
111 |
112 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | Icon right
124 | {/* */}
125 |
126 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | {/* */}
138 | Buttons
139 |
140 |
167 | >
168 | )
169 | }
170 |
171 | export default Forms
172 |
--------------------------------------------------------------------------------
/src/utils/demo/tableData.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/lokesh_coder/128.jpg',
4 | name: 'Chandler Jacobi',
5 | job: 'Direct Security Executive',
6 | amount: 989.4,
7 | status: 'primary',
8 | date: 'Mon Feb 03 2020 04:13:15 GMT-0300 (Brasilia Standard Time)',
9 | },
10 | {
11 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/suribbles/128.jpg',
12 | name: 'Monserrat Marquardt',
13 | job: 'Forward Accountability Producer',
14 | amount: 471.44,
15 | status: 'danger',
16 | date: 'Fri Nov 29 2019 10:43:17 GMT-0300 (Brasilia Standard Time)',
17 | },
18 | {
19 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jjsiii/128.jpg',
20 | name: 'Lonie Wyman',
21 | job: 'Legacy Program Director',
22 | amount: 934.24,
23 | status: 'success',
24 | date: 'Fri Apr 03 2020 03:07:53 GMT-0300 (Brasilia Standard Time)',
25 | },
26 | {
27 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/holdenweb/128.jpg',
28 | name: 'Corine Abernathy',
29 | job: 'Chief Factors Planner',
30 | amount: 351.28,
31 | status: 'warning',
32 | date: 'Fri Jun 21 2019 20:21:55 GMT-0300 (Brasilia Standard Time)',
33 | },
34 | {
35 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/operatino/128.jpg',
36 | name: 'Lorenz Botsford',
37 | job: 'Central Accountability Developer',
38 | amount: 355.3,
39 | status: 'neutral',
40 | date: 'Wed Aug 28 2019 15:31:43 GMT-0300 (Brasilia Standard Time)',
41 | },
42 | {
43 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/akmur/128.jpg',
44 | name: 'Everette Botsford',
45 | job: 'Product Group Architect',
46 | amount: 525.42,
47 | status: 'success',
48 | date: 'Thu Jan 16 2020 09:53:33 GMT-0300 (Brasilia Standard Time)',
49 | },
50 | {
51 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/nateschulte/128.jpg',
52 | name: 'Marilou Beahan',
53 | job: 'Future Security Planner',
54 | amount: 414.99,
55 | status: 'success',
56 | date: 'Mon Oct 28 2019 14:44:31 GMT-0300 (Brasilia Standard Time)',
57 | },
58 | {
59 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jerrybai1907/128.jpg',
60 | name: 'Ceasar Sauer',
61 | job: 'Direct Brand Specialist',
62 | amount: 488.0,
63 | status: 'primary',
64 | date: 'Tue Jul 23 2019 00:24:44 GMT-0300 (Brasilia Standard Time)',
65 | },
66 | {
67 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/igorgarybaldi/128.jpg',
68 | name: 'Rae McDermott',
69 | job: 'Lead Branding Engineer',
70 | amount: 502.69,
71 | status: 'danger',
72 | date: 'Sat Feb 01 2020 12:57:03 GMT-0300 (Brasilia Standard Time)',
73 | },
74 | {
75 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/rtgibbons/128.jpg',
76 | name: 'Mable Steuber',
77 | job: 'National Group Executive',
78 | amount: 911.09,
79 | status: 'danger',
80 | date: 'Mon Sep 09 2019 12:03:25 GMT-0300 (Brasilia Standard Time)',
81 | },
82 | {
83 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/danthms/128.jpg',
84 | name: 'Julian Glover',
85 | job: 'Global Branding Assistant',
86 | amount: 656.94,
87 | status: 'danger',
88 | date: 'Fri May 22 2020 17:46:12 GMT-0300 (Brasilia Standard Time)',
89 | },
90 | {
91 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/andysolomon/128.jpg',
92 | name: 'Duncan Toy',
93 | job: 'Central Intranet Manager',
94 | amount: 120.78,
95 | status: 'danger',
96 | date: 'Sun Nov 17 2019 11:59:50 GMT-0300 (Brasilia Standard Time)',
97 | },
98 | {
99 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jesseddy/128.jpg',
100 | name: 'Blanche Friesen',
101 | job: 'Forward Identity Executive',
102 | amount: 676.95,
103 | status: 'danger',
104 | date: 'Sun Jun 21 2020 16:46:39 GMT-0300 (Brasilia Standard Time)',
105 | },
106 | {
107 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/haruintesettden/128.jpg',
108 | name: 'Orion Koepp',
109 | job: 'Global Accountability Strategist',
110 | amount: 447.56,
111 | status: 'danger',
112 | date: 'Thu Jun 04 2020 18:29:41 GMT-0300 (Brasilia Standard Time)',
113 | },
114 | {
115 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/vocino/128.jpg',
116 | name: 'Dakota Vandervort',
117 | job: 'Future Assurance Coordinator',
118 | amount: 765.22,
119 | status: 'danger',
120 | date: 'Fri Jan 31 2020 13:02:55 GMT-0300 (Brasilia Standard Time)',
121 | },
122 | {
123 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/zaki3d/128.jpg',
124 | name: 'Lily Collier',
125 | job: 'Forward Configuration Orchestrator',
126 | amount: 449.11,
127 | status: 'danger',
128 | date: 'Sun Aug 18 2019 14:52:01 GMT-0300 (Brasilia Standard Time)',
129 | },
130 | {
131 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/evanshajed/128.jpg',
132 | name: 'Kayleigh Schumm',
133 | job: 'Central Division Agent',
134 | amount: 65.54,
135 | status: 'danger',
136 | date: 'Wed May 06 2020 17:49:09 GMT-0300 (Brasilia Standard Time)',
137 | },
138 | {
139 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/mdsisto/128.jpg',
140 | name: 'General McGlynn',
141 | job: 'Central Web Analyst',
142 | amount: 30.51,
143 | status: 'danger',
144 | date: 'Mon Mar 30 2020 01:24:54 GMT-0300 (Brasilia Standard Time)',
145 | },
146 | {
147 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/woodydotmx/128.jpg',
148 | name: 'Shayna Schumm',
149 | job: 'Future Directives Engineer',
150 | amount: 313.73,
151 | status: 'danger',
152 | date: 'Wed Jul 03 2019 10:01:06 GMT-0300 (Brasilia Standard Time)',
153 | },
154 | {
155 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/mfacchinello/128.jpg',
156 | name: 'Giovanna Sanford',
157 | job: 'Dynamic Interactions Executive',
158 | amount: 398.3,
159 | status: 'danger',
160 | date: 'Tue Oct 08 2019 17:08:43 GMT-0300 (Brasilia Standard Time)',
161 | },
162 | {
163 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/ademilter/128.jpg',
164 | name: 'Emie Mante',
165 | job: 'Direct Factors Supervisor',
166 | amount: 142.51,
167 | status: 'danger',
168 | date: 'Wed Jul 24 2019 19:17:16 GMT-0300 (Brasilia Standard Time)',
169 | },
170 | {
171 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/tobysaxon/128.jpg',
172 | name: 'Chance Muller',
173 | job: 'Lead Usability Planner',
174 | amount: 963.26,
175 | status: 'danger',
176 | date: 'Sun Dec 01 2019 14:04:10 GMT-0300 (Brasilia Standard Time)',
177 | },
178 | {
179 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg',
180 | name: 'Armani Torphy',
181 | job: 'Forward Functionality Analyst',
182 | amount: 445.23,
183 | status: 'danger',
184 | date: 'Tue Dec 24 2019 13:28:36 GMT-0300 (Brasilia Standard Time)',
185 | },
186 | {
187 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/devinhalladay/128.jpg',
188 | name: 'Braeden Ward',
189 | job: 'Central Integration Director',
190 | amount: 588.53,
191 | status: 'danger',
192 | date: 'Wed Apr 15 2020 21:40:11 GMT-0300 (Brasilia Standard Time)',
193 | },
194 | {
195 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/kimcool/128.jpg',
196 | name: 'Malcolm Price',
197 | job: 'District Program Planner',
198 | amount: 181.93,
199 | status: 'danger',
200 | date: 'Thu Oct 24 2019 07:09:03 GMT-0300 (Brasilia Standard Time)',
201 | },
202 | {
203 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/angelceballos/128.jpg',
204 | name: 'Susan Jast',
205 | job: 'Future Paradigm Associate',
206 | amount: 928.41,
207 | status: 'danger',
208 | date: 'Sun Feb 09 2020 23:22:23 GMT-0300 (Brasilia Standard Time)',
209 | },
210 | {
211 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/peachananr/128.jpg',
212 | name: 'Torrey Reynolds',
213 | job: 'Lead Security Agent',
214 | amount: 447.37,
215 | status: 'danger',
216 | date: 'Mon Oct 28 2019 04:10:39 GMT-0300 (Brasilia Standard Time)',
217 | },
218 | {
219 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/agromov/128.jpg',
220 | name: 'Travon Harber',
221 | job: 'Legacy Marketing Facilitator',
222 | amount: 422.48,
223 | status: 'danger',
224 | date: 'Sat Nov 09 2019 05:04:09 GMT-0300 (Brasilia Standard Time)',
225 | },
226 | {
227 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/danpliego/128.jpg',
228 | name: 'Hattie Gutkowski',
229 | job: 'Chief Configuration Supervisor',
230 | amount: 714.34,
231 | status: 'danger',
232 | date: 'Tue Oct 22 2019 22:36:23 GMT-0300 (Brasilia Standard Time)',
233 | },
234 | {
235 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/ahmetalpbalkan/128.jpg',
236 | name: 'Demarco Lang',
237 | job: 'Investor Program Designer',
238 | amount: 536.92,
239 | status: 'danger',
240 | date: 'Wed Apr 08 2020 03:05:59 GMT-0300 (Brasilia Standard Time)',
241 | },
242 | {
243 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/nasirwd/128.jpg',
244 | name: 'Glennie Ziemann',
245 | job: 'Dynamic Interactions Analyst',
246 | amount: 597.25,
247 | status: 'danger',
248 | date: 'Mon Jun 22 2020 21:27:06 GMT-0300 (Brasilia Standard Time)',
249 | },
250 | {
251 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/juaumlol/128.jpg',
252 | name: 'Alta Howe',
253 | job: 'District Intranet Executive',
254 | amount: 42.28,
255 | status: 'danger',
256 | date: 'Sat Oct 12 2019 22:57:22 GMT-0300 (Brasilia Standard Time)',
257 | },
258 | {
259 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/victordeanda/128.jpg',
260 | name: 'Sydnee Gottlieb',
261 | job: 'Global Response Specialist',
262 | amount: 868.92,
263 | status: 'danger',
264 | date: 'Wed Feb 05 2020 05:17:34 GMT-0300 (Brasilia Standard Time)',
265 | },
266 | {
267 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/baumannzone/128.jpg',
268 | name: 'Arlene Schmitt',
269 | job: 'Lead Metrics Consultant',
270 | amount: 364.68,
271 | status: 'danger',
272 | date: 'Thu Oct 03 2019 08:47:32 GMT-0300 (Brasilia Standard Time)',
273 | },
274 | {
275 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/chacky14/128.jpg',
276 | name: 'Hilda Schoen',
277 | job: 'National Solutions Facilitator',
278 | amount: 260.91,
279 | status: 'danger',
280 | date: 'Wed Dec 04 2019 06:28:30 GMT-0300 (Brasilia Standard Time)',
281 | },
282 | {
283 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/kostaspt/128.jpg',
284 | name: 'Chase Bahringer',
285 | job: 'Dynamic Communications Designer',
286 | amount: 454.61,
287 | status: 'danger',
288 | date: 'Mon Nov 25 2019 16:59:38 GMT-0300 (Brasilia Standard Time)',
289 | },
290 | {
291 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/timgthomas/128.jpg',
292 | name: 'Lucile Hansen',
293 | job: 'Customer Usability Facilitator',
294 | amount: 982.22,
295 | status: 'danger',
296 | date: 'Sun Aug 25 2019 17:15:59 GMT-0300 (Brasilia Standard Time)',
297 | },
298 | {
299 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/safrankov/128.jpg',
300 | name: 'Mollie Heaney',
301 | job: 'Forward Communications Director',
302 | amount: 390.33,
303 | status: 'danger',
304 | date: 'Mon Jul 22 2019 01:45:19 GMT-0300 (Brasilia Standard Time)',
305 | },
306 | {
307 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/justme_timothyg/128.jpg',
308 | name: 'Bennie Kuvalis',
309 | job: 'Future Factors Agent',
310 | amount: 456.64,
311 | status: 'danger',
312 | date: 'Sat Feb 08 2020 07:55:08 GMT-0300 (Brasilia Standard Time)',
313 | },
314 | {
315 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/Chakintosh/128.jpg',
316 | name: 'Jodie Luettgen',
317 | job: 'Customer Implementation Associate',
318 | amount: 398.37,
319 | status: 'danger',
320 | date: 'Tue Jun 09 2020 11:19:53 GMT-0300 (Brasilia Standard Time)',
321 | },
322 | {
323 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/weavermedia/128.jpg',
324 | name: 'Bethel Quitzon',
325 | job: 'Dynamic Web Strategist',
326 | amount: 183.58,
327 | status: 'danger',
328 | date: 'Sun Dec 29 2019 18:56:54 GMT-0300 (Brasilia Standard Time)',
329 | },
330 | {
331 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/gmourier/128.jpg',
332 | name: 'Jon Dietrich',
333 | job: 'Legacy Creative Planner',
334 | amount: 439.01,
335 | status: 'danger',
336 | date: 'Sun Dec 29 2019 11:11:34 GMT-0300 (Brasilia Standard Time)',
337 | },
338 | {
339 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/stefvdham/128.jpg',
340 | name: 'Nakia Kihn',
341 | job: 'Central Interactions Coordinator',
342 | amount: 824.12,
343 | status: 'danger',
344 | date: 'Sun Sep 15 2019 06:43:56 GMT-0300 (Brasilia Standard Time)',
345 | },
346 | {
347 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/thedamianhdez/128.jpg',
348 | name: 'Assunta Grady',
349 | job: 'Investor Operations Specialist',
350 | amount: 172.97,
351 | status: 'danger',
352 | date: 'Tue Dec 17 2019 01:46:37 GMT-0300 (Brasilia Standard Time)',
353 | },
354 | {
355 | avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/nehfy/128.jpg',
356 | name: 'Lukas Klocko',
357 | job: 'Human Usability Associate',
358 | amount: 515.74,
359 | status: 'danger',
360 | date: 'Mon Jun 15 2020 04:04:32 GMT-0300 (Brasilia Standard Time)',
361 | },
362 | ]
363 |
--------------------------------------------------------------------------------