├── vite-env.d.ts
├── src
├── routes
│ ├── components
│ │ ├── index.ts
│ │ └── RouterLink.tsx
│ └── hooks
│ │ ├── index.ts
│ │ ├── use-pathname.ts
│ │ └── use-router.ts
├── _mock
│ └── index.ts
├── components
│ ├── logo
│ │ ├── index.ts
│ │ └── classes.ts
│ ├── label
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── iconify
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ ├── types.ts
│ │ ├── Iconify.tsx
│ │ └── FlagIcon.tsx
│ ├── scrollbar
│ │ ├── index.ts
│ │ ├── classes.ts
│ │ ├── styles.css
│ │ ├── types.ts
│ │ └── Scrollbar.tsx
│ ├── svg-color
│ │ ├── index.ts
│ │ ├── classes.ts
│ │ ├── types.ts
│ │ └── SvgColor.tsx
│ ├── color-utils
│ │ ├── index.ts
│ │ ├── types.ts
│ │ └── ColorPreview.tsx
│ ├── chart
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ ├── types.ts
│ │ ├── chart.tsx
│ │ ├── styles.css
│ │ └── ChartLegends.tsx
│ ├── controls
│ │ ├── Input.tsx
│ │ ├── Button.tsx
│ │ ├── Checkbox.tsx
│ │ ├── Select.tsx
│ │ ├── RadioGroup.tsx
│ │ └── SnapNotice.tsx
│ ├── sb
│ │ ├── button.css
│ │ ├── Button.tsx
│ │ ├── InboxScreen.tsx
│ │ ├── Task.tsx
│ │ └── TaskList.tsx
│ ├── table
│ │ ├── TableEmptyRows.tsx
│ │ └── TableNoData.tsx
│ ├── form
│ │ └── use-form.ts
│ ├── product
│ │ ├── CartIcon.tsx
│ │ ├── ProductItem.tsx
│ │ └── ProductSort.tsx
│ ├── blog
│ │ ├── PostSearch.tsx
│ │ └── PostSort.tsx
│ ├── analytics
│ │ ├── AnalyticsWebsiteVisits.tsx
│ │ ├── AnalyticsCurrentSubject.tsx
│ │ ├── AnalyticsConversionRates.tsx
│ │ ├── AnalyticsTrafficBySite.tsx
│ │ ├── AnalyticsCurrentVisits.tsx
│ │ ├── AnalyticsOrderTimeline.tsx
│ │ └── AnalyticsNews.tsx
│ ├── order
│ │ ├── OrderTableToolbar.tsx
│ │ └── OrderableHead.tsx
│ ├── customer
│ │ ├── CustomerTableToolbar.tsx
│ │ └── CustomerTableHead.tsx
│ └── agent
│ │ ├── AgentTableToolbar.tsx
│ │ └── AgentTableHead.tsx
├── layouts
│ ├── simple
│ │ ├── index.ts
│ │ ├── layout.tsx
│ │ └── main.tsx
│ ├── classes.ts
│ ├── Dashboard.tsx
│ └── components
│ │ ├── MenuButton.tsx
│ │ ├── NavUpgrade.tsx
│ │ └── Searchbar.tsx
├── theme
│ ├── styles
│ │ ├── index.ts
│ │ ├── mixins.ts
│ │ └── utils.ts
│ ├── core
│ │ ├── index.ts
│ │ ├── colors.json
│ │ ├── custom-shadows.ts
│ │ ├── shadows.ts
│ │ └── typography.ts
│ ├── theme-provider.tsx
│ └── create-theme.ts
├── assets
│ ├── it-logo.png
│ ├── it-logo-mid.png
│ ├── it-logo-min.png
│ ├── icon
│ │ ├── percolate.eot
│ │ ├── percolate.ttf
│ │ └── percolate.woff
│ └── font
│ │ ├── OpenSans-Light-webfont.eot
│ │ ├── OpenSans-Light-webfont.ttf
│ │ ├── OpenSans-Light-webfont.woff
│ │ ├── OpenSans-Regular-webfont.eot
│ │ ├── OpenSans-Regular-webfont.ttf
│ │ └── OpenSans-Regular-webfont.woff
├── SessionContext.tsx
├── App.css
├── stories
│ ├── Task.stories.tsx
│ ├── InboxSceen.stories.tsx
│ └── Button.stories.ts
├── pages
│ ├── NotFoundView.tsx
│ ├── SignInView.tsx
│ └── BlogView.tsx
├── index.d.ts
├── services
│ ├── orderService.ts
│ ├── customerService.ts
│ ├── productService.ts
│ └── agentService.ts
├── utils
│ ├── format-time.ts
│ └── format-number.ts
├── main.tsx
└── store
│ └── store.ts
├── public
├── it-logo.png
├── it-logo-mid.png
├── it-logo-min.png
├── assets
│ ├── background
│ │ └── overlay.jpg
│ ├── images
│ │ ├── cover
│ │ │ ├── cover-1.webp
│ │ │ ├── cover-10.webp
│ │ │ ├── cover-11.webp
│ │ │ ├── cover-12.webp
│ │ │ ├── cover-13.webp
│ │ │ ├── cover-14.webp
│ │ │ ├── cover-15.webp
│ │ │ ├── cover-16.webp
│ │ │ ├── cover-17.webp
│ │ │ ├── cover-18.webp
│ │ │ ├── cover-19.webp
│ │ │ ├── cover-2.webp
│ │ │ ├── cover-20.webp
│ │ │ ├── cover-21.webp
│ │ │ ├── cover-22.webp
│ │ │ ├── cover-23.webp
│ │ │ ├── cover-24.webp
│ │ │ ├── cover-25.webp
│ │ │ ├── cover-26.webp
│ │ │ ├── cover-3.webp
│ │ │ ├── cover-4.webp
│ │ │ ├── cover-5.webp
│ │ │ ├── cover-6.webp
│ │ │ ├── cover-7.webp
│ │ │ ├── cover-8.webp
│ │ │ └── cover-9.webp
│ │ ├── avatar
│ │ │ ├── avatar-1.webp
│ │ │ ├── avatar-10.webp
│ │ │ ├── avatar-11.webp
│ │ │ ├── avatar-12.webp
│ │ │ ├── avatar-13.webp
│ │ │ ├── avatar-14.webp
│ │ │ ├── avatar-15.webp
│ │ │ ├── avatar-16.webp
│ │ │ ├── avatar-17.webp
│ │ │ ├── avatar-18.webp
│ │ │ ├── avatar-19.webp
│ │ │ ├── avatar-2.webp
│ │ │ ├── avatar-20.webp
│ │ │ ├── avatar-21.webp
│ │ │ ├── avatar-22.webp
│ │ │ ├── avatar-23.webp
│ │ │ ├── avatar-24.webp
│ │ │ ├── avatar-25.webp
│ │ │ ├── avatar-3.webp
│ │ │ ├── avatar-4.webp
│ │ │ ├── avatar-5.webp
│ │ │ ├── avatar-6.webp
│ │ │ ├── avatar-7.webp
│ │ │ ├── avatar-8.webp
│ │ │ └── avatar-9.webp
│ │ ├── customer
│ │ │ ├── avatar-1.png
│ │ │ ├── avatar-2.png
│ │ │ ├── avatar-4.png
│ │ │ ├── avatar-5.png
│ │ │ ├── avatar-6.png
│ │ │ ├── avatar-7.png
│ │ │ ├── avatar-8.png
│ │ │ ├── avatar-9.png
│ │ │ ├── avatar-10.png
│ │ │ ├── avatar-11.png
│ │ │ └── avatar-12.png
│ │ ├── minimal-free-preview.jpg
│ │ └── product
│ │ │ ├── product-1.webp
│ │ │ ├── product-10.webp
│ │ │ ├── product-11.webp
│ │ │ ├── product-12.webp
│ │ │ ├── product-13.webp
│ │ │ ├── product-14.webp
│ │ │ ├── product-15.webp
│ │ │ ├── product-16.webp
│ │ │ ├── product-17.webp
│ │ │ ├── product-18.webp
│ │ │ ├── product-19.webp
│ │ │ ├── product-2.webp
│ │ │ ├── product-20.webp
│ │ │ ├── product-21.webp
│ │ │ ├── product-22.webp
│ │ │ ├── product-23.webp
│ │ │ ├── product-24.webp
│ │ │ ├── product-25.webp
│ │ │ ├── product-26.webp
│ │ │ ├── product-27.webp
│ │ │ ├── product-28.webp
│ │ │ ├── product-29.webp
│ │ │ ├── product-3.webp
│ │ │ ├── product-30.webp
│ │ │ ├── product-31.webp
│ │ │ ├── product-4.webp
│ │ │ ├── product-5.webp
│ │ │ ├── product-6.webp
│ │ │ ├── product-7.webp
│ │ │ ├── product-8.webp
│ │ │ └── product-9.webp
│ ├── icons
│ │ ├── workspaces
│ │ │ ├── it-logo.png
│ │ │ ├── logo-1.webp
│ │ │ ├── logo-2.webp
│ │ │ └── logo-3.webp
│ │ ├── shape-avatar.svg
│ │ ├── flags
│ │ │ ├── ic-flag-fr.svg
│ │ │ ├── ic-flag-en.svg
│ │ │ └── ic-flag-de.svg
│ │ ├── navbar
│ │ │ ├── ic-disabled.svg
│ │ │ ├── ic-user.svg
│ │ │ ├── ic-blog.svg
│ │ │ ├── ic-lock.svg
│ │ │ ├── ic-cart.svg
│ │ │ └── ic-analytics.svg
│ │ └── notification
│ │ │ ├── ic-notification-mail.svg
│ │ │ ├── ic-notification-shipping.svg
│ │ │ └── ic-notification-chat.svg
│ └── illustrations
│ │ └── illustration-dashboard.webp
└── vite.svg
├── .storybook
├── addons.ts
├── preview.ts
├── webpack.config.js
├── main.ts
└── config.ts
├── screenshots
├── screenshot-1.1.jpg
├── screenshot-1.jpg
├── screenshot-2.jpg
├── screenshot-3.1.jpg
├── screenshot-3.jpg
├── screenshot-4.jpg
├── screenshot-6.jpg
├── react-demo-v6-screen1.png
├── react-demo-v6-screen2.png
├── react-demo-v6-screen3.png
├── react-demo-v6-screen4.png
└── storybook-interactions.png
├── vite.config.ts
├── tsconfig.vite.json
├── index.html
├── .gitignore
├── Dockerfile
├── .eslintrc.cjs
├── config
└── nginx.conf
├── tsconfig.json
├── LICENSE
├── package.json
└── README.md
/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/routes/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './RouterLink';
2 |
--------------------------------------------------------------------------------
/src/_mock/index.ts:
--------------------------------------------------------------------------------
1 | export * from './_mock';
2 | export * from './_data';
3 |
--------------------------------------------------------------------------------
/public/it-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/it-logo.png
--------------------------------------------------------------------------------
/src/components/logo/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Logo';
2 |
3 | export * from './classes';
4 |
--------------------------------------------------------------------------------
/src/layouts/simple/index.ts:
--------------------------------------------------------------------------------
1 | export * from './main';
2 |
3 | export * from './layout';
4 |
--------------------------------------------------------------------------------
/src/theme/styles/index.ts:
--------------------------------------------------------------------------------
1 | export * from './utils';
2 |
3 | export * from './mixins';
4 |
--------------------------------------------------------------------------------
/public/it-logo-mid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/it-logo-mid.png
--------------------------------------------------------------------------------
/public/it-logo-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/it-logo-min.png
--------------------------------------------------------------------------------
/src/assets/it-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/it-logo.png
--------------------------------------------------------------------------------
/src/components/logo/classes.ts:
--------------------------------------------------------------------------------
1 | export const logoClasses = {
2 | root: 'mnl__logo__root',
3 | };
4 |
--------------------------------------------------------------------------------
/src/assets/it-logo-mid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/it-logo-mid.png
--------------------------------------------------------------------------------
/src/assets/it-logo-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/it-logo-min.png
--------------------------------------------------------------------------------
/.storybook/addons.ts:
--------------------------------------------------------------------------------
1 | import '@storybook/addon-actions/register';
2 | import '@storybook/addon-links/register';
--------------------------------------------------------------------------------
/screenshots/screenshot-1.1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-1.1.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-1.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-2.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-3.1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-3.1.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-3.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-4.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/screenshot-6.jpg
--------------------------------------------------------------------------------
/src/assets/icon/percolate.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/icon/percolate.eot
--------------------------------------------------------------------------------
/src/assets/icon/percolate.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/icon/percolate.ttf
--------------------------------------------------------------------------------
/src/assets/icon/percolate.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/icon/percolate.woff
--------------------------------------------------------------------------------
/src/components/label/classes.ts:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/assets/background/overlay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/background/overlay.jpg
--------------------------------------------------------------------------------
/screenshots/react-demo-v6-screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/react-demo-v6-screen1.png
--------------------------------------------------------------------------------
/screenshots/react-demo-v6-screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/react-demo-v6-screen2.png
--------------------------------------------------------------------------------
/screenshots/react-demo-v6-screen3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/react-demo-v6-screen3.png
--------------------------------------------------------------------------------
/screenshots/react-demo-v6-screen4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/react-demo-v6-screen4.png
--------------------------------------------------------------------------------
/src/routes/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export { useRouter } from './use-router';
2 |
3 | export { usePathname } from './use-pathname';
4 |
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-1.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-10.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-11.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-12.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-13.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-14.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-15.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-15.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-16.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-17.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-18.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-19.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-2.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-20.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-21.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-22.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-23.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-23.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-24.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-24.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-25.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-25.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-26.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-26.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-3.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-4.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-5.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-6.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-7.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-8.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/cover/cover-9.webp
--------------------------------------------------------------------------------
/screenshots/storybook-interactions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/screenshots/storybook-interactions.png
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/it-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/icons/workspaces/it-logo.png
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/logo-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/icons/workspaces/logo-1.webp
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/logo-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/icons/workspaces/logo-2.webp
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/logo-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/icons/workspaces/logo-3.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-1.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-10.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-11.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-12.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-13.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-14.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-15.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-15.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-16.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-17.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-18.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-19.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-2.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-20.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-21.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-22.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-23.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-23.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-24.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-24.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-25.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-25.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-3.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-4.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-5.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-6.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-7.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-8.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/avatar/avatar-9.webp
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-1.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-2.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-4.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-5.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-6.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-7.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-8.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-9.png
--------------------------------------------------------------------------------
/src/assets/font/OpenSans-Light-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/font/OpenSans-Light-webfont.eot
--------------------------------------------------------------------------------
/src/assets/font/OpenSans-Light-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/font/OpenSans-Light-webfont.ttf
--------------------------------------------------------------------------------
/src/components/iconify/classes.ts:
--------------------------------------------------------------------------------
1 | export const iconifyClasses = {
2 | root: 'mnl__icon__root',
3 | flag: 'mnl__icon__flag',
4 | };
5 |
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-10.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-11.png
--------------------------------------------------------------------------------
/public/assets/images/customer/avatar-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/customer/avatar-12.png
--------------------------------------------------------------------------------
/public/assets/images/minimal-free-preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/minimal-free-preview.jpg
--------------------------------------------------------------------------------
/public/assets/images/product/product-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-1.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-10.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-11.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-12.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-13.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-14.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-15.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-15.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-16.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-17.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-18.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-19.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-2.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-20.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-21.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-22.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-23.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-23.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-24.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-24.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-25.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-25.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-26.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-26.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-27.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-27.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-28.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-28.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-29.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-29.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-3.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-30.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-30.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-31.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-31.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-4.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-5.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-6.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-7.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-8.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/images/product/product-9.webp
--------------------------------------------------------------------------------
/src/assets/font/OpenSans-Light-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/font/OpenSans-Light-webfont.woff
--------------------------------------------------------------------------------
/src/assets/font/OpenSans-Regular-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/font/OpenSans-Regular-webfont.eot
--------------------------------------------------------------------------------
/src/assets/font/OpenSans-Regular-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/font/OpenSans-Regular-webfont.ttf
--------------------------------------------------------------------------------
/src/assets/font/OpenSans-Regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/src/assets/font/OpenSans-Regular-webfont.woff
--------------------------------------------------------------------------------
/src/components/scrollbar/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './Scrollbar';
4 |
5 | export type * from './types';
6 |
--------------------------------------------------------------------------------
/src/components/svg-color/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './SvgColor';
4 |
5 | export type * from './types';
6 |
--------------------------------------------------------------------------------
/src/components/color-utils/index.ts:
--------------------------------------------------------------------------------
1 | export type * from './types';
2 |
3 | export * from './ColorPicker';
4 |
5 | export * from './ColorPreview';
6 |
--------------------------------------------------------------------------------
/public/assets/illustrations/illustration-dashboard.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harryho/react-crm/HEAD/public/assets/illustrations/illustration-dashboard.webp
--------------------------------------------------------------------------------
/src/components/chart/classes.ts:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------
2 |
3 | export const chartClasses = { root: 'mnl__chart__root' };
4 |
--------------------------------------------------------------------------------
/src/components/chart/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chart';
2 |
3 | export * from './use-chart';
4 |
5 | export type * from './types';
6 |
7 | export * from './ChartLegends';
8 |
--------------------------------------------------------------------------------
/src/components/iconify/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './Iconify';
4 |
5 | export * from './FlagIcon';
6 |
7 | export type * from './types';
8 |
--------------------------------------------------------------------------------
/src/components/label/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Label';
2 |
3 | // export * from './styles';
4 |
5 | // export * from './classes';
6 |
7 | // export type * from './types';
8 |
--------------------------------------------------------------------------------
/src/components/scrollbar/classes.ts:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------
2 |
3 | export const scrollbarClasses = { root: 'mnl__scrollbar__root' };
4 |
--------------------------------------------------------------------------------
/src/components/svg-color/classes.ts:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------
2 |
3 | export const svgColorClasses = { root: 'mnl__svg__color__root' };
4 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/theme/core/index.ts:
--------------------------------------------------------------------------------
1 | export * from './shadows';
2 |
3 | export * from './palette';
4 |
5 | export * from './typography';
6 |
7 | export * from './components';
8 |
9 | export * from './custom-shadows';
10 |
--------------------------------------------------------------------------------
/src/components/svg-color/types.ts:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export type SvgColorProps = BoxProps & {
6 | src: string;
7 | };
8 |
--------------------------------------------------------------------------------
/tsconfig.vite.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/scrollbar/styles.css:
--------------------------------------------------------------------------------
1 | @import 'simplebar-react/dist/simplebar.min.css';
2 |
3 | .simplebar-scrollbar:before {
4 | background-color: var(--palette-text-disabled);
5 | }
6 | .simplebar-scrollbar.simplebar-visible:before {
7 | opacity: 0.48;
8 | }
--------------------------------------------------------------------------------
/src/components/iconify/types.ts:
--------------------------------------------------------------------------------
1 | import type { IconProps } from '@iconify/react';
2 | import type { BoxProps } from '@mui/material/Box';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export type IconifyProps = BoxProps & IconProps;
7 |
--------------------------------------------------------------------------------
/src/layouts/classes.ts:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------
2 |
3 | export const layoutClasses = {
4 | root: 'layout__root',
5 | main: 'layout__main',
6 | header: 'layout__header',
7 | content: 'layout__main__content',
8 | hasSidebar: 'layout__has__sidebar',
9 | };
10 |
--------------------------------------------------------------------------------
/public/assets/icons/shape-avatar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/routes/hooks/use-pathname.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useLocation } from 'react-router-dom';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export function usePathname() {
7 | const { pathname } = useLocation();
8 |
9 | return useMemo(() => pathname, [pathname]);
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React Demo V6
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/SessionContext.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import type { Session } from '@toolpad/core';
3 |
4 | export interface SessionContextValue {
5 | session: Session | null;
6 | setSession: (session: Session | null) => void;
7 | }
8 |
9 | export const SessionContext = React.createContext({
10 | session: {},
11 | setSession: () => {},
12 | });
13 |
14 | export function useSession() {
15 | return React.useContext(SessionContext);
16 | }
--------------------------------------------------------------------------------
/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import '../src/index.css';
2 | import { initialize, mswLoader } from 'msw-storybook-addon';
3 |
4 | initialize();
5 | /** @type { import('@storybook/react').Preview } */
6 | const preview = {
7 | parameters: {
8 | controls: {
9 | matchers: {
10 | color: /(background|color)$/i,
11 | date: /Date$/,
12 | },
13 | expanded: true ,
14 | },
15 | },
16 | loaders: [mswLoader],
17 | };
18 |
19 | export default preview;
20 |
--------------------------------------------------------------------------------
/public/assets/icons/flags/ic-flag-fr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/routes/components/RouterLink.tsx:
--------------------------------------------------------------------------------
1 | import type { LinkProps } from 'react-router-dom';
2 |
3 | import { forwardRef } from 'react';
4 | import { Link } from 'react-router-dom';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | interface RouterLinkProps extends Omit {
9 | href: string;
10 | }
11 |
12 | export const RouterLink = forwardRef(
13 | ({ href, ...other }, ref) =>
14 | );
15 |
--------------------------------------------------------------------------------
/src/components/scrollbar/types.ts:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 | import type { Props as SimplebarProps } from 'simplebar-react';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export type ScrollbarProps = SimplebarProps & {
7 | sx?: SxProps;
8 | children?: React.ReactNode;
9 | fillContent?: boolean;
10 | slotProps?: {
11 | wrapper?: SxProps;
12 | contentWrapper?: SxProps;
13 | content?: Partial>;
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 | .eslintcache
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 |
28 | build-storybook.log
29 |
30 | # Mac
31 | .DS_Store
32 | DS_Store
33 |
34 | # shell script
35 | *.sh
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ###### Build #####
2 | FROM node:12-slim AS node
3 | LABEL author="Harry Ho"
4 | WORKDIR /
5 | COPY . .
6 | RUN npm install
7 | RUN npm run build
8 |
9 |
10 | ###### Run #####
11 | FROM nginx:alpine
12 | LABEL author="Harry Ho"
13 | WORKDIR /var/cache/nginx
14 | COPY --from=node /build /usr/share/nginx/html
15 | COPY ./config/nginx.conf /etc/nginx/conf.d/default.conf
16 |
17 |
18 | #########################################################
19 | ## docker build . -t rc-prd:2.0
20 | ## docker run --publish 8080:80 --name rc2 vc-prd:2.0
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/components/chart/types.ts:
--------------------------------------------------------------------------------
1 | import type { Props } from 'react-apexcharts';
2 | import type { Theme, SxProps } from '@mui/material/styles';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export type ChartProps = {
7 | type: Props['type'];
8 | series: Props['series'];
9 | options: Props['options'];
10 | };
11 |
12 | export type ChartBaseProps = Props;
13 |
14 | export type ChartOptions = Props['options'];
15 |
16 | export type ChartLoadingProps = {
17 | disabled?: boolean;
18 | sx?: SxProps;
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/color-utils/types.ts:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export type ColorPickerProps = {
6 | multi?: boolean;
7 | colors: string[];
8 | selected: string | string[];
9 | limit?: 'auto' | number;
10 | onSelectColor: (color: string | string[]) => void;
11 | slotProps?: {
12 | button?: SxProps;
13 | };
14 | };
15 |
16 | export type ColorPreviewProps = {
17 | limit?: number;
18 | colors: ColorPickerProps['colors'];
19 | };
20 |
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | // module.exports = async ({ config }) => console.dir(config, { depth: null }) || config;
2 | module.exports = ({ config }) => {
3 | config.module.rules.push({
4 | test: /\.(ts|tsx)$/,
5 | use: [
6 | {
7 | loader: require.resolve('awesome-typescript-loader'),
8 | },
9 | // Optional
10 | {
11 | loader: require.resolve('react-docgen-typescript-loader'),
12 | },
13 | ],
14 | });
15 | config.resolve.extensions.push('.ts', '.tsx');
16 | return config;
17 | };
--------------------------------------------------------------------------------
/src/components/label/types.ts:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export type LabelColor =
6 | | 'default'
7 | | 'primary'
8 | | 'secondary'
9 | | 'info'
10 | | 'success'
11 | | 'warning'
12 | | 'error';
13 |
14 | export type LabelVariant = 'filled' | 'outlined' | 'soft' | 'inverted';
15 |
16 | export interface LabelProps extends BoxProps {
17 | color?: LabelColor;
18 | variant?: LabelVariant;
19 | endIcon?: React.ReactElement | null;
20 | startIcon?: React.ReactElement | null;
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/controls/Input.tsx:
--------------------------------------------------------------------------------
1 | import { TextField } from "@mui/material";
2 | import React from "react";
3 |
4 | export default function Input(props: any) {
5 | const { name, label, variant, value, error = null, onChange, ...others } = props;
6 | console.log( JSON.stringify(others))
7 | return (
8 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/routes/hooks/use-router.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useNavigate } from 'react-router-dom';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export function useRouter() {
7 | const navigate = useNavigate();
8 |
9 | const router = useMemo(
10 | () => ({
11 | back: () => navigate(-1),
12 | forward: () => navigate(1),
13 | refresh: () => navigate(0),
14 | push: (href: string) => navigate(href),
15 | replace: (href: string) => navigate(href, { replace: true }),
16 | }),
17 | [navigate]
18 | );
19 |
20 | return router;
21 | }
22 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:react/jsx-runtime', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'],
5 | ignorePatterns: ['dist', '.eslintrc.cjs'],
6 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
7 | settings: { react: { version: '18.2' } },
8 | plugins: ['react-refresh'],
9 | rules: {
10 | 'react/jsx-no-target-blank': 'off',
11 | 'react-refresh/only-export-components': [
12 | 'warn',
13 | { allowConstantExport: true },
14 | ],
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/config/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 0.0.0.0:80;
3 | listen [::]:80;
4 | default_type application/octet-stream;
5 |
6 | gzip on;
7 | gzip_comp_level 6;
8 | gzip_vary on;
9 | gzip_min_length 1000;
10 | gzip_proxied any;
11 | gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
12 | gzip_buffers 16 8k;
13 | client_max_body_size 256M;
14 |
15 | root /usr/share/nginx/html;
16 |
17 | location / {
18 | try_files $uri $uri/ /index.html =404;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/theme/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | import type {} from '@mui/lab/themeAugmentation';
2 | import type {} from '@mui/material/themeCssVarsAugmentation';
3 |
4 | import CssBaseline from '@mui/material/CssBaseline';
5 | import { CssVarsProvider } from '@mui/material/styles';
6 |
7 | import { createTheme2 } from './create-theme';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | type Props = {
12 | children: React.ReactNode;
13 | };
14 |
15 | export function ThemeProvider({ children }: Props) {
16 | const theme = createTheme2();
17 |
18 | return (
19 | <>
20 |
21 |
22 | {children}
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-disabled.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/sb/button.css:
--------------------------------------------------------------------------------
1 | .storybook-button {
2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | font-weight: 700;
4 | border: 0;
5 | border-radius: 3em;
6 | cursor: pointer;
7 | display: inline-block;
8 | line-height: 1;
9 | }
10 | .storybook-button--primary {
11 | color: white;
12 | background-color: #1ea7fd;
13 | }
14 | .storybook-button--secondary {
15 | color: #333;
16 | background-color: transparent;
17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
18 | }
19 | .storybook-button--small {
20 | font-size: 12px;
21 | padding: 10px 16px;
22 | }
23 | .storybook-button--medium {
24 | font-size: 14px;
25 | padding: 11px 20px;
26 | }
27 | .storybook-button--large {
28 | font-size: 16px;
29 | padding: 12px 24px;
30 | }
31 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | /** @type { import('@storybook/react-vite').StorybookConfig } */
2 | const config = {
3 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
4 | staticDirs: ["../public"],
5 | addons: [
6 | "@storybook/addon-links",
7 | "@storybook/addon-essentials",
8 | "@storybook/addon-interactions",
9 | ],
10 | framework: {
11 | name: "@storybook/react-vite",
12 | options: {},
13 | },
14 | typescript: {
15 | check: false,
16 | checkOptions: {},
17 | reactDocgen: 'react-docgen-typescript',
18 | reactDocgenTypescriptOptions: {
19 | shouldExtractLiteralValuesFromEnum: true,
20 | propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
21 | },
22 | },
23 | };
24 | export default config;
25 |
--------------------------------------------------------------------------------
/src/components/controls/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button } from "@mui/material";
3 | import { makeStyles } from "@mui/styles";
4 |
5 | const useStyles = makeStyles((theme:TODO) => ({
6 | root: {
7 | margin: theme.spacing(1)
8 | },
9 | label: {
10 | textTransform: "none"
11 | }
12 | }));
13 |
14 | export default function ButtonGenerator(props: TODO) {
15 | const classes = useStyles();
16 | const { text, size, color, variant, onClick, ...other } = props;
17 | return (
18 |
26 | {text}
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/table/TableEmptyRows.tsx:
--------------------------------------------------------------------------------
1 | import type { TableRowProps } from '@mui/material/TableRow';
2 |
3 | import TableRow from '@mui/material/TableRow';
4 | import TableCell from '@mui/material/TableCell';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | type TableEmptyRowsProps = TableRowProps & {
9 | emptyRows: number;
10 | height?: number;
11 | };
12 |
13 | export function TableEmptyRows({ emptyRows, height, sx, ...other }: TableEmptyRowsProps) {
14 | if (!emptyRows) {
15 | return null;
16 | }
17 |
18 | return (
19 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "ESNext",
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "strict": true,
7 | "noEmit": true,
8 | "allowJs": true,
9 | "module": "ESNext",
10 | "jsx": "react-jsx",
11 | "skipLibCheck": true,
12 | "noImplicitAny": true,
13 | "noImplicitThis": true,
14 | "esModuleInterop": true,
15 | "isolatedModules": true,
16 | "strictNullChecks": true,
17 | "resolveJsonModule": true,
18 | "moduleResolution": "Node",
19 | "noFallthroughCasesInSwitch": true,
20 | "useUnknownInCatchVariables": false,
21 | "allowSyntheticDefaultImports": true,
22 | "forceConsistentCasingInFileNames": true
23 | },
24 | "include": ["src"],
25 | "exclude": ["node_modules"],
26 | "references": [
27 | {
28 | "path": "./tsconfig.vite.json"
29 | }
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/controls/Checkbox.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | FormControl,
3 | FormControlLabel,
4 | FormGroup,
5 | FormLabel,
6 | Checkbox
7 | } from "@mui/material";
8 | import React, { ReactEventHandler } from "react";
9 |
10 | const checkedtoValue = (e: React.SyntheticEvent & TODO) => ({
11 | target: {
12 | name: e.target.name,
13 | value: e.target.checked
14 | }
15 | });
16 |
17 | export default function CheckboxGenerator(props:any) {
18 | const { name, label, value, onChange } = props;
19 | return (
20 |
21 | onChange(checkedtoValue(e))}
26 | name={name}
27 | />
28 | }
29 | label={label}
30 | />
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/controls/Select.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | FormControl,
3 | FormHelperText,
4 | InputLabel,
5 | MenuItem,
6 | Select
7 | } from "@mui/material";
8 | import React from "react";
9 |
10 | export default function SelectDropdown(props:any) {
11 | const { name, label, value, error = null, options, onChange } = props;
12 | return (
13 |
14 | {label}
15 |
16 | None
17 | {options.map((item:any) => (
18 |
19 | {item.title}
20 |
21 | ))}
22 |
23 | {error && {error} }
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/controls/RadioGroup.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | FormControl,
4 | FormLabel,
5 | FormControlLabel,
6 | Radio,
7 | RadioGroup
8 | } from "@mui/material";
9 |
10 | function RadioItem(props:any) {
11 | const { value, label } = props;
12 | return } label={label} />;
13 | }
14 |
15 | export default function RadioGroupGenerator(props:any) {
16 | const { name, label, value, onChange, items } = props;
17 | return (
18 |
19 | {label}
20 |
27 | {items.map((item:TODO) => (
28 |
29 | ))}
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/layouts/Dashboard.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Navigate, Outlet, useLocation } from 'react-router-dom';
3 | import { DashboardLayout } from '@toolpad/core/DashboardLayout';
4 | import { PageContainer } from '@toolpad/core/PageContainer';
5 | import { AccountPopover } from './components/AccountPopover';
6 | import { varAlpha } from '../theme/styles';
7 | import { lightPalette as palette } from '../theme/core/palette';
8 |
9 | export default function Layout() {
10 | // const { session , setSession} = useSession() as TODO;
11 | // const location = useLocation() as TODO;
12 |
13 | return (
14 |
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/iconify/Iconify.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef } from 'react';
2 | import { Icon, disableCache } from '@iconify/react';
3 |
4 | import Box from '@mui/material/Box';
5 |
6 | import { iconifyClasses } from './classes';
7 |
8 | import type { IconifyProps } from './types';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export const Iconify = forwardRef(
13 | ({ className, width = 20, sx, ...other }, ref) => (
14 |
28 | )
29 | );
30 |
31 | // https://iconify.design/docs/iconify-icon/disable-cache.html
32 | disableCache('local');
33 |
--------------------------------------------------------------------------------
/src/components/svg-color/SvgColor.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef } from 'react';
2 |
3 | import Box from '@mui/material/Box';
4 |
5 | import { svgColorClasses } from './classes';
6 |
7 | import type { SvgColorProps } from './types';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export const SvgColor = forwardRef(
12 | ({ src, width = 24, height, className, sx, ...other }, ref) => (
13 |
29 | )
30 | );
31 |
--------------------------------------------------------------------------------
/src/layouts/simple/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps, Breakpoint } from '@mui/material/styles';
2 |
3 | import Link from '@mui/material/Link';
4 | import Alert from '@mui/material/Alert';
5 |
6 |
7 | import { Logo } from '../../components/logo';
8 |
9 | import { Main, CompactContent } from './main';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | export type SimpleLayoutProps = {
14 | sx?: SxProps;
15 | children: React.ReactNode;
16 | header?: {
17 | sx?: SxProps;
18 | };
19 | content?: {
20 | compact?: boolean;
21 | };
22 | };
23 |
24 | export function SimpleLayout({ sx, children, header, content }: SimpleLayoutProps) {
25 | const layoutQuery: Breakpoint = 'lg';
26 |
27 | return (
28 |
29 | {content?.compact ? (
30 | {children}
31 | ) : (
32 | children
33 | )}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/chart/chart.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import ApexChart from 'react-apexcharts';
4 |
5 | import Box from '@mui/material/Box';
6 |
7 | import { chartClasses } from './classes';
8 |
9 | import type { ChartProps } from './types';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | export function Chart({
14 | sx,
15 | type,
16 | series,
17 | height,
18 | options,
19 | className,
20 | width = '100%',
21 | ...other
22 | }: BoxProps & ChartProps) {
23 | return (
24 |
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/table/TableNoData.tsx:
--------------------------------------------------------------------------------
1 | import type { TableRowProps } from '@mui/material/TableRow';
2 |
3 | import Box from '@mui/material/Box';
4 | import TableRow from '@mui/material/TableRow';
5 | import TableCell from '@mui/material/TableCell';
6 | import Typography from '@mui/material/Typography';
7 |
8 | // ----------------------------------------------------------------------
9 |
10 | type TableNoDataProps = TableRowProps & {
11 | searchQuery: string;
12 | };
13 |
14 | export function TableNoData({ searchQuery, ...other }: TableNoDataProps) {
15 | return (
16 |
17 |
18 |
19 |
20 | Not found
21 |
22 |
23 |
24 | No results found for
25 | "{searchQuery}" .
26 | Try checking for typos or using complete words.
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-user.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/form/use-form.ts:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { makeStyles } from "@mui/styles";
3 |
4 | export function useForm(initialFieldValues: TODO, selectedData: TODO) {
5 |
6 | const [values, setValues] = useState(selectedData?selectedData:initialFieldValues);
7 | const [errors, setErrors] = useState({});
8 | const [currentField, setcurrentField] = useState("");
9 |
10 | const handleInputChange = (e:TODO) => {
11 | const { name, value } = e.target;
12 | setValues({
13 | ...values,
14 | [name]: value
15 | });
16 | console.log("inputChange");
17 | setcurrentField(name);
18 | };
19 |
20 | const resetForm = () => {
21 | setValues(initialFieldValues);
22 | setErrors({});
23 | };
24 |
25 | return {
26 | values,
27 | setValues,
28 | errors,
29 | setErrors,
30 | handleInputChange,
31 | resetForm,
32 | currentField
33 | };
34 | }
35 |
36 | const useStyles = makeStyles((theme) => ({
37 | root: {
38 | "& .MuiFormControl-root": {
39 | width: "80%",
40 | margin: (theme as TODO).spacing(1)
41 | }
42 | }
43 | }));
44 |
45 |
--------------------------------------------------------------------------------
/.storybook/config.ts:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/react';
2 | import { addDecorator } from '@storybook/react';
3 | import { withInfo } from '@storybook/addon-info';
4 | import requireContext from 'require-context.macro';
5 |
6 | // Globally in your .storybook/config.js, or alternatively, per-chapter
7 | addDecorator(
8 | withInfo({
9 | styles: {
10 | header: {
11 | h1: {
12 | marginRight: '20px',
13 | fontSize: '25px',
14 | display: 'inline',
15 | },
16 | body: {
17 | paddingTop: 0,
18 | paddingBottom: 0,
19 | },
20 | h2: {
21 | display: 'inline',
22 | color: '#999',
23 | },
24 | },
25 | infoBody: {
26 | backgroundColor: '#eee',
27 | padding: '0px 5px',
28 | lineHeight: '2',
29 | },
30 | },
31 | inline: true,
32 | source: false,
33 | })
34 | );
35 | // automatically import all files ending in *.stories.tsx
36 | configure(requireContext('../src', true, /\.stories\.tsx?$/), module)
37 | // configure(equire.context'../src', true, /\.stories\.tsx?$/), module)
38 |
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Chroma Software Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/controls/SnapNotice.tsx:
--------------------------------------------------------------------------------
1 | import { HorizontalRule } from "@mui/icons-material";
2 | import Alert from "@mui/material/Alert";
3 | import Snackbar from "@mui/material/Snackbar";
4 | import { TransitionProps } from "@mui/material/transitions/transition";
5 |
6 | export type SnapNoticeProps = {
7 | open: boolean,
8 | transition: React.ComponentType<
9 | TransitionProps & {
10 | children: React.ReactElement;
11 | }
12 | >,
13 | handleClose: () => void
14 | }
15 |
16 | export default function SnapNotice(
17 | { open, transition, handleClose }: SnapNoticeProps
18 | ) {
19 | return (
27 |
33 | Operation is done successfully!
34 |
35 | )
36 |
37 | }
--------------------------------------------------------------------------------
/public/assets/icons/flags/ic-flag-en.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/sb/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // import PropTypes from 'prop-types';
3 | import './button.css';
4 | // import * as CSS from 'csstype';
5 |
6 | export interface ButtonProps {
7 | /**
8 | * Is this the principal call to action on the page?
9 | */
10 | primary: boolean;
11 | /**
12 | * What style to use
13 | */
14 | style: React.CSSProperties | undefined;
15 | /**
16 | * How large should the button be?
17 | */
18 | size: 'small' | 'medium' | 'large';
19 | /**
20 | * Button contents
21 | */
22 | label: string;
23 | /**
24 | * Optional click handler
25 | */
26 | onClick: () => void;
27 | }
28 |
29 | /**
30 | * Primary UI component for user interaction
31 | */
32 | export const Button = ({ primary = false, size = 'medium', ...buttonProps }: ButtonProps) => {
33 | const {
34 | label,
35 | style,
36 | ...props
37 | } = buttonProps;
38 |
39 | const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
40 | return (
41 |
47 | {label}
48 |
49 | );
50 | };
51 |
52 |
--------------------------------------------------------------------------------
/src/components/sb/InboxScreen.tsx:
--------------------------------------------------------------------------------
1 |
2 | import { useEffect } from 'react';
3 |
4 | import { useDispatch, useSelector } from 'react-redux';
5 | // import { Box } from '@mui/material';
6 | import { fetchTasks } from '../../store/store';
7 |
8 | import TaskList from './TaskList';
9 |
10 |
11 | export default function InboxScreen() {
12 | const dispatch = useDispatch();
13 | // We're retrieving the error field from our updated store
14 | const { error } = useSelector((state: any) => state.taskbox);
15 | // The useEffect triggers the data fetching when the component is mounted
16 | useEffect(() => {
17 | dispatch(fetchTasks() as any);
18 | // eslint-disable-next-line react-hooks/exhaustive-deps
19 | }, []);
20 |
21 | if (error) {
22 | return (
23 |
24 |
25 |
26 |
Oh no!
27 |
Something went wrong
28 |
29 |
30 | );
31 | }
32 | return (
33 |
34 |
35 |
Taskbox
36 |
37 |
38 |
39 | );
40 | }
--------------------------------------------------------------------------------
/public/assets/icons/flags/ic-flag-de.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layouts/components/MenuButton.tsx:
--------------------------------------------------------------------------------
1 | import type { IconButtonProps } from '@mui/material/IconButton';
2 |
3 | import SvgIcon from '@mui/material/SvgIcon';
4 | import IconButton from '@mui/material/IconButton';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | export function MenuButton({ sx, ...other }: IconButtonProps) {
9 | return (
10 |
11 |
12 |
17 |
21 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/stories/Task.stories.tsx:
--------------------------------------------------------------------------------
1 |
2 | import { fn } from "@storybook/test";
3 |
4 | import Task from '../components/sb/Task';
5 |
6 | const ActionsData = {
7 | onArchiveTask: fn(),
8 | onPinTask: fn(),
9 | };
10 |
11 | export default {
12 | component: Task,
13 | title: 'Task',
14 | tags: ['autodocs'],
15 | args: {
16 | ...ActionsData,
17 | },
18 | };
19 |
20 | export const Default = {
21 | args: {
22 | task: {
23 | id: '1',
24 | title: 'Test Task',
25 | state: 'TASK_INBOX',
26 | },
27 | },
28 | };
29 |
30 | export const Pinned = {
31 | args: {
32 | task: {
33 | ...Default.args.task,
34 | state: 'TASK_PINNED',
35 | },
36 | },
37 | };
38 |
39 | export const Archived = {
40 | args: {
41 | task: {
42 | ...Default.args.task,
43 | state: 'TASK_ARCHIVED',
44 | },
45 | },
46 | }
47 |
48 | const longTitleString = `This task's name is absurdly large. In fact, I think if I keep going I might end up with content overflow. What will happen? The star that represents a pinned task could have text overlapping. The text could cut-off abruptly when it reaches the star. I hope not!`;
49 |
50 | export const LongTitle = {
51 | args: {
52 | task: {
53 | ...Default.args.task,
54 | title: longTitleString,
55 | },
56 | },
57 | };
58 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-blog.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/pages/NotFoundView.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import Button from '@mui/material/Button';
3 | import Container from '@mui/material/Container';
4 | import Typography from '@mui/material/Typography';
5 |
6 | import { RouterLink } from '../routes/components';
7 |
8 | import { SimpleLayout } from '../layouts/simple';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export function NotFoundView() {
13 | return (
14 |
15 |
16 |
17 | Sorry, page not found!
18 |
19 |
20 |
21 | Sorry, we couldn’t find the page you’re looking for. Perhaps you’ve mistyped the URL? Be
22 | sure to check your spelling.
23 |
24 |
25 |
34 |
35 |
36 | Go to home
37 |
38 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-mail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/product/CartIcon.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 | import { useTheme } from '@mui/material/styles';
3 |
4 | import Box from '@mui/material/Box';
5 | import Badge from '@mui/material/Badge';
6 | import InputLabel from '@mui/material/InputLabel';
7 |
8 | import { RouterLink } from '../../routes/components';
9 |
10 | import { Iconify } from '../iconify';
11 |
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type Props = BoxProps & {
16 | totalItems: number;
17 | };
18 |
19 | export function CartIcon({ totalItems, sx, ref, ...other }: Props) {
20 | const theme = useTheme();
21 | return (
22 | theme.spacing(1, 3, 1, 2),
38 | boxShadow: (theme:TODO) => theme.shadows['5'],
39 | transition: (theme:TODO) => theme.transitions.create(['opacity']),
40 | '&:hover': { opacity: 0.72 },
41 | ...sx,
42 | }}
43 | {...other}
44 | >
45 |
46 |
47 |
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/chart/styles.css:
--------------------------------------------------------------------------------
1 | .apexcharts-canvas {
2 | /**
3 | * Tooltip
4 | */
5 | .apexcharts-tooltip {
6 | min-width: 80px;
7 | border-radius: 10px;
8 | backdrop-filter: blur(6px);
9 | color: var(--palette-text-primary);
10 | box-shadow: var(--customShadows-dropdown);
11 | background-color: rgba(var(--palette-background-defaultChannel) / 0.9);
12 | }
13 | .apexcharts-xaxistooltip {
14 | border-radius: 10px;
15 | border-color: transparent;
16 | backdrop-filter: blur(6px);
17 | color: var(--palette-text-primary);
18 | box-shadow: var(--customShadows-dropdown);
19 | background-color: rgba(var(--palette-background-defaultChannel) / 0.9);
20 | &::before {
21 | border-bottom-color: rgba(var(--palette-grey-500Channel) / 0.16);
22 | }
23 | &::after {
24 | border-bottom-color: rgba(var(--palette-background-defaultChannel) / 0.9);
25 | }
26 | }
27 | .apexcharts-tooltip-title {
28 | font-weight: 700;
29 | text-align: center;
30 | color: var(--palette-text-secondary);
31 | background-color: var(--palette-background-neutral);
32 | }
33 | /**
34 | * Tooltip: group
35 | */
36 | .apexcharts-tooltip-series-group {
37 | padding: 4px 12px;
38 | }
39 | .apexcharts-tooltip-marker {
40 | margin-right: 8px;
41 | }
42 | /**
43 | * Legend
44 | */
45 | .apexcharts-legend {
46 | padding: 0;
47 | }
48 | .apexcharts-legend-marker {
49 | margin-right: 6px;
50 | }
51 | .apexcharts-legend-text {
52 | margin-left: 0;
53 | padding-left: 0;
54 | line-height: 18px;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/scrollbar/Scrollbar.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef } from 'react';
2 | import SimpleBar from 'simplebar-react';
3 |
4 | import Box from '@mui/material/Box';
5 |
6 | import { scrollbarClasses } from './classes';
7 |
8 | import type { ScrollbarProps } from './types';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export const Scrollbar = forwardRef(
13 | ({ slotProps, children, fillContent, sx, ...other }, ref) => (
14 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
15 |
43 | {children}
44 |
45 | )
46 | );
47 |
--------------------------------------------------------------------------------
/src/theme/core/colors.json:
--------------------------------------------------------------------------------
1 | {
2 | "primary": {
3 | "lighter": "#D0ECFE",
4 | "light": "#73BAFB",
5 | "main": "#1877F2",
6 | "dark": "#0C44AE",
7 | "darker": "#042174",
8 | "contrastText": "#FFFFFF"
9 | },
10 | "secondary": {
11 | "lighter": "#EFD6FF",
12 | "light": "#C684FF",
13 | "main": "#8E33FF",
14 | "dark": "#5119B7",
15 | "darker": "#27097A",
16 | "contrastText": "#FFFFFF"
17 | },
18 | "info": {
19 | "lighter": "#CAFDF5",
20 | "light": "#61F3F3",
21 | "main": "#00B8D9",
22 | "dark": "#006C9C",
23 | "darker": "#003768",
24 | "contrastText": "#FFFFFF"
25 | },
26 | "success": {
27 | "lighter": "#D3FCD2",
28 | "light": "#77ED8B",
29 | "main": "#22C55E",
30 | "dark": "#118D57",
31 | "darker": "#065E49",
32 | "contrastText": "#ffffff"
33 | },
34 | "warning": {
35 | "lighter": "#FFF5CC",
36 | "light": "#FFD666",
37 | "main": "#FFAB00",
38 | "dark": "#B76E00",
39 | "darker": "#7A4100",
40 | "contrastText": "#1C252E"
41 | },
42 | "error": {
43 | "lighter": "#FFE9D5",
44 | "light": "#FFAC82",
45 | "main": "#FF5630",
46 | "dark": "#B71D18",
47 | "darker": "#7A0916",
48 | "contrastText": "#FFFFFF"
49 | },
50 | "grey": {
51 | "50": "#FCFDFD",
52 | "100": "#F9FAFB",
53 | "200": "#F4F6F8",
54 | "300": "#DFE3E8",
55 | "400": "#C4CDD5",
56 | "500": "#919EAB",
57 | "600": "#637381",
58 | "700": "#454F5B",
59 | "800": "#1C252E",
60 | "900": "#141A21"
61 | },
62 | "common": {
63 | "black": "#000000",
64 | "white": "#FFFFFF"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/iconify/FlagIcon.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 | import type { Theme, SxProps } from '@mui/material/styles';
3 |
4 | import { forwardRef } from 'react';
5 |
6 | import Box from '@mui/material/Box';
7 |
8 | import { iconifyClasses } from './classes';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export type FlagIconProps = BoxProps & {
13 | code?: string;
14 | };
15 |
16 | export const FlagIcon = forwardRef(
17 | ({ code, className, sx, ...other }, ref) => {
18 | const baseStyles: SxProps = {
19 | width: 26,
20 | height: 20,
21 | flexShrink: 0,
22 | overflow: 'hidden',
23 | borderRadius: '5px',
24 | alignItems: 'center',
25 | display: 'inline-flex',
26 | justifyContent: 'center',
27 | bgcolor: 'background.neutral',
28 | };
29 |
30 | if (!code) {
31 | return null;
32 | }
33 |
34 | return (
35 |
42 |
54 |
55 | );
56 | }
57 | );
58 |
--------------------------------------------------------------------------------
/src/layouts/components/NavUpgrade.tsx:
--------------------------------------------------------------------------------
1 | import type { StackProps } from '@mui/material/Stack';
2 |
3 | import Box from '@mui/material/Box';
4 | import Button from '@mui/material/Button';
5 | import Typography from '@mui/material/Typography';
6 |
7 | import { textGradient } from 'src/theme/styles';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export function NavUpgrade({ sx, ...other }: StackProps) {
12 | return (
13 |
20 | ({
23 | ...textGradient(
24 | `to right, ${theme.vars.palette.secondary.main}, ${theme.vars.palette.warning.main}`
25 | ),
26 | })}
27 | >
28 | More features?
29 |
30 |
31 | {`From only `}
32 |
33 | $69
34 |
35 |
36 |
37 |
43 |
44 |
50 | Upgrade to Pro
51 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/src/layouts/simple/main.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 | import type { Breakpoint } from '@mui/material/styles';
3 |
4 | import Box from '@mui/material/Box';
5 | import { useTheme } from '@mui/material/styles';
6 |
7 | import { layoutClasses } from '../classes';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export function Main({ children, sx, ...other }: BoxProps) {
12 | return (
13 |
24 | {children}
25 |
26 | );
27 | }
28 |
29 | // ----------------------------------------------------------------------
30 |
31 | export function CompactContent({
32 | sx,
33 | layoutQuery,
34 | children,
35 | ...other
36 | }: BoxProps & { layoutQuery: Breakpoint }) {
37 | const theme = useTheme();
38 |
39 | return (
40 |
59 | {children}
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-lock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.svg" {
2 | const content: React.FunctionComponent>;
3 | export default content;
4 | };
5 |
6 | declare module "*.png";
7 |
8 | declare type TODO = any
9 |
10 | declare module "@emotion/styled" {
11 | import { CreateStyled } from '@emotion/styled/types/index';
12 |
13 | import { MyTheme } from '../../src/myTheme';
14 | export * from '@emotion/styled/types/index';
15 | const customStyled: CreateStyled;
16 | export default customStyled;
17 | }
18 |
19 | declare type Customer = {
20 | id: string;
21 | name: string;
22 | firstName?: string;
23 | lastName?: string;
24 | email: string;
25 | shippingAddress?: string;
26 | billingAddress?: string;
27 | city?: string;
28 | state?: string;
29 | country?: string;
30 | mobile: string;
31 | phone: string;
32 | credit?: string;
33 | avatarUrl?: string;
34 | hasItemInShoppingCart: boolean;
35 | membership: string;
36 | }
37 |
38 | declare type Agent = {
39 | id: string;
40 | name: string;
41 | firstName?: string;
42 | lastName?: string;
43 | role: string;
44 | status: string;
45 | company: string;
46 | email: string;
47 | mobile: string;
48 | avatarUrl?: string;
49 | isVerified: boolean;
50 | city?: string;
51 | state?: string;
52 |
53 | }
54 |
55 | declare type Order = {
56 | id: string;
57 | orderId: string;
58 | itemSummary: string;
59 | customer: string;
60 | totalPrice: string;
61 | status: string;
62 | discount: string;
63 | promoteCode? : string;
64 | couponCode?: string;
65 |
66 | avatarUrl?: string;
67 | isDelayed: boolean;
68 | shippingAddress: string;
69 | billingAddress?: string;
70 | };
71 |
72 |
--------------------------------------------------------------------------------
/src/pages/SignInView.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import * as React from 'react';
3 | import { SignInPage } from '@toolpad/core/SignInPage';
4 | import type { Session } from '@toolpad/core/AppProvider';
5 | import { useNavigate } from 'react-router-dom';
6 | import { useSession } from '../SessionContext';
7 |
8 | const fakeAsyncGetSession = async (formData: any): Promise => {
9 | return new Promise((resolve, reject) => {
10 | setTimeout(() => {
11 | console.log(JSON.stringify(formData))
12 | // if (formData.get('password') === 'admin' ) {
13 | if (formData.get('password') !=='' ) {
14 | resolve({
15 | user: {
16 | name: 'Harry Ho',
17 | email: formData.get('email') || '',
18 | image: 'https://avatars.githubusercontent.com/u/19550456',
19 | },
20 | });
21 | }
22 | reject(new Error('Incorrect credentials.'));
23 | }, 1000);
24 | });
25 | };
26 |
27 | export default function SignInView() {
28 | const { setSession } = useSession();
29 | const navigate = useNavigate();
30 | return (
31 | {
34 | // Demo session
35 | try {
36 | const session = await fakeAsyncGetSession(formData);
37 | if (session) {
38 | setSession(session);
39 | navigate(callbackUrl || '/', { replace: true });
40 | return {};
41 | }
42 | } catch (error) {
43 | return { error: error instanceof Error ? error.message : 'An error occurred' };
44 | }
45 | return {};
46 | }}
47 | />
48 | );
49 | }
--------------------------------------------------------------------------------
/src/theme/create-theme.ts:
--------------------------------------------------------------------------------
1 | import type { Theme } from '@mui/material/styles';
2 |
3 | import { extendTheme } from '@mui/material/styles';
4 |
5 | import { shadows, typography, components, colorSchemes, customShadows } from './core';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export function createTheme2(): Theme {
10 | const initialTheme = {
11 | colorSchemes,
12 | shadows: shadows(),
13 | customShadows: customShadows(),
14 | shape: { borderRadius: 8 },
15 | components,
16 | typography,
17 | cssVarPrefix: '',
18 | shouldSkipGeneratingVar,
19 | };
20 |
21 | const theme = extendTheme(initialTheme);
22 | console.log(theme)
23 | return theme;
24 | }
25 |
26 | // ----------------------------------------------------------------------
27 |
28 | export function shouldSkipGeneratingVar(keys: string[], value: string | number): boolean {
29 | const skipGlobalKeys = [
30 | 'mixins',
31 | 'overlays',
32 | 'direction',
33 | 'typography',
34 | 'breakpoints',
35 | 'transitions',
36 | 'cssVarPrefix',
37 | 'unstable_sxConfig',
38 | ];
39 |
40 | const skipPaletteKeys: {
41 | [key: string]: string[];
42 | } = {
43 | global: ['tonalOffset', 'dividerChannel', 'contrastThreshold'],
44 | grey: ['A100', 'A200', 'A400', 'A700'],
45 | text: ['icon'],
46 | };
47 |
48 | const isPaletteKey = keys[0] === 'palette';
49 |
50 | if (isPaletteKey) {
51 | const paletteType = keys[1];
52 | const skipKeys = skipPaletteKeys[paletteType] || skipPaletteKeys.global;
53 |
54 | return keys.some((key) => skipKeys?.includes(key));
55 | }
56 |
57 | return keys.some((key) => skipGlobalKeys?.includes(key));
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/color-utils/ColorPreview.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import { forwardRef } from 'react';
4 |
5 | import Box from '@mui/material/Box';
6 | import { lightPalette as palette } from '../../theme/core/palette';
7 | import { varAlpha } from '../../theme/styles';
8 |
9 | import type { ColorPreviewProps } from './types';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | export const ColorPreview = forwardRef(
14 | ({ colors, limit = 3, sx, ...other }, ref) => {
15 | const colorsRange = colors.slice(0, limit);
16 |
17 | const restColors = colors.length - limit;
18 |
19 | return (
20 |
31 | {colorsRange.map((color, index) => (
32 | `solid 2px ${palette.background.paper}`,
41 | boxShadow: (theme) =>
42 | `inset -1px 1px 2px ${varAlpha(palette.common.blackChannel, 0.24)}`,
43 | }}
44 | />
45 | ))}
46 |
47 | {colors.length > limit && (
48 | {`+${restColors}`}
49 | )}
50 |
51 | );
52 | }
53 | );
54 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-cart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/stories/InboxSceen.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Provider } from 'react-redux';
2 | import { http, HttpResponse } from 'msw';
3 | import InboxScreen from '../components/sb/InboxScreen';
4 |
5 | import store from '../store/store';
6 |
7 | import { MockedState } from './TaskList.stories';
8 | import {
9 | fireEvent,
10 | waitFor,
11 | within,
12 | waitForElementToBeRemoved
13 | } from '@storybook/test';
14 |
15 |
16 | export default {
17 | component: InboxScreen,
18 | title: 'InboxScreen',
19 | decorators: [(story: any) => {story()} ],
20 | tags: ['autodocs'],
21 | };
22 |
23 | export const Default = {
24 | parameters: {
25 | msw: {
26 | handlers: [
27 | http.get('https://jsonplaceholder.typicode.com/todos?userId=1', () =>
28 | HttpResponse.json(MockedState.tasks)
29 | ),
30 | ],
31 | },
32 | },
33 |
34 | play: async ( {canvasElement}: any ) => {
35 | const canvas = within(canvasElement);
36 | // Waits for the component to transition from the loading state
37 | await waitForElementToBeRemoved(await canvas.findByTestId('loading'));
38 | // Waits for the component to be updated based on the store
39 | await waitFor(async () => {
40 | // Simulates pinning the first task
41 | await fireEvent.click(canvas.getByLabelText('Task 1'));
42 | // Simulates pinning the third task
43 | await fireEvent.click(canvas.getByLabelText('Task 3'));
44 | });
45 | },
46 | };
47 |
48 | export const Error = {
49 | parameters: {
50 | msw: {
51 | handlers: [
52 | http.get('https://jsonplaceholder.typicode.com/todos?userId=1', () =>
53 | new HttpResponse(null, {
54 | status: 403
55 | }))
56 | ],
57 | },
58 | },
59 | };
--------------------------------------------------------------------------------
/src/components/blog/PostSearch.tsx:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 |
3 | import TextField from '@mui/material/TextField';
4 | import InputAdornment from '@mui/material/InputAdornment';
5 | import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
6 |
7 | import { Iconify } from '../iconify';
8 |
9 | import type { PostItemProps } from './PostItem';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | type PostSearchProps = {
14 | posts: PostItemProps[];
15 | sx?: SxProps;
16 | };
17 |
18 | export function PostSearch({ posts, sx }: PostSearchProps) {
19 | return (
20 | post.title}
37 | isOptionEqualToValue={(option, value) => option.id === value.id}
38 | renderInput={(params) => (
39 |
46 |
50 |
51 | ),
52 | }}
53 | />
54 | )}
55 | />
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsWebsiteVisits.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from '../chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import CardHeader from '@mui/material/CardHeader';
6 | import { useTheme, alpha as hexAlpha } from '@mui/material/styles';
7 |
8 | import { Chart, useChart } from '../chart';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type Props = CardProps & {
13 | title?: string;
14 | subheader?: string;
15 | chart: {
16 | colors?: string[];
17 | categories?: string[];
18 | series: {
19 | name: string;
20 | data: number[];
21 | }[];
22 | options?: ChartOptions;
23 | };
24 | };
25 |
26 | export function AnalyticsWebsiteVisits({ title, subheader, chart, ...other }: Props) {
27 | const theme = useTheme();
28 |
29 | const chartColors = chart.colors ?? [
30 | theme.palette.primary.dark,
31 | hexAlpha(theme.palette.primary.light, 0.64),
32 | ];
33 |
34 | const chartOptions = useChart({
35 | colors: chartColors,
36 | stroke: {
37 | width: 2,
38 | colors: ['transparent'],
39 | },
40 | xaxis: {
41 | categories: chart.categories,
42 | },
43 | legend: {
44 | show: true,
45 | },
46 | tooltip: {
47 | y: {
48 | formatter: (value: number) => `${value} visits`,
49 | },
50 | },
51 | ...chart.options,
52 | });
53 |
54 | return (
55 |
56 |
57 |
58 |
65 |
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/src/stories/Button.stories.ts:
--------------------------------------------------------------------------------
1 | // Replace your-renderer with the renderer you are using (e.g., react, vue3, etc.)
2 | import type { Meta, StoryObj } from '@storybook/react';
3 |
4 |
5 | import { Button , ButtonProps} from '../components/sb/Button';
6 |
7 |
8 | const meta: Meta = {
9 | title: 'Example/Button',
10 | component: Button,
11 | parameters: {
12 | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
13 | layout: 'centered',
14 | },
15 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
16 | tags: ['autodocs'],
17 | // More on argTypes: https://storybook.js.org/docs/api/argtypes
18 | argTypes: {
19 | style: {
20 | color: String,
21 | margin: String,
22 | },
23 | },
24 | // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
25 | args: { onClick: undefined }
26 | };
27 |
28 | export default meta;
29 | type Story = StoryObj;
30 |
31 |
32 |
33 | export const Primary: Story = {
34 | args: {
35 | primary: true,
36 | label: 'Button',
37 | },
38 | };
39 |
40 |
41 | export const Secondary: Story = {
42 | args: {
43 | primary: false,
44 | style: {
45 | color: 'orange',
46 | margin: '1em',
47 | },
48 | label: 'Button',
49 | },
50 | };
51 |
52 |
53 | export const Large : Story = {
54 | args: {
55 | size: 'large',
56 | style: {
57 | color: 'navy',
58 | margin: '1em',
59 | },
60 | label: 'Button',
61 | },
62 | };
63 |
64 | export const Small : Story = {
65 | args: {
66 | size: 'small',
67 | style: {
68 | color: 'red',
69 | margin: '1em',
70 | },
71 | label: 'Button',
72 | },
73 | };
--------------------------------------------------------------------------------
/src/components/sb/Task.tsx:
--------------------------------------------------------------------------------
1 | // import { Button, InputLabel } from "@mui/material";
2 | // import Box from "@mui/material/Box";
3 | // import { Iconify } from "../iconify";
4 |
5 | import { Button } from "src/components/sb/Button";
6 |
7 | export interface TaskType {
8 | id: string; title: string; state: string;
9 | }
10 |
11 | export interface TaskProps {
12 | task: TaskType;
13 | onPinTask: Function;
14 | onArchiveTask: Function;
15 | }
16 |
17 | export default function Task({ task, onArchiveTask, onPinTask }: TaskProps ) {
18 | const { id, title, state} = task;
19 | return (
20 |
21 |
26 |
33 | onArchiveTask(id)}
36 | />
37 |
38 |
39 |
40 |
49 |
50 |
51 | {state !== "TASK_ARCHIVED" && (
52 | onPinTask(id)}
55 | id={`pinTask-${id}`}
56 | aria-label={`pinTask-${id}`}
57 | key={`pinTask-${id}`}
58 | >
59 |
60 |
61 | )}
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsCurrentSubject.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from '../chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import Divider from '@mui/material/Divider';
6 | import { useTheme } from '@mui/material/styles';
7 | import CardHeader from '@mui/material/CardHeader';
8 |
9 | import { Chart, useChart, ChartLegends } from '../chart';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | type Props = CardProps & {
14 | title?: string;
15 | subheader?: string;
16 | chart: {
17 | colors?: string[];
18 | categories: string[];
19 | series: {
20 | name: string;
21 | data: number[];
22 | }[];
23 | options?: ChartOptions;
24 | };
25 | };
26 |
27 | export function AnalyticsCurrentSubject({ title, subheader, chart, ...other }: Props) {
28 | const theme = useTheme();
29 |
30 | const chartColors = chart.colors ?? [
31 | theme.palette.primary.main,
32 | theme.palette.warning.main,
33 | theme.palette.info.main,
34 | ];
35 |
36 | const chartOptions = useChart({
37 | colors: chartColors,
38 | stroke: { width: 2 },
39 | fill: { opacity: 0.48 },
40 | xaxis: {
41 | categories: chart.categories,
42 | labels: { style: { colors: [...Array(6)].map(() => theme.palette.text.secondary) } },
43 | },
44 | ...chart.options,
45 | });
46 |
47 | return (
48 |
49 |
50 |
51 |
59 |
60 |
61 |
62 | item.name)}
64 | colors={chartOptions?.colors}
65 | sx={{ p: 3, justifyContent: 'center' }}
66 | />
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsConversionRates.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from '../chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import CardHeader from '@mui/material/CardHeader';
6 | import { useTheme, alpha as hexAlpha } from '@mui/material/styles';
7 |
8 | import { fnNumber } from '../../utils/format-number';
9 |
10 | import { Chart, useChart } from '../chart';
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | type Props = CardProps & {
15 | title?: string;
16 | subheader?: string;
17 | chart: {
18 | colors?: string[];
19 | categories?: string[];
20 | series: {
21 | name: string;
22 | data: number[];
23 | }[];
24 | options?: ChartOptions;
25 | };
26 | };
27 |
28 | export function AnalyticsConversionRates({ title, subheader, chart, ...other }: Props) {
29 | const theme = useTheme();
30 |
31 | const chartColors = chart.colors ?? [
32 | theme.palette.primary.dark,
33 | hexAlpha(theme.palette.primary.dark, 0.24),
34 | ];
35 |
36 | const chartOptions = useChart({
37 | colors: chartColors,
38 | stroke: { width: 0, colors: ['transparent'] },
39 | tooltip: {
40 | shared: true,
41 | intersect: false,
42 | y: {
43 | formatter: (value: number) => fnNumber(value),
44 | title: { formatter: (seriesName: string) => `${seriesName}: ` },
45 | },
46 | },
47 | xaxis: { categories: chart.categories },
48 | dataLabels: {
49 | enabled: true,
50 | offsetX: -6,
51 | style: { fontSize: '12px', colors: ['#FFFFFF', theme.palette.text.primary] },
52 | },
53 | plotOptions: {
54 | bar: {
55 | horizontal: true,
56 | borderRadius: 2,
57 | barHeight: '48%',
58 | dataLabels: { position: 'top' },
59 | },
60 | },
61 | ...chart.options,
62 | });
63 |
64 | return (
65 |
66 |
67 |
68 |
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/chart/ChartLegends.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import Box from '@mui/material/Box';
4 | import Stack from '@mui/material/Stack';
5 | import { styled } from '@mui/material/styles';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export const StyledLegend = styled(Box)(({ theme }) => ({
10 | gap: 6,
11 | alignItems: 'center',
12 | display: 'inline-flex',
13 | justifyContent: 'flex-start',
14 | fontSize: theme.typography.pxToRem(13),
15 | fontWeight: theme.typography.fontWeightMedium,
16 | }));
17 |
18 | export const StyledDot = styled(Box)(() => ({
19 | width: 12,
20 | height: 12,
21 | flexShrink: 0,
22 | display: 'flex',
23 | borderRadius: '50%',
24 | position: 'relative',
25 | alignItems: 'center',
26 | justifyContent: 'center',
27 | backgroundColor: 'currentColor',
28 | }));
29 |
30 | // ----------------------------------------------------------------------
31 |
32 | type Props = BoxProps & {
33 | labels?: string[];
34 | colors?: string[];
35 | values?: string[];
36 | sublabels?: string[];
37 | icons?: React.ReactNode[];
38 | };
39 |
40 | export function ChartLegends({
41 | icons,
42 | values,
43 | sublabels,
44 | labels = [],
45 | colors = [],
46 | ...other
47 | }: Props) {
48 | return (
49 |
50 | {labels?.map((series, index) => (
51 |
52 |
53 | {icons?.length ? (
54 |
58 | {icons?.[index]}
59 |
60 | ) : (
61 |
64 | )}
65 |
66 |
67 | {series}
68 | {sublabels && <> {` (${sublabels[index]})`}>}
69 |
70 |
71 |
72 | {values && {values[index]} }
73 |
74 | ))}
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/order/OrderTableToolbar.tsx:
--------------------------------------------------------------------------------
1 | import Tooltip from '@mui/material/Tooltip';
2 | import Toolbar from '@mui/material/Toolbar';
3 | import Typography from '@mui/material/Typography';
4 | import IconButton from '@mui/material/IconButton';
5 | import OutlinedInput from '@mui/material/OutlinedInput';
6 | import InputAdornment from '@mui/material/InputAdornment';
7 |
8 | import { Iconify } from '../iconify';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type OrderTableToolbarProps = {
13 | numSelected: number;
14 | filterName: string;
15 | onFilterName: (event: React.ChangeEvent) => void;
16 | onMultipleDelete: (event: React.MouseEvent) => void;
17 | };
18 |
19 | export function OrderTableToolbar({
20 | numSelected, filterName, onFilterName, onMultipleDelete }: OrderTableToolbarProps) {
21 | return (
22 | theme.spacing(0, 1, 0, 3),
28 | ...(numSelected > 0 && {
29 | color: 'primary.main',
30 | bgcolor: 'primary.lighter',
31 | }),
32 | }}
33 | >
34 | {numSelected > 0 ? (
35 |
36 | {numSelected} selected
37 |
38 | ) : (
39 |
46 |
47 |
48 | }
49 | sx={{ maxWidth: 320 }}
50 | />
51 | )}
52 |
53 | {numSelected > 0 ? (
54 |
55 |
56 |
57 |
58 |
59 | ) : (
60 |
61 |
62 |
63 |
64 |
65 | )}
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/customer/CustomerTableToolbar.tsx:
--------------------------------------------------------------------------------
1 | import Tooltip from '@mui/material/Tooltip';
2 | import Toolbar from '@mui/material/Toolbar';
3 | import Typography from '@mui/material/Typography';
4 | import IconButton from '@mui/material/IconButton';
5 | import OutlinedInput from '@mui/material/OutlinedInput';
6 | import InputAdornment from '@mui/material/InputAdornment';
7 |
8 | import { Iconify } from '../iconify';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type CustomerTableToolbarProps = {
13 | numSelected: number;
14 | filterName: string;
15 | onFilterName: (event: React.ChangeEvent) => void;
16 | onMultipleDelete: (event: React.MouseEvent) => void;
17 |
18 | };
19 |
20 | export function CustomerTableToolbar({ numSelected, filterName, onFilterName,onMultipleDelete }: CustomerTableToolbarProps) {
21 | return (
22 | theme.spacing(0, 1, 0, 3),
28 | ...(numSelected > 0 && {
29 | color: 'primary.main',
30 | bgcolor: 'primary.lighter',
31 | }),
32 | }}
33 | >
34 | {numSelected > 0 ? (
35 |
36 | {numSelected} selected
37 |
38 | ) : (
39 |
46 |
47 |
48 | }
49 | sx={{ maxWidth: 320 }}
50 | />
51 | )}
52 |
53 | {numSelected > 0 ? (
54 |
55 |
56 |
57 |
58 |
59 | ) : (
60 |
61 |
62 |
63 |
64 |
65 | )}
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/agent/AgentTableToolbar.tsx:
--------------------------------------------------------------------------------
1 | import Tooltip from '@mui/material/Tooltip';
2 | import Toolbar from '@mui/material/Toolbar';
3 | import Typography from '@mui/material/Typography';
4 | import IconButton from '@mui/material/IconButton';
5 | import OutlinedInput from '@mui/material/OutlinedInput';
6 | import InputAdornment from '@mui/material/InputAdornment';
7 |
8 | import { Iconify } from '../iconify';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type AgentTableToolbarProps = {
13 | numSelected: number;
14 | filterName: string;
15 | onFilterName: (event: React.ChangeEvent) => void;
16 | onMultipleDelete: (event: React.MouseEvent) => void;
17 |
18 | };
19 |
20 | export function AgentTableToolbar({
21 | numSelected, filterName, onFilterName, onMultipleDelete }
22 | : AgentTableToolbarProps) {
23 | return (
24 | theme.spacing(0, 1, 0, 3),
30 | ...(numSelected > 0 && {
31 | color: 'primary.main',
32 | bgcolor: 'primary.lighter',
33 | }),
34 | }}
35 | >
36 | {numSelected > 0 ? (
37 |
38 | {numSelected} selected
39 |
40 | ) : (
41 |
48 |
49 |
50 | }
51 | sx={{ maxWidth: 320 }}
52 | />
53 | )}
54 |
55 | {numSelected > 0 ? (
56 |
57 |
58 |
59 |
60 |
61 | ) : (
62 |
63 |
64 |
65 |
66 |
67 | )}
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsTrafficBySite.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 |
3 | import Box from '@mui/material/Box';
4 | import Card from '@mui/material/Card';
5 | import CardHeader from '@mui/material/CardHeader';
6 | import Typography from '@mui/material/Typography';
7 |
8 | import { fnShortenNumber } from '../../utils/format-number';
9 |
10 | import { varAlpha } from '../../theme/styles';
11 |
12 | import { Iconify } from '../iconify';
13 | import { grey } from '../../theme/core/palette';
14 |
15 | // ----------------------------------------------------------------------
16 |
17 | type Props = CardProps & {
18 | title?: string;
19 | subheader?: string;
20 | list: { value: string; label: string; total: number }[];
21 | };
22 |
23 | export function AnalyticsTrafficBySite({ title, subheader, list, sx, ...other }: Props) {
24 | return (
25 |
26 |
27 |
28 |
29 | {list.map((site) => (
30 | ({
33 | py: 2.5,
34 | display: 'flex',
35 | borderRadius: 1.5,
36 | textAlign: 'center',
37 | alignItems: 'center',
38 | flexDirection: 'column',
39 | border: `solid 1px ${varAlpha(grey['500Channel'], 0.12)}`,
40 | })}
41 | >
42 | {site.value === 'facebook' && (
43 |
44 | )}
45 | {site.value === 'google' && }
46 | {site.value === 'linkedin' && (
47 |
48 | )}
49 | {site.value === 'twitter' && }
50 |
51 |
52 | {fnShortenNumber(site.total)}
53 |
54 |
55 |
56 | {site.label}
57 |
58 |
59 | ))}
60 |
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/src/theme/core/custom-shadows.ts:
--------------------------------------------------------------------------------
1 | import { varAlpha } from '../styles';
2 | import { grey, info, error, common, primary, success, warning, secondary } from './palette';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export interface CustomShadows {
7 | z1?: string;
8 | z4?: string;
9 | z8?: string;
10 | z12?: string;
11 | z16?: string;
12 | z20?: string;
13 | z24?: string;
14 | //
15 | primary?: string;
16 | secondary?: string;
17 | info?: string;
18 | success?: string;
19 | warning?: string;
20 | error?: string;
21 | //
22 | card?: string;
23 | dialog?: string;
24 | dropdown?: string;
25 | }
26 |
27 | declare module '@mui/material/styles' {
28 | interface Theme {
29 | customShadows: CustomShadows;
30 | }
31 | interface ThemeOptions {
32 | customShadows?: CustomShadows;
33 | }
34 | interface ThemeVars {
35 | customShadows: CustomShadows;
36 | }
37 | }
38 |
39 | // ----------------------------------------------------------------------
40 |
41 | export function createShadowColor(colorChannel: string) {
42 | return `0 8px 16px 0 ${varAlpha(colorChannel, 0.24)}`;
43 | }
44 |
45 | export function customShadows() {
46 | const colorChannel = grey['500Channel'];
47 |
48 | return {
49 | z1: `0 1px 2px 0 ${varAlpha(colorChannel, 0.16)}`,
50 | z4: `0 4px 8px 0 ${varAlpha(colorChannel, 0.16)}`,
51 | z8: `0 8px 16px 0 ${varAlpha(colorChannel, 0.16)}`,
52 | z12: `0 12px 24px -4px ${varAlpha(colorChannel, 0.16)}`,
53 | z16: `0 16px 32px -4px ${varAlpha(colorChannel, 0.16)}`,
54 | z20: `0 20px 40px -4px ${varAlpha(colorChannel, 0.16)}`,
55 | z24: `0 24px 48px 0 ${varAlpha(colorChannel, 0.16)}`,
56 | //
57 | dialog: `-40px 40px 80px -8px ${varAlpha(common.blackChannel, 0.24)}`,
58 | card: `0 0 2px 0 ${varAlpha(colorChannel, 0.2)}, 0 12px 24px -4px ${varAlpha(colorChannel, 0.12)}`,
59 | dropdown: `0 0 2px 0 ${varAlpha(colorChannel, 0.24)}, -20px 20px 40px -4px ${varAlpha(colorChannel, 0.24)}`,
60 | //
61 | primary: createShadowColor(primary.mainChannel),
62 | secondary: createShadowColor(secondary.mainChannel),
63 | info: createShadowColor(info.mainChannel),
64 | success: createShadowColor(success.mainChannel),
65 | warning: createShadowColor(warning.mainChannel),
66 | error: createShadowColor(error.mainChannel),
67 | };
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/agent/AgentTableHead.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import TableRow from '@mui/material/TableRow';
3 | import Checkbox from '@mui/material/Checkbox';
4 | import TableHead from '@mui/material/TableHead';
5 | import TableCell from '@mui/material/TableCell';
6 | import TableSortLabel from '@mui/material/TableSortLabel';
7 |
8 | import { visuallyHidden } from '../table/utils';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type AgentTableHeadProps = {
13 | orderBy: string;
14 | rowCount: number;
15 | numSelected: number;
16 | order: 'asc' | 'desc';
17 | onSort: (id: string) => void;
18 | headLabel: Record[];
19 | onSelectAllRows: (checked: boolean) => void;
20 | };
21 |
22 | export function AgentTableHead({
23 | order,
24 | onSort,
25 | orderBy,
26 | rowCount,
27 | headLabel,
28 | numSelected,
29 | onSelectAllRows,
30 | }: AgentTableHeadProps) {
31 | return (
32 |
33 |
34 |
35 | 0 && numSelected < rowCount}
37 | checked={rowCount > 0 && numSelected === rowCount}
38 | onChange={(event: React.ChangeEvent) =>
39 | onSelectAllRows(event.target.checked)
40 | }
41 | />
42 |
43 |
44 | {headLabel.map((headCell) => (
45 |
51 | onSort(headCell.id)}
56 | >
57 | {headCell.label}
58 | {orderBy === headCell.id ? (
59 |
60 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
61 |
62 | ) : null}
63 |
64 |
65 | ))}
66 |
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/order/OrderableHead.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import TableRow from '@mui/material/TableRow';
3 | import Checkbox from '@mui/material/Checkbox';
4 | import TableHead from '@mui/material/TableHead';
5 | import TableCell from '@mui/material/TableCell';
6 | import TableSortLabel from '@mui/material/TableSortLabel';
7 |
8 | import { visuallyHidden } from '../table/utils';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type OrderTableHeadProps = {
13 | orderBy: string;
14 | rowCount: number;
15 | numSelected: number;
16 | order: 'asc' | 'desc';
17 | onSort: (id: string) => void;
18 | headLabel: Record[];
19 | onSelectAllRows: (checked: boolean) => void;
20 | };
21 |
22 | export function OrderTableHead({
23 | order,
24 | onSort,
25 | orderBy,
26 | rowCount,
27 | headLabel,
28 | numSelected,
29 | onSelectAllRows,
30 | }: OrderTableHeadProps) {
31 | return (
32 |
33 |
34 |
35 | 0 && numSelected < rowCount}
37 | checked={rowCount > 0 && numSelected === rowCount}
38 | onChange={(event: React.ChangeEvent) =>
39 | onSelectAllRows(event.target.checked)
40 | }
41 | />
42 |
43 |
44 | {headLabel.map((headCell) => (
45 |
51 | onSort(headCell.id)}
56 | >
57 | {headCell.label}
58 | {orderBy === headCell.id ? (
59 |
60 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
61 |
62 | ) : null}
63 |
64 |
65 | ))}
66 |
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/customer/CustomerTableHead.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import TableRow from '@mui/material/TableRow';
3 | import Checkbox from '@mui/material/Checkbox';
4 | import TableHead from '@mui/material/TableHead';
5 | import TableCell from '@mui/material/TableCell';
6 | import TableSortLabel from '@mui/material/TableSortLabel';
7 |
8 | import { visuallyHidden } from '../table/utils';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type CustomerTableHeadProps = {
13 | orderBy: string;
14 | rowCount: number;
15 | numSelected: number;
16 | order: 'asc' | 'desc';
17 | onSort: (id: string) => void;
18 | headLabel: Record[];
19 | onSelectAllRows: (checked: boolean) => void;
20 | };
21 |
22 | export function CustomerTableHead({
23 | order,
24 | onSort,
25 | orderBy,
26 | rowCount,
27 | headLabel,
28 | numSelected,
29 | onSelectAllRows,
30 | }: CustomerTableHeadProps) {
31 | return (
32 |
33 |
34 |
35 | 0 && numSelected < rowCount}
37 | checked={rowCount > 0 && numSelected === rowCount}
38 | onChange={(event: React.ChangeEvent) =>
39 | onSelectAllRows(event.target.checked)
40 | }
41 | />
42 |
43 |
44 | {headLabel.map((headCell) => (
45 |
51 | onSort(headCell.id)}
56 | >
57 | {headCell.label}
58 | {orderBy === headCell.id ? (
59 |
60 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
61 |
62 | ) : null}
63 |
64 |
65 | ))}
66 |
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-shipping.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/pages/BlogView.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react';
2 |
3 | import Box from '@mui/material/Box';
4 | import Button from '@mui/material/Button';
5 | import Grid from '@mui/material//Grid2';
6 | import Typography from '@mui/material/Typography';
7 | import Pagination from '@mui/material/Pagination';
8 |
9 | import { _posts } from '../_mock';
10 |
11 | import { Iconify } from '../components/iconify';
12 |
13 | import { PostItem } from '../components/blog/PostItem';
14 | import { PostSort } from '../components/blog/PostSort';
15 | import { PostSearch } from '../components/blog/PostSearch';
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | export function BlogView() {
20 | const [sortBy, setSortBy] = useState('latest');
21 |
22 | const handleSort = useCallback((newSort: string) => {
23 | setSortBy(newSort);
24 | }, []);
25 |
26 | console.log(_posts)
27 |
28 | return (
29 | <>
30 |
31 |
32 | Blogs
33 |
34 | }
38 | >
39 | New post
40 |
41 |
42 |
43 |
44 |
45 |
54 |
55 |
56 |
57 | {_posts.map((post, index) => {
58 | const latestPostLarge = index === 0;
59 | const latestPost = index === 1 || index === 2;
60 |
61 | return (
62 |
63 |
64 |
65 | );
66 | })}
67 |
68 |
69 |
70 | >
71 | );
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsCurrentVisits.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from '../chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import Divider from '@mui/material/Divider';
6 | import { useTheme } from '@mui/material/styles';
7 | import CardHeader from '@mui/material/CardHeader';
8 |
9 | import { fnNumber } from '../../utils/format-number';
10 |
11 | import { Chart, useChart, ChartLegends } from '../chart';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type Props = CardProps & {
16 | title?: string;
17 | subheader?: string;
18 | chart: {
19 | colors?: string[];
20 | series: {
21 | label: string;
22 | value: number;
23 | }[];
24 | options?: ChartOptions;
25 | };
26 | };
27 |
28 | export function AnalyticsCurrentVisits({ title, subheader, chart, ...other }: Props) {
29 | const theme = useTheme();
30 |
31 | const chartSeries = chart.series.map((item) => item.value);
32 |
33 | const chartColors = chart.colors ?? [
34 | theme.palette.primary.light,
35 | theme.palette.warning.light,
36 | theme.palette.secondary.light,
37 | theme.palette.error.light,
38 | ];
39 |
40 | const chartOptions = useChart({
41 | chart: { sparkline: { enabled: true } },
42 | colors: chartColors,
43 | labels: chart.series.map((item) => item.label),
44 | stroke: { width: 0 },
45 | dataLabels: { enabled: true, dropShadow: { enabled: false } },
46 | tooltip: {
47 | y: {
48 | formatter: (value: number) => fnNumber(value),
49 | title: { formatter: (seriesName: string) => `${seriesName}` },
50 | },
51 | },
52 | plotOptions: { pie: { donut: { labels: { show: false } } } },
53 | ...chart.options,
54 | });
55 |
56 | return (
57 |
58 |
59 |
60 |
68 |
69 |
70 |
71 |
76 |
77 | );
78 | }
79 |
--------------------------------------------------------------------------------
/src/services/orderService.ts:
--------------------------------------------------------------------------------
1 | import { _guid, _orders, _roles } from '../_mock';
2 |
3 |
4 | const KEYS = {
5 | items: "orders",
6 | itemId: "orderId"
7 | };
8 |
9 | export function init() {
10 |
11 | localStorage.setItem(KEYS.items, JSON.stringify(_orders));
12 | }
13 |
14 |
15 |
16 | export function addItem(data: TODO) {
17 | let items = getAllItems();
18 | data["id"] = generateItemId(items.length);
19 | items.push(data);
20 | localStorage.setItem(KEYS.items, JSON.stringify(items));
21 | }
22 |
23 | export function generateItemId(totalCount: number) {
24 | return `${_guid}${(totalCount+1)}`;
25 | }
26 |
27 | export function updateItem(data: TODO) {
28 | let items = getAllItems() as TODO;
29 | let index = items.findIndex((a: TODO) => a.id === data.id);
30 | items[index] = data;
31 | localStorage.setItem(KEYS.items, JSON.stringify(items));
32 | }
33 |
34 |
35 |
36 |
37 | export function getAllItems() {
38 | if (localStorage.getItem(KEYS.items) === null) {
39 | localStorage.setItem(KEYS.items, JSON.stringify([]));
40 | }
41 | const es = localStorage.getItem(KEYS.items);
42 | return JSON.parse(es ? es : "");
43 | }
44 |
45 |
46 | export function getItemById(id: string | number) {
47 | if (localStorage.getItem(KEYS.items) === null) {
48 | localStorage.setItem(KEYS.items, JSON.stringify([]));
49 | }
50 | const us = localStorage.getItem(KEYS.items);
51 | const ul = JSON.parse(us ? us : "");
52 | return ul.find((u: TODO) => u.id === id);
53 | }
54 |
55 | export function deleteItemById(id: string | number) {
56 | if (localStorage.getItem(KEYS.items) === null) {
57 | localStorage.setItem(KEYS.items, JSON.stringify([]));
58 | }
59 | const _items = localStorage.getItem(KEYS.items);
60 | const items = JSON.parse(_items ? _items : "");
61 | const index = items.findIndex((u: TODO) => u.id === id);
62 | items.splice(index,1)
63 | localStorage.setItem(KEYS.items, JSON.stringify(items));
64 | }
65 |
66 |
67 |
68 | export function getItemsByPageNumber(
69 | pageNumber: number, pageSize: number = 10 ) {
70 | if (localStorage.getItem(KEYS.items) === null) {
71 | localStorage.setItem(KEYS.items, JSON.stringify([]));
72 | }
73 | const es = localStorage.getItem(KEYS.items);
74 | const products = JSON.parse(es ? es : "");
75 |
76 | console.log( ` pageNumber ${pageNumber} `)
77 | const start = (pageNumber -1)* pageSize;
78 | const end = start + pageSize
79 | return products.slice( start, end);
80 | }
--------------------------------------------------------------------------------
/src/services/customerService.ts:
--------------------------------------------------------------------------------
1 | import { _customers, _guid, _roles } from '../_mock';
2 |
3 |
4 | const KEYS = {
5 | items: "customers",
6 | itemId: "customerId"
7 | };
8 |
9 | export function init() {
10 | console.log(_customers)
11 | localStorage.setItem(KEYS.items, JSON.stringify(_customers));
12 | }
13 |
14 |
15 |
16 | export function addItem(data: TODO) {
17 | let items = getAllItems();
18 | data["id"] = generateItemId(items.length);
19 | items.push(data);
20 | localStorage.setItem(KEYS.items, JSON.stringify(items));
21 | }
22 |
23 | export function generateItemId(totalCount: number) {
24 | return `${_guid}${(totalCount+1)}`;
25 | }
26 |
27 | export function updateItem(data: TODO) {
28 | let items = getAllItems() as TODO;
29 | let index = items.findIndex((a: TODO) => a.id === data.id);
30 | items[index] = data;
31 | localStorage.setItem(KEYS.items, JSON.stringify(items));
32 | }
33 |
34 |
35 |
36 |
37 | export function getAllItems() {
38 | if (localStorage.getItem(KEYS.items) === null) {
39 | localStorage.setItem(KEYS.items, JSON.stringify([]));
40 | }
41 | const es = localStorage.getItem(KEYS.items);
42 | return JSON.parse(es ? es : "");
43 | }
44 |
45 |
46 | export function getItemById(id: string | number) {
47 | if (localStorage.getItem(KEYS.items) === null) {
48 | localStorage.setItem(KEYS.items, JSON.stringify([]));
49 | }
50 | const us = localStorage.getItem(KEYS.items);
51 | const ul = JSON.parse(us ? us : "");
52 | return ul.find((u: TODO) => u.id === id);
53 | }
54 |
55 | export function deleteItemById(id: string | number) {
56 | if (localStorage.getItem(KEYS.items) === null) {
57 | localStorage.setItem(KEYS.items, JSON.stringify([]));
58 | }
59 | const _items = localStorage.getItem(KEYS.items);
60 | const items = JSON.parse(_items ? _items : "");
61 | const index = items.findIndex((u: TODO) => u.id === id);
62 | items.splice(index,1)
63 | localStorage.setItem(KEYS.items, JSON.stringify(items));
64 | }
65 |
66 |
67 |
68 | export function getItemsByPageNumber(
69 | pageNumber: number, pageSize: number = 10 ) {
70 | if (localStorage.getItem(KEYS.items) === null) {
71 | localStorage.setItem(KEYS.items, JSON.stringify([]));
72 | }
73 | const es = localStorage.getItem(KEYS.items);
74 | const products = JSON.parse(es ? es : "");
75 |
76 | console.log( ` pageNumber ${pageNumber} `)
77 | const start = (pageNumber -1)* pageSize;
78 | const end = start + pageSize
79 | return products.slice( start, end);
80 | }
--------------------------------------------------------------------------------
/src/services/productService.ts:
--------------------------------------------------------------------------------
1 | import { _guid, _products, _roles } from '../_mock';
2 |
3 |
4 | const KEYS = {
5 | items: "products",
6 | itemId: "productId"
7 | };
8 |
9 | export function init() {
10 | console.log(_products)
11 | localStorage.setItem(KEYS.items, JSON.stringify(_products));
12 | }
13 |
14 |
15 |
16 | export function addItem(data: TODO) {
17 | let items = getAllItems();
18 | data["id"] = generateItemId(items.length);
19 | items.push(data);
20 | localStorage.setItem(KEYS.items, JSON.stringify(items));
21 | }
22 |
23 | export function generateItemId(totalCount: number) {
24 | return `${_guid}${(totalCount + 1)}`;
25 | }
26 |
27 | export function updateItem(data: TODO) {
28 | let items = getAllItems() as TODO;
29 | let index = items.findIndex((a: TODO) => a.id === data.id);
30 | items[index] = data;
31 | localStorage.setItem(KEYS.items, JSON.stringify(items));
32 | }
33 |
34 |
35 |
36 |
37 | export function getAllItems() {
38 | if (localStorage.getItem(KEYS.items) === null) {
39 | localStorage.setItem(KEYS.items, JSON.stringify([]));
40 | }
41 | const es = localStorage.getItem(KEYS.items);
42 | return JSON.parse(es ? es : "");
43 | }
44 |
45 |
46 | export function getItemById(id: string | number) {
47 | if (localStorage.getItem(KEYS.items) === null) {
48 | localStorage.setItem(KEYS.items, JSON.stringify([]));
49 | }
50 | const us = localStorage.getItem(KEYS.items);
51 | const ul = JSON.parse(us ? us : "");
52 | return ul.find((u: TODO) => u.id === id);
53 | }
54 |
55 | export function deleteItemById(id: string | number) {
56 | if (localStorage.getItem(KEYS.items) === null) {
57 | localStorage.setItem(KEYS.items, JSON.stringify([]));
58 | }
59 | const _items = localStorage.getItem(KEYS.items);
60 | const items = JSON.parse(_items ? _items : "");
61 | const index = items.findIndex((u: TODO) => u.id === id);
62 | items.splice(index, 1)
63 | localStorage.setItem(KEYS.items, JSON.stringify(items));
64 | }
65 |
66 |
67 |
68 |
69 | export function getItemsByPageNumber(
70 | pageNumber: number, pageSize: number = 10) {
71 | if (localStorage.getItem(KEYS.items) === null) {
72 | localStorage.setItem(KEYS.items, JSON.stringify([]));
73 | }
74 | const es = localStorage.getItem(KEYS.items);
75 | const products = JSON.parse(es ? es : "");
76 |
77 | console.log(` pageNumber ${pageNumber} `)
78 | const start = (pageNumber - 1) * pageSize;
79 | const end = start + pageSize
80 | return products.slice(start, end);
81 | }
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-analytics.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-demo-v6",
3 | "version": "6.0.1",
4 | "description": "Boiler template to get up and running quickly with React, Storybook, Vite and TypeScript",
5 | "author": "Harry Ho ",
6 | "type": "module",
7 | "engines": {
8 | "node": ">=22.0.0"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/chromaui/intro-storybook-react-template"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/chromaui/intro-storybook-react-template/issues"
16 | },
17 | "license": "MIT",
18 | "private": true,
19 | "dependencies": {
20 | "@emotion/react": "^11",
21 | "@emotion/styled": "^11",
22 | "@iconify/react": "latest",
23 | "@mui/icons-material": "^6",
24 | "@mui/material": "^6",
25 | "@mui/styles": "^6.1.6",
26 | "@reduxjs/toolkit": "^2.3.0",
27 | "@toolpad/core": "latest",
28 | "apexcharts": "^4.0.0",
29 | "dayjs": "^1.11.13",
30 | "react": "^18.2.0",
31 | "react-apexcharts": "^1.5.0",
32 | "react-dom": "^18.2.0",
33 | "react-helmet-async": "^2.0.5",
34 | "react-redux": "^9.1.2",
35 | "react-router-dom": "^6.26.1",
36 | "simplebar-react": "^3.2.6"
37 | },
38 | "scripts": {
39 | "compile": "tsc",
40 | "dev": "vite",
41 | "build": "tsc && vite build",
42 | "preview": "vite preview",
43 | "storybook": "storybook dev -p 6006",
44 | "build-storybook": "storybook build",
45 | "init-msw": "msw init public/",
46 | "test-storybook": "test-storybook"
47 | },
48 | "devDependencies": {
49 | "@storybook/addon-essentials": "^8.3.2",
50 | "@storybook/addon-interactions": "^8.3.2",
51 | "@storybook/addon-links": "^8.3.2",
52 | "@storybook/blocks": "^8.3.2",
53 | "@storybook/react": "^8.3.2",
54 | "@storybook/react-vite": "^8.3.2",
55 | "@storybook/test": "^8.3.2",
56 | "@storybook/test-runner": "^0.19.1",
57 | "@types/react": "^18.2.66",
58 | "@types/react-dom": "^18.2.22",
59 | "@vitejs/plugin-react": "^4.2.1",
60 | "eslint": "^8.57.0",
61 | "eslint-plugin-react": "^7.34.1",
62 | "eslint-plugin-react-hooks": "^4.6.0",
63 | "eslint-plugin-react-refresh": "^0.4.6",
64 | "eslint-plugin-storybook": "^0.8.0",
65 | "msw": "^2.3.0",
66 | "msw-storybook-addon": "^2.0.3",
67 | "prop-types": "^15.8.1",
68 | "react-docgen-typescript-plugin": "^1.0.8",
69 | "storybook": "^8.3.2",
70 | "vite": "^5.4.21",
71 | "vitest": "^2.1.4"
72 | },
73 | "resolutions": {
74 | "jackspeak": "2.1.1"
75 | },
76 | "msw": {
77 | "workerDirectory": [
78 | "public"
79 | ]
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/components/product/ProductItem.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import Link from '@mui/material/Link';
3 | import Card from '@mui/material/Card';
4 | import Stack from '@mui/material/Stack';
5 | import Typography from '@mui/material/Typography';
6 |
7 | import { fnCurrency } from '../../utils/format-number';
8 |
9 | import { Label } from '../label';
10 | // import Label from '@mui/material/InputLabel';
11 | import { ColorPreview } from '../color-utils';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | export type ProductItemProps = {
16 | id: string;
17 | name: string;
18 | price: number;
19 | status: string;
20 | coverUrl: string;
21 | colors: string[];
22 | priceSale: number | null;
23 | };
24 |
25 | export function ProductItem({ product }: { product: ProductItemProps }) {
26 | const renderStatus = (
27 |
39 | {product.status}
40 |
41 | );
42 |
43 | const renderImg = (
44 |
56 | );
57 |
58 | const renderPrice = (
59 |
60 |
68 | {product.priceSale && fnCurrency(product.priceSale)}
69 |
70 |
71 | {fnCurrency(product.price)}
72 |
73 | );
74 |
75 | return (
76 |
77 |
78 | {product.status && renderStatus}
79 |
80 | {renderImg}
81 |
82 |
83 |
84 |
85 | {product.name}
86 |
87 |
88 |
89 |
90 | {renderPrice}
91 |
92 |
93 |
94 | );
95 | }
96 |
--------------------------------------------------------------------------------
/src/theme/core/shadows.ts:
--------------------------------------------------------------------------------
1 | import type { Shadows } from '@mui/material/styles';
2 |
3 | import { grey } from './palette';
4 | import { varAlpha } from '../styles';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | export function shadows(): Shadows {
9 | const colorChannel = grey['500Channel'];
10 |
11 | const color1 = varAlpha(colorChannel, 0.2);
12 | const color2 = varAlpha(colorChannel, 0.14);
13 | const color3 = varAlpha(colorChannel, 0.12);
14 |
15 | return [
16 | 'none',
17 | `0px 2px 1px -1px ${color1},0px 1px 1px 0px ${color2},0px 1px 3px 0px ${color3}`,
18 | `0px 3px 1px -2px ${color1},0px 2px 2px 0px ${color2},0px 1px 5px 0px ${color3}`,
19 | `0px 3px 3px -2px ${color1},0px 3px 4px 0px ${color2},0px 1px 8px 0px ${color3}`,
20 | `0px 2px 4px -1px ${color1},0px 4px 5px 0px ${color2},0px 1px 10px 0px ${color3}`,
21 | `0px 3px 5px -1px ${color1},0px 5px 8px 0px ${color2},0px 1px 14px 0px ${color3}`,
22 | `0px 3px 5px -1px ${color1},0px 6px 10px 0px ${color2},0px 1px 18px 0px ${color3}`,
23 | `0px 4px 5px -2px ${color1},0px 7px 10px 1px ${color2},0px 2px 16px 1px ${color3}`,
24 | `0px 5px 5px -3px ${color1},0px 8px 10px 1px ${color2},0px 3px 14px 2px ${color3}`,
25 | `0px 5px 6px -3px ${color1},0px 9px 12px 1px ${color2},0px 3px 16px 2px ${color3}`,
26 | `0px 6px 6px -3px ${color1},0px 10px 14px 1px ${color2},0px 4px 18px 3px ${color3}`,
27 | `0px 6px 7px -4px ${color1},0px 11px 15px 1px ${color2},0px 4px 20px 3px ${color3}`,
28 | `0px 7px 8px -4px ${color1},0px 12px 17px 2px ${color2},0px 5px 22px 4px ${color3}`,
29 | `0px 7px 8px -4px ${color1},0px 13px 19px 2px ${color2},0px 5px 24px 4px ${color3}`,
30 | `0px 7px 9px -4px ${color1},0px 14px 21px 2px ${color2},0px 5px 26px 4px ${color3}`,
31 | `0px 8px 9px -5px ${color1},0px 15px 22px 2px ${color2},0px 6px 28px 5px ${color3}`,
32 | `0px 8px 10px -5px ${color1},0px 16px 24px 2px ${color2},0px 6px 30px 5px ${color3}`,
33 | `0px 8px 11px -5px ${color1},0px 17px 26px 2px ${color2},0px 6px 32px 5px ${color3}`,
34 | `0px 9px 11px -5px ${color1},0px 18px 28px 2px ${color2},0px 7px 34px 6px ${color3}`,
35 | `0px 9px 12px -6px ${color1},0px 19px 29px 2px ${color2},0px 7px 36px 6px ${color3}`,
36 | `0px 10px 13px -6px ${color1},0px 20px 31px 3px ${color2},0px 8px 38px 7px ${color3}`,
37 | `0px 10px 13px -6px ${color1},0px 21px 33px 3px ${color2},0px 8px 40px 7px ${color3}`,
38 | `0px 10px 14px -6px ${color1},0px 22px 35px 3px ${color2},0px 8px 42px 7px ${color3}`,
39 | `0px 11px 14px -7px ${color1},0px 23px 36px 3px ${color2},0px 9px 44px 8px ${color3}`,
40 | `0px 11px 15px -7px ${color1},0px 24px 38px 3px ${color2},0px 9px 46px 8px ${color3}`,
41 | ];
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsOrderTimeline.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { TimelineItemProps } from '@mui/lab/TimelineItem';
3 |
4 | import Card from '@mui/material/Card';
5 | import Timeline from '@mui/lab/Timeline';
6 | import TimelineDot from '@mui/lab/TimelineDot';
7 | import Typography from '@mui/material/Typography';
8 | import CardHeader from '@mui/material/CardHeader';
9 | import TimelineContent from '@mui/lab/TimelineContent';
10 | import TimelineSeparator from '@mui/lab/TimelineSeparator';
11 | import TimelineConnector from '@mui/lab/TimelineConnector';
12 | import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
13 |
14 | import { fnDateTime } from '../../utils/format-time';
15 |
16 | // ----------------------------------------------------------------------
17 |
18 | type Props = CardProps & {
19 | title?: string;
20 | subheader?: string;
21 | list: {
22 | id: string;
23 | type: string;
24 | title: string;
25 | time: string | number | null;
26 | }[];
27 | };
28 |
29 | export function AnalyticsOrderTimeline({ title, subheader, list, ...other }: Props) {
30 | return (
31 |
32 |
33 |
34 |
44 | {list.map((item, index) => (
45 |
46 | ))}
47 |
48 |
49 | );
50 | }
51 |
52 | // ----------------------------------------------------------------------
53 |
54 | type ItemProps = TimelineItemProps & {
55 | lastItem: boolean;
56 | item: Props['list'][number];
57 | };
58 |
59 | function Item({ item, lastItem, ...other }: ItemProps) {
60 | return (
61 |
62 |
63 |
72 | {lastItem ? null : }
73 |
74 |
75 |
76 | {item.title}
77 |
78 |
79 | {fnDateTime(item.time)}
80 |
81 |
82 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/sb/TaskList.tsx:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from 'react-redux';
2 | // import { Box, Stack } from '@mui/material';
3 |
4 | import Task, { TaskType } from './Task';
5 | import { updateTaskState } from '../../store/store';
6 |
7 | export interface TaskListProps {
8 | loading: boolean;
9 | tasks: TaskType[];
10 | onPinTask: Function;
11 | onArchiveTask: Function;
12 | }
13 |
14 | export default function TaskList() {
15 | // We're retrieving our state from the store
16 | const tasks = useSelector((state: any) => {
17 | const tasksInOrder = [
18 | ...state.taskbox.tasks.filter((t: any)=> t.state === 'TASK_PINNED'),
19 | ...state.taskbox.tasks.filter((t: any)=> t.state !== 'TASK_PINNED'),
20 | ];
21 | const filteredTasks = tasksInOrder.filter(
22 | (t: any)=> t.state === 'TASK_INBOX' || t.state === 'TASK_PINNED'
23 | );
24 | return filteredTasks;
25 | });
26 |
27 | const { status } = useSelector((state: any) => state.taskbox);
28 |
29 | const dispatch = useDispatch();
30 |
31 | const pinTask = (value: any) => {
32 | // We're dispatching the Pinned event back to our store
33 | dispatch(updateTaskState({ id: value, newTaskState: 'TASK_PINNED' }));
34 | };
35 | const archiveTask = (value: any) => {
36 | // We're dispatching the Archive event back to our store
37 | dispatch(updateTaskState({ id: value, newTaskState: 'TASK_ARCHIVED' }));
38 | };
39 | const LoadingRow = (
40 |
41 |
42 |
43 | Loading cool state
44 |
45 |
46 | );
47 | if (status === 'loading') {
48 | return (
49 |
50 | {LoadingRow}
51 | {LoadingRow}
52 | {LoadingRow}
53 | {LoadingRow}
54 | {LoadingRow}
55 | {LoadingRow}
56 |
57 | );
58 | }
59 | if (tasks.length === 0) {
60 | return (
61 |
62 |
63 |
64 |
You have no tasks
65 |
Sit back and relax
66 |
67 |
68 | );
69 | }
70 |
71 | return (
72 |
73 | {tasks.map((task: any) => (
74 | pinTask(t)}
78 | onArchiveTask={(t: any) => archiveTask(t)}
79 | />
80 | ))}
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/src/components/analytics/AnalyticsNews.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 | import type { CardProps } from '@mui/material/Card';
3 |
4 | import Box from '@mui/material/Box';
5 | import Card from '@mui/material/Card';
6 | import Button from '@mui/material/Button';
7 | import Avatar from '@mui/material/Avatar';
8 | import CardHeader from '@mui/material/CardHeader';
9 | import ListItemText from '@mui/material/ListItemText';
10 |
11 | import { fToNow } from '../../utils/format-time';
12 |
13 | import { Iconify } from '../iconify';
14 | import { Scrollbar } from '../scrollbar';
15 |
16 | import type { PostItemProps } from '../blog/PostItem';
17 |
18 | // ----------------------------------------------------------------------
19 |
20 | type Props = CardProps & {
21 | title?: string;
22 | subheader?: string;
23 | list: PostItemProps[];
24 | };
25 |
26 | export function AnalyticsNews({ title, subheader, list, ...other }: Props) {
27 | return (
28 |
29 |
30 |
31 | {/* */}
32 |
33 | {list.map((post) => (
34 |
35 | ))}
36 |
37 | {/* */}
38 |
39 |
40 | }
44 | >
45 | View all
46 |
47 |
48 |
49 | );
50 | }
51 |
52 | // ----------------------------------------------------------------------
53 |
54 | function PostItem({ sx, item, ...other }: BoxProps & { item: Props['list'][number] }) {
55 | return (
56 | `dashed 1px ${theme.vars.palette.divider}`,
64 | ...sx,
65 | }}
66 | {...other}
67 | >
68 |
74 |
75 |
81 |
82 |
83 | {fToNow(item.postedAt)}
84 |
85 |
86 | );
87 | }
88 |
--------------------------------------------------------------------------------
/src/theme/styles/mixins.ts:
--------------------------------------------------------------------------------
1 | import type { CSSObject } from '@mui/material/styles';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | /**
6 | * Usage:
7 | * ...hideScrollX,
8 | * ...hideScrollY,
9 | */
10 | export const hideScrollX: CSSObject = {
11 | msOverflowStyle: 'none',
12 | scrollbarWidth: 'none',
13 | overflowX: 'auto',
14 | '&::-webkit-scrollbar': { display: 'none' },
15 | };
16 |
17 | export const hideScrollY: CSSObject = {
18 | msOverflowStyle: 'none',
19 | scrollbarWidth: 'none',
20 | overflowY: 'auto',
21 | '&::-webkit-scrollbar': { display: 'none' },
22 | };
23 |
24 | /**
25 | * Usage:
26 | * ...textGradient(`to right, ${theme.vars.palette.text.primary}, ${alpha(theme.vars.palette.text.primary, 0.2)}`
27 | */
28 | export function textGradient(color: string): CSSObject {
29 | return {
30 | background: `linear-gradient(${color})`,
31 | WebkitBackgroundClip: 'text',
32 | WebkitTextFillColor: 'transparent',
33 | backgroundClip: 'text',
34 | textFillColor: 'transparent',
35 | color: 'transparent',
36 | };
37 | }
38 |
39 | /**
40 | * Usage:
41 | * ...bgGradient({ color: `to right, ${theme.vars.palette.grey[900]} 25%, ${varAlpha(theme.vars.palette.primary.darkerChannel, 0.88)}`, imgUrl: '/assets/background/overlay.png' }),
42 | */
43 | export type BgGradientProps = {
44 | color: string;
45 | imgUrl?: string;
46 | };
47 |
48 | export function bgGradient({ color, imgUrl }: BgGradientProps): CSSObject {
49 | if (imgUrl) {
50 | return {
51 | background: `linear-gradient(${color}), url(${imgUrl})`,
52 | backgroundSize: 'cover',
53 | backgroundRepeat: 'no-repeat',
54 | backgroundPosition: 'center center',
55 | };
56 | }
57 | return { background: `linear-gradient(${color})` };
58 | }
59 |
60 | /**
61 | * Usage:
62 | * ...bgBlur({ color: `varAlpha(theme.vars.palette.background.paperChannel, 0.8)`, imgUrl: '/assets/background/overlay.png', blur: 6 }),
63 | */
64 | export type BgBlurProps = {
65 | color: string;
66 | blur?: number;
67 | imgUrl?: string;
68 | };
69 |
70 | export function bgBlur({ color, blur = 6, imgUrl }: BgBlurProps): CSSObject {
71 | if (imgUrl) {
72 | return {
73 | position: 'relative',
74 | backgroundImage: `url(${imgUrl})`,
75 | '&::before': {
76 | position: 'absolute',
77 | top: 0,
78 | left: 0,
79 | zIndex: 9,
80 | content: '""',
81 | width: '100%',
82 | height: '100%',
83 | backdropFilter: `blur(${blur}px)`,
84 | WebkitBackdropFilter: `blur(${blur}px)`,
85 | backgroundColor: color,
86 | },
87 | };
88 | }
89 | return {
90 | backdropFilter: `blur(${blur}px)`,
91 | WebkitBackdropFilter: `blur(${blur}px)`,
92 | backgroundColor: color,
93 | };
94 | }
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | React MUI Demo v6
3 |
4 |
5 | This react demo is built on the top of my customized React boilerplate with Storybook.
6 |
7 | The boilerplate repo is available [here](https://github.com/harryho/storybook-react-vite-ts-template).
8 |
9 | If you want to build something simpler from scratch, you can follow the README to build your own app step by step.
10 |
11 |
12 |
13 | ### Live Demo
14 |
15 | [Demo](https://react-demo-v6.harryho.org/) The demo is just a proof of concept. It doesn't have back-end API and all features of master branch.
16 |
17 | ### Screenshots
18 |
19 | 
20 |
21 | 
22 |
23 | 
24 |
25 | 
26 |
27 |
28 |
29 | ----
30 |
31 |
32 | ## Getting Start
33 |
34 | ```bash
35 | # Clone project
36 | git clone https://github.com/harryho/react-demo.git
37 |
38 |
39 | # install the packages with npm
40 | cd react-demo
41 |
42 | # development
43 | yarn dev
44 |
45 | # build
46 | yarn build
47 |
48 | ```
49 |
50 |
51 |
52 | ----
53 |
54 |
55 | ### Previous demo
56 |
57 | [Previous Demo](https://react-demo-v4.harryho.org/) The demo is built on React 16 and Mui 4. Source code is available [here](https://github.com/harryho/react-crm/tree/r16m4)
58 |
59 | #### Screenshots
60 |
61 | 
62 |
63 | 
64 |
65 | 
66 |
67 | #### Storybook
68 |
69 | 
70 |
71 |
72 |
73 | ### Change log
74 |
75 | - Apr 2025 - Merge latest demo to master branch.
76 |
77 | - Dec 2024 - React 18 and Mui 6 live demo is released.
78 |
79 | - Jun 2024 - Uplifting to React 18 and Mui 6 is in progress.
80 |
81 | - May 2020 - Merge the branch rctsx to master
82 |
83 | After the merge, the whole project moved to new technical stack - TypeScript 3. Also, the Material-UI is upgraded to 4.x version. Nodejs 12.x is recommended.
84 |
85 | - Dec 2018 - Rebase demo branch to master
86 |
87 | New master doesn't rely on Json-Server as fake API. It will only have Readonly fake API. It means any new or updated data will be stored to any physical file. All test data will be rolled back after system restart.
88 |
89 | - May 2018 - Create an archived branch json-server
90 |
91 | This branch was the master which used Json-Server as fake API. Considering the hiccup of setting Json-Server up and maintenance, it will be replaced by fake service ( Readonly fake API). You still can find clone this branch by branch name **json-server**, but it will be no longer updated. It is an archived branch.
--------------------------------------------------------------------------------
/src/layouts/components/Searchbar.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import { useState, useCallback } from 'react';
4 |
5 | import Box from '@mui/material/Box';
6 | import Slide from '@mui/material/Slide';
7 | import Input from '@mui/material/Input';
8 | import Button from '@mui/material/Button';
9 | import { useTheme } from '@mui/material/styles';
10 | import IconButton from '@mui/material/IconButton';
11 | import InputAdornment from '@mui/material/InputAdornment';
12 | import ClickAwayListener from '@mui/material/ClickAwayListener';
13 |
14 | import { bgBlur } from 'src/theme/styles';
15 |
16 | import { Iconify } from 'src/components/iconify';
17 |
18 | // ----------------------------------------------------------------------
19 |
20 | export function Searchbar({ sx, ...other }: BoxProps) {
21 | const theme = useTheme();
22 |
23 | const [open, setOpen] = useState(false);
24 |
25 | const handleOpen = useCallback(() => {
26 | setOpen((prev) => !prev);
27 | }, []);
28 |
29 | const handleClose = useCallback(() => {
30 | setOpen(false);
31 | }, []);
32 |
33 | return (
34 |
35 |
36 | {!open && (
37 |
38 |
39 |
40 | )}
41 |
42 |
43 |
65 |
72 |
73 |
74 | }
75 | sx={{ fontWeight: 'fontWeightBold' }}
76 | />
77 |
78 | Search
79 |
80 |
81 |
82 |
83 |
84 | );
85 | }
86 |
--------------------------------------------------------------------------------
/src/services/agentService.ts:
--------------------------------------------------------------------------------
1 | import { _agents, _roles , _guid} from '../_mock';
2 |
3 | export const roleArray = () => [
4 | { id: "1", title: 'Sales Leader' },
5 | { id: "2", title: 'Hr Manager' },
6 | { id: "3", title: 'Sales Agent' },
7 | { id: "4", title: 'Sales Operator' },
8 | { id: "5", title: 'Sales Manager' },
9 | { id: "6", title: 'Project Manager' },
10 | { id: "7", title: 'Business Analyst' },
11 | { id: "9", title: 'Product Designer' },
12 | { id: "10", title: 'Market Manager' },
13 | { id: "11", title: 'General Manager' }
14 | ]
15 |
16 |
17 | const KEYS = {
18 | items: "agents",
19 | itemId: "agentId"
20 | };
21 |
22 | export function init() {
23 | console.log(_agents)
24 | localStorage.setItem(KEYS.items, JSON.stringify(_agents));
25 | }
26 |
27 |
28 | export function addItem(data: TODO) {
29 | let items = getAllItems();
30 | data["id"] = generateItemId(items.length);
31 | items.push(data);
32 | localStorage.setItem(KEYS.items, JSON.stringify(items));
33 | }
34 |
35 | export function generateItemId(totalCount: number) {
36 | return `${_guid}${(totalCount+1)}`;
37 | }
38 |
39 | export function updateItem(data: TODO) {
40 | let items = getAllItems() as TODO;
41 | let index = items.findIndex((a: TODO) => a.id === data.id);
42 | items[index] = data;
43 | localStorage.setItem(KEYS.items, JSON.stringify(items));
44 | }
45 |
46 |
47 |
48 |
49 | export function getAllItems() {
50 | if (localStorage.getItem(KEYS.items) === null) {
51 | localStorage.setItem(KEYS.items, JSON.stringify([]));
52 | }
53 | const es = localStorage.getItem(KEYS.items);
54 | return JSON.parse(es ? es : "");
55 | }
56 |
57 |
58 | export function getItemById(id: string | number) {
59 | if (localStorage.getItem(KEYS.items) === null) {
60 | localStorage.setItem(KEYS.items, JSON.stringify([]));
61 | }
62 | const us = localStorage.getItem(KEYS.items);
63 | const ul = JSON.parse(us ? us : "");
64 | return ul.find((u: TODO) => u.id === id);
65 | }
66 |
67 | export function deleteItemById(id: string | number) {
68 | if (localStorage.getItem(KEYS.items) === null) {
69 | localStorage.setItem(KEYS.items, JSON.stringify([]));
70 | }
71 | const _items = localStorage.getItem(KEYS.items);
72 | const items = JSON.parse(_items ? _items : "");
73 | const index = items.findIndex((u: TODO) => u.id === id);
74 | items.splice(index,1)
75 | localStorage.setItem(KEYS.items, JSON.stringify(items));
76 | }
77 |
78 |
79 |
80 | export function getItemsByPageNumber(
81 | pageNumber: number, pageSize: number = 10 ) {
82 | if (localStorage.getItem(KEYS.items) === null) {
83 | localStorage.setItem(KEYS.items, JSON.stringify([]));
84 | }
85 | const es = localStorage.getItem(KEYS.items);
86 | const products = JSON.parse(es ? es : "");
87 |
88 | console.log( ` pageNumber ${pageNumber} `)
89 | const start = (pageNumber -1)* pageSize;
90 | const end = start + pageSize
91 | return products.slice( start, end);
92 | }
--------------------------------------------------------------------------------
/src/components/product/ProductSort.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonProps } from '@mui/material/Button';
2 |
3 | import { useState, useCallback } from 'react';
4 |
5 | import Button from '@mui/material/Button';
6 | import Popover from '@mui/material/Popover';
7 | import MenuList from '@mui/material/MenuList';
8 | import Typography from '@mui/material/Typography';
9 | import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
10 |
11 | import { Iconify } from '../iconify';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type ProductSortProps = ButtonProps & {
16 | sortBy: string;
17 | onSort: (newSort: string) => void;
18 | options: { value: string; label: string }[];
19 | };
20 |
21 | export function ProductSort({ options, sortBy, onSort, sx, ...other }: ProductSortProps) {
22 | const [openPopover, setOpenPopover] = useState(null);
23 |
24 | const handleOpenPopover = useCallback((event: React.MouseEvent) => {
25 | setOpenPopover(event.currentTarget);
26 | }, []);
27 |
28 | const handleClosePopover = useCallback(() => {
29 | setOpenPopover(null);
30 | }, []);
31 |
32 | return (
33 | <>
34 | }
39 | sx={sx}
40 | {...other}
41 | >
42 | Sort By:
43 |
44 | {options.find((option) => option.value === sortBy)?.label}
45 |
46 |
47 |
48 |
55 |
71 | {options.map((option) => (
72 | {
76 | onSort(option.value);
77 | handleClosePopover();
78 | }}
79 | >
80 | {option.label}
81 |
82 | ))}
83 |
84 |
85 | >
86 | );
87 | }
88 |
--------------------------------------------------------------------------------
/src/theme/styles/utils.ts:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------
2 |
3 | export const stylesMode = {
4 | light: '[data-mui-color-scheme="light"] &',
5 | dark: '[data-mui-color-scheme="dark"] &',
6 | };
7 |
8 | export const mediaQueries = {
9 | upXs: '@media (min-width:0px)',
10 | upSm: '@media (min-width:600px)',
11 | upMd: '@media (min-width:900px)',
12 | upLg: '@media (min-width:1200px)',
13 | upXl: '@media (min-width:1536px)',
14 | };
15 |
16 | /**
17 | * Set font family
18 | */
19 | export function setFont(fontName: string) {
20 | return `"${fontName}",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"`;
21 | }
22 |
23 | /**
24 | * Converts rem to px
25 | */
26 | export function remToPx(value: string): number {
27 | return Math.round(parseFloat(value) * 16);
28 | }
29 |
30 | /**
31 | * Converts px to rem
32 | */
33 | export function pxToRem(value: number): string {
34 | return `${value / 16}rem`;
35 | }
36 |
37 | /**
38 | * Responsive font sizes
39 | */
40 | export function responsiveFontSizes({ sm, md, lg }: { sm: number; md: number; lg: number }) {
41 | return {
42 | [mediaQueries.upSm]: { fontSize: pxToRem(sm) },
43 | [mediaQueries.upMd]: { fontSize: pxToRem(md) },
44 | [mediaQueries.upLg]: { fontSize: pxToRem(lg) },
45 | };
46 | }
47 |
48 | /**
49 | * Converts a hex color to RGB channels
50 | */
51 | export function hexToRgbChannel(hex: string) {
52 | if (!/^#[0-9A-F]{6}$/i.test(hex)) {
53 | throw new Error(`Invalid hex color: ${hex}`);
54 | }
55 |
56 | const r = parseInt(hex.substring(1, 3), 16);
57 | const g = parseInt(hex.substring(3, 5), 16);
58 | const b = parseInt(hex.substring(5, 7), 16);
59 |
60 | return `${r} ${g} ${b}`;
61 | }
62 |
63 | /**
64 | * Converts a hex color to RGB channels
65 | */
66 | export function createPaletteChannel(hexPalette: Record) {
67 | const channelPalette: Record = {};
68 |
69 | Object.entries(hexPalette).forEach(([key, value]) => {
70 | channelPalette[`${key}Channel`] = hexToRgbChannel(value);
71 | });
72 |
73 | return { ...hexPalette, ...channelPalette };
74 | }
75 |
76 | /**
77 | * Color with alpha channel
78 | */
79 | export function varAlpha(color: string, opacity = 1) {
80 | const unsupported =
81 | color.startsWith('#') ||
82 | color.startsWith('rgb') ||
83 | color.startsWith('rgba') ||
84 | (!color.includes('var') && color.includes('Channel'));
85 |
86 | if (unsupported) {
87 | throw new Error(
88 | `[Alpha]: Unsupported color format "${color}".
89 | Supported formats are:
90 | - RGB channels: "0 184 217".
91 | - CSS variables with "Channel" prefix: "var(--palette-common-blackChannel, #000000)".
92 | Unsupported formats are:
93 | - Hex: "#00B8D9".
94 | - RGB: "rgb(0, 184, 217)".
95 | - RGBA: "rgba(0, 184, 217, 1)".
96 | `
97 | );
98 | }
99 |
100 | return `rgba(${color} / ${opacity})`;
101 | }
102 |
--------------------------------------------------------------------------------
/src/components/blog/PostSort.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonProps } from '@mui/material/Button';
2 |
3 | import { useState, useCallback } from 'react';
4 |
5 | import Button from '@mui/material/Button';
6 | import Popover from '@mui/material/Popover';
7 | import MenuList from '@mui/material/MenuList';
8 | import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
9 |
10 | import { varAlpha } from '../../theme/styles';
11 | import { lightPalette as palette } from '../../theme/core';
12 | import { Iconify } from '../iconify';
13 |
14 | // ----------------------------------------------------------------------
15 |
16 | type PostSortProps = ButtonProps & {
17 | sortBy: string;
18 | onSort: (newSort: string) => void;
19 | options: { value: string; label: string }[];
20 | };
21 |
22 | export function PostSort({ options, sortBy, onSort, sx, ...other }: PostSortProps) {
23 | const [openPopover, setOpenPopover] = useState(null);
24 |
25 | const handleOpenPopover = useCallback((event: React.MouseEvent) => {
26 | setOpenPopover(event.currentTarget);
27 | }, []);
28 |
29 | const handleClosePopover = useCallback(() => {
30 | setOpenPopover(null);
31 | }, []);
32 |
33 | return (
34 | <>
35 |
46 | }
47 | sx={{
48 | // bgcolor: (theme) => varAlpha(theme.vars.palette.grey['500Channel'], 0.08),
49 | bgcolor: (theme) => varAlpha(palette.grey['500Channel'], 0.08),
50 | ...sx,
51 | }}
52 | {...other}
53 | >
54 | {options.find((option) => option.value === sortBy)?.label}
55 |
56 |
57 |
64 |
80 | {options.map((option) => (
81 | {
85 | onSort(option.value);
86 | handleClosePopover();
87 | }}
88 | >
89 | {option.label}
90 |
91 | ))}
92 |
93 |
94 | >
95 | );
96 | }
97 |
--------------------------------------------------------------------------------
/src/utils/format-time.ts:
--------------------------------------------------------------------------------
1 | import type { Dayjs } from 'dayjs';
2 |
3 | import dayjs from 'dayjs';
4 | import duration from 'dayjs/plugin/duration';
5 | import relativeTime from 'dayjs/plugin/relativeTime';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | dayjs.extend(duration);
10 | dayjs.extend(relativeTime);
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | export type DatePickerFormat = Dayjs | Date | string | number | null | undefined;
15 |
16 | /**
17 | * Docs: https://day.js.org/docs/en/display/format
18 | */
19 | export const formatStr = {
20 | dateTime: 'DD MMM YYYY h:mm a', // 17 Apr 2022 12:00 am
21 | date: 'DD MMM YYYY', // 17 Apr 2022
22 | time: 'h:mm a', // 12:00 am
23 | split: {
24 | dateTime: 'DD/MM/YYYY h:mm a', // 17/04/2022 12:00 am
25 | date: 'DD/MM/YYYY', // 17/04/2022
26 | },
27 | paramCase: {
28 | dateTime: 'DD-MM-YYYY h:mm a', // 17-04-2022 12:00 am
29 | date: 'DD-MM-YYYY', // 17-04-2022
30 | },
31 | };
32 |
33 | export function today(format?: string) {
34 | return dayjs(new Date()).startOf('day').format(format);
35 | }
36 |
37 | // ----------------------------------------------------------------------
38 |
39 | /** output: 17 Apr 2022 12:00 am
40 | */
41 | export function fnDateTime(date: DatePickerFormat, format?: string) {
42 | if (!date) {
43 | return null;
44 | }
45 |
46 | const isValid = dayjs(date).isValid();
47 |
48 | return isValid ? dayjs(date).format(format ?? formatStr.dateTime) : 'Invalid time value';
49 | }
50 |
51 | // ----------------------------------------------------------------------
52 |
53 | /** output: 17 Apr 2022
54 | */
55 | export function fnDate(date: DatePickerFormat, format?: string) {
56 | if (!date) {
57 | return null;
58 | }
59 |
60 | const isValid = dayjs(date).isValid();
61 |
62 | return isValid ? dayjs(date).format(format ?? formatStr.date) : 'Invalid time value';
63 | }
64 |
65 | // ----------------------------------------------------------------------
66 |
67 | /** output: 12:00 am
68 | */
69 | export function fTime(date: DatePickerFormat, format?: string) {
70 | if (!date) {
71 | return null;
72 | }
73 |
74 | const isValid = dayjs(date).isValid();
75 |
76 | return isValid ? dayjs(date).format(format ?? formatStr.time) : 'Invalid time value';
77 | }
78 |
79 | // ----------------------------------------------------------------------
80 |
81 | /** output: 1713250100
82 | */
83 | export function fTimestamp(date: DatePickerFormat) {
84 | if (!date) {
85 | return null;
86 | }
87 |
88 | const isValid = dayjs(date).isValid();
89 |
90 | return isValid ? dayjs(date).valueOf() : 'Invalid time value';
91 | }
92 |
93 | // ----------------------------------------------------------------------
94 |
95 | /** output: a few seconds, 2 years
96 | */
97 | export function fToNow(date: DatePickerFormat) {
98 | if (!date) {
99 | return null;
100 | }
101 |
102 | const isValid = dayjs(date).isValid();
103 |
104 | return isValid ? dayjs(date).toNow(true) : 'Invalid time value';
105 | }
106 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom/client';
3 | import { createBrowserRouter, RouterProvider } from 'react-router-dom';
4 | import App from './App';
5 | import Layout from './layouts/Dashboard';
6 | import ProductsView from './pages/ProductsView';
7 | import OrdersView from './pages/OrdersView';
8 | import { OverviewAnalyticsView } from './pages/OverviewAnalyticsView';
9 | import { BlogView } from './pages/BlogView';
10 | import { NotFoundView } from './pages/NotFoundView';
11 | import { HelmetProvider } from 'react-helmet-async';
12 | import { AgentView } from './pages/AgentView';
13 | import { CustomerView } from './pages/CustomerView';
14 | import AgentForm , { agentLoader }from './pages/AgentForm';
15 | import SignInView from './pages/SignInView';
16 | import CustomerForm , { customerLoader } from './pages/CustomerForm';
17 | import OrderForm , {orderLoader} from './pages/OrderForm';
18 |
19 |
20 | const router = createBrowserRouter([
21 | {
22 | Component: App,
23 | children: [
24 | {
25 | path: '',
26 | Component: Layout,
27 | children: [
28 | {
29 | path: '',
30 | Component: OverviewAnalyticsView,
31 | },
32 | {
33 | path: 'orders',
34 | Component: OrdersView,
35 | },
36 | {
37 | path: 'products',
38 | Component: ProductsView,
39 | },
40 | {
41 | path: 'customers',
42 | Component: CustomerView,
43 | },
44 | {
45 | path: 'blogs',
46 | Component: BlogView,
47 | },
48 | {
49 | path: 'agents',
50 | Component: AgentView,
51 | },
52 | {
53 | path: 'agent-form',
54 | Component: AgentForm,
55 | },
56 | {
57 | path: 'edit-agent/:id',
58 | Component: AgentForm,
59 | loader: agentLoader
60 |
61 | },
62 | {
63 | path: 'customer-form',
64 | Component: CustomerForm,
65 | },
66 | {
67 | path: 'edit-customer/:id',
68 | Component: CustomerForm,
69 | loader: customerLoader
70 |
71 | },
72 | {
73 | path: 'order-form',
74 | Component: OrderForm,
75 | },
76 | {
77 | path: 'edit-order/:id',
78 | Component: OrderForm,
79 | loader: orderLoader
80 |
81 | },
82 | {
83 | path: '*',
84 | Component: NotFoundView
85 | }
86 | ]
87 | },
88 | {
89 | path: '/sign-in',
90 | Component: SignInView
91 | },
92 | {
93 | path: '*',
94 | Component: NotFoundView
95 | }
96 | ],
97 | },
98 | ]);
99 |
100 |
101 |
102 |
103 | ReactDOM.createRoot(document.getElementById('root')!).render(
104 |
105 |
106 |
107 |
108 |
109 | );
110 |
--------------------------------------------------------------------------------
/src/store/store.ts:
--------------------------------------------------------------------------------
1 |
2 | /* A simple redux store/actions/reducer implementation.
3 | * A true app would be more complex and separated into different files.
4 | */
5 | import { configureStore, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
6 |
7 | /*
8 | * The initial state of our store when the app loads.
9 | * Usually, you would fetch this from a server. Let's not worry about that now
10 | */
11 | const defaultTasks = [
12 | { id: '1', title: 'Something', state: 'TASK_INBOX' },
13 | { id: '2', title: 'Something more', state: 'TASK_INBOX' },
14 | { id: '3', title: 'Something else', state: 'TASK_INBOX' },
15 | { id: '4', title: 'Something again', state: 'TASK_INBOX' },
16 | ];
17 | const TaskBoxData = {
18 | tasks: defaultTasks,
19 | status: 'idle',
20 | error: null,
21 | };
22 |
23 |
24 | /*
25 | * Creates an asyncThunk to fetch tasks from a remote endpoint.
26 | * You can read more about Redux Toolkit's thunks in the docs:
27 | * https://redux-toolkit.js.org/api/createAsyncThunk
28 | */
29 | export const fetchTasks = createAsyncThunk('todos/fetchTodos', async () => {
30 | const response = await fetch(
31 | 'https://jsonplaceholder.typicode.com/todos?userId=1'
32 | );
33 | const data = await response.json();
34 | const result = data.map((task: any) =>({
35 | id: `${task.id}`,
36 | title: task.title,
37 | state: task.completed ? 'TASK_ARCHIVED' : 'TASK_INBOX',
38 | }));
39 | return result;
40 | });
41 |
42 | /*
43 | * The store is created here.
44 | * You can read more about Redux Toolkit's slices in the docs:
45 | * https://redux-toolkit.js.org/api/createSlice
46 | */
47 | const TasksSlice = createSlice({
48 | name: 'taskbox',
49 | initialState: TaskBoxData,
50 | reducers: {
51 | updateTaskState: (state, action) => {
52 | const { id, newTaskState } = action.payload;
53 | const task = state.tasks.findIndex((t: any) =>t.id === id);
54 | if (task >= 0) {
55 | state.tasks[task].state = newTaskState;
56 | }
57 | },
58 | },
59 | /*
60 | * Extends the reducer for the async actions
61 | * You can read more about it at https://redux-toolkit.js.org/api/createAsyncThunk
62 | */
63 | extraReducers(builder) {
64 | builder
65 | .addCase(fetchTasks.pending, (state: any) => {
66 | state.status = 'loading';
67 | state.error = null;
68 | state.tasks = [];
69 | })
70 | .addCase(fetchTasks.fulfilled, (state, action) => {
71 | state.status = 'succeeded';
72 | state.error = null;
73 | // Add any fetched tasks to the array
74 | state.tasks = action.payload;
75 | })
76 | .addCase(fetchTasks.rejected, (state: any) => {
77 | state.status = 'failed';
78 | state.error = "Something went wrong";
79 | state.tasks = [];
80 | });
81 | },
82 | });
83 |
84 | // The actions contained in the slice are exported for usage in our components
85 | export const { updateTaskState } = TasksSlice.actions;
86 |
87 | /*
88 | * Our app's store configuration goes here.
89 | * Read more about Redux's configureStore in the docs:
90 | * https://redux-toolkit.js.org/api/configureStore
91 | */
92 | const store = configureStore({
93 | reducer: {
94 | taskbox: TasksSlice.reducer,
95 | },
96 | });
97 |
98 | export default store;
--------------------------------------------------------------------------------
/src/theme/core/typography.ts:
--------------------------------------------------------------------------------
1 | import type { TypographyOptions } from '@mui/material/styles/createTypography';
2 |
3 | import { setFont, pxToRem, responsiveFontSizes } from '../styles/utils';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | declare module '@mui/material/styles' {
8 | interface TypographyVariants {
9 | fontSecondaryFamily: React.CSSProperties['fontFamily'];
10 | fontWeightSemiBold: React.CSSProperties['fontWeight'];
11 | }
12 | interface TypographyVariantsOptions {
13 | fontSecondaryFamily?: React.CSSProperties['fontFamily'];
14 | fontWeightSemiBold?: React.CSSProperties['fontWeight'];
15 | }
16 | interface ThemeVars {
17 | typography: Theme['typography'];
18 | }
19 | }
20 |
21 | // ----------------------------------------------------------------------
22 |
23 | export const defaultFont = 'DM Sans Variable';
24 |
25 | export const primaryFont = setFont(defaultFont);
26 |
27 | export const secondaryFont = setFont('Barlow');
28 |
29 | // ----------------------------------------------------------------------
30 |
31 | export const typography: TypographyOptions = {
32 | fontFamily: primaryFont,
33 | fontSecondaryFamily: secondaryFont,
34 | fontWeightLight: '300',
35 | fontWeightRegular: '400',
36 | fontWeightMedium: '500',
37 | fontWeightSemiBold: '600',
38 | fontWeightBold: '700',
39 | h1: {
40 | fontWeight: 800,
41 | lineHeight: 80 / 64,
42 | fontSize: pxToRem(40),
43 | fontFamily: secondaryFont,
44 | ...responsiveFontSizes({ sm: 52, md: 58, lg: 64 }),
45 | },
46 | h2: {
47 | fontWeight: 800,
48 | lineHeight: 64 / 48,
49 | fontSize: pxToRem(32),
50 | fontFamily: secondaryFont,
51 | ...responsiveFontSizes({ sm: 40, md: 44, lg: 48 }),
52 | },
53 | h3: {
54 | fontWeight: 700,
55 | lineHeight: 1.5,
56 | fontSize: pxToRem(24),
57 | fontFamily: secondaryFont,
58 | ...responsiveFontSizes({ sm: 26, md: 30, lg: 32 }),
59 | },
60 | h4: {
61 | fontWeight: 700,
62 | lineHeight: 1.5,
63 | fontSize: pxToRem(20),
64 | ...responsiveFontSizes({ sm: 20, md: 24, lg: 24 }),
65 | },
66 | h5: {
67 | fontWeight: 700,
68 | lineHeight: 1.5,
69 | fontSize: pxToRem(18),
70 | ...responsiveFontSizes({ sm: 19, md: 20, lg: 20 }),
71 | },
72 | h6: {
73 | fontWeight: 600,
74 | lineHeight: 28 / 18,
75 | fontSize: pxToRem(17),
76 | ...responsiveFontSizes({ sm: 18, md: 18, lg: 18 }),
77 | },
78 | subtitle1: {
79 | fontWeight: 600,
80 | lineHeight: 1.5,
81 | fontSize: pxToRem(16),
82 | },
83 | subtitle2: {
84 | fontWeight: 600,
85 | lineHeight: 22 / 14,
86 | fontSize: pxToRem(14),
87 | },
88 | body1: {
89 | lineHeight: 1.5,
90 | fontSize: pxToRem(16),
91 | },
92 | body2: {
93 | lineHeight: 22 / 14,
94 | fontSize: pxToRem(14),
95 | },
96 | caption: {
97 | lineHeight: 1.5,
98 | fontSize: pxToRem(12),
99 | },
100 | overline: {
101 | fontWeight: 700,
102 | lineHeight: 1.5,
103 | fontSize: pxToRem(12),
104 | textTransform: 'uppercase',
105 | },
106 | button: {
107 | fontWeight: 700,
108 | lineHeight: 24 / 14,
109 | fontSize: pxToRem(14),
110 | textTransform: 'unset',
111 | },
112 | };
113 |
--------------------------------------------------------------------------------
/src/utils/format-number.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Locales code
3 | * https://gist.github.com/raushankrjha/d1c7e35cf87e69aa8b4208a8171a8416
4 | */
5 |
6 | export type InputNumberValue = string | number | null | undefined;
7 |
8 | type Options = Intl.NumberFormatOptions | undefined;
9 |
10 | const DEFAULT_LOCALE = { code: 'en-US', currency: 'USD' };
11 |
12 | function processInput(inputValue: InputNumberValue): number | null {
13 | if (inputValue == null || Number.isNaN(inputValue)) return null;
14 | return Number(inputValue);
15 | }
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | export function fnNumber(inputValue: InputNumberValue, options?: Options) {
20 | const locale = DEFAULT_LOCALE;
21 |
22 | const number = processInput(inputValue);
23 | if (number === null) return '';
24 |
25 | const fm = new Intl.NumberFormat(locale.code, {
26 | minimumFractionDigits: 0,
27 | maximumFractionDigits: 2,
28 | ...options,
29 | }).format(number);
30 |
31 | return fm;
32 | }
33 |
34 | // ----------------------------------------------------------------------
35 |
36 | export function fnCurrency(inputValue: InputNumberValue, options?: Options) {
37 | const locale = DEFAULT_LOCALE;
38 |
39 | const number = processInput(inputValue);
40 | if (number === null) return '';
41 |
42 | const fm = new Intl.NumberFormat(locale.code, {
43 | style: 'currency',
44 | currency: locale.currency,
45 | minimumFractionDigits: 0,
46 | maximumFractionDigits: 2,
47 | ...options,
48 | }).format(number);
49 |
50 | return fm;
51 | }
52 |
53 | // ----------------------------------------------------------------------
54 |
55 | export function fnPercent(inputValue: InputNumberValue, options?: Options) {
56 | const locale = DEFAULT_LOCALE;
57 |
58 | const number = processInput(inputValue);
59 | if (number === null) return '';
60 |
61 | const fm = new Intl.NumberFormat(locale.code, {
62 | style: 'percent',
63 | minimumFractionDigits: 0,
64 | maximumFractionDigits: 1,
65 | ...options,
66 | }).format(number / 100);
67 |
68 | return fm;
69 | }
70 |
71 | // ----------------------------------------------------------------------
72 |
73 | export function fnShortenNumber(inputValue: InputNumberValue, options?: Options) {
74 | const locale = DEFAULT_LOCALE;
75 |
76 | const number = processInput(inputValue);
77 | if (number === null) return '';
78 |
79 | const fm = new Intl.NumberFormat(locale.code, {
80 | notation: 'compact',
81 | maximumFractionDigits: 2,
82 | ...options,
83 | }).format(number);
84 |
85 | return fm.replace(/[A-Z]/g, (match) => match.toLowerCase());
86 | }
87 |
88 | // ----------------------------------------------------------------------
89 |
90 | export function fnData(inputValue: InputNumberValue) {
91 | const number = processInput(inputValue);
92 | if (number === null || number === 0) return '0 bytes';
93 |
94 | const units = ['bytes', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb'];
95 | const decimal = 2;
96 | const baseValue = 1024;
97 |
98 | const index = Math.floor(Math.log(number) / Math.log(baseValue));
99 | const fm = `${parseFloat((number / baseValue ** index).toFixed(decimal))} ${units[index]}`;
100 |
101 | return fm;
102 | }
103 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-chat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------