├── src ├── components │ ├── Frame │ │ ├── index.ts │ │ ├── frame.css │ │ ├── Frame.tsx │ │ └── SidebarFooter.tsx │ ├── Logo │ │ ├── index.tsx │ │ ├── logo.css │ │ └── Logo.tsx │ ├── ErrorPage │ │ ├── index.ts │ │ ├── error-page.css │ │ └── ErrorPage.tsx │ ├── index.ts │ ├── PageContent.tsx │ ├── Copyright.tsx │ ├── NavLink.tsx │ ├── Brand.tsx │ └── PageToolbar.tsx ├── app │ ├── (main) │ │ ├── error │ │ │ ├── 403 │ │ │ │ ├── page.tsx │ │ │ │ └── Error403.tsx │ │ │ ├── 404 │ │ │ │ ├── page.tsx │ │ │ │ └── Error404.tsx │ │ │ ├── 500 │ │ │ │ ├── page.tsx │ │ │ │ └── Error500.tsx │ │ │ └── 503 │ │ │ │ ├── page.tsx │ │ │ │ └── Error503.tsx │ │ ├── layout.tsx │ │ ├── table │ │ │ ├── members │ │ │ │ ├── members-table.css │ │ │ │ ├── page.tsx │ │ │ │ ├── DrawerView.tsx │ │ │ │ ├── Cells.tsx │ │ │ │ ├── DataTable.tsx │ │ │ │ └── users.ts │ │ │ └── virtualized │ │ │ │ ├── page.tsx │ │ │ │ └── VirtualizedTable.tsx │ │ ├── dashboard │ │ │ ├── colorful-chart.css │ │ │ ├── page.tsx │ │ │ ├── dashboard.css │ │ │ ├── PieChart.tsx │ │ │ ├── ColorfulChart.tsx │ │ │ ├── dashboard-chartist.css │ │ │ ├── BarChart.tsx │ │ │ ├── DataTable.tsx │ │ │ └── Dashboard.tsx │ │ ├── form │ │ │ ├── wizard │ │ │ │ ├── FormHeader.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── Completed.tsx │ │ │ │ ├── ProjectTypeForm.tsx │ │ │ │ ├── WizardForm.tsx │ │ │ │ ├── BusinessDetailForm.tsx │ │ │ │ ├── ProjectInfoForm.tsx │ │ │ │ └── TeamSettingsForm.tsx │ │ │ └── basic │ │ │ │ ├── page.tsx │ │ │ │ ├── form-basic.css │ │ │ │ └── BasicForm.tsx │ │ ├── page.tsx │ │ └── calendar │ │ │ ├── page.tsx │ │ │ ├── EventModal.tsx │ │ │ ├── Calendar.tsx │ │ │ └── calendar.css │ ├── not-found.tsx │ ├── sign-in │ │ ├── page.tsx │ │ └── SignIn.tsx │ ├── sign-up │ │ ├── page.tsx │ │ └── SignUp.tsx │ ├── providers.tsx │ ├── page.tsx │ ├── layout.tsx │ ├── auth.css │ └── globals.css ├── utils │ ├── toThousands.ts │ ├── index.ts │ ├── formatValue.ts │ └── highlightValue.tsx ├── hooks │ └── useWindowHeight.ts ├── config.tsx └── data │ ├── mock.ts │ └── calendarEvents.ts ├── public ├── favicon.ico └── images │ ├── preview-1.png │ ├── preview-2.png │ ├── errors │ ├── index.ts │ ├── 403.svg │ └── 500.svg │ ├── pv.svg │ ├── uv.svg │ └── vv.svg ├── .prettierrc.js ├── .eslintignore ├── next.config.js ├── .eslintrc.json ├── tsconfig.json ├── .gitignore ├── README.md └── package.json /src/components/Frame/index.ts: -------------------------------------------------------------------------------- 1 | import Frame from './Frame'; 2 | 3 | export default Frame; 4 | -------------------------------------------------------------------------------- /src/components/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | import Logo from './Logo'; 2 | 3 | export default Logo; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsuite/rsuite-admin-template/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/components/ErrorPage/index.ts: -------------------------------------------------------------------------------- 1 | import ErrorPage from './ErrorPage'; 2 | 3 | export default ErrorPage; 4 | -------------------------------------------------------------------------------- /public/images/preview-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsuite/rsuite-admin-template/HEAD/public/images/preview-1.png -------------------------------------------------------------------------------- /public/images/preview-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsuite/rsuite-admin-template/HEAD/public/images/preview-2.png -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ErrorPage } from './ErrorPage'; 2 | export { default as Frame } from './Frame'; 3 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | tabWidth: 2, 4 | singleQuote: true, 5 | arrowParens: 'avoid', 6 | trailingComma: 'none' 7 | }; 8 | -------------------------------------------------------------------------------- /src/app/(main)/error/403/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Error403 from './Error403'; 4 | 5 | export default function Error403Page() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/(main)/error/404/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Error404 from './Error404'; 4 | 5 | export default function Error404Page() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/(main)/error/500/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Error500 from './Error500'; 4 | 5 | export default function Error500Page() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/(main)/error/503/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Error503 from './Error503'; 4 | 5 | export default function Error503Page() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Error404 from './(main)/error/404/Error404'; 4 | 5 | export default function NotFound() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/sign-in/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import '../auth.css'; 4 | import SignIn from './SignIn'; 5 | 6 | export default function SignInPage() { 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/sign-up/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import '../auth.css'; 4 | import SignUp from './SignUp'; 5 | 6 | export default function SignUpPage() { 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/toThousands.ts: -------------------------------------------------------------------------------- 1 | export default function toThousands(value: number, fixed = 0) { 2 | return (value.toFixed(fixed) + '').replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,'); 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | pnpm-lock.yaml 4 | 5 | # Build outputs 6 | .next 7 | out 8 | dist 9 | 10 | # Environment files 11 | .env 12 | .env.local 13 | .env.*.local 14 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as toThousands } from './toThousands'; 2 | export { default as highlightValue } from './highlightValue'; 3 | export { default as formatValue } from './formatValue'; 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | unoptimized: true 5 | }, 6 | transpilePackages: ['@fullcalendar'], 7 | turbopack: {} 8 | }; 9 | 10 | module.exports = nextConfig; 11 | -------------------------------------------------------------------------------- /src/app/(main)/layout.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Frame from '@/components/Frame'; 4 | import { appNavs } from '@/config'; 5 | 6 | export default function MainLayout({ children }: { children: React.ReactNode }) { 7 | return {children}; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { CustomProvider } from 'rsuite'; 5 | import enGB from 'rsuite/locales/en_GB'; 6 | 7 | export function Providers({ children }: { children: React.ReactNode }) { 8 | return {children}; 9 | } 10 | -------------------------------------------------------------------------------- /public/images/errors/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | export const { default: Error404Img } = require('./404.svg'); 3 | export const { default: Error500Img } = require('./500.svg'); 4 | export const { default: Error503Img } = require('./503.svg'); 5 | export const { default: Error403Img } = require('./403.svg'); 6 | -------------------------------------------------------------------------------- /src/app/(main)/table/members/members-table.css: -------------------------------------------------------------------------------- 1 | .rs-table .link-group { 2 | cursor: pointer; 3 | } 4 | 5 | .rs-table .link-group .rs-table-cell-content { 6 | padding: 5px; 7 | } 8 | 9 | /* Tables - Members */ 10 | .table-toolbar { 11 | padding: 20px; 12 | background: var(--table-bg); 13 | border-radius: 4px 4px 0 0; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect } from 'react'; 4 | import { useRouter } from 'next/navigation'; 5 | import { Loader } from 'rsuite'; 6 | 7 | export default function HomePage() { 8 | const router = useRouter(); 9 | 10 | useEffect(() => { 11 | router.push('/dashboard'); 12 | }, [router]); 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/colorful-chart.css: -------------------------------------------------------------------------------- 1 | .colorful-chart { 2 | color: var(--colorful-chart-text); 3 | margin-top: 30px; 4 | border-radius: 6px; 5 | } 6 | 7 | .colorful-chart h3 { 8 | line-height: 22px; 9 | text-align: right; 10 | color: var(--colorful-chart-title); 11 | padding: 10px; 12 | font-size: 18px; 13 | } 14 | 15 | 16 | 17 | .apexcharts-theme-light .tooltip-custom { 18 | color: var(--tooltip-text); 19 | } 20 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/FormHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface FormHeaderProps { 4 | title: string; 5 | description: string; 6 | } 7 | 8 | const FormHeader = ({ title, description }: FormHeaderProps) => { 9 | return ( 10 |
11 |
{title}
12 |

{description}

13 |
14 | ); 15 | }; 16 | 17 | export default FormHeader; 18 | -------------------------------------------------------------------------------- /src/app/(main)/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Panel } from 'rsuite'; 4 | import Dashboard from './dashboard/Dashboard'; 5 | import Copyright from '@/components/Copyright'; 6 | import PageToolbar from '@/components/PageToolbar'; 7 | 8 | export default function HomePage() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/components/PageContent.tsx: -------------------------------------------------------------------------------- 1 | import { Panel, PanelProps } from 'rsuite'; 2 | import classNames from 'classnames'; 3 | import Copyright from '@/components/Copyright'; 4 | 5 | const PageContent = (props: PanelProps) => { 6 | const { className, ...rest } = props; 7 | return ( 8 | <> 9 | 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default PageContent; 16 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import dynamic from 'next/dynamic'; 4 | import { Panel } from 'rsuite'; 5 | import Dashboard from './Dashboard'; 6 | import Copyright from '@/components/Copyright'; 7 | 8 | const PageToolbar = dynamic(() => import('@/components/PageToolbar'), { ssr: false }); 9 | 10 | export default function DashboardPage() { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/components/ErrorPage/error-page.css: -------------------------------------------------------------------------------- 1 | .error-page { 2 | display: flex; 3 | height: 100vh; 4 | margin-top: -40px; 5 | justify-content: center; 6 | align-items: center; 7 | } 8 | 9 | .error-page-code { 10 | color: var(--text-muted); 11 | } 12 | 13 | .error-page-title { 14 | font-size: 1.5rem; 15 | font-weight: bold; 16 | } 17 | 18 | .error-page-subtitle { 19 | margin: 10px 0 20px 0; 20 | } 21 | 22 | .error-page .item img { 23 | height: 260px; 24 | } 25 | 26 | .error-page .item .text { 27 | text-align: center; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Copyright.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Stack } from 'rsuite'; 3 | 4 | const Copyright = () => { 5 | return ( 6 | 7 |
8 |

9 | © 2022, Made with ❤️ by{' '} 10 | 11 | React Suite 12 | 13 |

14 |
15 |
16 | ); 17 | }; 18 | 19 | export default Copyright; 20 | -------------------------------------------------------------------------------- /src/app/(main)/error/503/Error503.tsx: -------------------------------------------------------------------------------- 1 | import ErrorPage from '@/components/ErrorPage'; 2 | import { IconButton } from 'rsuite'; 3 | import { FiArrowLeft } from 'react-icons/fi'; 4 | 5 | const Error503 = () => ( 6 | 7 |

Oops… You just found an error page

8 |

This page is being updated and maintained.

9 | } appearance="primary" href="/"> 10 | Take me home 11 | 12 |
13 | ); 14 | 15 | export default Error503; 16 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 2020, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint", "react-hooks"], 9 | "extends": [ 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:react-hooks/recommended", 12 | "prettier" 13 | ], 14 | "ignorePatterns": ["node_modules", ".next", "pnpm-lock.yaml"], 15 | "rules": { 16 | "@typescript-eslint/no-explicit-any": "warn", 17 | "react-hooks/exhaustive-deps": "warn", 18 | "react/react-in-jsx-scope": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/(main)/error/404/Error404.tsx: -------------------------------------------------------------------------------- 1 | import ErrorPage from '@/components/ErrorPage'; 2 | import { IconButton } from 'rsuite'; 3 | import { FiArrowLeft } from 'react-icons/fi'; 4 | 5 | const Error404 = () => ( 6 | 7 |

Oops… You just found an error page

8 |

9 | We are sorry but the page you are looking for was not found 10 |

11 | } appearance="primary" href="/"> 12 | Take me home 13 | 14 |
15 | ); 16 | 17 | export default Error404; 18 | -------------------------------------------------------------------------------- /src/app/(main)/error/500/Error500.tsx: -------------------------------------------------------------------------------- 1 | import ErrorPage from '@/components/ErrorPage'; 2 | import { IconButton } from 'rsuite'; 3 | import { FiArrowLeft } from 'react-icons/fi'; 4 | 5 | const Error500 = () => ( 6 | 7 |

Oops… You just found an error page

8 |

9 | We are sorry but our server encountered an internal error 10 |

11 | } appearance="primary" href="/"> 12 | Take me home 13 | 14 |
15 | ); 16 | 17 | export default Error500; 18 | -------------------------------------------------------------------------------- /src/app/(main)/error/403/Error403.tsx: -------------------------------------------------------------------------------- 1 | import ErrorPage from '@/components/ErrorPage'; 2 | import { IconButton } from 'rsuite'; 3 | import { FiArrowLeft } from 'react-icons/fi'; 4 | 5 | const Error403 = () => ( 6 | 7 |

Oops… You just found an error page

8 |

9 | The current page is unavailable or you do not have permission to access.{' '} 10 |

11 | } appearance="primary" href="/"> 12 | Take me home 13 | 14 |
15 | ); 16 | 17 | export default Error403; 18 | -------------------------------------------------------------------------------- /src/app/(main)/calendar/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Panel, Breadcrumb, Text, VStack } from 'rsuite'; 4 | import Calendar from './Calendar'; 5 | 6 | export default function CalendarPage() { 7 | return ( 8 | 11 | 12 | Calendar 13 | 14 | 15 | Home 16 | Calendar 17 | 18 | 19 | } 20 | > 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Logo/logo.css: -------------------------------------------------------------------------------- 1 | .rsuite-logo .cls-1 { 2 | fill: #6292f0; 3 | } 4 | 5 | .rsuite-logo .cls-2 { 6 | fill: #ec727d; 7 | } 8 | 9 | .rsuite-logo .cls-1, 10 | .rsuite-logo .cls-2 { 11 | fill-rule: evenodd; 12 | } 13 | 14 | .rsuite-logo .polyline-limb { 15 | animation: limbLineMove 3s ease-out 1; 16 | } 17 | 18 | .rsuite-logo .polyline-axis { 19 | animation: axisLineMove 2s ease-out 1; 20 | } 21 | 22 | .rsuite-logo .circle { 23 | animation: circleMove 2s ease-out 1; 24 | } 25 | 26 | .logo-animated { 27 | animation-duration: 1s; 28 | animation-fill-mode: both; 29 | } 30 | 31 | .logo-animated-delay-half-seconds { 32 | animation-delay: 0.5s; 33 | } -------------------------------------------------------------------------------- /src/components/ErrorPage/ErrorPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Image from 'next/image'; 3 | import './error-page.css'; 4 | 5 | interface ErrorPageProps { 6 | code?: number; 7 | children?: React.ReactNode; 8 | } 9 | 10 | const ErrorPage = ({ code = 404, children }: ErrorPageProps) => ( 11 |
12 |
13 | {`Error 14 |
15 |

{code}

16 | {children} 17 |
18 |
19 |
20 | ); 21 | 22 | export default ErrorPage; 23 | -------------------------------------------------------------------------------- /src/components/NavLink.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import Link from 'next/link'; 5 | 6 | interface NavLinkProps { 7 | to?: string; 8 | children?: React.ReactNode; 9 | [key: string]: any; 10 | } 11 | 12 | const NavLink = React.forwardRef( 13 | ({ to, children, ...rest }, ref) => { 14 | if (!to) { 15 | return ( 16 | 17 | {children} 18 | 19 | ); 20 | } 21 | return ( 22 | 23 | {children} 24 | 25 | ); 26 | } 27 | ); 28 | 29 | NavLink.displayName = 'NavLink'; 30 | 31 | export default NavLink; 32 | -------------------------------------------------------------------------------- /src/app/(main)/form/basic/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Breadcrumb, Panel, Text, VStack } from 'rsuite'; 4 | import BasicForm from './BasicForm'; 5 | 6 | export default function FormBasicPage() { 7 | return ( 8 | 11 | 12 | Basic Form 13 | 14 | 15 | Home 16 | Forms 17 | Basic 18 | 19 | 20 | } 21 | > 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/app/(main)/table/members/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Breadcrumb, Panel, Text, VStack } from 'rsuite'; 4 | import DataTable from './DataTable'; 5 | 6 | export default function MembersPage() { 7 | return ( 8 | 11 | 12 | Members 13 | 14 | 15 | Home 16 | Tables 17 | Members 18 | 19 | 20 | } 21 | > 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Breadcrumb, Panel, Text, VStack } from 'rsuite'; 4 | import WizardForm from './WizardForm'; 5 | 6 | export default function FormWizardPage() { 7 | return ( 8 | 11 | 12 | Wizard Form 13 | 14 | 15 | Home 16 | Forms 17 | Wizard 18 | 19 | 20 | } 21 | > 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/formatValue.ts: -------------------------------------------------------------------------------- 1 | import toThousands from './toThousands'; 2 | 3 | export default function formatValue(value: number) { 4 | if (value === null || typeof value === 'undefined') { 5 | return '--'; 6 | } else if (value === 0) { 7 | return '0'; 8 | } else if (0 < value && value < 1000) { 9 | return '< 1k'; 10 | } else if (1000 <= value && value < 1000000) { 11 | return toThousands(value / 1000) + ' k'; 12 | } else if (1000000 <= value && value < 1000000000) { 13 | return toThousands(value / 1000000) + ' M'; 14 | } else if (1000000000 <= value && value < 1000000000000) { 15 | return toThousands(value / 1000000000) + ' B'; 16 | } else if (1000000000000 <= value) { 17 | return 'INF.'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Providers } from './providers'; 3 | import 'rsuite/dist/rsuite.css'; 4 | import './globals.css'; 5 | 6 | export const metadata = { 7 | title: 'React Suite Admin Template', 8 | description: 'A modern React Suite admin dashboard template built with Next.js and RSuite.', 9 | keywords: [ 10 | 'React Suite', 11 | 'RSuite', 12 | 'admin template', 13 | 'dashboard', 14 | 'Next.js', 15 | 'management system' 16 | ] 17 | }; 18 | 19 | export default function RootLayout({ children }: { children: React.ReactNode }) { 20 | return ( 21 | 22 | 23 | {children} 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/app/(main)/table/virtualized/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Breadcrumb, Panel, Text, VStack } from 'rsuite'; 4 | import VirtualizedTable from './VirtualizedTable'; 5 | 6 | export default function VirtualizedTablePage() { 7 | return ( 8 | 11 | 12 | Virtualized Table 13 | 14 | 15 | Home 16 | Tables 17 | Virtualized 18 | 19 | 20 | } 21 | > 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Brand.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import Link from 'next/link'; 5 | import Logo from './Logo'; 6 | import { Stack, Text, HStack } from 'rsuite'; 7 | 8 | interface BrandProps { 9 | collapsed?: boolean; 10 | } 11 | 12 | const Brand = ({ collapsed, ...rest }: BrandProps & React.HTMLAttributes) => { 13 | if (collapsed) { 14 | return ( 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | return ( 22 | 23 | 24 | 25 | Admin Template 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default Brand; 32 | -------------------------------------------------------------------------------- /src/hooks/useWindowHeight.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | function getHeight(win: Window | undefined): number { 4 | if (typeof window === 'undefined' || !win) return 0; 5 | return win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight; 6 | } 7 | 8 | export function useWindowHeight(): number { 9 | const [windowHeight, setWindowHeight] = useState(0); 10 | 11 | useEffect(() => { 12 | if (typeof window !== 'undefined') { 13 | setWindowHeight(getHeight(window)); 14 | 15 | const handleResize = () => setWindowHeight(getHeight(window)); 16 | window.addEventListener('resize', handleResize); 17 | 18 | return () => { 19 | window.removeEventListener('resize', handleResize); 20 | }; 21 | } 22 | }, []); 23 | 24 | return windowHeight; 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "esnext" 7 | ], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "bundler", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "react-jsx", 18 | "incremental": true, 19 | "plugins": [ 20 | { 21 | "name": "next" 22 | } 23 | ], 24 | "paths": { 25 | "@/*": [ 26 | "./src/*" 27 | ] 28 | }, 29 | "target": "ES2017" 30 | }, 31 | "include": [ 32 | "next-env.d.ts", 33 | "**/*.ts", 34 | "**/*.tsx", 35 | ".next/types/**/*.ts", 36 | ".next/dev/types/**/*.ts" 37 | ], 38 | "exclude": [ 39 | "node_modules" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | karma-* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # custom 37 | 38 | .vscode 39 | .DS_Store 40 | .idea 41 | scripts 42 | build 43 | lib 44 | dist 45 | assets 46 | 47 | # Next.js 48 | .next/ 49 | out/ 50 | next-env.d.ts 51 | 52 | # TypeScript 53 | *.tsbuildinfo 54 | -------------------------------------------------------------------------------- /src/utils/highlightValue.tsx: -------------------------------------------------------------------------------- 1 | import toThousands from './toThousands'; 2 | 3 | const Highlight = ({ value, unit }: { value: string; unit: string }) => ( 4 | 5 | {value} {unit}{' '} 6 | 7 | ); 8 | 9 | export default function highlightValue(value: number, fixed: number) { 10 | if (value === null || typeof value === 'undefined') { 11 | return '--'; 12 | } else if (value === 0) { 13 | return '0'; 14 | } else if (0 < value && value < 1000) { 15 | return '< 1k'; 16 | } else if (1000 <= value && value < 1000000) { 17 | return ; 18 | } else if (1000000 <= value && value < 1000000000) { 19 | return ; 20 | } else if (1000000000 <= value) { 21 | return ; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Frame/frame.css: -------------------------------------------------------------------------------- 1 | .frame { 2 | display: flex; 3 | height: 100vh; 4 | } 5 | 6 | .frame .rs-sidebar { 7 | background: var(--bg-sidebar); 8 | flex-shrink: 0; 9 | } 10 | 11 | .frame > .rs-container { 12 | flex: 1; 13 | display: flex; 14 | flex-direction: column; 15 | min-width: 0; 16 | } 17 | 18 | .frame .rs-sidenav { 19 | transition: none !important; 20 | border-top: 1px solid var(--border-color); 21 | width: 100%; 22 | } 23 | 24 | .frame .rs-content { 25 | flex: 1; 26 | overflow-y: auto; 27 | padding: 0 10px; 28 | } 29 | 30 | .frame .rs-content .rs-panel-header .title { 31 | font-size: 18px; 32 | } 33 | 34 | .frame .rs-content .rs-panel-header .rs-breadcrumb { 35 | margin-bottom: 0; 36 | } 37 | 38 | .frame .rs-sidenav-item.active, 39 | .frame .rs-dropdown-item.active { 40 | color: var(--active-color) !important; 41 | } 42 | 43 | .frame .rs-sidenav-item-icon, 44 | .frame .rs-dropdown-item-icon { 45 | height: 1.3em !important; 46 | width: 1.3em !important; 47 | } 48 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/dashboard.css: -------------------------------------------------------------------------------- 1 | .dashboard-header .rs-panel { 2 | color: #fff; 3 | } 4 | 5 | .dashboard-header .chart-img { 6 | width: 100px; 7 | position: absolute; 8 | left: 26px; 9 | top: 34px; 10 | opacity: 0.5; 11 | } 12 | 13 | .dashboard-header .trend-box .rs-panel-body { 14 | text-align: right; 15 | } 16 | 17 | .dashboard-header .trend-box .rs-panel-body .value { 18 | font-size: 36px; 19 | } 20 | 21 | .bg-gradient-orange { 22 | background: linear-gradient( 23 | 87deg, 24 | var(--bg-gradient-orange-from) 0, 25 | var(--bg-gradient-orange-to) 100% 26 | ); 27 | } 28 | 29 | .bg-gradient-red { 30 | background: linear-gradient(87deg, var(--bg-gradient-red-from) 0, var(--bg-gradient-red-to) 100%); 31 | } 32 | 33 | .bg-gradient-green { 34 | background: linear-gradient( 35 | 87deg, 36 | var(--bg-gradient-green-from) 0, 37 | var(--bg-gradient-green-to) 100% 38 | ); 39 | } 40 | 41 | .bg-gradient-blue { 42 | background: linear-gradient( 43 | 87deg, 44 | var(--bg-gradient-blue-from) 0, 45 | var(--bg-gradient-blue-to) 100% 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/Completed.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonToolbar, Button, Stack } from 'rsuite'; 2 | import { FiCheckCircle } from 'react-icons/fi'; 3 | 4 | const Completed = () => { 5 | return ( 6 |
7 |
8 | 9 | 10 |
11 |
Your Are Done!
12 |

You can start working on a new project.

13 |
14 |
15 |
16 | 17 |

18 | Once you have created this project, if you return to Project Web App you see it listed as a 19 | project in the Project Center. Updates made to the task list on the project site are 20 | reflected in the Project Center in Project Web App. 21 |

22 |

You can also click the button below to start working on the project.

23 | 24 | 25 | 26 | 27 | 28 |
29 | ); 30 | }; 31 | 32 | export default Completed; 33 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/PieChart.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Chart, { Props as ChartProps } from 'react-apexcharts'; 4 | import { Panel } from 'rsuite'; 5 | 6 | interface PieChartProps { 7 | title: string; 8 | data: ChartProps['series']; 9 | type?: ChartProps['type']; 10 | options?: ChartProps['options']; 11 | labels?: string[]; 12 | } 13 | 14 | const defaultOptions = { 15 | dataLabels: { 16 | enabled: false 17 | }, 18 | plotOptions: { 19 | pie: { 20 | customScale: 0.8, 21 | donut: { 22 | size: '75%' 23 | }, 24 | offsetY: 0 25 | }, 26 | stroke: { 27 | colors: undefined 28 | } 29 | }, 30 | colors: [ 31 | 'var(--chart-pie-series-1)', 32 | 'var(--chart-pie-series-2)', 33 | 'var(--chart-pie-series-3)', 34 | 'var(--chart-pie-series-4)', 35 | 'var(--chart-pie-series-5)' 36 | ], 37 | legend: { 38 | position: 'bottom', 39 | offsetY: 0 40 | } 41 | }; 42 | 43 | const PieChart = ({ title, data, type, labels, options }: PieChartProps) => ( 44 | 45 | 51 | 52 | ); 53 | 54 | export default PieChart; 55 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/ColorfulChart.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import classNames from 'classnames'; 4 | import Chart, { Props as ChartProps } from 'react-apexcharts'; 5 | import './colorful-chart.css'; 6 | 7 | interface ColorfulChartProps { 8 | className?: string; 9 | title: string; 10 | data: ChartProps['series']; 11 | type?: ChartProps['type']; 12 | options?: ChartProps['options']; 13 | } 14 | 15 | const defaultOptions = { 16 | chart: { 17 | id: 'sparkline1', 18 | type: 'line', 19 | height: 140, 20 | sparkline: { 21 | enabled: true 22 | }, 23 | group: 'sparklines' 24 | }, 25 | stroke: { 26 | curve: 'smooth' 27 | }, 28 | markers: { 29 | size: 0 30 | }, 31 | tooltip: { 32 | cssClass: 'tooltip-custom', 33 | marker: { 34 | show: false 35 | }, 36 | fixed: { 37 | enabled: true, 38 | position: 'right' 39 | }, 40 | x: { 41 | show: false 42 | } 43 | }, 44 | colors: ['var(--chart-sparkline-color)'] 45 | }; 46 | 47 | const ColorfulChart = ({ className, title, data, type, options }: ColorfulChartProps) => ( 48 |
49 |

{title}

50 | 56 |
57 | ); 58 | 59 | export default ColorfulChart; 60 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/dashboard-chartist.css: -------------------------------------------------------------------------------- 1 | .ct-chart-magenta { 2 | background: linear-gradient( 3 | 60deg, 4 | var(--chart-magenta-from, #ec407a), 5 | var(--chart-magenta-to, #d81b60) 6 | ); 7 | } 8 | 9 | .ct-chart-orange { 10 | background: linear-gradient( 11 | 60deg, 12 | var(--chart-orange-from, #ffa726), 13 | var(--chart-orange-to, #fb8c00) 14 | ); 15 | } 16 | 17 | .ct-chart-azure { 18 | background: linear-gradient( 19 | 60deg, 20 | var(--chart-azure-from, #26c6da), 21 | var(--chart-azure-to, #00acc1) 22 | ); 23 | } 24 | 25 | .ct-chart-magenta .ct-label, 26 | .ct-chart-orange .ct-label, 27 | .ct-chart-azure .ct-label { 28 | color: var(--chart-label-color); 29 | } 30 | 31 | .ct-chart-magenta .ct-series-a .ct-bar, 32 | .ct-chart-magenta .ct-series-a .ct-slice-donut, 33 | .ct-chart-magenta .ct-series-a .ct-line, 34 | .ct-chart-magenta .ct-series-a .ct-point, 35 | .ct-chart-orange .ct-series-a .ct-bar, 36 | .ct-chart-orange .ct-series-a .ct-slice-donut, 37 | .ct-chart-orange .ct-series-a .ct-line, 38 | .ct-chart-orange .ct-series-a .ct-point, 39 | .ct-chart-azure .ct-series-a .ct-bar, 40 | .ct-chart-azure .ct-series-a .ct-slice-donut, 41 | .ct-chart-azure .ct-series-a .ct-line, 42 | .ct-chart-azure .ct-series-a .ct-point { 43 | stroke: var(--chart-stroke-color); 44 | } 45 | 46 | .ct-chart-magenta .ct-grid, 47 | .ct-chart-orange .ct-grid, 48 | .ct-chart-azure .ct-grid { 49 | stroke: var(--chart-grid-color); 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RSuite Admin Template 2 | 3 | A Next.js + TypeScript admin dashboard template built with [RSuite](https://rsuitejs.com/). 4 | 5 | Provides a modern layout, prebuilt pages (dashboard, forms, calendar, auth, error pages) and a themeable design system based on CSS variables and RSuite themes. 6 | 7 | ![preview](https://rsuite-admin-template.vercel.app/images/preview-1.png) 8 | 9 | ![preview](https://rsuite-admin-template.vercel.app/images/preview-2.png) 10 | 11 | ## Tech stack 12 | 13 | - **Framework**: Next.js (App Router) 14 | - **UI Library**: RSuite 15 | - **Language**: TypeScript 16 | - **Styling**: CSS modules + global CSS variables (light / dark / system themes) 17 | 18 | ## Getting started 19 | 20 | Clone the repository: 21 | 22 | ```bash 23 | git clone git@github.com:/rsuite-admin-template.git 24 | cd rsuite-admin-template 25 | ``` 26 | 27 | Install dependencies: 28 | 29 | ```bash 30 | npm install 31 | ``` 32 | 33 | Run the development server (default: http://127.0.0.1:3000): 34 | 35 | ```bash 36 | npm run dev 37 | ``` 38 | 39 | ## Scripts 40 | 41 | - `npm run dev` – Start the development server 42 | - `npm run build` – Create a production build 43 | - `npm run start` – Run the production build 44 | 45 | ## Theming 46 | 47 | The template uses CSS variables and RSuite theme tokens to support light, dark and system themes. 48 | 49 | - Global variables are defined in `src/app/globals.css` 50 | - Layout and components consume variables instead of hardcoded colors 51 | - Theme switching is implemented in the sidebar user menu 52 | 53 | ## License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /src/app/(main)/table/virtualized/VirtualizedTable.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState, useEffect } from 'react'; 4 | import { DOMHelper, Table } from 'rsuite'; 5 | import { mockUsers } from '@/data/mock'; 6 | 7 | const { Column, HeaderCell, Cell } = Table; 8 | const { getHeight } = DOMHelper; 9 | 10 | const data = mockUsers(1000); 11 | 12 | const VirtualizedTable = () => { 13 | const [tableHeight, setTableHeight] = useState(600); 14 | 15 | useEffect(() => { 16 | if (typeof window !== 'undefined') { 17 | setTableHeight(Math.max(getHeight(window) - 120, 400)); 18 | } 19 | }, []); 20 | 21 | return ( 22 | 28 | 29 | Id 30 | 31 | 32 | 33 | 34 | First Name 35 | 36 | 37 | 38 | 39 | Last Name 40 | 41 | 42 | 43 | 44 | Gender 45 | 46 | 47 | 48 | 49 | Age 50 | 51 | 52 | 53 | 54 | City 55 | 56 | 57 | 58 | 59 | Email 60 | 61 | 62 |
63 | ); 64 | }; 65 | 66 | export default VirtualizedTable; 67 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/ProjectTypeForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Form, Stack, RadioTile, RadioTileGroup } from 'rsuite'; 3 | import { VscNotebookTemplate, VscRepoClone, VscFile } from 'react-icons/vsc'; 4 | import FormHeader from './FormHeader'; 5 | 6 | const ProjectTypeForm = () => { 7 | const [type, setType] = useState('personal'); 8 | 9 | return ( 10 |
11 | 15 | 16 | 17 | setType(String(value))}> 18 | } 20 | label="Create blank project" 21 | value="personal" 22 | > 23 | Create a blank project to house your files, plan your work, and collaborate on code, among 24 | other things. 25 | 26 | } 28 | label="Create from template" 29 | value="brand" 30 | > 31 | Create a project pre-populated with the necessary files to get you started quickly. 32 | 33 | } 35 | label="Import project" 36 | value="group" 37 | > 38 | Migrate your data from an external source like GitHub, Bitbucket, or another instance of 39 | GitLab. 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export default ProjectTypeForm; 48 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/WizardForm.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState } from 'react'; 4 | import { Steps, Divider, Stack, Button } from 'rsuite'; 5 | import PageContent from '@/components/PageContent'; 6 | import { FiChevronRight, FiChevronLeft } from 'react-icons/fi'; 7 | 8 | import ProjectTypeForm from './ProjectTypeForm'; 9 | import TeamSettingsForm from './TeamSettingsForm'; 10 | import BusinessDetailForm from './BusinessDetailForm'; 11 | import ProjectInfoForm from './ProjectInfoForm'; 12 | import Completed from './Completed'; 13 | 14 | const forms = [ProjectTypeForm, ProjectInfoForm, TeamSettingsForm, BusinessDetailForm, Completed]; 15 | 16 | const WizardForm = () => { 17 | const [step, setStep] = useState(0); 18 | const Form = forms[step]; 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | 36 | {step !== 0 && ( 37 | 40 | )} 41 | 42 | {step !== forms.length - 1 && ( 43 | 50 | )} 51 | 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default WizardForm; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rsuite-admin-template", 3 | "version": "1.0.0", 4 | "description": "React Suite Management System", 5 | "main": "lib/index.js", 6 | "directories": { 7 | "lib": "lib/" 8 | }, 9 | "scripts": { 10 | "dev": "next dev", 11 | "build": "next build", 12 | "start": "next start", 13 | "lint": "eslint src --ext .js,.jsx,.ts,.tsx" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "rsuite", 18 | "admin", 19 | "template" 20 | ], 21 | "files": [ 22 | "CHANGELOG.md", 23 | "lib", 24 | "src" 25 | ], 26 | "author": "HYPERS Team", 27 | "license": "MIT", 28 | "repository": { 29 | "type": "git", 30 | "url": "git@github.com:rsuite/rsuite-admin-template.git" 31 | }, 32 | "dependencies": { 33 | "@faker-js/faker": "^7.4.0", 34 | "@fullcalendar/core": "^6.1.15", 35 | "@fullcalendar/daygrid": "^6.1.15", 36 | "@fullcalendar/interaction": "^6.1.15", 37 | "@fullcalendar/react": "^6.1.15", 38 | "@fullcalendar/timegrid": "^6.1.15", 39 | "@rsuite/icons": "^1.4.0", 40 | "apexcharts": "^3.35.4", 41 | "classnames": "^2.3.1", 42 | "date-fns": "^4.0.0", 43 | "lodash": "^4.17.21", 44 | "next": "^16.0.0", 45 | "react": "^18.3.1", 46 | "react-apexcharts": "^1.9.0", 47 | "react-dom": "^18.3.1", 48 | "react-icons": "^5.5.0", 49 | "rsuite": "^6.0.1" 50 | }, 51 | "devDependencies": { 52 | "@types/lodash": "^4.14.195", 53 | "@types/node": "^20.10.0", 54 | "@types/react": "^18.3.0", 55 | "@types/react-dom": "^18.3.0", 56 | "@typescript-eslint/eslint-plugin": "^6.13.0", 57 | "@typescript-eslint/parser": "^6.13.0", 58 | "eslint": "^8.57.0", 59 | "eslint-config-prettier": "^9.0.0", 60 | "eslint-plugin-react": "^7.33.2", 61 | "eslint-plugin-react-hooks": "^4.6.0", 62 | "prettier": "^3.1.0", 63 | "typescript": "^5.3.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/BarChart.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import dynamic from 'next/dynamic'; 5 | import type { Props as ChartProps } from 'react-apexcharts'; 6 | const Chart = dynamic(() => import('react-apexcharts'), { ssr: false }); 7 | import { Panel, Stack } from 'rsuite'; 8 | 9 | interface BarChartProps { 10 | title?: React.ReactNode; 11 | actions?: React.ReactNode; 12 | data: ChartProps['series']; 13 | type?: ChartProps['type']; 14 | options?: ChartProps['options']; 15 | labels?: string[]; 16 | } 17 | 18 | const defaultOptions = { 19 | chart: { 20 | fontFamily: 'inherit', 21 | parentHeightOffset: 0, 22 | toolbar: { 23 | show: false 24 | }, 25 | animations: { 26 | enabled: false 27 | }, 28 | stacked: true 29 | }, 30 | plotOptions: { 31 | bar: { 32 | columnWidth: '50%' 33 | } 34 | }, 35 | dataLabels: { 36 | enabled: false 37 | }, 38 | fill: { 39 | opacity: 1 40 | }, 41 | grid: { 42 | padding: { 43 | top: -20, 44 | right: 0, 45 | left: -4, 46 | bottom: -4 47 | }, 48 | strokeDashArray: 4, 49 | xaxis: { 50 | lines: { 51 | show: true 52 | } 53 | } 54 | }, 55 | xaxis: { 56 | tooltip: { 57 | enabled: false 58 | }, 59 | axisBorder: { 60 | show: false 61 | }, 62 | type: 'datetime' 63 | }, 64 | yaxis: { 65 | labels: { 66 | padding: 4 67 | } 68 | }, 69 | colors: ['var(--chart-bar-series-1)', 'var(--chart-bar-series-2)', 'var(--chart-bar-series-3)'], 70 | legend: { 71 | show: false 72 | } 73 | }; 74 | 75 | const BarChart = ({ title, actions, data, type, labels, options }: BarChartProps) => ( 76 | 80 | {title} 81 | {actions} 82 | 83 | } 84 | > 85 | 91 | 92 | ); 93 | 94 | export default BarChart; 95 | -------------------------------------------------------------------------------- /src/app/(main)/calendar/EventModal.tsx: -------------------------------------------------------------------------------- 1 | import type { MouseEvent } from 'react'; 2 | import { Modal, Button, Form, DatePicker, ModalProps, Stack, Checkbox } from 'rsuite'; 3 | 4 | interface EventModalProps extends ModalProps { 5 | onAddEvent: (event: MouseEvent) => void; 6 | } 7 | 8 | const EventModal = (props: EventModalProps) => { 9 | const { onClose, open, onAddEvent, ...rest } = props; 10 | return ( 11 | 12 | 13 | Add a New Event 14 | 15 | 16 | 17 | 18 | Event Name 19 | 20 | 21 | 22 | Event Description 23 | 24 | 25 | 26 | Event Location 27 | 28 | 29 | 30 | Event Date 31 | 32 | 38 | 44 | All Day 45 | 46 | 47 | 48 | 49 | 50 | 53 | 56 | 57 | 58 | ); 59 | }; 60 | 61 | export default EventModal; 62 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/DataTable.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Table, Panel } from 'rsuite'; 4 | 5 | const { Column, HeaderCell, Cell } = Table; 6 | 7 | const data = [ 8 | { 9 | id: 1, 10 | url: 'https://rsuitejs.com', 11 | visits: '105,253', 12 | unique: '23,361', 13 | bounce: '11%' 14 | }, 15 | { 16 | id: 2, 17 | url: 'https://rsuitejs.com/components/overview/', 18 | visits: '103,643', 19 | unique: '23,385', 20 | bounce: '17%' 21 | }, 22 | { 23 | id: 3, 24 | url: 'https://rsuitejs.com/components/table/', 25 | visits: '140,013', 26 | unique: '41,256', 27 | bounce: '13%' 28 | }, 29 | { 30 | id: 4, 31 | url: 'https://rsuitejs.com/components/drawer/', 32 | visits: '194,532', 33 | unique: '19,038', 34 | bounce: '18%' 35 | }, 36 | { 37 | id: 5, 38 | url: 'https://rsuitejs.com/guide/usage/', 39 | visits: '26,353', 40 | unique: '1,000', 41 | bounce: '20%' 42 | }, 43 | { 44 | id: 6, 45 | url: 'https://rsuitejs.com/guide/customization/', 46 | visits: '11,973', 47 | unique: '4,786', 48 | bounce: '24%' 49 | } 50 | ]; 51 | 52 | const DataTable = () => { 53 | return ( 54 | 55 | 56 | 57 | PAGE NAME 58 | 59 | {rowData => { 60 | return ( 61 | 62 | {rowData.url} 63 | 64 | ); 65 | }} 66 | 67 | 68 | 69 | 70 | VISITORS 71 | 72 | 73 | 74 | 75 | UNIQUE 76 | 77 | 78 | 79 | 80 | BOUNCE RATE 81 | 82 | 83 |
84 |
85 | ); 86 | }; 87 | 88 | export default DataTable; 89 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/BusinessDetailForm.tsx: -------------------------------------------------------------------------------- 1 | import { Form, SelectPicker, Textarea } from 'rsuite'; 2 | import FormHeader from './FormHeader'; 3 | 4 | const BusinessDetailForm = () => { 5 | return ( 6 |
7 | 11 | 12 | 13 | Business Name 14 | 15 | 16 | 17 | 18 | Shortened Descriptor 19 | 20 | 21 | Customers will see this shortened version of your statement descriptor. 22 | 23 | 24 | 25 | 26 | Corporation Type 27 | 45 | 46 | Different team sizes will be assigned different management modes. Of course the fees are 47 | different. 48 | 49 | 50 | 51 | 52 | Business Description 53 | 54 | 55 | 56 | 57 | Contact Email 58 | 59 | 60 | 61 | ); 62 | }; 63 | 64 | export default BusinessDetailForm; 65 | -------------------------------------------------------------------------------- /src/app/(main)/calendar/Calendar.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState } from 'react'; 4 | import FullCalendar from '@fullcalendar/react'; 5 | import type { DateSelectArg, EventClickArg, EventContentArg } from '@fullcalendar/core'; 6 | import dayGridPlugin from '@fullcalendar/daygrid'; 7 | import timeGridPlugin from '@fullcalendar/timegrid'; 8 | import interactionPlugin from '@fullcalendar/interaction'; 9 | import PageContent from '@/components/PageContent'; 10 | import { INITIAL_EVENTS } from '@/data/calendarEvents'; 11 | import EventModal from './EventModal'; 12 | import './calendar.css'; 13 | 14 | const Calendar = () => { 15 | const [editable, setEditable] = useState(false); 16 | const handleDateSelect = (selectInfo: DateSelectArg) => { 17 | console.log(selectInfo); 18 | setEditable(true); 19 | }; 20 | 21 | const handleEventClick = (clickInfo: EventClickArg) => { 22 | console.log(clickInfo); 23 | setEditable(true); 24 | }; 25 | 26 | return ( 27 | 28 | 47 | setEditable(false)} 50 | onAddEvent={() => { 51 | setEditable(false); 52 | }} 53 | /> 54 | 55 | ); 56 | }; 57 | 58 | function renderEventContent(eventContent: EventContentArg) { 59 | const { timeText, event } = eventContent; 60 | return ( 61 | <> 62 | {timeText && ( 63 | <> 64 |
65 |
{eventContent.timeText}
66 | 67 | )} 68 |
{event.title}
69 | 70 | ); 71 | } 72 | 73 | export default Calendar; 74 | -------------------------------------------------------------------------------- /src/app/(main)/calendar/calendar.css: -------------------------------------------------------------------------------- 1 | .rs-container .fc .fc-button { 2 | display: inline-block; 3 | margin-bottom: 0; 4 | font-weight: 400; 5 | text-align: center; 6 | vertical-align: middle; 7 | cursor: pointer; 8 | white-space: nowrap; 9 | transition: all 0.3s ease-in-out; 10 | border: var(--rs-btn-default-border, none); 11 | user-select: none; 12 | text-decoration: none; 13 | color: var(--rs-btn-default-text); 14 | background-color: var(--rs-btn-default-bg); 15 | border-radius: 6px; 16 | box-shadow: none !important; 17 | text-transform: capitalize; 18 | padding: 8px 12px; 19 | font-size: 14px; 20 | line-height: 1.42857; 21 | } 22 | 23 | .rs-container .fc .fc-button:hover { 24 | color: var(--rs-btn-default-hover-text); 25 | background-color: var(--rs-btn-default-hover-bg); 26 | text-decoration: none; 27 | } 28 | 29 | .rs-container .fc .fc-button:active { 30 | color: var(--rs-btn-default-active-text); 31 | background-color: var(--rs-btn-default-active-bg); 32 | } 33 | 34 | .rs-container .fc .fc-button:disabled { 35 | cursor: not-allowed; 36 | color: var(--rs-btn-default-disabled-text); 37 | background-color: var(--rs-btn-default-disabled-bg); 38 | } 39 | 40 | .rs-container .fc .fc-button.fc-button-active { 41 | color: var(--rs-btn-default-active-text) !important; 42 | background-color: var(--rs-btn-default-active-bg) !important; 43 | } 44 | 45 | .rs-container .fc .fc-col-header-cell-cushion { 46 | font-weight: 500; 47 | color: var(--rs-text-primary); 48 | } 49 | 50 | .rs-container .fc .fc-daygrid-day-number { 51 | color: var(--rs-text-primary); 52 | } 53 | 54 | .rs-container .fc .fc-scrollgrid { 55 | border: none; 56 | } 57 | 58 | .rs-container .fc .fc-scrollgrid td, 59 | .rs-container .fc .fc-scrollgrid th { 60 | border: 1px solid var(--rs-border-secondary); 61 | } 62 | 63 | .rs-container .fc .fc-scrollgrid th { 64 | height: 36px; 65 | line-height: 36px; 66 | } 67 | 68 | .rs-container .fc .fc-daygrid-event { 69 | margin-top: 1px; 70 | } 71 | 72 | .rs-container .fc .fc-daygrid-dot-event { 73 | padding: 4px; 74 | background-color: var(--rs-btn-default-bg); 75 | color: var(--rs-text-primary); 76 | } 77 | 78 | .rs-container .fc .fc-event-main { 79 | padding: 4px; 80 | } 81 | 82 | .rs-container .fc .fc-h-event { 83 | background-color: var(--rs-btn-primary-bg); 84 | color: var(--rs-btn-primary-text); 85 | border-color: var(--rs-btn-primary-bg); 86 | } 87 | 88 | .rs-container .fc .fc-event-title { 89 | font-weight: 200; 90 | } 91 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/ProjectInfoForm.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Form, Stack, InputGroup, Textarea, RadioTile, RadioTileGroup } from 'rsuite'; 3 | import { VscLock, VscWorkspaceTrusted } from 'react-icons/vsc'; 4 | import FormHeader from './FormHeader'; 5 | 6 | const ProjectInfoForm = () => { 7 | const [level, setLevel] = React.useState('Private'); 8 | 9 | return ( 10 |
11 | 16 | 17 | 18 | Project Name 19 | 20 | Project names must be unique. 21 | 22 | 23 | 24 | Project URL 25 | 26 | 27 | https://rsuitejs.com/ 28 | 29 | 30 | 31 | Want to house several dependent projects under the same namespace? Create a group. 32 | 33 | 34 | 35 | 36 | Project description (optional) 37 | 38 | 39 | 40 | 41 | Visibility Level 42 | 43 | setLevel(String(value))}> 44 | } 46 | label="Private" 47 | value="Private" 48 | > 49 | Project access must be granted explicitly to each user. If this project is part of a 50 | group, access will be granted to members of the group. 51 | 52 | } 54 | label="Internal" 55 | value="Internal" 56 | > 57 | The project can be accessed by any logged in user except external users. 58 | 59 | 60 | 61 | 62 | 63 | ); 64 | }; 65 | 66 | export default ProjectInfoForm; 67 | -------------------------------------------------------------------------------- /src/config.tsx: -------------------------------------------------------------------------------- 1 | import { Icon } from '@rsuite/icons'; 2 | import { VscTable, VscCalendar } from 'react-icons/vsc'; 3 | import { MdFingerprint, MdDashboard, MdModeEditOutline } from 'react-icons/md'; 4 | import { FaCubes } from 'react-icons/fa6'; 5 | 6 | export const appNavs = [ 7 | { 8 | eventKey: 'dashboard', 9 | icon: , 10 | title: 'Dashboard', 11 | to: '/dashboard' 12 | }, 13 | { 14 | eventKey: 'calendar', 15 | icon: , 16 | title: 'Calendar', 17 | to: '/calendar' 18 | }, 19 | { 20 | eventKey: 'tables', 21 | icon: , 22 | title: 'Tables', 23 | to: '/table/members', 24 | children: [ 25 | { 26 | eventKey: 'members', 27 | title: 'Members', 28 | to: '/table/members' 29 | }, 30 | { 31 | eventKey: 'virtualized', 32 | title: 'Virtualized Table', 33 | to: '/table/virtualized' 34 | } 35 | ] 36 | }, 37 | { 38 | eventKey: 'forms', 39 | icon: , 40 | title: 'Forms', 41 | to: '/form/basic', 42 | children: [ 43 | { 44 | eventKey: 'form-basic', 45 | title: 'Basic', 46 | to: '/form/basic' 47 | }, 48 | { 49 | eventKey: 'form-wizard', 50 | title: 'Wizard', 51 | to: '/form/wizard' 52 | } 53 | ] 54 | }, 55 | { 56 | eventKey: 'authentication', 57 | title: 'Authentication', 58 | icon: , 59 | children: [ 60 | { 61 | eventKey: 'sign-in', 62 | title: 'Sign In', 63 | to: '/sign-in' 64 | }, 65 | 66 | { 67 | eventKey: 'sign-up', 68 | title: 'Sign Up', 69 | to: '/sign-up' 70 | }, 71 | { 72 | eventKey: 'error403', 73 | title: 'Error 403', 74 | to: '/error/403' 75 | }, 76 | { 77 | eventKey: 'error404', 78 | title: 'Error 404', 79 | to: '/error/404' 80 | }, 81 | { 82 | eventKey: 'error500', 83 | title: 'Error 500', 84 | to: '/error/500' 85 | }, 86 | { 87 | eventKey: 'error503', 88 | title: 'Error 503', 89 | to: '/error/503' 90 | } 91 | ] 92 | }, 93 | 94 | { 95 | eventKey: 'components', 96 | title: 'Components', 97 | icon: , 98 | href: 'https://rsuitejs.com/components/overview/', 99 | target: '_blank' 100 | } 101 | ]; 102 | -------------------------------------------------------------------------------- /src/app/(main)/table/members/DrawerView.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Drawer, 3 | DrawerProps, 4 | Button, 5 | Form, 6 | Stack, 7 | NumberInput, 8 | InputGroup, 9 | Slider, 10 | Rate 11 | } from 'rsuite'; 12 | 13 | const DrawerView = (props: DrawerProps) => { 14 | const { onClose, ...rest } = props; 15 | return ( 16 | 17 | 18 | Add a new member 19 | 20 | 23 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | First Name 34 | 35 | 36 | 37 | Last Name 38 | 39 | 40 | 41 | 42 | Email 43 | 44 | 45 | 46 | City 47 | 48 | 49 | 50 | Street 51 | 52 | 53 | 54 | 55 | Rating 56 | 57 | 58 | 59 | 60 | Skill Proficiency 61 | 62 | 63 | 64 | 65 | Income 66 | 67 | $ 68 | 69 | 70 | 71 |
72 |
73 |
74 | ); 75 | }; 76 | 77 | export default DrawerView; 78 | -------------------------------------------------------------------------------- /src/app/sign-up/SignUp.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Form, Button, Stack, Checkbox, PasswordInput } from 'rsuite'; 5 | 6 | import Link from 'next/link'; 7 | 8 | const SignUp = () => { 9 | return ( 10 |
11 |
12 |
13 |
RSUITE
14 |

Join Our Platform

15 |

16 | Create your account and start building amazing applications with our powerful admin 17 | template. 18 |

19 |
20 |
21 | 22 |
23 |
24 |
25 |

Create Account

26 |

27 | Already have an account? Sign in here 28 |

29 |
30 | 31 |
32 | 33 | Username 34 | 35 | 36 | 37 | 38 | Email 39 | 40 | 41 | 42 | 43 | Password 44 | 49 | 50 | 51 | 52 | Confirm Password 53 | 58 | 59 | 60 |
61 | 62 | I agree to the 63 | 64 | 65 |
66 | 67 | 68 | 71 | 72 |
73 |
74 |
75 |
76 | ); 77 | }; 78 | 79 | export default SignUp; 80 | -------------------------------------------------------------------------------- /src/data/mock.ts: -------------------------------------------------------------------------------- 1 | import { faker } from '@faker-js/faker/locale/en'; 2 | 3 | export function mockUsers(length: number) { 4 | const createRowData = (rowIndex: number) => { 5 | const firstName = faker.name.firstName(); 6 | const lastName = faker.name.lastName(); 7 | const gender = faker.name.gender(true) as 'female' | 'male'; 8 | 9 | const name = faker.name.fullName({ firstName, lastName, sex: gender }); 10 | const avatar = `https://i.pravatar.cc/150?u=${faker.datatype.number({ min: 1, max: 10000 })}`; 11 | 12 | const city = faker.address.city(); 13 | const street = faker.address.street(); 14 | const email = faker.internet.email(); 15 | const postcode = faker.address.zipCode(); 16 | const phone = faker.phone.number(); 17 | const amount = faker.finance.amount(1000, 90000); 18 | 19 | const age = Math.floor(Math.random() * 30) + 18; 20 | const stars = Math.floor(Math.random() * 10000); 21 | const followers = Math.floor(Math.random() * 10000); 22 | const rating = 2 + Math.floor(Math.random() * 3); 23 | const progress = Math.floor(Math.random() * 100); 24 | 25 | return { 26 | id: rowIndex + 1, 27 | name, 28 | firstName, 29 | lastName, 30 | avatar, 31 | city, 32 | street, 33 | postcode, 34 | email, 35 | phone, 36 | gender, 37 | age, 38 | stars, 39 | followers, 40 | rating, 41 | progress, 42 | amount 43 | }; 44 | }; 45 | 46 | return Array.from({ length }).map((_, index) => { 47 | return createRowData(index); 48 | }); 49 | } 50 | 51 | export function mockTreeData(options: { 52 | limits: number[]; 53 | labels: string | string[] | ((layer: number, value: string, faker: any) => string); 54 | getRowData?: (layer: number, value: string) => any[]; 55 | }) { 56 | const { limits, labels, getRowData } = options; 57 | const depth = limits.length; 58 | 59 | const data: any[] = []; 60 | const mock = (list: any[], parentValue?: string, layer = 0) => { 61 | const length = limits[layer]; 62 | 63 | Array.from({ length }).forEach((_, index) => { 64 | const value = parentValue ? parentValue + '-' + (index + 1) : index + 1 + ''; 65 | const children: any[] = []; 66 | const label = Array.isArray(labels) ? labels[layer] : labels; 67 | 68 | let row: any = { 69 | label: typeof label === 'function' ? label(layer, value, faker) : label + ' ' + value, 70 | value 71 | }; 72 | 73 | if (getRowData) { 74 | row = { 75 | ...row, 76 | ...getRowData(layer, value) 77 | }; 78 | } 79 | 80 | list.push(row); 81 | 82 | if (layer < depth - 1) { 83 | row.children = children; 84 | mock(children, value, layer + 1); 85 | } 86 | }); 87 | }; 88 | 89 | mock(data); 90 | 91 | return data; 92 | } 93 | -------------------------------------------------------------------------------- /src/data/calendarEvents.ts: -------------------------------------------------------------------------------- 1 | import type { EventInput } from '@fullcalendar/core'; 2 | import uniqueId from 'lodash/uniqueId'; 3 | import { startOfMonth, addDays, format, endOfMonth } from 'date-fns'; 4 | 5 | const today = new Date(); 6 | const firstDay = startOfMonth(today); 7 | const lastDay = endOfMonth(today); 8 | const todayStr = format(today, 'yyyy-MM-dd'); 9 | 10 | export const INITIAL_EVENTS: EventInput[] = [ 11 | { 12 | id: uniqueId(), 13 | title: '🎊 Project kick-off meeting', 14 | allDay: true, 15 | start: format(firstDay, 'yyyy-MM-dd') 16 | }, 17 | { 18 | id: uniqueId(), 19 | title: '🎉 Product launch', 20 | start: format(addDays(firstDay, 2), 'yyyy-MM-dd') + 'T10:00:00' 21 | }, 22 | 23 | { 24 | id: uniqueId(), 25 | title: 'Product training.', 26 | start: format(addDays(firstDay, 3), 'yyyy-MM-dd') + 'T10:00:00' 27 | }, 28 | { 29 | id: uniqueId(), 30 | title: 'Product Demo', 31 | start: format(addDays(firstDay, 3), 'yyyy-MM-dd') + 'T11:00:00' 32 | }, 33 | { 34 | id: uniqueId(), 35 | title: 'Product Exam', 36 | start: format(addDays(firstDay, 3), 'yyyy-MM-dd') + 'T12:00:00' 37 | }, 38 | 39 | { 40 | id: uniqueId(), 41 | title: 'Monitoring and alerting service design communication', 42 | start: format(addDays(firstDay, 5), 'yyyy-MM-dd') + 'T10:00:00' 43 | }, 44 | { 45 | id: uniqueId(), 46 | title: 'Design system brainstorming', 47 | start: format(addDays(firstDay, 5), 'yyyy-MM-dd') + 'T11:00:00' 48 | }, 49 | 50 | { 51 | id: uniqueId(), 52 | title: 'Test Case Review', 53 | start: format(addDays(firstDay, 15), 'yyyy-MM-dd') + 'T14:00:00' 54 | }, 55 | { 56 | id: uniqueId(), 57 | title: 'Development Design Review', 58 | start: format(addDays(firstDay, 15), 'yyyy-MM-dd') + 'T16:00:00' 59 | }, 60 | 61 | { 62 | id: uniqueId(), 63 | title: '💎 Product meeting', 64 | start: todayStr + 'T09:00:00', 65 | end: todayStr + 'T10:30:00' 66 | }, 67 | { 68 | id: uniqueId(), 69 | title: '👨‍💻 Coding ', 70 | start: todayStr + 'T10:00:00', 71 | end: todayStr + 'T11:30:00' 72 | }, 73 | { 74 | id: uniqueId(), 75 | title: '📖 Leadership training', 76 | start: todayStr + 'T12:00:00', 77 | end: todayStr + 'T14:00:00' 78 | }, 79 | { 80 | id: uniqueId(), 81 | title: '☕️ Afternoon tea time', 82 | start: todayStr + 'T14:00:00', 83 | end: todayStr + 'T16:00:00' 84 | }, 85 | { 86 | id: uniqueId(), 87 | title: 'Interview engineers.', 88 | start: todayStr + 'T16:00:00', 89 | end: todayStr + 'T18:00:00' 90 | }, 91 | { 92 | id: uniqueId(), 93 | title: '🎉 Product release', 94 | allDay: true, 95 | start: format(lastDay, 'yyyy-MM-dd') + 'T14:00:00' 96 | }, 97 | { 98 | id: uniqueId(), 99 | title: '🔬 Product acceptance', 100 | start: format(lastDay, 'yyyy-MM-dd') + 'T16:00:00' 101 | } 102 | ]; 103 | -------------------------------------------------------------------------------- /src/app/(main)/form/basic/form-basic.css: -------------------------------------------------------------------------------- 1 | /* Form Basic Page - Flat Clean Layout */ 2 | 3 | /* Header Section */ 4 | .form-basic-header { 5 | margin-bottom: 40px; 6 | } 7 | 8 | .form-basic-title { 9 | font-size: 1.75rem; 10 | font-weight: 600; 11 | color: var(--text-primary); 12 | margin-bottom: 8px; 13 | } 14 | 15 | .form-basic-description { 16 | font-size: 0.9375rem; 17 | color: var(--text-tertiary); 18 | line-height: 1.6; 19 | } 20 | 21 | .form-basic-description a { 22 | color: var(--link-color); 23 | text-decoration: none; 24 | } 25 | 26 | .form-basic-description a:hover { 27 | text-decoration: underline; 28 | } 29 | 30 | /* Form Sections */ 31 | .form-section { 32 | margin-bottom: 48px; 33 | width: 100%; 34 | } 35 | 36 | .form-section-title { 37 | font-size: 1.125rem; 38 | font-weight: 600; 39 | color: var(--text-secondary); 40 | margin-bottom: 16px; 41 | } 42 | 43 | .form-section .rs-divider { 44 | margin: 0 0 24px 0; 45 | } 46 | 47 | /* Full-width form groups (span both columns) */ 48 | .form-group-full { 49 | grid-column: 1 / -1; 50 | } 51 | .form-group-full.align-center { 52 | align-items: center; 53 | } 54 | .form-group-full.align-start { 55 | align-items: flex-start; 56 | } 57 | 58 | /* Form Group Styling */ 59 | .basic-form .rs-form-group { 60 | margin-bottom: 0; 61 | } 62 | 63 | .basic-form .rs-form-control-label-wrapper, 64 | .basic-form .rs-form-control-label { 65 | font-weight: 500; 66 | color: var(--form-label-color); 67 | margin-bottom: 8px; 68 | font-size: 0.875rem; 69 | } 70 | 71 | /* Checkbox and Radio Groups */ 72 | .basic-form .rs-checkbox-group, 73 | .basic-form .rs-radio-group { 74 | padding: 4px 0; 75 | } 76 | 77 | .basic-form .rs-checkbox, 78 | .basic-form .rs-radio { 79 | margin-right: 20px; 80 | font-size: 0.875rem; 81 | } 82 | 83 | /* Form Actions */ 84 | .form-actions { 85 | width: 100%; 86 | margin-top: 40px; 87 | padding-top: 24px; 88 | border-top: 1px solid var(--divider-color); 89 | display: flex; 90 | } 91 | 92 | .form-actions .rs-btn-toolbar { 93 | gap: 12px; 94 | } 95 | 96 | .form-actions .rs-btn { 97 | min-width: 100px; 98 | } 99 | 100 | /* Responsive Design */ 101 | @media (max-width: 968px) { 102 | .form-grid { 103 | grid-template-columns: 1fr; 104 | gap: 20px; 105 | } 106 | 107 | .form-group-full { 108 | grid-column: 1; 109 | } 110 | 111 | .form-basic-title { 112 | font-size: 1.5rem; 113 | } 114 | 115 | .form-section { 116 | margin-bottom: 40px; 117 | } 118 | 119 | .form-actions .rs-btn-toolbar { 120 | flex-direction: column; 121 | width: 100%; 122 | } 123 | 124 | .form-actions .rs-btn { 125 | width: 100%; 126 | } 127 | } 128 | 129 | @media (max-width: 576px) { 130 | .form-basic-header { 131 | margin-bottom: 32px; 132 | } 133 | 134 | .form-section { 135 | margin-bottom: 32px; 136 | } 137 | 138 | .form-grid { 139 | gap: 16px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/app/(main)/form/wizard/TeamSettingsForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Form, Stack, SelectPicker, RadioTile, RadioTileGroup } from 'rsuite'; 3 | import { FaGit, FaGithub, FaGitlab } from 'react-icons/fa'; 4 | 5 | import FormHeader from './FormHeader'; 6 | 7 | const TeamSettingsForm = () => { 8 | const [type, setType] = useState('1'); 9 | 10 | return ( 11 |
12 | 16 | 17 | 18 | Team Name 19 | 20 | 21 | 22 | 23 | Specify Team Size 24 | 5 people' }, 30 | { value: 2, label: '5-20 people' }, 31 | { value: 3, label: '20-50 people' }, 32 | { value: 4, label: '50+ people' } 33 | ]} 34 | block 35 | /> 36 | 37 | Different team sizes will be assigned different management modes. Of course the fees are 38 | different. 39 | 40 | 41 | 42 | 43 | Choose a team workflow 44 | 45 | setType(String(value))}> 46 | } 48 | label="Git Flow" 49 | value="1" 50 | > 51 | Considered to be a bit complicated and advanced for many of today’s projects, GitFlow 52 | enables parallel development where developers can work separately from the master branch 53 | on features where a feature branch is created from the master branch. 54 | 55 | } 57 | label="GitHub Flow" 58 | value="2" 59 | > 60 | GitHub Flow is a simpler alternative to GitFlow ideal for smaller teams as they don’t 61 | need to manage multiple versions. 62 | 63 | 64 | } 66 | label="GitLab Flow" 67 | value="3" 68 | > 69 | GitLab Flow is a simpler alternative to GitFlow that combines feature-driven development 70 | and feature branching with issue tracking. 71 | 72 | 73 | 74 | 75 | 76 | ); 77 | }; 78 | 79 | export default TeamSettingsForm; 80 | -------------------------------------------------------------------------------- /src/components/Frame/Frame.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | 'use client'; 3 | 4 | import React, { useState } from 'react'; 5 | import { Container, Sidebar, Sidenav, Content, Nav, useMediaQuery } from 'rsuite'; 6 | import NavLink from '../NavLink'; 7 | import Brand from '../Brand'; 8 | import SidebarFooter from './SidebarFooter'; 9 | import './frame.css'; 10 | 11 | const NavItem = (props: any) => { 12 | const { title, eventKey, ...rest } = props; 13 | return ( 14 | 15 | {title} 16 | 17 | ); 18 | }; 19 | 20 | export interface NavItemData { 21 | eventKey: string; 22 | title: string; 23 | icon?: any; 24 | to?: string; 25 | target?: string; 26 | children?: NavItemData[]; 27 | } 28 | 29 | export interface FrameProps { 30 | navs: NavItemData[]; 31 | children?: React.ReactNode; 32 | } 33 | 34 | const Frame = (props: FrameProps) => { 35 | const { navs } = props; 36 | const [expanded, setExpanded] = useState(true); 37 | const [activeKey, setActiveKey] = useState('1'); 38 | const [isMobile] = useMediaQuery('(max-width: 768px)'); 39 | const isExpanded = expanded && !isMobile; 40 | 41 | return ( 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 78 | 79 | setExpanded(!expanded)} /> 80 | 81 | 82 | 83 | 84 | {props.children} 85 | 86 | 87 | ); 88 | }; 89 | 90 | export default Frame; 91 | -------------------------------------------------------------------------------- /src/app/(main)/table/members/Cells.tsx: -------------------------------------------------------------------------------- 1 | import { Popover, Whisper, Checkbox, Menu, IconButton, Table, CellProps } from 'rsuite'; 2 | import { FiMoreVertical } from 'react-icons/fi'; 3 | 4 | const { Cell } = Table; 5 | 6 | export const NameCell = ({ rowData, dataKey, ...props }: CellProps) => { 7 | const speaker = ( 8 | 9 |

10 | Name: {rowData.name} 11 |

12 |

13 | Gender: {rowData.gender} 14 |

15 |

16 | City: {rowData.city} 17 |

18 |

19 | Street: {rowData.street} 20 |

21 |
22 | ); 23 | 24 | return ( 25 | 26 | 27 | {dataKey ? rowData[dataKey] : null} 28 | 29 | 30 | ); 31 | }; 32 | 33 | export const ImageCell = ({ rowData, dataKey, ...props }: CellProps) => ( 34 | 35 |
46 | 47 |
48 |
49 | ); 50 | 51 | export const CheckCell = ({ 52 | rowData, 53 | onChange, 54 | checkedKeys, 55 | dataKey, 56 | ...props 57 | }: Omit, 'onChange'> & { 58 | checkedKeys: number[]; 59 | onChange: (value: any, checked: boolean) => void; 60 | }) => ( 61 | 62 |
63 | onChange(value, checked)} 67 | checked={checkedKeys.some(item => item === rowData[dataKey!])} 68 | /> 69 |
70 |
71 | ); 72 | 73 | const renderMenu = ({ onClose, left, top, className }: any, ref: any) => { 74 | const handleSelect = (eventKey?: string | number) => { 75 | onClose(); 76 | console.log(eventKey); 77 | }; 78 | return ( 79 | 80 | 81 | Follow 82 | Sponsor 83 | Add to friends 84 | View Profile 85 | Block 86 | 87 | 88 | ); 89 | }; 90 | 91 | export const ActionCell = (props: any) => { 92 | return ( 93 | 94 | 95 | } /> 96 | 97 | 98 | ); 99 | }; 100 | -------------------------------------------------------------------------------- /src/app/sign-in/SignIn.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Form, Button, IconButton, Divider, PasswordInput } from 'rsuite'; 5 | import Link from 'next/link'; 6 | import GithubIcon from '@rsuite/icons/legacy/Github'; 7 | import FacebookIcon from '@rsuite/icons/legacy/Facebook'; 8 | import GoogleIcon from '@rsuite/icons/legacy/Google'; 9 | import WechatIcon from '@rsuite/icons/legacy/Wechat'; 10 | 11 | const SignIn = () => { 12 | return ( 13 |
14 |
15 |
16 |
RSUITE
17 |

Welcome to Admin Dashboard

18 |

19 | Build beautiful, modern web applications with our comprehensive React admin template. 20 |

21 |
22 |
23 | 24 |
25 |
26 |
27 |

Sign In

28 |

29 | New here? Create an account 30 |

31 |
32 | 33 |
34 | 35 | Username or email address 36 | 37 | 38 | 39 | 40 | 41 | Password 42 | 43 | Forgot password? 44 | 45 | 46 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | OR 60 | 61 |
62 | } 64 | circle 65 | appearance="subtle" 66 | className="auth-social-btn" 67 | /> 68 | } 70 | circle 71 | appearance="subtle" 72 | className="auth-social-btn" 73 | /> 74 | } 76 | circle 77 | appearance="subtle" 78 | className="auth-social-btn" 79 | /> 80 | } 82 | circle 83 | appearance="subtle" 84 | className="auth-social-btn" 85 | /> 86 |
87 |
88 |
89 |
90 |
91 | ); 92 | }; 93 | 94 | export default SignIn; 95 | -------------------------------------------------------------------------------- /src/components/Logo/Logo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, BoxProps } from 'rsuite'; 3 | import classNames from 'classnames'; 4 | import './logo.css'; 5 | 6 | interface LogoProps extends BoxProps { 7 | width?: number; 8 | height?: number; 9 | className?: string; 10 | style?: React.CSSProperties; 11 | } 12 | 13 | export default function Logo({ width, height, style, className = '', ...rest }: LogoProps) { 14 | const styles = { width, height, display: 'inline-block', ...style }; 15 | return ( 16 | 27 | 35 | React Suite 36 | 37 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 66 | 76 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /src/components/PageToolbar.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState, useRef } from 'react'; 4 | import { Affix, Stack, DateRangePicker, IconButton, SelectPicker } from 'rsuite'; 5 | import SettingIcon from '@rsuite/icons/Setting'; 6 | import { 7 | subDays, 8 | startOfWeek, 9 | endOfWeek, 10 | addDays, 11 | startOfMonth, 12 | endOfMonth, 13 | addMonths 14 | } from 'date-fns'; 15 | 16 | interface Range { 17 | label: string; 18 | // A fixed range or a function range that computes a date range from current value 19 | value: [Date, Date] | ((value?: [Date, Date] | null) => [Date, Date]); 20 | placement?: 'top' | 'bottom' | 'left' | 'right'; 21 | closeOverlay?: boolean; 22 | appearance?: 'default' | 'primary' | 'link' | 'subtle' | 'ghost'; 23 | } 24 | 25 | const predefinedRanges: Range[] = [ 26 | { 27 | label: 'Today', 28 | value: [new Date(), new Date()], 29 | placement: 'left' 30 | }, 31 | { 32 | label: 'Yesterday', 33 | value: [addDays(new Date(), -1), addDays(new Date(), -1)], 34 | placement: 'left' 35 | }, 36 | { 37 | label: 'This week', 38 | value: [startOfWeek(new Date()), endOfWeek(new Date())], 39 | placement: 'left' 40 | }, 41 | { 42 | label: 'Last 7 days', 43 | value: [subDays(new Date(), 6), new Date()], 44 | placement: 'left' 45 | }, 46 | { 47 | label: 'Last 30 days', 48 | value: [subDays(new Date(), 29), new Date()], 49 | placement: 'left' 50 | }, 51 | { 52 | label: 'This month', 53 | value: [startOfMonth(new Date()), new Date()], 54 | placement: 'left' 55 | }, 56 | { 57 | label: 'Last month', 58 | value: [startOfMonth(addMonths(new Date(), -1)), endOfMonth(addMonths(new Date(), -1))], 59 | placement: 'left' 60 | }, 61 | { 62 | label: 'This year', 63 | value: [new Date(new Date().getFullYear(), 0, 1), new Date()], 64 | placement: 'left' 65 | }, 66 | { 67 | label: 'Last year', 68 | value: [new Date(new Date().getFullYear() - 1, 0, 1), new Date(new Date().getFullYear(), 0, 0)], 69 | placement: 'left' 70 | }, 71 | { 72 | label: 'All time', 73 | value: [new Date(new Date().getFullYear() - 1, 0, 1), new Date()], 74 | placement: 'left' 75 | }, 76 | { 77 | label: 'Last week', 78 | closeOverlay: false, 79 | value: value => { 80 | const [start = new Date()] = value || []; 81 | return [ 82 | addDays(startOfWeek(start, { weekStartsOn: 0 }), -7), 83 | addDays(endOfWeek(start, { weekStartsOn: 0 }), -7) 84 | ]; 85 | }, 86 | appearance: 'default' 87 | }, 88 | { 89 | label: 'Next week', 90 | closeOverlay: false, 91 | value: value => { 92 | const [start = new Date()] = value || []; 93 | return [ 94 | addDays(startOfWeek(start, { weekStartsOn: 0 }), 7), 95 | addDays(endOfWeek(start, { weekStartsOn: 0 }), 7) 96 | ]; 97 | }, 98 | appearance: 'default' 99 | } 100 | ]; 101 | 102 | const PageToolbar = () => { 103 | const [fixed, setFixed] = useState(false); 104 | const containerRef = useRef(null); 105 | 106 | return ( 107 | 108 | 121 | 122 | containerRef.current as HTMLDivElement} 128 | data={[ 129 | { label: 'Daily', value: 'Daily' }, 130 | { label: 'Weekly', value: 'Weekly' }, 131 | { label: 'Monthly', value: 'Monthly' } 132 | ]} 133 | /> 134 | containerRef.current as HTMLDivElement} 140 | /> 141 | 142 | 143 | } /> 144 | 145 | 146 | ); 147 | }; 148 | 149 | export default PageToolbar; 150 | -------------------------------------------------------------------------------- /src/app/(main)/dashboard/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import dynamic from 'next/dynamic'; 3 | import './dashboard-chartist.css'; 4 | import './dashboard.css'; 5 | import Image from 'next/image'; 6 | import { Row, Col, Panel, SegmentedControl } from 'rsuite'; 7 | 8 | const BarChart = dynamic(() => import('./BarChart'), { ssr: false }); 9 | const PieChart = dynamic(() => import('./PieChart'), { ssr: false }); 10 | const DataTable = dynamic(() => import('./DataTable'), { ssr: false }); 11 | 12 | const barChartData = [ 13 | { 14 | name: 'Web', 15 | data: [ 16 | 11, 8, 9, 10, 3, 11, 11, 11, 12, 13, 2, 12, 5, 8, 22, 6, 8, 6, 4, 1, 8, 24, 29, 51, 40, 47, 17 | 23, 26, 50, 26, 22, 27, 46, 47, 81, 46, 40 18 | ] 19 | }, 20 | { 21 | name: 'Social', 22 | data: [ 23 | 7, 5, 4, 3, 3, 11, 4, 7, 5, 12, 12, 15, 13, 12, 6, 7, 7, 1, 5, 5, 2, 12, 4, 6, 18, 3, 5, 2, 24 | 13, 15, 20, 47, 18, 15, 11, 10, 9 25 | ] 26 | }, 27 | { 28 | name: 'Other', 29 | data: [ 30 | 4, 9, 11, 7, 8, 3, 6, 5, 5, 4, 6, 4, 11, 10, 3, 6, 7, 5, 2, 8, 4, 9, 9, 2, 6, 7, 5, 1, 8, 3, 31 | 12, 3, 4, 9, 7, 11, 10 32 | ] 33 | } 34 | ]; 35 | 36 | const Dashboard = () => { 37 | return ( 38 | <> 39 | 40 | 41 | 42 | Page Views 49 |
Page Views
50 |
281,358
51 |
52 | 53 | 54 | 55 | Visits 62 |
Visits
63 |
251,901
64 |
65 | 66 | 67 | 68 | Unique Visitors 75 |
Unique Visitors
76 |
25,135
77 |
78 | 79 |
80 | 81 | 82 | 83 | 94 | } 95 | data={barChartData} 96 | type="bar" 97 | labels={[ 98 | '2022-01-20', 99 | '2022-01-21', 100 | '2022-01-22', 101 | '2022-01-23', 102 | '2022-01-24', 103 | '2022-01-25', 104 | '2022-01-26', 105 | '2022-01-27', 106 | '2022-01-28', 107 | '2022-01-29', 108 | '2022-01-30', 109 | '2022-02-01', 110 | '2022-02-02', 111 | '2022-02-03', 112 | '2022-02-04', 113 | '2022-02-05', 114 | '2022-02-06', 115 | '2022-02-07', 116 | '2022-02-08', 117 | '2022-02-09', 118 | '2022-02-10', 119 | '2022-02-11', 120 | '2022-02-12', 121 | '2022-02-13', 122 | '2022-02-14', 123 | '2022-02-15', 124 | '2022-02-16', 125 | '2022-02-17', 126 | '2022-02-18', 127 | '2022-02-19', 128 | '2022-02-20', 129 | '2022-02-21', 130 | '2022-02-22', 131 | '2022-02-23', 132 | '2022-02-24', 133 | '2022-02-25', 134 | '2022-02-26' 135 | ]} 136 | /> 137 | 138 | 139 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 158 | 159 | 160 | 161 | ); 162 | }; 163 | 164 | export default Dashboard; 165 | -------------------------------------------------------------------------------- /src/app/auth.css: -------------------------------------------------------------------------------- 1 | /* Authentication Pages - Modern Split Screen Layout */ 2 | .auth-page { 3 | display: flex; 4 | min-height: 100vh; 5 | width: 100%; 6 | background: var(--auth-bg); 7 | } 8 | 9 | /* Left Banner Section */ 10 | .auth-banner { 11 | flex: 1; 12 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 13 | position: relative; 14 | display: flex; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | padding: 60px; 19 | overflow: hidden; 20 | } 21 | 22 | .auth-banner::before { 23 | content: ''; 24 | position: absolute; 25 | width: 500px; 26 | height: 500px; 27 | background: rgba(255, 255, 255, 0.1); 28 | border-radius: 50%; 29 | top: -150px; 30 | left: -150px; 31 | } 32 | 33 | .auth-banner::after { 34 | content: ''; 35 | position: absolute; 36 | width: 400px; 37 | height: 400px; 38 | background: rgba(255, 255, 255, 0.08); 39 | border-radius: 50%; 40 | bottom: -100px; 41 | right: -100px; 42 | } 43 | 44 | .auth-banner-content { 45 | position: relative; 46 | z-index: 2; 47 | text-align: center; 48 | color: #fff; 49 | } 50 | 51 | .auth-banner-logo { 52 | font-size: 3.5rem; 53 | font-weight: 800; 54 | margin-bottom: 24px; 55 | letter-spacing: -1px; 56 | } 57 | 58 | .auth-banner-title { 59 | font-size: 2rem; 60 | font-weight: 600; 61 | margin-bottom: 16px; 62 | line-height: 1.3; 63 | } 64 | 65 | .auth-banner-subtitle { 66 | font-size: 1.125rem; 67 | opacity: 0.95; 68 | line-height: 1.7; 69 | max-width: 420px; 70 | margin: 0 auto; 71 | } 72 | 73 | /* Right Form Section */ 74 | .auth-form-wrapper { 75 | flex: 1; 76 | display: flex; 77 | justify-content: center; 78 | align-items: center; 79 | padding: 40px; 80 | background: var(--auth-form-bg); 81 | } 82 | 83 | .auth-form-container { 84 | width: 100%; 85 | max-width: 440px; 86 | } 87 | 88 | .auth-form-header { 89 | margin-bottom: 48px; 90 | } 91 | 92 | .auth-form-title { 93 | font-size: 2rem; 94 | font-weight: 700; 95 | color: var(--auth-title-color); 96 | margin-bottom: 12px; 97 | } 98 | 99 | .auth-form-subtitle { 100 | font-size: 1rem; 101 | color: var(--auth-subtitle-color); 102 | margin-bottom: 8px; 103 | } 104 | 105 | .auth-form-subtitle a { 106 | color: var(--auth-link-color); 107 | font-weight: 600; 108 | text-decoration: none; 109 | transition: color 0.2s; 110 | } 111 | 112 | .auth-form-subtitle a:hover { 113 | color: var(--auth-link-hover); 114 | } 115 | 116 | /* Form Styling */ 117 | .auth-form .rs-form-group { 118 | margin-bottom: 24px; 119 | } 120 | 121 | .auth-form .rs-form-control-label, 122 | .auth-form .rs-form-control-label-wrapper { 123 | font-weight: 500; 124 | color: var(--text-secondary); 125 | margin-bottom: 8px; 126 | } 127 | 128 | .auth-form .rs-input, 129 | .auth-form .rs-input-group { 130 | height: 48px; 131 | font-size: 1rem; 132 | border-radius: 8px; 133 | } 134 | 135 | .auth-form .rs-input { 136 | border: 2px solid var(--auth-input-border); 137 | transition: all 0.2s; 138 | } 139 | 140 | .auth-form .rs-input:focus { 141 | border-color: var(--auth-input-focus-border); 142 | box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); 143 | } 144 | 145 | .auth-submit-btn { 146 | height: 52px; 147 | font-size: 1.0625rem; 148 | font-weight: 600; 149 | border-radius: 8px; 150 | margin-top: 8px; 151 | transition: all 0.3s; 152 | } 153 | 154 | .auth-submit-btn:hover { 155 | transform: translateY(-1px); 156 | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); 157 | } 158 | 159 | .auth-divider { 160 | margin: 32px 0; 161 | } 162 | 163 | .auth-divider .rs-divider-inner-text { 164 | color: var(--auth-divider-text); 165 | font-size: 0.875rem; 166 | font-weight: 500; 167 | } 168 | 169 | .auth-social-buttons { 170 | display: flex; 171 | justify-content: center; 172 | gap: 16px; 173 | } 174 | 175 | .auth-social-btn { 176 | border: 2px solid var(--auth-input-border); 177 | transition: all 0.2s; 178 | } 179 | 180 | .auth-social-btn:hover { 181 | border-color: var(--auth-link-color); 182 | background: var(--hover-bg); 183 | transform: translateY(-2px); 184 | } 185 | 186 | .forgot-password-link { 187 | margin-left: 0.5rem; 188 | color: var(--auth-link-color); 189 | font-size: 0.875rem; 190 | font-weight: 500; 191 | text-decoration: none; 192 | transition: color 0.2s; 193 | } 194 | 195 | .forgot-password-link:hover { 196 | color: var(--auth-link-hover); 197 | } 198 | 199 | .auth-checkbox-group { 200 | margin-top: 24px; 201 | margin-bottom: 24px; 202 | } 203 | 204 | .auth-checkbox-group .rs-checkbox { 205 | font-size: 0.9375rem; 206 | color: var(--auth-checkbox-color); 207 | } 208 | 209 | .auth-checkbox-group .rs-btn-link { 210 | padding: 0; 211 | font-size: 0.9375rem; 212 | color: var(--auth-link-color); 213 | font-weight: 500; 214 | } 215 | 216 | .auth-checkbox-group .rs-btn-link:hover { 217 | color: var(--auth-link-hover); 218 | } 219 | 220 | /* Responsive Design */ 221 | @media (max-width: 968px) { 222 | .auth-page { 223 | flex-direction: column; 224 | } 225 | 226 | .auth-banner { 227 | min-height: 280px; 228 | padding: 40px 20px; 229 | } 230 | 231 | .auth-banner-logo { 232 | font-size: 2.5rem; 233 | } 234 | 235 | .auth-banner-title { 236 | font-size: 1.5rem; 237 | } 238 | 239 | .auth-banner-subtitle { 240 | font-size: 1rem; 241 | } 242 | 243 | .auth-form-wrapper { 244 | padding: 32px 20px; 245 | } 246 | 247 | .auth-form-container { 248 | max-width: 100%; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | /* Theme CSS Variables */ 2 | :root[data-theme='light'], 3 | :root:not([data-theme]) { 4 | --bg-body: #f5f8fa; 5 | --bg-sidebar: #fff; 6 | --bg-panel: #fff; 7 | --text-primary: #272c36; 8 | --text-secondary: #2d3748; 9 | --text-muted: #575757; 10 | --text-tertiary: #718096; 11 | --border-color: #f2f2f5; 12 | --border-secondary: #e2e8f0; 13 | --divider-color: #e5e5ea; 14 | --hover-bg: rgba(0, 0, 0, 0.02); 15 | --active-color: #34c3ff; 16 | --link-color: #3498ff; 17 | --link-hover-color: #2980d9; 18 | 19 | /* Form colors */ 20 | --form-label-color: #575757; 21 | --form-border-color: #e2e8f0; 22 | --form-border-focus-color: #667eea; 23 | 24 | /* Chart colors */ 25 | --chart-label-color: #eee; 26 | --chart-stroke-color: #eee; 27 | --chart-grid-color: hsla(0, 0%, 100%, 0.2); 28 | --chart-bar-series-1: #206bc4; 29 | --chart-bar-series-2: #79a6dc; 30 | --chart-bar-series-3: #bfe399; 31 | --chart-sparkline-color: #ffffff; 32 | --chart-pie-series-1: #5f71e4; 33 | --chart-pie-series-2: #2dce88; 34 | --chart-pie-series-3: #fa6340; 35 | --chart-pie-series-4: #f5365d; 36 | --chart-pie-series-5: #13cdef; 37 | --chart-magenta-from: #ec407a; 38 | --chart-magenta-to: #d81b60; 39 | --chart-orange-from: #ffa726; 40 | --chart-orange-to: #fb8c00; 41 | --chart-azure-from: #26c6da; 42 | --chart-azure-to: #00acc1; 43 | 44 | /* Dashboard / cards */ 45 | --card-bg: var(--rs-card-bg); 46 | --colorful-chart-text: #ffffff; 47 | --colorful-chart-title: rgba(255, 255, 255, 0.5); 48 | --table-bg: var(--rs-card-bg); 49 | --tooltip-text: #575757; 50 | 51 | /* Dashboard gradients */ 52 | --bg-gradient-orange-from: #fb6340; 53 | --bg-gradient-orange-to: #fbb140; 54 | --bg-gradient-red-from: #f5365c; 55 | --bg-gradient-red-to: #f56036; 56 | --bg-gradient-green-from: #2dce89; 57 | --bg-gradient-green-to: #2dcecc; 58 | --bg-gradient-blue-from: #11cdef; 59 | --bg-gradient-blue-to: #1171ef; 60 | 61 | /* Toolbar */ 62 | --toolbar-shadow-elevated: 0 0 15px 0 rgb(0 0 0 / 10%); 63 | 64 | /* Auth page colors */ 65 | --auth-bg: #f5f8fa; 66 | --auth-form-bg: #fff; 67 | --auth-title-color: #1a202c; 68 | --auth-subtitle-color: #718096; 69 | --auth-input-border: #e2e8f0; 70 | --auth-input-focus-border: #667eea; 71 | --auth-link-color: #667eea; 72 | --auth-link-hover: #764ba2; 73 | --auth-divider-text: #a0aec0; 74 | --auth-checkbox-color: #4a5568; 75 | } 76 | 77 | :root[data-theme='dark'] { 78 | --bg-body: var(--rs-gray-900); 79 | --bg-sidebar: #272c36; 80 | --bg-panel: var(--rs-gray-800); 81 | --text-primary: #e8e8e8; 82 | --text-secondary: #d1d5db; 83 | --text-muted: #a0a0a0; 84 | --text-tertiary: #9ca3af; 85 | --border-color: #3a3f4b; 86 | --border-secondary: #4a5568; 87 | --divider-color: #3a3f4b; 88 | --hover-bg: rgba(255, 255, 255, 0.05); 89 | --active-color: #34c3ff; 90 | --link-color: #60a5fa; 91 | --link-hover-color: #93c5fd; 92 | 93 | /* Form colors */ 94 | --form-label-color: #d1d5db; 95 | --form-border-color: #4a5568; 96 | --form-border-focus-color: #818cf8; 97 | 98 | /* Chart colors */ 99 | --chart-label-color: #d1d5db; 100 | --chart-stroke-color: #d1d5db; 101 | --chart-grid-color: rgba(255, 255, 255, 0.1); 102 | --chart-bar-series-1: #60a5fa; 103 | --chart-bar-series-2: #38bdf8; 104 | --chart-bar-series-3: #4ade80; 105 | --chart-sparkline-color: #ffffff; 106 | --chart-pie-series-1: #818cf8; 107 | --chart-pie-series-2: #22c55e; 108 | --chart-pie-series-3: #fb923c; 109 | --chart-pie-series-4: #f97373; 110 | --chart-pie-series-5: #2dd4bf; 111 | --chart-magenta-from: #ec407a; 112 | --chart-magenta-to: #d81b60; 113 | --chart-orange-from: #f97316; 114 | --chart-orange-to: #ea580c; 115 | --chart-azure-from: #22d3ee; 116 | --chart-azure-to: #06b6d4; 117 | 118 | /* Dashboard / cards */ 119 | --card-bg: var(--rs-gray-800); 120 | --colorful-chart-text: #f9fafb; 121 | --colorful-chart-title: rgba(249, 250, 251, 0.7); 122 | --table-bg: var(--rs-gray-800); 123 | --tooltip-text: #e5e7eb; 124 | 125 | /* Dashboard gradients */ 126 | --bg-gradient-orange-from: #fb923c; 127 | --bg-gradient-orange-to: #fdba74; 128 | --bg-gradient-red-from: #f97373; 129 | --bg-gradient-red-to: #fb7185; 130 | --bg-gradient-green-from: #22c55e; 131 | --bg-gradient-green-to: #2dd4bf; 132 | --bg-gradient-blue-from: #38bdf8; 133 | --bg-gradient-blue-to: #6366f1; 134 | 135 | /* Toolbar */ 136 | --toolbar-shadow-elevated: 0 0 15px 0 rgb(0 0 0 / 40%); 137 | 138 | /* Auth page colors */ 139 | --auth-bg: #1a1d24; 140 | --auth-form-bg: #272c36; 141 | --auth-title-color: #e8e8e8; 142 | --auth-subtitle-color: #9ca3af; 143 | --auth-input-border: #4a5568; 144 | --auth-input-focus-border: #818cf8; 145 | --auth-link-color: #818cf8; 146 | --auth-link-hover: #a5b4fc; 147 | --auth-divider-text: #6b7280; 148 | --auth-checkbox-color: #d1d5db; 149 | } 150 | 151 | /* Custom styles */ 152 | body { 153 | background: var(--bg-body) !important; 154 | color: var(--text-primary); 155 | } 156 | 157 | .text-muted { 158 | color: var(--text-muted); 159 | } 160 | 161 | .brand { 162 | padding: 10px; 163 | font-size: 16px; 164 | white-space: nowrap; 165 | overflow: hidden; 166 | font-weight: bold; 167 | text-transform: uppercase; 168 | 169 | a { 170 | text-decoration: none; 171 | } 172 | } 173 | 174 | .card { 175 | background: var(--card-bg); 176 | margin-top: 30px; 177 | border-radius: 6px; 178 | } 179 | 180 | .card h3 { 181 | line-height: 22px; 182 | margin-bottom: 20px; 183 | font-size: 18px; 184 | } 185 | 186 | /* Generic page content wrapper */ 187 | .page-content { 188 | background: var(--card-bg); 189 | } 190 | -------------------------------------------------------------------------------- /public/images/pv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/app/(main)/table/members/DataTable.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState } from 'react'; 4 | import { 5 | Input, 6 | InputGroup, 7 | Table, 8 | Button, 9 | DOMHelper, 10 | Progress, 11 | Checkbox, 12 | Stack, 13 | SelectPicker 14 | } from 'rsuite'; 15 | import { FiSearch, FiMoreVertical } from 'react-icons/fi'; 16 | import DrawerView from './DrawerView'; 17 | import { mockUsers } from '@/data/mock'; 18 | import { NameCell, ImageCell, CheckCell, ActionCell } from './Cells'; 19 | import './members-table.css'; 20 | 21 | const data = mockUsers(20); 22 | 23 | const { Column, HeaderCell, Cell } = Table; 24 | const { getHeight } = DOMHelper; 25 | 26 | const ratingList = Array.from({ length: 5 }).map((_, index) => { 27 | return { 28 | value: index + 1, 29 | label: Array.from({ length: index + 1 }) 30 | .map(() => '⭐️') 31 | .join('') 32 | }; 33 | }); 34 | 35 | type Member = (typeof data)[number]; 36 | 37 | const DataTable = () => { 38 | const [showDrawer, setShowDrawer] = useState(false); 39 | const [checkedKeys, setCheckedKeys] = useState([]); 40 | const [sortColumn, setSortColumn] = useState(); 41 | const [sortType, setSortType] = useState<'asc' | 'desc' | undefined>(); 42 | const [searchKeyword, setSearchKeyword] = useState(''); 43 | const [rating, setRating] = useState(null); 44 | 45 | let checked = false; 46 | let indeterminate = false; 47 | 48 | if (checkedKeys.length === data.length) { 49 | checked = true; 50 | } else if (checkedKeys.length === 0) { 51 | checked = false; 52 | } else if (checkedKeys.length > 0 && checkedKeys.length < data.length) { 53 | indeterminate = true; 54 | } 55 | 56 | const handleCheckAll = (_value: unknown, checked: boolean) => { 57 | const keys = checked ? data.map(item => item.id) : []; 58 | setCheckedKeys(keys); 59 | }; 60 | const handleCheck = (value: number, checked: boolean) => { 61 | const keys = checked ? [...checkedKeys, value] : checkedKeys.filter(item => item !== value); 62 | setCheckedKeys(keys); 63 | }; 64 | 65 | const handleSortColumn = (dataKey: string, sortType?: 'asc' | 'desc') => { 66 | setSortColumn(dataKey as keyof Member); 67 | setSortType(sortType); 68 | }; 69 | 70 | const filteredData = () => { 71 | const filtered = data.filter(item => { 72 | if (!item.name.includes(searchKeyword)) { 73 | return false; 74 | } 75 | 76 | if (rating && item.rating !== rating) { 77 | return false; 78 | } 79 | 80 | return true; 81 | }); 82 | 83 | if (sortColumn && sortType) { 84 | return filtered.sort((a, b) => { 85 | let x = a[sortColumn] as string | number; 86 | let y = b[sortColumn] as string | number; 87 | 88 | if (typeof x === 'string') { 89 | x = x.charCodeAt(0); 90 | } 91 | if (typeof y === 'string') { 92 | y = y.charCodeAt(0); 93 | } 94 | 95 | if (sortType === 'asc') { 96 | return x - y; 97 | } else { 98 | return y - x; 99 | } 100 | }); 101 | } 102 | return filtered; 103 | }; 104 | 105 | return ( 106 | <> 107 | 108 | 111 | 112 | 113 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 136 | 137 | Id 138 | 139 | 140 | 141 | 142 | 143 |
144 | 150 |
151 |
152 | 153 |
154 | 155 | Avatar 156 | 157 | 158 | 159 | 160 | Name 161 | 162 | 163 | 164 | 165 | Skill Proficiency 166 | 167 | {rowData => } 168 | 169 | 170 | 171 | 172 | Rating 173 | 174 | {rowData => 175 | Array.from({ length: rowData.rating }).map((_, i) => ⭐️) 176 | } 177 | 178 | 179 | 180 | 181 | Income 182 | {rowData => `$${rowData.amount}`} 183 | 184 | 185 | 186 | Email 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |
197 | 198 | setShowDrawer(false)} /> 199 | 200 | ); 201 | }; 202 | 203 | export default DataTable; 204 | -------------------------------------------------------------------------------- /src/components/Frame/SidebarFooter.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useRef, useState, useEffect } from 'react'; 4 | import { 5 | Popover, 6 | Whisper, 7 | WhisperInstance, 8 | HStack, 9 | VStack, 10 | Avatar, 11 | Menu, 12 | Divider, 13 | Text, 14 | Sidenav, 15 | SegmentedControl 16 | } from 'rsuite'; 17 | import { 18 | BsSun, 19 | BsMoon, 20 | BsDisplay, 21 | BsPersonCircle, 22 | BsGear, 23 | BsQuestionCircle, 24 | BsBoxArrowRight 25 | } from 'react-icons/bs'; 26 | import { FiActivity } from 'react-icons/fi'; 27 | 28 | interface SidebarFooterProps { 29 | isExpanded: boolean; 30 | onToggle: () => void; 31 | } 32 | 33 | type ThemeMode = 'light' | 'dark' | 'system'; 34 | 35 | // User menu popover 36 | const renderUserMenu = ( 37 | { onClose, left, top, className }: any, 38 | ref: any, 39 | theme: ThemeMode, 40 | onThemeChange: (value: ThemeMode) => void 41 | ) => { 42 | const handleSelect = (eventKey?: string | number) => { 43 | onClose(); 44 | console.log(eventKey); 45 | }; 46 | 47 | return ( 48 | 49 | 50 | 51 | 52 | Administrator 53 | 54 | admin@example.com 55 | 56 | 57 | 58 | 59 | 60 | {/* Theme Switcher */} 61 | 62 | 63 | THEME 64 | 65 | onThemeChange(value as ThemeMode)} 68 | size="sm" 69 | block 70 | data={[ 71 | { 72 | label: ( 73 | 74 | 75 | System 76 | 77 | ), 78 | value: 'system' 79 | }, 80 | { 81 | label: ( 82 | 83 | 84 | Light 85 | 86 | ), 87 | value: 'light' 88 | }, 89 | { 90 | label: ( 91 | 92 | 93 | Dark 94 | 95 | ), 96 | value: 'dark' 97 | } 98 | ]} 99 | /> 100 | 101 | 102 | 103 | 104 | }> 105 | Profile & Account 106 | 107 | }> 108 | Set Status 109 | 110 | 111 | }> 112 | Settings 113 | 114 | }> 115 | Help & Support 116 | 117 | 118 | }> 119 | Sign Out 120 | 121 | 122 | 123 | ); 124 | }; 125 | 126 | const SidebarFooter: React.FC = ({ isExpanded }) => { 127 | const whisperRef = useRef(null); 128 | const [theme, setTheme] = useState('system'); 129 | 130 | // Apply theme on mount and when theme changes 131 | useEffect(() => { 132 | if (typeof window === 'undefined' || typeof document === 'undefined') { 133 | return; 134 | } 135 | 136 | const applyTheme = (mode: ThemeMode) => { 137 | const html = document.documentElement; 138 | 139 | if (mode === 'system') { 140 | // Check system preference 141 | const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 142 | html.setAttribute('data-theme', prefersDark ? 'dark' : 'light'); 143 | html.classList.toggle('rs-theme-dark', prefersDark); 144 | } else { 145 | html.setAttribute('data-theme', mode); 146 | html.classList.toggle('rs-theme-dark', mode === 'dark'); 147 | } 148 | }; 149 | 150 | // Load saved theme from localStorage 151 | const savedTheme = 152 | typeof window !== 'undefined' ? (localStorage.getItem('theme') as ThemeMode | null) : null; 153 | if (savedTheme) { 154 | setTheme(savedTheme); 155 | applyTheme(savedTheme); 156 | } else { 157 | applyTheme('system'); 158 | } 159 | 160 | // Listen for system theme changes when in system mode 161 | const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); 162 | const handleSystemThemeChange = (e: MediaQueryListEvent) => { 163 | if (theme === 'system') { 164 | const html = document.documentElement; 165 | html.setAttribute('data-theme', e.matches ? 'dark' : 'light'); 166 | html.classList.toggle('rs-theme-dark', e.matches); 167 | } 168 | }; 169 | 170 | mediaQuery.addEventListener('change', handleSystemThemeChange); 171 | return () => mediaQuery.removeEventListener('change', handleSystemThemeChange); 172 | }, [theme]); 173 | 174 | const handleThemeChange = (newTheme: ThemeMode) => { 175 | setTheme(newTheme); 176 | localStorage.setItem('theme', newTheme); 177 | 178 | const html = document.documentElement; 179 | if (newTheme === 'system') { 180 | const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 181 | html.setAttribute('data-theme', prefersDark ? 'dark' : 'light'); 182 | html.classList.toggle('rs-theme-dark', prefersDark); 183 | } else { 184 | html.setAttribute('data-theme', newTheme); 185 | html.classList.toggle('rs-theme-dark', newTheme === 'dark'); 186 | } 187 | }; 188 | 189 | return ( 190 | 191 | renderUserMenu(props, ref, theme, handleThemeChange)} 196 | > 197 | 205 | 206 | {isExpanded && ( 207 | 208 | 209 | Administrator 210 | 211 | 212 | admin@example.com 213 | 214 | 215 | )} 216 | 217 | 218 | 219 | ); 220 | }; 221 | 222 | export default SidebarFooter; 223 | -------------------------------------------------------------------------------- /public/images/errors/403.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /public/images/uv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 11 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/app/(main)/form/basic/BasicForm.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { 4 | Form, 5 | RadioGroup, 6 | Radio, 7 | Checkbox, 8 | CheckboxGroup, 9 | DatePicker, 10 | DateRangePicker, 11 | CheckPicker, 12 | SelectPicker, 13 | TagPicker, 14 | Textarea, 15 | TagInput, 16 | MaskedInput, 17 | InputPicker, 18 | NumberInput, 19 | Cascader, 20 | MultiCascader, 21 | Rate, 22 | Uploader, 23 | TreePicker, 24 | CheckTreePicker, 25 | ButtonToolbar, 26 | Button, 27 | Toggle, 28 | AutoComplete, 29 | Divider 30 | } from 'rsuite'; 31 | import PageContent from '@/components/PageContent'; 32 | import './form-basic.css'; 33 | 34 | import { mockTreeData } from '@/data/mock'; 35 | 36 | const treeData = mockTreeData({ limits: [2, 3, 3], labels: ['Provincial', 'County', 'Town'] }); 37 | const selectData = ['Eugenia', 'Bryan', 'Linda', 'Nancy', 'Lloyd', 'Alice'].map(item => ({ 38 | label: item, 39 | value: item 40 | })); 41 | 42 | const BasicForm = () => { 43 | return ( 44 | 45 |
46 |

Form Components

47 |

48 | Comprehensive showcase of RSuite form components. For detailed documentation, visit{' '} 49 | 50 | RSuite Form Docs 51 | 52 | . 53 |

54 |
55 | 56 |
57 | {/* Text Input Section */} 58 |
59 |

Text Input Controls

60 | 61 | 62 | 63 | 64 | Input 65 | 66 | 67 | 68 | 69 | Masked Input 70 | 91 | 92 | 93 | 94 | Tag Input 95 | 96 | 97 | 98 | 99 | Number Input 100 | 101 | 102 | 103 | 104 | Auto Complete 105 | 111 | 112 | 113 | 114 | Textarea 115 | 121 | 122 | 123 |
124 | 125 | {/* Selection Controls */} 126 |
127 |

Selection Controls

128 | 129 | 130 | 131 | Checkbox Group 132 | 133 | HTML 134 | CSS 135 | Javascript 136 | 137 | 138 | 139 | 140 | Radio Group 141 | 142 | HTML 143 | CSS 144 | Javascript 145 | 146 | 147 | 148 | 149 | Rate 150 | 151 | 152 | 153 | 154 | Toggle 155 | 156 | 157 | 158 |
159 | 160 | {/* Date & Time Pickers */} 161 |
162 |

Date & Time Pickers

163 | 164 | 165 | 166 | Date Picker 167 | 173 | 174 | 175 | 176 | Date Range Picker 177 | 183 | 184 | 185 |
186 | 187 | {/* Picker Components */} 188 |
189 |

Picker Components

190 | 191 | 192 | 193 | Select Picker 194 | 201 | 202 | 203 | 204 | Check Picker 205 | 212 | 213 | 214 | 215 | Tag Picker 216 | 223 | 224 | 225 | 226 | Input Picker 227 | 234 | 235 | 236 |
237 | 238 | {/* Tree & Cascader Components */} 239 |
240 |

Tree & Cascader Components

241 | 242 | 243 | 244 | Cascader 245 | 252 | 253 | 254 | 255 | Multi Cascader 256 | 263 | 264 | 265 | 266 | Tree Picker 267 | 274 | 275 | 276 | 277 | Check Tree Picker 278 | 285 | 286 | 287 |
288 | 289 | {/* File Upload */} 290 |
291 |

File Upload

292 | 293 | 294 | 295 | Uploader 296 | 297 | 298 | 299 |
300 | 301 | {/* Form Actions */} 302 |
303 | 304 | 307 | 310 | 311 |
312 |
313 |
314 | ); 315 | }; 316 | 317 | export default BasicForm; 318 | -------------------------------------------------------------------------------- /src/app/(main)/table/members/users.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | id: 1, 4 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/justinrob/128.jpg', 5 | city: 'New Amieshire', 6 | email: 'Leora13@yahoo.com', 7 | firstName: 'Ernest Schuppe', 8 | lastName: 'Schuppe', 9 | street: 'Ratke Port', 10 | zipCode: '17026-3154', 11 | date: '2016-09-23T07:57:40.195Z', 12 | bs: 'global drive functionalities', 13 | catchPhrase: 'Intuitive impactful software', 14 | companyName: 'Lebsack - Nicolas', 15 | words: 'saepe et omnis', 16 | sentence: 'Quos aut sunt id nihil qui.', 17 | stars: 820, 18 | followers: 70 19 | }, 20 | { 21 | id: 2, 22 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/thaisselenator_/128.jpg', 23 | city: 'New Gust', 24 | email: 'Mose_Gerhold51@yahoo.com', 25 | firstName: 'Janis', 26 | lastName: 'Vandervort', 27 | street: 'Dickinson Keys', 28 | zipCode: '43767', 29 | date: '2017-03-06T09:59:12.551Z', 30 | bs: 'e-business maximize bandwidth', 31 | catchPhrase: 'De-engineered discrete secured line', 32 | companyName: 'Glover - Hermiston', 33 | words: 'deleniti dolor nihil', 34 | sentence: 'Illo quidem libero corporis laborum.', 35 | stars: 1200, 36 | followers: 170 37 | }, 38 | { 39 | id: 3, 40 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/arpitnj/128.jpg', 41 | city: 'Lefflerstad', 42 | email: 'Frieda.Sauer61@gmail.com', 43 | firstName: 'Makenzie', 44 | lastName: 'Bode', 45 | street: 'Legros Divide', 46 | zipCode: '54812', 47 | date: '2016-12-08T13:44:26.557Z', 48 | bs: 'plug-and-play e-enable content', 49 | catchPhrase: 'Ergonomic 6th generation challenge', 50 | companyName: 'Williamson - Kassulke', 51 | words: 'quidem earum magnam', 52 | sentence: 'Nam qui perferendis ut rem vitae saepe.', 53 | stars: 610, 54 | followers: 170 55 | }, 56 | { 57 | id: 4, 58 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/brajeshwar/128.jpg', 59 | city: 'East Catalina', 60 | email: 'Eloisa.OHara@hotmail.com', 61 | firstName: 'Ciara', 62 | lastName: 'Towne', 63 | street: 'Schimmel Ramp', 64 | zipCode: '76315-2246', 65 | date: '2016-07-19T12:54:30.994Z', 66 | bs: 'extensible innovate e-business', 67 | catchPhrase: 'Upgradable local model', 68 | companyName: 'Hilpert, Eichmann and Brown', 69 | words: 'exercitationem rerum sit', 70 | sentence: 'Qui rerum ipsa atque qui.', 71 | stars: 5322, 72 | followers: 170 73 | }, 74 | { 75 | id: 5, 76 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/dev_essentials/128.jpg', 77 | city: 'Ritchieborough', 78 | email: 'Brisa46@hotmail.com', 79 | firstName: 'Suzanne', 80 | lastName: 'Wolff', 81 | street: 'Lemuel Radial', 82 | zipCode: '88870-3897', 83 | date: '2017-02-23T17:11:53.875Z', 84 | bs: 'back-end orchestrate networks', 85 | catchPhrase: 'Exclusive human-resource knowledge base', 86 | companyName: 'Mayer - Considine', 87 | words: 'voluptatum tempore at', 88 | sentence: 'Enim quia deleniti molestiae aut.', 89 | stars: 852, 90 | followers: 770 91 | }, 92 | { 93 | id: 6, 94 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/petrangr/128.jpg', 95 | city: 'Lake Emery', 96 | email: 'Cody.Schultz56@gmail.com', 97 | firstName: 'Alessandra', 98 | lastName: 'Feeney', 99 | street: 'Mosciski Estate', 100 | zipCode: '81514', 101 | date: '2016-06-30T05:23:18.734Z', 102 | bs: 'sexy evolve technologies', 103 | catchPhrase: 'Virtual hybrid throughput', 104 | companyName: 'Nikolaus and Sons', 105 | words: 'alias minus repudiandae', 106 | sentence: 'Sed qui eius excepturi sunt voluptates.', 107 | stars: 3209, 108 | followers: 2780 109 | }, 110 | { 111 | id: 7, 112 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/knilob/128.jpg', 113 | city: 'East Dejuan', 114 | email: 'Enrico_Beer@yahoo.com', 115 | firstName: 'Margret', 116 | lastName: 'Heller', 117 | street: 'Gunner Drive', 118 | zipCode: '17423-9317', 119 | date: '2017-03-13T21:09:47.253Z', 120 | bs: 'wireless morph synergies', 121 | catchPhrase: 'Profit-focused radical help-desk', 122 | companyName: 'Corwin, Maggio and Wintheiser', 123 | words: 'temporibus possimus neque', 124 | sentence: 'Eum amet ea non natus qui assumenda illo officia qui.', 125 | stars: 9920, 126 | followers: 570 127 | }, 128 | { 129 | id: 8, 130 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/tom_even/128.jpg', 131 | city: 'Schummtown', 132 | email: 'Mitchel.Herman@yahoo.com', 133 | firstName: 'Emiliano', 134 | lastName: 'Moore', 135 | street: 'Maria Junctions', 136 | zipCode: '33930-7081', 137 | date: '2016-03-27T07:26:57.345Z', 138 | bs: 'customized integrate e-tailers', 139 | catchPhrase: 'Total system-worthy contingency', 140 | companyName: 'Gulgowski - Botsford', 141 | words: 'deleniti ipsa hic', 142 | sentence: 'Ducimus id quaerat neque.', 143 | stars: 3820, 144 | followers: 880 145 | }, 146 | { 147 | id: 9, 148 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/chandlervdw/128.jpg', 149 | city: 'Gilberthaven', 150 | email: 'Gaylord_Reichel16@yahoo.com', 151 | firstName: 'Alessandra', 152 | lastName: 'Smith', 153 | street: 'Kali Spurs', 154 | zipCode: '01370', 155 | date: '2017-01-24T22:11:53.835Z', 156 | bs: 'extensible repurpose action-items', 157 | catchPhrase: 'Virtual dedicated definition', 158 | companyName: 'Maggio LLC', 159 | words: 'libero unde est', 160 | sentence: 'Non adipisci hic laboriosam et qui laudantium aspernatur.', 161 | stars: 330, 162 | followers: 590 163 | }, 164 | { 165 | id: 10, 166 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/mwarkentin/128.jpg', 167 | city: 'Felicitychester', 168 | email: 'Eileen48@gmail.com', 169 | firstName: 'Eldridge', 170 | lastName: 'Bins', 171 | street: 'Casper Squares', 172 | zipCode: '80025-1552', 173 | date: '2016-07-20T05:59:45.630Z', 174 | bs: 'cutting-edge expedite partnerships', 175 | catchPhrase: 'Organic user-facing functionalities', 176 | companyName: 'Leffler, Cummerata and Price', 177 | words: 'sed exercitationem quas', 178 | sentence: 'Voluptas dolorem quasi aspernatur.', 179 | stars: 923, 180 | followers: 704 181 | }, 182 | { 183 | id: 11, 184 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/gipsy_raf/128.jpg', 185 | city: 'Caleighhaven', 186 | email: 'Rico_Nolan@hotmail.com', 187 | firstName: 'Claude', 188 | lastName: 'Hermiston', 189 | street: 'Bode Pine', 190 | zipCode: '76773', 191 | date: '2017-03-13T08:02:41.211Z', 192 | bs: 'back-end innovate infomediaries', 193 | catchPhrase: 'Stand-alone global customer loyalty', 194 | companyName: 'Heller, Rosenbaum and Lockman', 195 | words: 'ut quia ut', 196 | sentence: 'Eos consequatur magni incidunt.', 197 | stars: 421, 198 | followers: 403 199 | }, 200 | { 201 | id: 12, 202 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/knilob/128.jpg', 203 | city: 'Herzogmouth', 204 | email: 'Dawn_Metz@yahoo.com', 205 | firstName: 'Clarabelle', 206 | lastName: 'Ankunding', 207 | street: 'Nolan Summit', 208 | zipCode: '04355', 209 | date: '2016-07-09T09:07:34.744Z', 210 | bs: 'granular deliver relationships', 211 | catchPhrase: 'Multi-lateral zero defect analyzer', 212 | companyName: 'Mante, Oberbrunner and Collins', 213 | words: 'eos fuga repellat', 214 | sentence: 'Cum corporis molestias quia.', 215 | stars: 8203, 216 | followers: 704 217 | }, 218 | { 219 | id: 13, 220 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/kirangopal/128.jpg', 221 | city: 'Eulaliabury', 222 | email: 'Ron.Franecki@gmail.com', 223 | firstName: 'Hubert', 224 | lastName: 'Boehm', 225 | street: 'Anastacio Springs', 226 | zipCode: '91444', 227 | date: '2016-04-22T16:37:24.331Z', 228 | bs: 'one-to-one transition methodologies', 229 | catchPhrase: 'Switchable asymmetric function', 230 | companyName: 'Greenholt, Homenick and Considine', 231 | words: 'sed incidunt quo', 232 | sentence: 'Sed adipisci aliquam ut eius ut ipsa consequatur.', 233 | stars: 8209, 234 | followers: 909 235 | }, 236 | { 237 | id: 14, 238 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/kerem/128.jpg', 239 | city: 'East Alice', 240 | email: 'Hayley52@yahoo.com', 241 | firstName: 'Vladimir', 242 | lastName: 'Breitenberg', 243 | street: 'Lula Port', 244 | zipCode: '04635', 245 | date: '2016-09-26T01:25:23.057Z', 246 | bs: 'virtual monetize communities', 247 | catchPhrase: 'Mandatory user-facing methodology', 248 | companyName: 'Kshlerin - Pfeffer', 249 | words: 'eaque enim unde', 250 | sentence: 'Sed voluptas fugiat nihil delectus architecto et voluptatibus quis voluptas.', 251 | stars: 8251, 252 | followers: 178 253 | }, 254 | { 255 | id: 15, 256 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/layerssss/128.jpg', 257 | city: 'East Frankie', 258 | email: 'Duane.Rempel@hotmail.com', 259 | firstName: 'Haylee', 260 | lastName: 'Purdy', 261 | street: 'Dena Walk', 262 | zipCode: '94111-0802', 263 | date: '2016-11-26T16:36:38.472Z', 264 | bs: 'enterprise drive users', 265 | catchPhrase: 'Customizable non-volatile paradigm', 266 | companyName: 'Lemke, Mitchell and Harber', 267 | words: 'dolores ipsum earum', 268 | sentence: 'Nemo molestiae ad sit cupiditate neque.', 269 | stars: 3099, 270 | followers: 707 271 | }, 272 | { 273 | id: 16, 274 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/dreizle/128.jpg', 275 | city: 'New Kendall', 276 | email: 'Eddie_Bartell@hotmail.com', 277 | firstName: 'Herminia', 278 | lastName: 'Altenwerth', 279 | street: 'Kshlerin Cape', 280 | zipCode: '86614-9727', 281 | date: '2016-09-28T19:50:18.308Z', 282 | bs: 'cutting-edge target models', 283 | catchPhrase: 'Triple-buffered fault-tolerant concept', 284 | companyName: 'Gislason - Nicolas', 285 | words: 'perferendis magnam minima', 286 | sentence: 'Fuga in dolorem vel eligendi deserunt voluptatem.', 287 | stars: 8491, 288 | followers: 463 289 | }, 290 | { 291 | id: 17, 292 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/nessoila/128.jpg', 293 | city: 'Port Whitney', 294 | email: 'Josephine_Legros@yahoo.com', 295 | firstName: 'Erick', 296 | lastName: 'Klein', 297 | street: 'Megane Cliffs', 298 | zipCode: '42168', 299 | date: '2016-04-02T05:03:42.377Z', 300 | bs: 'user-centric leverage experiences', 301 | catchPhrase: 'Centralized systematic parallelism', 302 | companyName: 'Olson and Sons', 303 | words: 'facere est in', 304 | sentence: 'Ducimus aliquid ut.', 305 | stars: 9820, 306 | followers: 670 307 | }, 308 | { 309 | id: 18, 310 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/mrebay007/128.jpg', 311 | city: 'West Meda', 312 | email: 'Jared.Hudson@hotmail.com', 313 | firstName: 'Lisandro', 314 | lastName: 'Barton', 315 | street: 'Torrance Union', 316 | zipCode: '19477', 317 | date: '2016-08-01T14:24:45.209Z', 318 | bs: 'open-source exploit markets', 319 | catchPhrase: 'Open-source impactful framework', 320 | companyName: 'Volkman and Sons', 321 | words: 'a tempore hic', 322 | sentence: 'Quod veniam nemo impedit mollitia.', 323 | stars: 1220, 324 | followers: 708 325 | }, 326 | { 327 | id: 19, 328 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/brenton_clarke/128.jpg', 329 | city: 'Darrenport', 330 | email: 'Delpha.Tromp9@yahoo.com', 331 | firstName: 'Ashton', 332 | lastName: 'Daugherty', 333 | street: 'Hermann Port', 334 | zipCode: '25133-9181', 335 | date: '2016-07-29T09:49:39.424Z', 336 | bs: 'wireless optimize deliverables', 337 | catchPhrase: 'Ergonomic human-resource algorithm', 338 | companyName: 'Grady LLC', 339 | words: 'libero ut repellat', 340 | sentence: 'Vel quod ullam.', 341 | stars: 420, 342 | followers: 30 343 | }, 344 | { 345 | id: 20, 346 | avartar: 'https://s3.amazonaws.com/uifaces/faces/twitter/josep_martins/128.jpg', 347 | city: 'Janiyahaven', 348 | email: 'Ariel.Maggio9@yahoo.com', 349 | firstName: 'Cassandra', 350 | lastName: 'Schmitt', 351 | street: 'Windler Lodge', 352 | zipCode: '87582-2944', 353 | date: '2017-01-21T12:35:27.741Z', 354 | bs: 'holistic cultivate relationships', 355 | catchPhrase: 'Enterprise-wide system-worthy data-warehouse', 356 | companyName: 'Ankunding Group', 357 | words: 'blanditiis voluptates repellat', 358 | sentence: 'Non quis non dignissimos sit rerum voluptatem culpa quibusdam.', 359 | stars: 20, 360 | followers: 188 361 | } 362 | ]; 363 | -------------------------------------------------------------------------------- /public/images/vv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 6 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /public/images/errors/500.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Group 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | --------------------------------------------------------------------------------