├── .eslintrc.json
├── .github
└── windmill-dashboard-thumbnail.jpg
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── context
├── SidebarContext.tsx
└── ThemeContext.tsx
├── example
├── components
│ ├── AccessibleNavigationAnnouncer.tsx
│ ├── CTA.tsx
│ ├── Cards
│ │ └── InfoCard.tsx
│ ├── Chart
│ │ ├── ChartCard.tsx
│ │ └── ChartLegend.tsx
│ ├── Header.tsx
│ ├── Loader
│ │ ├── Loader.module.css
│ │ └── Loader.tsx
│ ├── RoundIcon.tsx
│ ├── Sidebar
│ │ ├── DesktopSidebar.tsx
│ │ ├── MobileSidebar.tsx
│ │ ├── SidebarContent.tsx
│ │ ├── SidebarSubmenu.tsx
│ │ └── index.tsx
│ ├── ThemedSuspense.tsx
│ └── Typography
│ │ ├── PageTitle.tsx
│ │ └── SectionTitle.tsx
└── containers
│ ├── Layout.tsx
│ └── Main.tsx
├── icons
├── bell.svg
├── buttons.svg
├── cards.svg
├── cart.svg
├── charts.svg
├── chat.svg
├── dropdown.svg
├── edit.svg
├── forbidden.svg
├── forms.svg
├── github.svg
├── heart.svg
├── home.svg
├── index.ts
├── mail.svg
├── menu.svg
├── modals.svg
├── money.svg
├── moon.svg
├── outlineCog.svg
├── outlineLogout.svg
├── outlinePerson.svg
├── pages.svg
├── people.svg
├── search.svg
├── sun.svg
├── tables.svg
├── trash.svg
└── twitter.svg
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages
├── _app.tsx
├── api
│ └── hello.ts
├── example
│ ├── 404.tsx
│ ├── blank.tsx
│ ├── buttons.tsx
│ ├── cards.tsx
│ ├── charts.tsx
│ ├── create-account.tsx
│ ├── forgot-password.tsx
│ ├── forms.tsx
│ ├── index.tsx
│ ├── login.tsx
│ ├── modals.tsx
│ └── tables.tsx
└── index.tsx
├── postcss.config.js
├── public
├── assets
│ └── img
│ │ ├── create-account-office-dark.jpeg
│ │ ├── create-account-office.jpeg
│ │ ├── forgot-password-office-dark.jpeg
│ │ ├── forgot-password-office.jpeg
│ │ ├── login-office-dark.jpeg
│ │ └── login-office.jpeg
├── favicon.ico
└── vercel.svg
├── routes
└── sidebar.tsx
├── styles
├── Home.module.css
└── globals.css
├── tailwind.config.js
├── tsconfig.json
├── utils
└── demo
│ ├── chartsData.ts
│ └── tableData.ts
└── yarn.lock
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.github/windmill-dashboard-thumbnail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/.github/windmill-dashboard-thumbnail.jpg
--------------------------------------------------------------------------------
/.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 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "editor.formatOnSave": true,
4 | "editor.formatOnPaste": true,
5 | "editor.tabSize": 2
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Estevan Maito
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
Windmill Dashboard Next.js Typescript
3 |
4 |
5 |
6 |
7 |
8 | See the [Original Project by @estevanmaito](https://github.com/estevanmaito/windmill-dashboard-react/)
9 |
10 | With help from other contributors :
11 | - [Typescript version by @neutralboy](https://github.com/neutralboy/windmill-dashboard-react-ts)
12 | - [Nextjs version by @Aldhanekaa](https://github.com/Aldhanekaa/windmill-dashboard-nextjs)
13 |
14 |
15 | 🚀 [See it live](https://windmill-dashboard-nextjs-typescript.vercel.app/example)
16 |
17 | 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.
18 |
19 | 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.
20 |
21 | ## 📦 Features
22 |
23 | - 🦮 Throughly accessible (developed using screen readers)
24 | - 🌗 Dark theme enabled (load even different images based on theme)
25 | - 🧩 Multiple (custom) components
26 | - ⚡ Code splitting
27 | - Tailwind CSS
28 | - [Windmill React UI](https://windmillui.com/react-ui)
29 | - Heroicons
30 | - Chart.js
31 |
32 | ## 📚 Docs
33 |
34 | ### General components
35 |
36 | 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.
37 |
38 | ### Example Boilerplate
39 |
40 | All components and containers are saved in folder [example](example)
41 |
42 | ### Routing
43 |
44 | Change default redirecting when hit the `/` or `home` in file [next.config.js](next.config.js)
45 | ```js
46 | async redirects() {
47 | return [
48 | {
49 | source: '/',
50 | destination: '/example/login',
51 | permanent: false,
52 | },
53 | ]
54 | }
55 | ```
56 |
57 | #### Sidebar routes
58 |
59 | To configure sidebar menus, see file ([routes/sidebar.tsx](routes/sidebar.tsx)).
60 |
61 | These are the routes that will show in the sidebar. They expect three properties:
62 |
63 | - `path`: the destination;
64 | - `name`: the name to be shown;
65 | - `icon`: an icon to illustrate the item
66 |
67 | 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`:
68 |
69 | ```js
70 | // sidebar.js
71 | {
72 | path: '/example/tables',
73 | icon: 'TablesIcon',
74 | name: 'Tables',
75 | },
76 | {
77 | icon: 'PagesIcon', // <-- this is used as a submenu, so no path
78 | name: 'Pages',
79 | routes: [
80 | // submenu
81 | {
82 | path: '/example/login',
83 | name: 'Login', // <-- these don't have icons
84 | },
85 | {
86 | path: '/example/create-account',
87 | name: 'Create account',
88 | },
89 | ```
90 |
91 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
92 |
93 | ## Getting Started
94 |
95 | First, install dependencies :
96 | ```bash
97 | npm install
98 | # or
99 | yarn install
100 | ```
101 |
102 | then, you can run the development server:
103 |
104 | ```bash
105 | npm run dev
106 | # or
107 | yarn dev
108 | ```
109 |
110 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
111 |
112 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
113 |
114 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
115 |
116 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
117 |
118 | ## Learn More
119 |
120 | To learn more about Next.js, take a look at the following resources:
121 |
122 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
123 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
124 |
125 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
126 |
127 | ## Deploy on Vercel
128 |
129 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
130 |
131 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
132 |
--------------------------------------------------------------------------------
/context/SidebarContext.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'
2 |
3 | // create context
4 |
5 | interface IScrollY {
6 | id: string | null
7 | position: number
8 | }
9 | interface ISidebarContext {
10 | isSidebarOpen: boolean
11 | scrollY: IScrollY
12 | closeSidebar: () => void
13 | toggleSidebar: () => void
14 | saveScroll: (el: HTMLElement | null) => void
15 | }
16 |
17 | const SidebarContext = React.createContext(
18 | {
19 | isSidebarOpen: false,
20 | scrollY: {id: null, position: 0},
21 | closeSidebar: () => {},
22 | toggleSidebar: () => {},
23 | saveScroll: (el: HTMLElement | null) => {}
24 | }
25 | );
26 |
27 | interface ISidebarPovider{ children: React.ReactNode }
28 |
29 | export const SidebarProvider = ({ children }: ISidebarPovider) => {
30 | const [isSidebarOpen, setIsSidebarOpen] = useState(false)
31 |
32 | function toggleSidebar() {
33 | setIsSidebarOpen(!isSidebarOpen)
34 | }
35 |
36 | function closeSidebar() {
37 | setIsSidebarOpen(false)
38 | }
39 |
40 | const defaultScrollY = useMemo(() => {
41 | return {id: null, position: 0}
42 | }, [])
43 |
44 | const storageScrollY = useCallback(() => {
45 | return JSON.parse(localStorage.getItem('sidebarScrollY') || JSON.stringify(defaultScrollY))
46 | }, [defaultScrollY])
47 |
48 | const [scrollY, setScrollY] = useState(
49 | process.browser ? storageScrollY() : defaultScrollY
50 | )
51 |
52 | function saveScroll(el: HTMLElement | null) {
53 | const id = el?.id || null
54 | const position = el?.scrollTop || 0
55 | setScrollY({id, position})
56 | }
57 |
58 | useEffect(() => {
59 | if (process.browser) {
60 | localStorage.setItem('sidebarScrollY', JSON.stringify(scrollY))
61 | }
62 | }, [scrollY])
63 |
64 | useLayoutEffect(() => {
65 | if (process.browser) {
66 | const { id, position } = storageScrollY()
67 | document.getElementById(id)?.scrollTo(0, position)
68 |
69 | if (isSidebarOpen) {
70 | document.getElementById(id)?.scrollTo(0, position)
71 | }
72 | }
73 | }, [scrollY, storageScrollY, isSidebarOpen])
74 |
75 | const context = {
76 | isSidebarOpen,
77 | scrollY,
78 | toggleSidebar,
79 | closeSidebar,
80 | saveScroll,
81 | }
82 |
83 | return {children}
84 | }
85 |
86 | export default SidebarContext
87 |
--------------------------------------------------------------------------------
/context/ThemeContext.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef, useLayoutEffect } from 'react'
2 |
3 | /**
4 | * Saves the old theme for future use
5 | * @param {string} theme - Name of curent theme
6 | * @return {string} previousTheme
7 | */
8 | function usePrevious(theme: string) {
9 | const ref = useRef()
10 | useEffect(() => {
11 | ref.current = theme
12 | })
13 | return ref.current
14 | }
15 |
16 | /**
17 | * Gets user preferences from local storage
18 | * @param {string} key - localStorage key
19 | * @return {array} getter and setter for user preferred theme
20 | */
21 | function useStorageTheme(key: string): [string, React.Dispatch>]{
22 | const userPreference =
23 | !!window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
24 |
25 | const [theme, setTheme] = useState(
26 | // use stored theme fallback to user preference
27 | localStorage.getItem(key) || userPreference
28 | )
29 |
30 | // update stored theme
31 | useEffect(() => {
32 | localStorage.setItem(key.toString(), theme.toString())
33 | }, [theme, key])
34 |
35 | return [theme.toString(), setTheme]
36 | }
37 |
38 | interface IThemeContext{
39 | theme: string | React.Dispatch>
40 | toggleTheme: () => void
41 | }
42 |
43 | // create context
44 | export const ThemeContext = React.createContext({ theme: "", toggleTheme: () => {} })
45 |
46 | interface IThemeProvider{
47 | children: React.ReactNode
48 | }
49 |
50 | // create context provider
51 | export const ThemeProvider = ({ children }: IThemeProvider) => {
52 | const [theme, setTheme] = useStorageTheme('theme')
53 |
54 | // update root element class on theme change
55 | const oldTheme = usePrevious(theme.toString())
56 | useLayoutEffect(() => {
57 | document.documentElement.classList.remove(`theme-${oldTheme}`)
58 | document.documentElement.classList.add(`theme-${theme}`)
59 | }, [theme, oldTheme])
60 |
61 | function toggleTheme() {
62 |
63 | if (theme === 'light'){
64 | setTheme('dark')
65 | }
66 | else{
67 | setTheme('light')
68 | }
69 | }
70 |
71 | const context = {
72 | theme,
73 | toggleTheme,
74 | }
75 |
76 | return {children}
77 | }
--------------------------------------------------------------------------------
/example/components/AccessibleNavigationAnnouncer.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useRouter } from 'next/router'
3 |
4 | function AccessibleNavigationAnnouncer() {
5 | const [message, setMessage] = useState('')
6 | const router = useRouter()
7 |
8 | useEffect(() => {
9 | // ignore the /
10 | if (router.pathname.slice(1)) {
11 | // make sure navigation has occurred and screen reader is ready
12 | setTimeout(() => setMessage(`Navigated to ${router.pathname.slice(1)} page.`), 500)
13 | } else {
14 | setMessage('')
15 | }
16 | }, [router])
17 |
18 | return (
19 |
20 | {message}
21 |
22 | )
23 | }
24 |
25 | export default AccessibleNavigationAnnouncer
26 |
--------------------------------------------------------------------------------
/example/components/CTA.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function CTA() {
4 | return (
5 |
9 |
10 |
11 |
12 |
13 |
Star this project on GitHub
14 |
15 |
16 | View more
17 |
18 |
19 | )
20 | }
21 |
22 | export default CTA
23 |
--------------------------------------------------------------------------------
/example/components/Cards/InfoCard.tsx:
--------------------------------------------------------------------------------
1 | import { ReactSVGElement } from 'react'
2 | import { Card, CardBody } from '@roketid/windmill-react-ui'
3 |
4 | interface IInfoCard{
5 | title: string
6 | value: string
7 | children?: ReactSVGElement
8 | }
9 |
10 | function InfoCard({ title, value, children }: IInfoCard) {
11 | return (
12 |
13 |
14 | {children}
15 |
16 |
{title}
17 |
{value}
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | export default InfoCard
25 |
--------------------------------------------------------------------------------
/example/components/Chart/ChartCard.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface IChart{
4 | children: React.ReactNode
5 | title: string
6 | }
7 |
8 | function Chart({ children, title }: IChart) {
9 | return (
10 |
11 |
{title}
12 | {children}
13 |
14 | )
15 | }
16 |
17 | export default Chart
18 |
--------------------------------------------------------------------------------
/example/components/Chart/ChartLegend.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {ILegends} from 'utils/demo/chartsData'
4 |
5 | interface IChartLegend{
6 | legends: ILegends[]
7 | }
8 |
9 | function ChartLegend({ legends }: IChartLegend) {
10 | return (
11 |
12 | {legends.map((legend) => (
13 |
14 |
15 | {legend.title}
16 |
17 | ))}
18 |
19 | )
20 | }
21 |
22 | export default ChartLegend
23 |
--------------------------------------------------------------------------------
/example/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import SidebarContext from 'context/SidebarContext'
3 | import {
4 | SearchIcon,
5 | MoonIcon,
6 | SunIcon,
7 | BellIcon,
8 | MenuIcon,
9 | OutlinePersonIcon,
10 | OutlineCogIcon,
11 | OutlineLogoutIcon,
12 | } from 'icons'
13 | import { Avatar, Badge, Input, Dropdown, DropdownItem, WindmillContext } from '@roketid/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 |
--------------------------------------------------------------------------------
/example/components/Loader/Loader.module.css:
--------------------------------------------------------------------------------
1 | .LoaderDots div {
2 | animation-timing-function: cubic-bezier(0, 1, 1, 0);
3 | }
4 | .LoaderDots div:nth-child(1) {
5 | left: 8px;
6 | animation: LoaderDots1 0.6s infinite;
7 | }
8 | .LoaderDots div:nth-child(2) {
9 | left: 8px;
10 | animation: LoaderDots2 0.6s infinite;
11 | }
12 | .LoaderDots div:nth-child(3) {
13 | left: 32px;
14 | animation: LoaderDots2 0.6s infinite;
15 | }
16 | .LoaderDots div:nth-child(4) {
17 | left: 56px;
18 | animation: LoaderDots3 0.6s infinite;
19 | }
20 | @keyframes LoaderDots1 {
21 | 0% {
22 | transform: scale(0);
23 | }
24 | 100% {
25 | transform: scale(1);
26 | }
27 | }
28 | @keyframes LoaderDots3 {
29 | 0% {
30 | transform: scale(1);
31 | }
32 | 100% {
33 | transform: scale(0);
34 | }
35 | }
36 | @keyframes LoaderDots2 {
37 | 0% {
38 | transform: translate(0, 0);
39 | }
40 | 100% {
41 | transform: translate(24px, 0);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/example/components/Loader/Loader.tsx:
--------------------------------------------------------------------------------
1 | import styles from './Loader.module.css'
2 |
3 | function Loader() {
4 | return (
5 |
6 |
7 |
8 |
14 |
15 | Please wait...
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default Loader
24 |
--------------------------------------------------------------------------------
/example/components/RoundIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classNames from 'classnames'
3 |
4 | interface IRoundIcon{
5 | icon: any
6 | className: string
7 | iconColorClass: string
8 | bgColorClass: string
9 | }
10 |
11 | function RoundIcon({
12 | icon,
13 | iconColorClass = 'text-purple-600 dark:text-purple-100',
14 | bgColorClass = 'bg-purple-100 dark:bg-purple-600',
15 | className
16 | }: IRoundIcon) {
17 | const baseStyle = 'p-3 rounded-full'
18 | const cls = classNames(baseStyle, iconColorClass, bgColorClass, className)
19 | return(
20 |
21 | {/* */}
22 |
23 | )
24 | }
25 |
26 | export default RoundIcon
27 |
--------------------------------------------------------------------------------
/example/components/Sidebar/DesktopSidebar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useRef } from 'react'
2 | import SidebarContext from 'context/SidebarContext'
3 | import SidebarContent from './SidebarContent'
4 |
5 | function DesktopSidebar() {
6 | const sidebarRef = useRef(null)
7 | const { saveScroll } = useContext(SidebarContext)
8 |
9 | const linkClickedHandler = () => {
10 | saveScroll(sidebarRef.current)
11 | }
12 |
13 | return (
14 |
21 | )
22 | }
23 |
24 | export default DesktopSidebar
25 |
--------------------------------------------------------------------------------
/example/components/Sidebar/MobileSidebar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useRef } from 'react'
2 | import { Transition, Backdrop } from '@roketid/windmill-react-ui'
3 | import SidebarContext from 'context/SidebarContext'
4 | import SidebarContent from './SidebarContent'
5 |
6 |
7 | function MobileSidebar() {
8 | const sidebarRef = useRef(null)
9 | const { isSidebarOpen, closeSidebar, saveScroll } = useContext(SidebarContext)
10 |
11 | const linkClickedHandler = () => {
12 | saveScroll(sidebarRef.current)
13 | }
14 |
15 | return (
16 |
17 | <>
18 |
26 |
27 |
28 |
29 |
37 |
44 |
45 | >
46 |
47 | )
48 | }
49 |
50 | export default MobileSidebar
51 |
--------------------------------------------------------------------------------
/example/components/Sidebar/SidebarContent.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import routes, { routeIsActive } from 'routes/sidebar'
3 | import * as Icons from 'icons'
4 | import { IIcon } from 'icons'
5 | import SidebarSubmenu from './SidebarSubmenu'
6 | import { Button } from '@roketid/windmill-react-ui'
7 | import { useRouter } from 'next/router'
8 |
9 | function Icon({ icon, ...props }: IIcon){
10 | // @ts-ignore
11 | const Icon= Icons[icon]
12 | return
13 | }
14 |
15 | interface ISidebarContent{
16 | linkClicked: () => void
17 | }
18 |
19 | function SidebarContent({ linkClicked }: ISidebarContent) {
20 | const { pathname } = useRouter();
21 | const appName = process.env.NEXT_PUBLIC_APP_NAME
22 |
23 | return (
24 |
25 |
26 |
33 |
34 |
71 |
72 |
73 | Create account
74 |
75 | +
76 |
77 |
78 |
79 |
80 | )
81 | }
82 |
83 | export default SidebarContent
--------------------------------------------------------------------------------
/example/components/Sidebar/SidebarSubmenu.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react'
2 | import Link from 'next/link'
3 | import { useRouter } from 'next/router'
4 | import { DropdownIcon, IIcon } from 'icons'
5 | import * as Icons from 'icons'
6 | import { Transition } from '@roketid/windmill-react-ui'
7 | import { IRoute, routeIsActive } from 'routes/sidebar'
8 | import SidebarContext from 'context/SidebarContext'
9 |
10 | function Icon({ icon, ...props }: IIcon) {
11 | // @ts-ignore
12 | const _Icon = Icons[icon]
13 | return <_Icon {...props} />
14 | }
15 |
16 | interface ISidebarSubmenu {
17 | route: IRoute
18 | linkClicked: () => void
19 | }
20 |
21 | function SidebarSubmenu({ route, linkClicked }: ISidebarSubmenu) {
22 | const { pathname } = useRouter()
23 | const { saveScroll } = useContext(SidebarContext)
24 |
25 | const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(
26 | route.routes
27 | ? route.routes.filter((r) => {
28 | return routeIsActive(pathname, r)
29 | }).length > 0
30 | : false
31 | )
32 |
33 | function handleDropdownMenuClick() {
34 | setIsDropdownMenuOpen(!isDropdownMenuOpen)
35 | }
36 |
37 | return (
38 |
39 | {isDropdownMenuOpen && (
40 |
44 | )}
45 |
54 |
55 |
56 |
57 | {route.name}
58 |
59 |
60 |
61 |
70 |
74 | {
75 | route.routes && route.routes.map((r) => (
76 |
80 |
84 |
92 | {r.name}
93 |
94 |
95 |
96 | ))
97 | }
98 |
99 |
100 |
101 | )
102 | }
103 |
104 | export default SidebarSubmenu
105 |
--------------------------------------------------------------------------------
/example/components/Sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import DesktopSidebar from './DesktopSidebar'
3 | import MobileSidebar from './MobileSidebar'
4 |
5 | function Sidebar() {
6 | return (
7 | <>
8 |
9 |
10 | >
11 | )
12 | }
13 |
14 | export default Sidebar
15 |
--------------------------------------------------------------------------------
/example/components/ThemedSuspense.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function ThemedSuspense() {
4 | return (
5 |
6 | Loading...
7 |
8 | )
9 | }
10 |
11 | export default ThemedSuspense
12 |
--------------------------------------------------------------------------------
/example/components/Typography/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface IPageTitle{
4 | children: React.ReactNode
5 | }
6 |
7 | function PageTitle({ children }: IPageTitle) {
8 | return (
9 | {children}
10 | )
11 | }
12 |
13 | export default PageTitle
14 |
--------------------------------------------------------------------------------
/example/components/Typography/SectionTitle.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface ISectionTitle{
4 | children: React.ReactNode
5 | }
6 |
7 | function SectionTitle({ children }: ISectionTitle) {
8 | return {children}
9 | }
10 |
11 | export default SectionTitle
12 |
--------------------------------------------------------------------------------
/example/containers/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import SidebarContext, { SidebarProvider } from 'context/SidebarContext'
3 | import Sidebar from 'example/components/Sidebar'
4 | import Header from 'example/components/Header'
5 | import Main from './Main'
6 |
7 | interface ILayout{
8 | children: React.ReactNode
9 | }
10 |
11 | function Layout({ children }: ILayout) {
12 | const { isSidebarOpen } = useContext(SidebarContext)
13 |
14 | return
15 |
18 |
19 |
20 |
21 |
22 | {children}
23 |
24 |
25 |
26 |
27 | }
28 |
29 | export default Layout
--------------------------------------------------------------------------------
/example/containers/Main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface IMain{
4 | children: React.ReactNode
5 | }
6 |
7 | function Main({ children }: IMain) {
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | }
14 |
15 | export default Main
16 |
--------------------------------------------------------------------------------
/icons/bell.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/icons/buttons.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/cards.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/cart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/icons/charts.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/icons/chat.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/dropdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/icons/forbidden.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/forms.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/icons/heart.svg:
--------------------------------------------------------------------------------
1 |
6 |
11 |
--------------------------------------------------------------------------------
/icons/home.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/index.ts:
--------------------------------------------------------------------------------
1 | import ButtonsIcon from './buttons.svg'
2 | import CardsIcon from './cards.svg'
3 | import ChartsIcon from './charts.svg'
4 | import FormsIcon from './forms.svg'
5 | import HomeIcon from './home.svg'
6 | import ModalsIcon from './modals.svg'
7 | import PagesIcon from './pages.svg'
8 | import TablesIcon from './tables.svg'
9 | import HeartIcon from './heart.svg'
10 | import EditIcon from './edit.svg'
11 | import TrashIcon from './trash.svg'
12 | import ForbiddenIcon from './forbidden.svg'
13 | import GithubIcon from './github.svg'
14 | import TwitterIcon from './twitter.svg'
15 | import MailIcon from './mail.svg'
16 | import CartIcon from './cart.svg'
17 | import ChatIcon from './chat.svg'
18 | import MoneyIcon from './money.svg'
19 | import PeopleIcon from './people.svg'
20 | import SearchIcon from './search.svg'
21 | import MoonIcon from './moon.svg'
22 | import SunIcon from './sun.svg'
23 | import BellIcon from './bell.svg'
24 | import MenuIcon from './menu.svg'
25 | import DropdownIcon from './dropdown.svg'
26 | import OutlinePersonIcon from './outlinePerson.svg'
27 | import OutlineCogIcon from './outlineCog.svg'
28 | import 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 |
61 | interface IIcon{
62 | icon: string
63 | [key: string]: string | undefined
64 | }
65 |
66 | export type {
67 | IIcon
68 | };
--------------------------------------------------------------------------------
/icons/mail.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/modals.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/money.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/moon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/icons/outlineCog.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/icons/outlineLogout.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/outlinePerson.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/pages.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/people.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/sun.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/icons/tables.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
--------------------------------------------------------------------------------
/icons/trash.svg:
--------------------------------------------------------------------------------
1 |
5 |
10 |
--------------------------------------------------------------------------------
/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // NOTE: This file should not be edited
6 | // see https://nextjs.org/docs/basic-features/typescript for more information.
7 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | module.exports = {
3 | reactStrictMode: false,
4 | webpack(config) {
5 | config.module.rules.push({
6 | test: /\.svg$/i,
7 | issuer: /\.[jt]sx?$/,
8 | use: ['@svgr/webpack'],
9 | })
10 |
11 | return config
12 | },
13 | async redirects() {
14 | return [
15 | {
16 | source: '/',
17 | destination: '/example/login',
18 | permanent: false,
19 | },
20 | ]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "windmill-dashboard-nextjs-typescript",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "chart.js": "^3.6.1",
13 | "classnames": "^2.3.1",
14 | "next": "11.1.2",
15 | "react": "17.0.2",
16 | "react-chartjs-2": "^4.0.0",
17 | "react-dom": "17.0.2"
18 | },
19 | "devDependencies": {
20 | "@roketid/windmill-react-ui": "^0.1.1",
21 | "@svgr/webpack": "^6.1.0",
22 | "@types/react": "17.0.21",
23 | "autoprefixer": "^10.4.2",
24 | "cssnano": "^5.0.12",
25 | "eslint": "7.32.0",
26 | "eslint-config-next": "11.1.2",
27 | "postcss": "^8.4.6",
28 | "tailwindcss": "^3.0.22",
29 | "typescript": "4.4.3"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 | import 'tailwindcss/tailwind.css';
3 |
4 | import React from 'react'
5 | import { Windmill } from '@roketid/windmill-react-ui'
6 | import type { AppProps } from 'next/app'
7 |
8 | function MyApp({ Component, pageProps }: AppProps) {
9 | // suppress useLayoutEffect warnings when running outside a browser
10 | if (!process.browser) React.useLayoutEffect = React.useEffect;
11 |
12 | return (
13 |
14 |
15 |
16 | )
17 | }
18 | export default MyApp
19 |
--------------------------------------------------------------------------------
/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from 'next'
3 |
4 | type Data = {
5 | name: string
6 | }
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse
11 | ) {
12 | res.status(200).json({ name: 'John Doe' })
13 | }
14 |
--------------------------------------------------------------------------------
/pages/example/404.tsx:
--------------------------------------------------------------------------------
1 | import Layout from 'example/containers/Layout'
2 | import { ForbiddenIcon } from 'icons'
3 |
4 | function Page404() {
5 | return (
6 |
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 |
22 | export default Page404
23 |
--------------------------------------------------------------------------------
/pages/example/blank.tsx:
--------------------------------------------------------------------------------
1 | import PageTitle from 'example/components/Typography/PageTitle'
2 | import Layout from 'example/containers/Layout'
3 |
4 | function Blank() {
5 | return (
6 |
7 | Blank
8 |
9 | )
10 | }
11 |
12 | export default Blank
13 |
--------------------------------------------------------------------------------
/pages/example/buttons.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button } from '@roketid/windmill-react-ui'
3 | import PageTitle from 'example/components/Typography/PageTitle'
4 | import SectionTitle from 'example/components/Typography/SectionTitle'
5 | import CTA from 'example/components/CTA'
6 | import Layout from 'example/containers/Layout'
7 | import { HeartIcon, EditIcon } from 'icons'
8 |
9 | function Buttons() {
10 | return (
11 |
12 | Buttons
13 |
14 |
15 |
16 | Primary
17 |
18 |
19 | Larger Button
20 |
21 |
22 |
23 | Large Button
24 |
25 |
26 |
27 | Regular
28 |
29 |
30 | {/*
31 |
32 | Router Link
33 |
34 |
*/}
35 |
36 |
37 | Disabled
38 |
39 |
40 |
41 | Small
42 |
43 |
44 |
45 | Outline
46 |
47 |
48 |
49 | Larger Button
50 |
51 |
52 |
53 |
54 |
55 | Large Button
56 |
57 |
58 |
59 |
60 | Regular
61 |
62 |
63 | {/*
64 |
65 | Router Link
66 |
67 |
*/}
68 |
69 |
70 |
71 | Disabled
72 |
73 |
74 |
75 |
76 |
77 | Small
78 |
79 |
80 |
81 |
82 | Link
83 |
84 |
85 |
86 | Larger Button
87 |
88 |
89 |
90 |
91 |
92 | Large Button
93 |
94 |
95 |
96 |
97 | Regular
98 |
99 |
100 | {/*
101 |
102 | Router Link
103 |
104 |
*/}
105 |
106 |
107 |
108 | Disabled
109 |
110 |
111 |
112 |
113 |
114 | Small
115 |
116 |
117 |
118 |
119 | Icons
120 |
121 |
122 | {/* @ts-ignore */}
123 |
124 | Icon right
125 |
126 |
127 |
128 |
129 | {/* @ts-ignore */}
130 |
131 | Icon Left
132 |
133 |
134 |
135 |
136 | {/* @ts-ignore */}
137 |
138 |
139 |
140 |
141 | {/* @ts-ignore */}
142 |
143 |
144 |
145 |
146 | {/* @ts-ignore */}
147 |
148 |
149 |
150 | {/* @ts-ignore */}
151 |
152 |
153 |
154 |
155 | )
156 | }
157 |
158 | export default Buttons
159 |
--------------------------------------------------------------------------------
/pages/example/cards.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Card, CardBody } from '@roketid/windmill-react-ui'
4 | import PageTitle from 'example/components/Typography/PageTitle'
5 | import SectionTitle from 'example/components/Typography/SectionTitle'
6 | import CTA from 'example/components/CTA'
7 | import InfoCard from 'example/components/Cards/InfoCard'
8 | import RoundIcon from 'example/components/RoundIcon'
9 | import Layout from 'example/containers/Layout'
10 | import { CartIcon, ChatIcon, MoneyIcon, PeopleIcon } from 'icons'
11 |
12 | function Cards() {
13 | return (
14 |
15 | Cards
16 |
17 |
18 |
19 | Big section cards
20 |
21 |
22 |
23 |
24 | Large, full width sections goes here
25 |
26 |
27 |
28 |
29 | Responsive cards
30 |
31 |
32 |
33 | {/* @ts-ignore */}
34 |
40 |
41 |
42 |
43 | {/* @ts-ignore */}
44 |
50 |
51 |
52 |
53 | {/* @ts-ignore */}
54 |
60 |
61 |
62 |
63 | {/* @ts-ignore */}
64 |
70 |
71 |
72 |
73 | Cards with title
74 |
75 |
76 |
77 |
78 | Revenue
79 |
80 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fuga, cum commodi a omnis
81 | numquam quod? Totam exercitationem quos hic ipsam at qui cum numquam, sed amet
82 | ratione! Ratione, nihil dolorum.
83 |
84 |
85 |
86 |
87 |
88 |
89 | Colored card
90 |
91 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fuga, cum commodi a omnis
92 | numquam quod? Totam exercitationem quos hic ipsam at qui cum numquam, sed amet
93 | ratione! Ratione, nihil dolorum.
94 |
95 |
96 |
97 |
98 |
99 | )
100 | }
101 |
102 | export default Cards
103 |
--------------------------------------------------------------------------------
/pages/example/charts.tsx:
--------------------------------------------------------------------------------
1 | import { Doughnut, Line, Bar } from 'react-chartjs-2'
2 | import ChartCard from 'example/components/Chart/ChartCard'
3 | import ChartLegend from 'example/components/Chart/ChartLegend'
4 | import PageTitle from 'example/components/Typography/PageTitle'
5 | import Layout from 'example/containers/Layout'
6 | import {
7 | doughnutOptions,
8 | lineOptions,
9 | barOptions,
10 | doughnutLegends,
11 | lineLegends,
12 | barLegends,
13 | } from 'utils/demo/chartsData'
14 | import {
15 | Chart,
16 | ArcElement,
17 | BarElement,
18 | CategoryScale,
19 | LinearScale,
20 | PointElement,
21 | LineElement,
22 | Title,
23 | Tooltip,
24 | Legend,
25 | } from 'chart.js'
26 |
27 | function Charts() {
28 | Chart.register(
29 | ArcElement,
30 | BarElement,
31 | CategoryScale,
32 | LinearScale,
33 | PointElement,
34 | LineElement,
35 | Title,
36 | Tooltip,
37 | Legend
38 | )
39 |
40 | return (
41 |
42 | Charts
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | )
62 | }
63 |
64 | export default Charts
65 |
--------------------------------------------------------------------------------
/pages/example/create-account.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import Link from 'next/link'
3 | import Image from 'next/image'
4 |
5 | import { GithubIcon, TwitterIcon } from 'icons'
6 | import { Input, Label, Button, WindmillContext } from '@roketid/windmill-react-ui'
7 |
8 | function CrateAccount() {
9 | const { mode } = useContext(WindmillContext)
10 | const imgSource = mode === 'dark' ? '/assets/img/create-account-office-dark.jpeg' : '/assets/img/create-account-office.jpeg'
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
28 | Create account
29 |
30 |
31 | Email
32 |
33 |
34 |
35 | Password
36 |
37 |
38 |
39 | Confirm password
40 |
41 |
42 |
43 |
44 |
45 |
46 | I agree to the privacy policy
47 |
48 |
49 |
50 |
54 |
55 | Create account
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Github
64 |
65 |
66 |
67 | Twitter
68 |
69 |
70 |
71 |
72 |
75 | Already have an account? Login
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | )
85 | }
86 |
87 | export default CrateAccount
88 |
--------------------------------------------------------------------------------
/pages/example/forgot-password.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import Image from 'next/image'
3 | import Link from 'next/link'
4 |
5 | import { Label, Input, Button, WindmillContext } from '@roketid/windmill-react-ui'
6 |
7 | function ForgotPassword() {
8 | const { mode } = useContext(WindmillContext)
9 | const imgSource = mode === 'dark' ? '/assets/img/forgot-password-office-dark.jpeg' : '/assets/img/forgot-password-office.jpeg'
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
23 |
24 |
25 |
26 |
27 | Forgot password
28 |
29 |
30 |
31 | Email
32 |
33 |
34 |
35 |
36 |
37 | Recover password
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
48 | export default ForgotPassword
49 |
--------------------------------------------------------------------------------
/pages/example/forms.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Input, HelperText, Label, Select, Textarea } from '@roketid/windmill-react-ui'
4 | import CTA from 'example/components/CTA'
5 | import PageTitle from 'example/components/Typography/PageTitle'
6 | import SectionTitle from 'example/components/Typography/SectionTitle'
7 |
8 | import Layout from 'example/containers/Layout'
9 | import { MailIcon } from 'icons'
10 |
11 | function Forms() {
12 | return (
13 |
14 | Forms
15 |
16 | Elements
17 |
18 |
82 |
83 | Validation
84 |
85 |
86 |
87 | Invalid input
88 |
89 | Your password is too short.
90 |
91 |
92 |
93 | Valid input
94 |
95 | Your password is strong.
96 |
97 |
98 |
99 | Helper text
100 |
101 | Your password must be at least 6 characters long.
102 |
103 |
104 |
105 | {/* */}
106 | Icons
107 |
108 |
109 |
110 | Icon left
111 | {/* */}
112 |
113 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | Icon right
125 | {/* */}
126 |
127 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | {/* */}
139 | Buttons
140 |
141 |
168 |
169 | )
170 | }
171 |
172 | export default Forms
173 |
--------------------------------------------------------------------------------
/pages/example/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Doughnut, Line } from 'react-chartjs-2'
3 |
4 | import CTA from 'example/components/CTA'
5 | import InfoCard from 'example/components/Cards/InfoCard'
6 | import ChartCard from 'example/components/Chart/ChartCard'
7 | import ChartLegend from 'example/components/Chart/ChartLegend'
8 | import PageTitle from 'example/components/Typography/PageTitle'
9 | import RoundIcon from 'example/components/RoundIcon'
10 | import Layout from 'example/containers/Layout'
11 | import response, { ITableData } from 'utils/demo/tableData'
12 | import { ChatIcon, CartIcon, MoneyIcon, PeopleIcon } from 'icons'
13 |
14 | import {
15 | TableBody,
16 | TableContainer,
17 | Table,
18 | TableHeader,
19 | TableCell,
20 | TableRow,
21 | TableFooter,
22 | Avatar,
23 | Badge,
24 | Pagination,
25 | } from '@roketid/windmill-react-ui'
26 |
27 | import {
28 | doughnutOptions,
29 | lineOptions,
30 | doughnutLegends,
31 | lineLegends,
32 | } from 'utils/demo/chartsData'
33 |
34 | import {
35 | Chart,
36 | ArcElement,
37 | CategoryScale,
38 | LinearScale,
39 | PointElement,
40 | LineElement,
41 | Title,
42 | Tooltip,
43 | Legend,
44 | } from 'chart.js'
45 |
46 | function Dashboard() {
47 | Chart.register(
48 | ArcElement,
49 | CategoryScale,
50 | LinearScale,
51 | PointElement,
52 | LineElement,
53 | Title,
54 | Tooltip,
55 | Legend
56 | )
57 |
58 | const [page, setPage] = useState(1)
59 | const [data, setData] = useState([])
60 |
61 | // pagination setup
62 | const resultsPerPage = 10
63 | const totalResults = response.length
64 |
65 | // pagination change control
66 | function onPageChange(p: number) {
67 | setPage(p)
68 | }
69 |
70 | // on page change, load new sliced data
71 | // here you would make another server request for new data
72 | useEffect(() => {
73 | setData(response.slice((page - 1) * resultsPerPage, page * resultsPerPage))
74 | }, [page])
75 |
76 | return (
77 |
78 | Dashboard
79 |
80 |
81 |
82 | {/* */}
83 |
84 |
85 | {/* @ts-ignore */}
86 |
92 |
93 |
94 |
95 | {/* @ts-ignore */}
96 |
102 |
103 |
104 |
105 | {/* @ts-ignore */}
106 |
112 |
113 |
114 |
115 | {/* @ts-ignore */}
116 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | Client
130 | Amount
131 | Status
132 | Date
133 |
134 |
135 |
136 | {data.map((user, i) => (
137 |
138 |
139 |
140 |
145 |
146 |
{user.name}
147 |
148 | {user.job}
149 |
150 |
151 |
152 |
153 |
154 | $ {user.amount}
155 |
156 |
157 | {user.status}
158 |
159 |
160 |
161 | {new Date(user.date).toLocaleDateString()}
162 |
163 |
164 |
165 | ))}
166 |
167 |
168 |
169 |
175 |
176 |
177 |
178 | Charts
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 | )
192 | }
193 |
194 | export default Dashboard
195 |
--------------------------------------------------------------------------------
/pages/example/login.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import Link from 'next/link'
3 | import Image from 'next/image'
4 |
5 | import { Label, Input, Button, WindmillContext } from '@roketid/windmill-react-ui'
6 | import { GithubIcon, TwitterIcon } from 'icons'
7 |
8 | function LoginPage() {
9 | const { mode } = useContext(WindmillContext)
10 | const imgSource = mode === 'dark' ? '/assets/img/login-office-dark.jpeg' : '/assets/img/login-office.jpeg'
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
28 | Login
29 |
30 |
31 | Email
32 |
37 |
38 |
39 |
40 | Password
41 |
46 |
47 |
48 |
49 |
50 | Log in
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Github
59 |
60 |
61 |
62 | Twitter
63 |
64 |
65 |
66 |
67 |
68 | Forgot your password?
69 |
70 |
71 |
72 |
73 |
74 |
75 | Create account
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | );
85 | }
86 |
87 | export default LoginPage
88 |
--------------------------------------------------------------------------------
/pages/example/modals.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 |
3 | import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from '@roketid/windmill-react-ui'
4 | import PageTitle from 'example/components/Typography/PageTitle'
5 | import CTA from 'example/components/CTA'
6 | import Layout from 'example/containers/Layout'
7 |
8 | function Modals() {
9 | const [isModalOpen, setIsModalOpen] = useState(false)
10 |
11 | function openModal() {
12 | setIsModalOpen(true)
13 | }
14 |
15 | function closeModal() {
16 | setIsModalOpen(false)
17 | }
18 |
19 | return (
20 |
21 | Modals
22 |
23 |
24 |
25 | Open modal
26 |
27 |
28 |
29 | Modal header
30 |
31 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nostrum et eligendi repudiandae
32 | voluptatem tempore!
33 |
34 |
35 | {/* I don't like this approach. Consider passing a prop to ModalFooter
36 | * that if present, would duplicate the buttons in a way similar to this.
37 | * Or, maybe find some way to pass something like size="large md:regular"
38 | * to Button
39 | */}
40 |
41 |
42 | Cancel
43 |
44 |
45 |
46 | Accept
47 |
48 |
49 |
50 | Cancel
51 |
52 |
53 |
54 |
55 | Accept
56 |
57 |
58 |
59 |
60 |
61 | )
62 | }
63 |
64 | export default Modals
65 |
--------------------------------------------------------------------------------
/pages/example/tables.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 |
3 | import PageTitle from 'example/components/Typography/PageTitle'
4 | import SectionTitle from 'example/components/Typography/SectionTitle'
5 | import CTA from 'example/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 '@roketid/windmill-react-ui'
19 | import { EditIcon, TrashIcon } from 'icons'
20 |
21 | import response, { ITableData } from 'utils/demo/tableData'
22 | import Layout from 'example/containers/Layout'
23 | // make a copy of the data, for the second table
24 | const response2 = response.concat([])
25 |
26 | function Tables() {
27 | /**
28 | * DISCLAIMER: This code could be badly improved, but for the sake of the example
29 | * and readability, all the logic for both table are here.
30 | * You would be better served by dividing each table in its own
31 | * component, like Table(?) and TableWithActions(?) hiding the
32 | * presentation details away from the page view.
33 | */
34 |
35 | // setup pages control for every table
36 | const [pageTable1, setPageTable1] = useState(1)
37 | const [pageTable2, setPageTable2] = useState(1)
38 |
39 | // setup data for every table
40 | const [dataTable1, setDataTable1] = useState([])
41 | const [dataTable2, setDataTable2] = useState([])
42 |
43 | // pagination setup
44 | const resultsPerPage = 10
45 | const totalResults = response.length
46 |
47 | // pagination change control
48 | function onPageChangeTable1(p: number) {
49 | setPageTable1(p)
50 | }
51 |
52 | // pagination change control
53 | function onPageChangeTable2(p: number) {
54 | setPageTable2(p)
55 | }
56 |
57 | // on page change, load new sliced data
58 | // here you would make another server request for new data
59 | useEffect(() => {
60 | setDataTable1(response.slice((pageTable1 - 1) * resultsPerPage, pageTable1 * resultsPerPage))
61 | }, [pageTable1])
62 |
63 | // on page change, load new sliced data
64 | // here you would make another server request for new data
65 | useEffect(() => {
66 | setDataTable2(response2.slice((pageTable2 - 1) * resultsPerPage, pageTable2 * resultsPerPage))
67 | }, [pageTable2])
68 |
69 | return (
70 |
71 | Tables
72 |
73 |
74 |
75 | Simple table
76 |
77 |
78 |
79 |
80 | Client
81 | Amount
82 | Status
83 | Date
84 |
85 |
86 |
87 | {dataTable1.map((user, i) => (
88 |
89 |
90 |
91 |
92 |
93 |
{user.name}
94 |
{user.job}
95 |
96 |
97 |
98 |
99 | $ {user.amount}
100 |
101 |
102 | {user.status}
103 |
104 |
105 | {new Date(user.date).toLocaleDateString()}
106 |
107 |
108 | ))}
109 |
110 |
111 |
112 |
118 |
119 |
120 |
121 | Table with actions
122 |
123 |
124 |
125 |
126 | Client
127 | Amount
128 | Status
129 | Date
130 | Actions
131 |
132 |
133 |
134 | {dataTable2.map((user, i) => (
135 |
136 |
137 |
138 |
139 |
140 |
{user.name}
141 |
{user.job}
142 |
143 |
144 |
145 |
146 | $ {user.amount}
147 |
148 |
149 | {user.status}
150 |
151 |
152 | {new Date(user.date).toLocaleDateString()}
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | ))}
166 |
167 |
168 |
169 |
175 |
176 |
177 |
178 | )
179 | }
180 |
181 | export default Tables
182 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { NextPage } from 'next'
2 | import Head from 'next/head'
3 | import Image from 'next/image'
4 | import styles from '../styles/Home.module.css'
5 |
6 | const Home: NextPage = () => {
7 | return (
8 |
9 |
10 |
Create Next App
11 |
12 |
13 |
14 |
15 |
16 |
17 | Welcome to Next.js!
18 |
19 |
20 |
21 | Get started by editing{' '}
22 | pages/index.js
23 |
24 |
25 |
54 |
55 |
56 |
68 |
69 | )
70 | }
71 |
72 | export default Home
73 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | cssnano: {
6 | preset: 'default',
7 | },
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/public/assets/img/create-account-office-dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/assets/img/create-account-office-dark.jpeg
--------------------------------------------------------------------------------
/public/assets/img/create-account-office.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/assets/img/create-account-office.jpeg
--------------------------------------------------------------------------------
/public/assets/img/forgot-password-office-dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/assets/img/forgot-password-office-dark.jpeg
--------------------------------------------------------------------------------
/public/assets/img/forgot-password-office.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/assets/img/forgot-password-office.jpeg
--------------------------------------------------------------------------------
/public/assets/img/login-office-dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/assets/img/login-office-dark.jpeg
--------------------------------------------------------------------------------
/public/assets/img/login-office.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/assets/img/login-office.jpeg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roketid/windmill-dashboard-nextjs-typescript/4b26d05839141df2cfb38936842ec48e6be8d610/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/routes/sidebar.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * ⚠ These are used just to render the Sidebar!
3 | * You can include any link here, local or external.
4 | *
5 | */
6 |
7 | interface IRoute{
8 | path?: string
9 | icon?: string
10 | name: string
11 | routes?: IRoute[]
12 | checkActive?(pathname: String, route: IRoute): boolean
13 | exact?: boolean
14 | }
15 |
16 | export function routeIsActive (pathname: String, route: IRoute): boolean {
17 | if (route.checkActive) {
18 | return route.checkActive(pathname, route)
19 | }
20 |
21 | return route?.exact
22 | ? pathname == route?.path
23 | : (route?.path ? pathname.indexOf(route.path) === 0 : false)
24 | }
25 |
26 | const routes: IRoute[] = [
27 | {
28 | path: '/example', // the url
29 | icon: 'HomeIcon', // the component being exported from icons/index.js
30 | name: 'Dashboard', // name that appear in Sidebar
31 | exact: true,
32 | },
33 | {
34 | path: '/example/forms',
35 | icon: 'FormsIcon',
36 | name: 'Forms',
37 | },
38 | {
39 | path: '/example/cards',
40 | icon: 'CardsIcon',
41 | name: 'Cards',
42 | },
43 | {
44 | path: '/example/charts',
45 | icon: 'ChartsIcon',
46 | name: 'Charts',
47 | },
48 | {
49 | path: '/example/buttons',
50 | icon: 'ButtonsIcon',
51 | name: 'Buttons',
52 | },
53 | {
54 | path: '/example/modals',
55 | icon: 'ModalsIcon',
56 | name: 'Modals',
57 | },
58 | {
59 | path: '/example/tables',
60 | icon: 'TablesIcon',
61 | name: 'Tables',
62 | },
63 | {
64 | icon: 'PagesIcon',
65 | name: 'Pages',
66 | routes: [
67 | // submenu
68 | {
69 | path: '/example/login',
70 | name: 'Login',
71 | },
72 | {
73 | path: '/example/create-account',
74 | name: 'Create account',
75 | },
76 | {
77 | path: '/example/forgot-password',
78 | name: 'Forgot password',
79 | },
80 | {
81 | path: '/example/404',
82 | name: '404',
83 | },
84 | {
85 | path: '/example/blank',
86 | name: 'Blank',
87 | },
88 | ],
89 | },
90 | ]
91 |
92 | export type {IRoute}
93 | export default routes
94 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | padding: 0 0.5rem;
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | height: 100vh;
9 | }
10 |
11 | .main {
12 | padding: 5rem 0;
13 | flex: 1;
14 | display: flex;
15 | flex-direction: column;
16 | justify-content: center;
17 | align-items: center;
18 | }
19 |
20 | .footer {
21 | width: 100%;
22 | height: 100px;
23 | border-top: 1px solid #eaeaea;
24 | display: flex;
25 | justify-content: center;
26 | align-items: center;
27 | }
28 |
29 | .footer a {
30 | display: flex;
31 | justify-content: center;
32 | align-items: center;
33 | flex-grow: 1;
34 | }
35 |
36 | .title a {
37 | color: #0070f3;
38 | text-decoration: none;
39 | }
40 |
41 | .title a:hover,
42 | .title a:focus,
43 | .title a:active {
44 | text-decoration: underline;
45 | }
46 |
47 | .title {
48 | margin: 0;
49 | line-height: 1.15;
50 | font-size: 4rem;
51 | }
52 |
53 | .title,
54 | .description {
55 | text-align: center;
56 | }
57 |
58 | .description {
59 | line-height: 1.5;
60 | font-size: 1.5rem;
61 | }
62 |
63 | .code {
64 | background: #fafafa;
65 | border-radius: 5px;
66 | padding: 0.75rem;
67 | font-size: 1.1rem;
68 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
69 | Bitstream Vera Sans Mono, Courier New, monospace;
70 | }
71 |
72 | .grid {
73 | display: flex;
74 | align-items: center;
75 | justify-content: center;
76 | flex-wrap: wrap;
77 | max-width: 800px;
78 | margin-top: 3rem;
79 | }
80 |
81 | .card {
82 | margin: 1rem;
83 | padding: 1.5rem;
84 | text-align: left;
85 | color: inherit;
86 | text-decoration: none;
87 | border: 1px solid #eaeaea;
88 | border-radius: 10px;
89 | transition: color 0.15s ease, border-color 0.15s ease;
90 | width: 45%;
91 | }
92 |
93 | .card:hover,
94 | .card:focus,
95 | .card:active {
96 | color: #0070f3;
97 | border-color: #0070f3;
98 | }
99 |
100 | .card h2 {
101 | margin: 0 0 1rem 0;
102 | font-size: 1.5rem;
103 | }
104 |
105 | .card p {
106 | margin: 0;
107 | font-size: 1.25rem;
108 | line-height: 1.5;
109 | }
110 |
111 | .logo {
112 | height: 1em;
113 | margin-left: 0.5rem;
114 | }
115 |
116 | @media (max-width: 600px) {
117 | .grid {
118 | width: 100%;
119 | flex-direction: column;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme')
2 | const windmill = require('@roketid/windmill-react-ui/config')
3 | module.exports = windmill({
4 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}', './containers/**/*.{js,ts,jsx,tsx}', './example/**/*.{js,ts,jsx,tsx}'],
5 | extend: {},
6 | plugins: [],
7 | })
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "baseUrl": ".",
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/utils/demo/chartsData.ts:
--------------------------------------------------------------------------------
1 | export interface ILegends{
2 | title: string
3 | color: string
4 | }
5 |
6 |
7 | export const doughnutLegends: ILegends[] = [
8 | { title: 'Shirts', color: 'bg-blue-500' },
9 | { title: 'Shoes', color: 'bg-teal-600' },
10 | { title: 'Bags', color: 'bg-purple-600' },
11 | ]
12 |
13 | export const lineLegends: ILegends[] = [
14 | { title: 'Organic', color: 'bg-teal-600' },
15 | { title: 'Paid', color: 'bg-purple-600' },
16 | ]
17 |
18 | export const barLegends: ILegends[] = [
19 | { title: 'Shoes', color: 'bg-teal-600' },
20 | { title: 'Bags', color: 'bg-purple-600' },
21 | ]
22 |
23 | export const doughnutOptions = {
24 | data: {
25 | datasets: [
26 | {
27 | data: [33, 33, 33],
28 | /**
29 | * These colors come from Tailwind CSS palette
30 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette
31 | */
32 | backgroundColor: ['#0694a2', '#1c64f2', '#7e3af2'],
33 | label: 'Dataset 1',
34 | },
35 | ],
36 | labels: ['Shoes', 'Shirts', 'Bags'],
37 | },
38 | options: {
39 | responsive: true,
40 | cutoutPercentage: 80,
41 | },
42 | legend: {
43 | display: false,
44 | },
45 | }
46 |
47 | export const lineOptions = {
48 | data: {
49 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
50 | datasets: [
51 | {
52 | label: 'Organic',
53 | /**
54 | * These colors come from Tailwind CSS palette
55 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette
56 | */
57 | backgroundColor: '#0694a2',
58 | borderColor: '#0694a2',
59 | data: [43, 48, 40, 54, 67, 73, 70],
60 | fill: false,
61 | },
62 | {
63 | label: 'Paid',
64 | fill: false,
65 | /**
66 | * These colors come from Tailwind CSS palette
67 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette
68 | */
69 | backgroundColor: '#7e3af2',
70 | borderColor: '#7e3af2',
71 | data: [24, 50, 64, 74, 52, 51, 65],
72 | },
73 | ],
74 | },
75 | options: {
76 | responsive: true,
77 | tooltips: {
78 | mode: 'index',
79 | intersect: false,
80 | },
81 | scales: {
82 | x: {
83 | display: true,
84 | scaleLabel: {
85 | display: true,
86 | labelString: 'Month',
87 | },
88 | },
89 | y: {
90 | display: true,
91 | scaleLabel: {
92 | display: true,
93 | labelString: 'Value',
94 | },
95 | },
96 | },
97 | },
98 | legend: {
99 | display: false,
100 | },
101 | }
102 |
103 | export const barOptions = {
104 | data: {
105 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
106 | datasets: [
107 | {
108 | label: 'Shoes',
109 | backgroundColor: '#0694a2',
110 | // borderColor: window.chartColors.red,
111 | borderWidth: 1,
112 | data: [-3, 14, 52, 74, 33, 90, 70],
113 | },
114 | {
115 | label: 'Bags',
116 | backgroundColor: '#7e3af2',
117 | // borderColor: window.chartColors.blue,
118 | borderWidth: 1,
119 | data: [66, 33, 43, 12, 54, 62, 84],
120 | },
121 | ],
122 | },
123 | options: {
124 | responsive: true,
125 | },
126 | legend: {
127 | display: false,
128 | },
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/utils/demo/tableData.ts:
--------------------------------------------------------------------------------
1 | interface ITableData{
2 | avatar: string
3 | name: string
4 | job: string
5 | amount: number
6 | status: "success" | "danger" | "warning" | "neutral" | "primary" | undefined
7 | date: string
8 | }
9 |
10 |
11 | const tableData: ITableData[] = [
12 | {
13 | avatar: 'https://randomuser.me/api/portraits/men/64.jpg',
14 | name: 'Chandler Jacobi',
15 | job: 'Direct Security Executive',
16 | amount: 989.4,
17 | status: 'primary',
18 | date: 'Mon Feb 03 2020 04:13:15 GMT-0300 (Brasilia Standard Time)',
19 | },
20 | {
21 | avatar: 'https://randomuser.me/api/portraits/men/76.jpg',
22 | name: 'Monserrat Marquardt',
23 | job: 'Forward Accountability Producer',
24 | amount: 471.44,
25 | status: 'danger',
26 | date: 'Fri Nov 29 2019 10:43:17 GMT-0300 (Brasilia Standard Time)',
27 | },
28 | {
29 | avatar: 'https://randomuser.me/api/portraits/men/79.jpg',
30 | name: 'Lonie Wyman',
31 | job: 'Legacy Program Director',
32 | amount: 934.24,
33 | status: 'success',
34 | date: 'Fri Apr 03 2020 03:07:53 GMT-0300 (Brasilia Standard Time)',
35 | },
36 | {
37 | avatar: 'https://randomuser.me/api/portraits/men/43.jpg',
38 | name: 'Corine Abernathy',
39 | job: 'Chief Factors Planner',
40 | amount: 351.28,
41 | status: 'warning',
42 | date: 'Fri Jun 21 2019 20:21:55 GMT-0300 (Brasilia Standard Time)',
43 | },
44 | {
45 | avatar: 'https://randomuser.me/api/portraits/men/39.jpg',
46 | name: 'Lorenz Botsford',
47 | job: 'Central Accountability Developer',
48 | amount: 355.3,
49 | status: 'neutral',
50 | date: 'Wed Aug 28 2019 15:31:43 GMT-0300 (Brasilia Standard Time)',
51 | },
52 | {
53 | avatar: 'https://randomuser.me/api/portraits/men/8.jpg',
54 | name: 'Everette Botsford',
55 | job: 'Product Group Architect',
56 | amount: 525.42,
57 | status: 'success',
58 | date: 'Thu Jan 16 2020 09:53:33 GMT-0300 (Brasilia Standard Time)',
59 | },
60 | {
61 | avatar: 'https://randomuser.me/api/portraits/men/34.jpg',
62 | name: 'Marilou Beahan',
63 | job: 'Future Security Planner',
64 | amount: 414.99,
65 | status: 'success',
66 | date: 'Mon Oct 28 2019 14:44:31 GMT-0300 (Brasilia Standard Time)',
67 | },
68 | {
69 | avatar: 'https://randomuser.me/api/portraits/men/80.jpg',
70 | name: 'Ceasar Sauer',
71 | job: 'Direct Brand Specialist',
72 | amount: 488.0,
73 | status: 'primary',
74 | date: 'Tue Jul 23 2019 00:24:44 GMT-0300 (Brasilia Standard Time)',
75 | },
76 | {
77 | avatar: 'https://randomuser.me/api/portraits/men/13.jpg',
78 | name: 'Rae McDermott',
79 | job: 'Lead Branding Engineer',
80 | amount: 502.69,
81 | status: 'danger',
82 | date: 'Sat Feb 01 2020 12:57:03 GMT-0300 (Brasilia Standard Time)',
83 | },
84 | {
85 | avatar: 'https://randomuser.me/api/portraits/men/86.jpg',
86 | name: 'Mable Steuber',
87 | job: 'National Group Executive',
88 | amount: 911.09,
89 | status: 'danger',
90 | date: 'Mon Sep 09 2019 12:03:25 GMT-0300 (Brasilia Standard Time)',
91 | },
92 | {
93 | avatar: 'https://randomuser.me/api/portraits/men/74.jpg',
94 | name: 'Julian Glover',
95 | job: 'Global Branding Assistant',
96 | amount: 656.94,
97 | status: 'danger',
98 | date: 'Fri May 22 2020 17:46:12 GMT-0300 (Brasilia Standard Time)',
99 | },
100 | {
101 | avatar: 'https://randomuser.me/api/portraits/men/71.jpg',
102 | name: 'Duncan Toy',
103 | job: 'Central Intranet Manager',
104 | amount: 120.78,
105 | status: 'danger',
106 | date: 'Sun Nov 17 2019 11:59:50 GMT-0300 (Brasilia Standard Time)',
107 | },
108 | {
109 | avatar: 'https://randomuser.me/api/portraits/men/81.jpg',
110 | name: 'Blanche Friesen',
111 | job: 'Forward Identity Executive',
112 | amount: 676.95,
113 | status: 'danger',
114 | date: 'Sun Jun 21 2020 16:46:39 GMT-0300 (Brasilia Standard Time)',
115 | },
116 | {
117 | avatar: 'https://randomuser.me/api/portraits/men/91.jpg',
118 | name: 'Orion Koepp',
119 | job: 'Global Accountability Strategist',
120 | amount: 447.56,
121 | status: 'danger',
122 | date: 'Thu Jun 04 2020 18:29:41 GMT-0300 (Brasilia Standard Time)',
123 | },
124 | {
125 | avatar: 'https://randomuser.me/api/portraits/men/49.jpg',
126 | name: 'Dakota Vandervort',
127 | job: 'Future Assurance Coordinator',
128 | amount: 765.22,
129 | status: 'danger',
130 | date: 'Fri Jan 31 2020 13:02:55 GMT-0300 (Brasilia Standard Time)',
131 | },
132 | {
133 | avatar: 'https://randomuser.me/api/portraits/men/85.jpg',
134 | name: 'Lily Collier',
135 | job: 'Forward Configuration Orchestrator',
136 | amount: 449.11,
137 | status: 'danger',
138 | date: 'Sun Aug 18 2019 14:52:01 GMT-0300 (Brasilia Standard Time)',
139 | },
140 | {
141 | avatar: 'https://randomuser.me/api/portraits/men/63.jpg',
142 | name: 'Kayleigh Schumm',
143 | job: 'Central Division Agent',
144 | amount: 65.54,
145 | status: 'danger',
146 | date: 'Wed May 06 2020 17:49:09 GMT-0300 (Brasilia Standard Time)',
147 | },
148 | {
149 | avatar: 'https://randomuser.me/api/portraits/men/25.jpg',
150 | name: 'General McGlynn',
151 | job: 'Central Web Analyst',
152 | amount: 30.51,
153 | status: 'danger',
154 | date: 'Mon Mar 30 2020 01:24:54 GMT-0300 (Brasilia Standard Time)',
155 | },
156 | {
157 | avatar: 'https://randomuser.me/api/portraits/men/19.jpg',
158 | name: 'Shayna Schumm',
159 | job: 'Future Directives Engineer',
160 | amount: 313.73,
161 | status: 'danger',
162 | date: 'Wed Jul 03 2019 10:01:06 GMT-0300 (Brasilia Standard Time)',
163 | },
164 | {
165 | avatar: 'https://randomuser.me/api/portraits/men/17.jpg',
166 | name: 'Giovanna Sanford',
167 | job: 'Dynamic Interactions Executive',
168 | amount: 398.3,
169 | status: 'danger',
170 | date: 'Tue Oct 08 2019 17:08:43 GMT-0300 (Brasilia Standard Time)',
171 | },
172 | {
173 | avatar: 'https://randomuser.me/api/portraits/men/12.jpg',
174 | name: 'Emie Mante',
175 | job: 'Direct Factors Supervisor',
176 | amount: 142.51,
177 | status: 'danger',
178 | date: 'Wed Jul 24 2019 19:17:16 GMT-0300 (Brasilia Standard Time)',
179 | },
180 | {
181 | avatar: 'https://randomuser.me/api/portraits/men/14.jpg',
182 | name: 'Chance Muller',
183 | job: 'Lead Usability Planner',
184 | amount: 963.26,
185 | status: 'danger',
186 | date: 'Sun Dec 01 2019 14:04:10 GMT-0300 (Brasilia Standard Time)',
187 | },
188 | {
189 | avatar: 'https://randomuser.me/api/portraits/men/16.jpg',
190 | name: 'Armani Torphy',
191 | job: 'Forward Functionality Analyst',
192 | amount: 445.23,
193 | status: 'danger',
194 | date: 'Tue Dec 24 2019 13:28:36 GMT-0300 (Brasilia Standard Time)',
195 | },
196 | {
197 | avatar: 'https://randomuser.me/api/portraits/men/19.jpg',
198 | name: 'Braeden Ward',
199 | job: 'Central Integration Director',
200 | amount: 588.53,
201 | status: 'danger',
202 | date: 'Wed Apr 15 2020 21:40:11 GMT-0300 (Brasilia Standard Time)',
203 | },
204 | {
205 | avatar: 'https://randomuser.me/api/portraits/men/20.jpg',
206 | name: 'Malcolm Price',
207 | job: 'District Program Planner',
208 | amount: 181.93,
209 | status: 'danger',
210 | date: 'Thu Oct 24 2019 07:09:03 GMT-0300 (Brasilia Standard Time)',
211 | },
212 | {
213 | avatar: 'https://randomuser.me/api/portraits/men/21.jpg',
214 | name: 'Susan Jast',
215 | job: 'Future Paradigm Associate',
216 | amount: 928.41,
217 | status: 'danger',
218 | date: 'Sun Feb 09 2020 23:22:23 GMT-0300 (Brasilia Standard Time)',
219 | },
220 | {
221 | avatar: 'https://randomuser.me/api/portraits/men/31.jpg',
222 | name: 'Torrey Reynolds',
223 | job: 'Lead Security Agent',
224 | amount: 447.37,
225 | status: 'danger',
226 | date: 'Mon Oct 28 2019 04:10:39 GMT-0300 (Brasilia Standard Time)',
227 | },
228 | {
229 | avatar: 'https://randomuser.me/api/portraits/men/48.jpg',
230 | name: 'Travon Harber',
231 | job: 'Legacy Marketing Facilitator',
232 | amount: 422.48,
233 | status: 'danger',
234 | date: 'Sat Nov 09 2019 05:04:09 GMT-0300 (Brasilia Standard Time)',
235 | },
236 | {
237 | avatar: 'https://randomuser.me/api/portraits/men/58.jpg',
238 | name: 'Hattie Gutkowski',
239 | job: 'Chief Configuration Supervisor',
240 | amount: 714.34,
241 | status: 'danger',
242 | date: 'Tue Oct 22 2019 22:36:23 GMT-0300 (Brasilia Standard Time)',
243 | },
244 | {
245 | avatar: 'https://randomuser.me/api/portraits/men/34.jpg',
246 | name: 'Demarco Lang',
247 | job: 'Investor Program Designer',
248 | amount: 536.92,
249 | status: 'danger',
250 | date: 'Wed Apr 08 2020 03:05:59 GMT-0300 (Brasilia Standard Time)',
251 | },
252 | {
253 | avatar: 'https://randomuser.me/api/portraits/men/64.jpg',
254 | name: 'Glennie Ziemann',
255 | job: 'Dynamic Interactions Analyst',
256 | amount: 597.25,
257 | status: 'danger',
258 | date: 'Mon Jun 22 2020 21:27:06 GMT-0300 (Brasilia Standard Time)',
259 | },
260 | {
261 | avatar: 'https://randomuser.me/api/portraits/men/73.jpg',
262 | name: 'Alta Howe',
263 | job: 'District Intranet Executive',
264 | amount: 42.28,
265 | status: 'danger',
266 | date: 'Sat Oct 12 2019 22:57:22 GMT-0300 (Brasilia Standard Time)',
267 | },
268 | {
269 | avatar: 'https://randomuser.me/api/portraits/men/66.jpg',
270 | name: 'Sydnee Gottlieb',
271 | job: 'Global Response Specialist',
272 | amount: 868.92,
273 | status: 'danger',
274 | date: 'Wed Feb 05 2020 05:17:34 GMT-0300 (Brasilia Standard Time)',
275 | },
276 | {
277 | avatar: 'https://randomuser.me/api/portraits/men/23.jpg',
278 | name: 'Arlene Schmitt',
279 | job: 'Lead Metrics Consultant',
280 | amount: 364.68,
281 | status: 'danger',
282 | date: 'Thu Oct 03 2019 08:47:32 GMT-0300 (Brasilia Standard Time)',
283 | },
284 | {
285 | avatar: 'https://randomuser.me/api/portraits/men/98.jpg',
286 | name: 'Hilda Schoen',
287 | job: 'National Solutions Facilitator',
288 | amount: 260.91,
289 | status: 'danger',
290 | date: 'Wed Dec 04 2019 06:28:30 GMT-0300 (Brasilia Standard Time)',
291 | },
292 | {
293 | avatar: 'https://randomuser.me/api/portraits/men/72.jpg',
294 | name: 'Chase Bahringer',
295 | job: 'Dynamic Communications Designer',
296 | amount: 454.61,
297 | status: 'danger',
298 | date: 'Mon Nov 25 2019 16:59:38 GMT-0300 (Brasilia Standard Time)',
299 | },
300 | {
301 | avatar: 'https://randomuser.me/api/portraits/men/91.jpg',
302 | name: 'Lucile Hansen',
303 | job: 'Customer Usability Facilitator',
304 | amount: 982.22,
305 | status: 'danger',
306 | date: 'Sun Aug 25 2019 17:15:59 GMT-0300 (Brasilia Standard Time)',
307 | },
308 | {
309 | avatar: 'https://randomuser.me/api/portraits/men/59.jpg',
310 | name: 'Mollie Heaney',
311 | job: 'Forward Communications Director',
312 | amount: 390.33,
313 | status: 'danger',
314 | date: 'Mon Jul 22 2019 01:45:19 GMT-0300 (Brasilia Standard Time)',
315 | },
316 | {
317 | avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
318 | name: 'Bennie Kuvalis',
319 | job: 'Future Factors Agent',
320 | amount: 456.64,
321 | status: 'danger',
322 | date: 'Sat Feb 08 2020 07:55:08 GMT-0300 (Brasilia Standard Time)',
323 | },
324 | {
325 | avatar: 'https://randomuser.me/api/portraits/men/84.jpg',
326 | name: 'Jodie Luettgen',
327 | job: 'Customer Implementation Associate',
328 | amount: 398.37,
329 | status: 'danger',
330 | date: 'Tue Jun 09 2020 11:19:53 GMT-0300 (Brasilia Standard Time)',
331 | },
332 | {
333 | avatar: 'https://randomuser.me/api/portraits/men/43.jpg',
334 | name: 'Bethel Quitzon',
335 | job: 'Dynamic Web Strategist',
336 | amount: 183.58,
337 | status: 'danger',
338 | date: 'Sun Dec 29 2019 18:56:54 GMT-0300 (Brasilia Standard Time)',
339 | },
340 | {
341 | avatar: 'https://randomuser.me/api/portraits/men/67.jpg',
342 | name: 'Jon Dietrich',
343 | job: 'Legacy Creative Planner',
344 | amount: 439.01,
345 | status: 'danger',
346 | date: 'Sun Dec 29 2019 11:11:34 GMT-0300 (Brasilia Standard Time)',
347 | },
348 | {
349 | avatar: 'https://randomuser.me/api/portraits/men/89.jpg',
350 | name: 'Nakia Kihn',
351 | job: 'Central Interactions Coordinator',
352 | amount: 824.12,
353 | status: 'danger',
354 | date: 'Sun Sep 15 2019 06:43:56 GMT-0300 (Brasilia Standard Time)',
355 | },
356 | {
357 | avatar: 'https://randomuser.me/api/portraits/men/44.jpg',
358 | name: 'Assunta Grady',
359 | job: 'Investor Operations Specialist',
360 | amount: 172.97,
361 | status: 'danger',
362 | date: 'Tue Dec 17 2019 01:46:37 GMT-0300 (Brasilia Standard Time)',
363 | },
364 | {
365 | avatar: 'https://randomuser.me/api/portraits/men/78.jpg',
366 | name: 'Lukas Klocko',
367 | job: 'Human Usability Associate',
368 | amount: 515.74,
369 | status: 'danger',
370 | date: 'Mon Jun 15 2020 04:04:32 GMT-0300 (Brasilia Standard Time)',
371 | },
372 | ]
373 |
374 | export default tableData
375 | export type {
376 | ITableData
377 | }
378 |
--------------------------------------------------------------------------------