test cicd
This commit is contained in:
64
.github/workflows/node-cicd.yml
vendored
Normal file
64
.github/workflows/node-cicd.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
name: Release package to Gitea and Deploy to AWS
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Check for change
|
||||||
|
id: diff
|
||||||
|
run: |
|
||||||
|
git diff --name-only origin/${{ github.ref_name }}~1 # This prints the list to screen only
|
||||||
|
echo "src=$(git diff --name-only origin/${{ github.ref_name }}~1 src| tr '\n' ' ')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
if: ${{ steps.diff.outputs.src }}
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
registry-url: ${{ github.server_url != 'https://github.com' && format('{0}/api/packages/{1}/npm/', github.server_url, github.repository_owner) || 'https://npm.pkg.github.com' }}
|
||||||
|
scope: ${{ github.repository_owner }}
|
||||||
|
token: ${{ secrets.ACTIONS_TOKEN || github.token }}
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
if: ${{ steps.diff.outputs.src }}
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.ACTIONS_TOKEN || github.token }}
|
||||||
|
|
||||||
|
- run: npm run build
|
||||||
|
if: ${{ steps.diff.outputs.src }}
|
||||||
|
|
||||||
|
- run: npm publish
|
||||||
|
if: ${{ steps.diff.outputs.src }}
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.ACTIONS_TOKEN || github.token}}
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
PACKAGE=$(npm pkg get name version|jq -r '"\(.name)@\(.version)"')
|
||||||
|
mkdir ../deployment && cd ../deployment && npm i $PACKAGE
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.ACTIONS_TOKEN || github.token }}
|
||||||
|
|
||||||
|
- name: Config AWS creds
|
||||||
|
uses: fc-actions/aws-login@v0.0.15
|
||||||
|
with:
|
||||||
|
#fireclover-client-id: ${{ vars.FIRECLOVER_CLIENT_ID }}
|
||||||
|
#fireclover-client-secret: ${{ secrets.FIRECLOVER_CLIENT_SECRET }}
|
||||||
|
aws-account-id: 515966519418
|
||||||
|
|
||||||
|
- name: Deploy Login to AWS
|
||||||
|
uses: fc-actions/deploy-cloudapp@v0.0.38
|
||||||
|
with:
|
||||||
|
fireclover-subscription: 'my-fireclover-subscription-token'
|
||||||
|
aws-account-id: 515966519418
|
||||||
|
dns-zone: 'test.aws.fireclover.cloud'
|
||||||
|
subdomain: 'test-example'
|
||||||
|
web-path: '../deployment/dist'
|
||||||
136
.gitignore
vendored
Normal file
136
.gitignore
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# vitepress build output
|
||||||
|
**/.vitepress/dist
|
||||||
|
|
||||||
|
# vitepress cache directory
|
||||||
|
**/.vitepress/cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM node:latest
|
||||||
|
|
||||||
|
ENV HOME=/home/app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install htop -y
|
||||||
|
|
||||||
|
COPY . $HOME/node_docker
|
||||||
|
|
||||||
|
WORKDIR $HOME/node_docker
|
||||||
|
|
||||||
|
RUN npm ci --silent --progress=false
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
20
index.html
Normal file
20
index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
|
<!-- Fonts to support Material Design -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
|
||||||
|
/>
|
||||||
|
<title>Vite + Material UI + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
36
package.json
Normal file
36
package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "@fireclover/example-react",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Infrastructure for an HTTPS static site using S3 and CloudFront",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"test": "echo test ok"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.0",
|
||||||
|
"@mui/icons-material": "^6.4.5",
|
||||||
|
"@mui/material": "^6.4.5",
|
||||||
|
"@mui/x-charts": "^7.27.0",
|
||||||
|
"@mui/x-data-grid": "^7.27.0",
|
||||||
|
"@mui/x-data-grid-pro": "^7.27.0",
|
||||||
|
"@mui/x-date-pickers": "^7.27.0",
|
||||||
|
"@mui/x-date-pickers-pro": "^7.27.0",
|
||||||
|
"@mui/x-tree-view": "^7.26.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"react": "latest",
|
||||||
|
"react-dom": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "latest",
|
||||||
|
"@types/react-dom": "latest",
|
||||||
|
"@types/node": "latest",
|
||||||
|
"@vitejs/plugin-react": "latest",
|
||||||
|
"typescript": "latest",
|
||||||
|
"vite": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
38
src/App.tsx
Normal file
38
src/App.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import ProTip from './ProTip';
|
||||||
|
|
||||||
|
function Copyright() {
|
||||||
|
return (
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
align="center"
|
||||||
|
sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{'Copyright © '}
|
||||||
|
<Link color="inherit" href="https://mui.com/">
|
||||||
|
Your Website
|
||||||
|
</Link>{' '}
|
||||||
|
{new Date().getFullYear()}.
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<Container maxWidth="sm">
|
||||||
|
<Box sx={{ my: 4 }}>
|
||||||
|
<Typography variant="h4" component="h1" sx={{ mb: 2 }}>
|
||||||
|
Material UI Vite.js example in TypeScript
|
||||||
|
</Typography>
|
||||||
|
<ProTip />
|
||||||
|
<Copyright />
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/ProTip.tsx
Normal file
23
src/ProTip.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
function LightBulbIcon(props: SvgIconProps) {
|
||||||
|
return (
|
||||||
|
<SvgIcon {...props}>
|
||||||
|
<path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z" />
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ProTip() {
|
||||||
|
return (
|
||||||
|
<Typography sx={{ mt: 6, mb: 3, color: 'text.secondary' }}>
|
||||||
|
<LightBulbIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
||||||
|
{'Pro tip: See more '}
|
||||||
|
<Link href="https://mui.com/material-ui/getting-started/templates/">templates</Link>
|
||||||
|
{' in the Material UI documentation.'}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
63
src/dashboard/Dashboard.tsx
Normal file
63
src/dashboard/Dashboard.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import type {} from '@mui/x-date-pickers/themeAugmentation';
|
||||||
|
import type {} from '@mui/x-charts/themeAugmentation';
|
||||||
|
import type {} from '@mui/x-data-grid-pro/themeAugmentation';
|
||||||
|
import type {} from '@mui/x-tree-view/themeAugmentation';
|
||||||
|
import { alpha } from '@mui/material/styles';
|
||||||
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import AppNavbar from './components/AppNavbar';
|
||||||
|
import Header from './components/Header';
|
||||||
|
import MainGrid from './components/MainGrid';
|
||||||
|
import SideMenu from './components/SideMenu';
|
||||||
|
import AppTheme from '../shared-theme/AppTheme';
|
||||||
|
import {
|
||||||
|
chartsCustomizations,
|
||||||
|
dataGridCustomizations,
|
||||||
|
datePickersCustomizations,
|
||||||
|
treeViewCustomizations,
|
||||||
|
} from './theme/customizations';
|
||||||
|
|
||||||
|
const xThemeComponents = {
|
||||||
|
...chartsCustomizations,
|
||||||
|
...dataGridCustomizations,
|
||||||
|
...datePickersCustomizations,
|
||||||
|
...treeViewCustomizations,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Dashboard(props: { disableCustomTheme?: boolean }) {
|
||||||
|
return (
|
||||||
|
<AppTheme {...props} themeComponents={xThemeComponents}>
|
||||||
|
<CssBaseline enableColorScheme />
|
||||||
|
<Box sx={{ display: 'flex' }}>
|
||||||
|
<SideMenu />
|
||||||
|
<AppNavbar />
|
||||||
|
{/* Main content */}
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={(theme) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
backgroundColor: theme.vars
|
||||||
|
? `rgba(${theme.vars.palette.background.defaultChannel} / 1)`
|
||||||
|
: alpha(theme.palette.background.default, 1),
|
||||||
|
overflow: 'auto',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
spacing={2}
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
mx: 3,
|
||||||
|
pb: 5,
|
||||||
|
mt: { xs: 8, md: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Header />
|
||||||
|
<MainGrid />
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</AppTheme>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
src/dashboard/README.md
Normal file
15
src/dashboard/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Dashboard template
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
<!-- #default-branch-switch -->
|
||||||
|
|
||||||
|
1. Copy these folders (`dashboard` and `shared-theme`) into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/v6.x/examples).
|
||||||
|
2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react, @mui/x-charts, @mui/x-date-pickers, @mui/x-data-grid, @mui/x-tree-view, dayjs
|
||||||
|
3. Import and use the `Dashboard` component.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
<!-- #default-branch-switch -->
|
||||||
|
|
||||||
|
View the demo at https://mui.com/material-ui/getting-started/templates/dashboard/.
|
||||||
3
src/dashboard/Title.tsx.preview
Normal file
3
src/dashboard/Title.tsx.preview
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
||||||
|
{props.children}
|
||||||
|
</Typography>
|
||||||
105
src/dashboard/components/AppNavbar.tsx
Normal file
105
src/dashboard/components/AppNavbar.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import AppBar from '@mui/material/AppBar';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import MuiToolbar from '@mui/material/Toolbar';
|
||||||
|
import { tabsClasses } from '@mui/material/Tabs';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import MenuRoundedIcon from '@mui/icons-material/MenuRounded';
|
||||||
|
import DashboardRoundedIcon from '@mui/icons-material/DashboardRounded';
|
||||||
|
import SideMenuMobile from './SideMenuMobile';
|
||||||
|
import MenuButton from './MenuButton';
|
||||||
|
import ColorModeIconDropdown from '../../shared-theme/ColorModeIconDropdown';
|
||||||
|
|
||||||
|
const Toolbar = styled(MuiToolbar)({
|
||||||
|
width: '100%',
|
||||||
|
padding: '12px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'start',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: '12px',
|
||||||
|
flexShrink: 0,
|
||||||
|
[`& ${tabsClasses.flexContainer}`]: {
|
||||||
|
gap: '8px',
|
||||||
|
p: '8px',
|
||||||
|
pb: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function AppNavbar() {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
const toggleDrawer = (newOpen: boolean) => () => {
|
||||||
|
setOpen(newOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppBar
|
||||||
|
position="fixed"
|
||||||
|
sx={{
|
||||||
|
display: { xs: 'auto', md: 'none' },
|
||||||
|
boxShadow: 0,
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
backgroundImage: 'none',
|
||||||
|
borderBottom: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
top: 'var(--template-frame-height, 0px)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Toolbar variant="regular">
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
flexGrow: 1,
|
||||||
|
width: '100%',
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
spacing={1}
|
||||||
|
sx={{ justifyContent: 'center', mr: 'auto' }}
|
||||||
|
>
|
||||||
|
<CustomIcon />
|
||||||
|
<Typography variant="h4" component="h1" sx={{ color: 'text.primary' }}>
|
||||||
|
Dashboard
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<ColorModeIconDropdown />
|
||||||
|
<MenuButton aria-label="menu" onClick={toggleDrawer(true)}>
|
||||||
|
<MenuRoundedIcon />
|
||||||
|
</MenuButton>
|
||||||
|
<SideMenuMobile open={open} toggleDrawer={toggleDrawer} />
|
||||||
|
</Stack>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CustomIcon() {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: '1.5rem',
|
||||||
|
height: '1.5rem',
|
||||||
|
bgcolor: 'black',
|
||||||
|
borderRadius: '999px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
alignSelf: 'center',
|
||||||
|
backgroundImage:
|
||||||
|
'linear-gradient(135deg, hsl(210, 98%, 60%) 0%, hsl(210, 100%, 35%) 100%)',
|
||||||
|
color: 'hsla(210, 100%, 95%, 0.9)',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'hsl(210, 100%, 55%)',
|
||||||
|
boxShadow: 'inset 0 2px 5px rgba(255, 255, 255, 0.3)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DashboardRoundedIcon color="inherit" sx={{ fontSize: '1rem' }} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
25
src/dashboard/components/CardAlert.tsx
Normal file
25
src/dashboard/components/CardAlert.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import AutoAwesomeRoundedIcon from '@mui/icons-material/AutoAwesomeRounded';
|
||||||
|
|
||||||
|
export default function CardAlert() {
|
||||||
|
return (
|
||||||
|
<Card variant="outlined" sx={{ m: 1.5, flexShrink: 0 }}>
|
||||||
|
<CardContent>
|
||||||
|
<AutoAwesomeRoundedIcon fontSize="small" />
|
||||||
|
<Typography gutterBottom sx={{ fontWeight: 600 }}>
|
||||||
|
Plan about to expire
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ mb: 2, color: 'text.secondary' }}>
|
||||||
|
Enjoy 10% off when renewing your plan today.
|
||||||
|
</Typography>
|
||||||
|
<Button variant="contained" size="small" fullWidth>
|
||||||
|
Get the discount
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
200
src/dashboard/components/ChartUserByCountry.tsx
Normal file
200
src/dashboard/components/ChartUserByCountry.tsx
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { PieChart } from '@mui/x-charts/PieChart';
|
||||||
|
import { useDrawingArea } from '@mui/x-charts/hooks';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import LinearProgress, { linearProgressClasses } from '@mui/material/LinearProgress';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IndiaFlag,
|
||||||
|
UsaFlag,
|
||||||
|
BrazilFlag,
|
||||||
|
GlobeFlag,
|
||||||
|
} from '../internals/components/CustomIcons';
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{ label: 'India', value: 50000 },
|
||||||
|
{ label: 'USA', value: 35000 },
|
||||||
|
{ label: 'Brazil', value: 10000 },
|
||||||
|
{ label: 'Other', value: 5000 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const countries = [
|
||||||
|
{
|
||||||
|
name: 'India',
|
||||||
|
value: 50,
|
||||||
|
flag: <IndiaFlag />,
|
||||||
|
color: 'hsl(220, 25%, 65%)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'USA',
|
||||||
|
value: 35,
|
||||||
|
flag: <UsaFlag />,
|
||||||
|
color: 'hsl(220, 25%, 45%)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Brazil',
|
||||||
|
value: 10,
|
||||||
|
flag: <BrazilFlag />,
|
||||||
|
color: 'hsl(220, 25%, 30%)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Other',
|
||||||
|
value: 5,
|
||||||
|
flag: <GlobeFlag />,
|
||||||
|
color: 'hsl(220, 25%, 20%)',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface StyledTextProps {
|
||||||
|
variant: 'primary' | 'secondary';
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledText = styled('text', {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'variant',
|
||||||
|
})<StyledTextProps>(({ theme }) => ({
|
||||||
|
textAnchor: 'middle',
|
||||||
|
dominantBaseline: 'central',
|
||||||
|
fill: (theme.vars || theme).palette.text.secondary,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'primary',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontSize: theme.typography.h5.fontSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: ({ variant }) => variant !== 'primary',
|
||||||
|
style: {
|
||||||
|
fontSize: theme.typography.body2.fontSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'primary',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontWeight: theme.typography.h5.fontWeight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: ({ variant }) => variant !== 'primary',
|
||||||
|
style: {
|
||||||
|
fontWeight: theme.typography.body2.fontWeight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface PieCenterLabelProps {
|
||||||
|
primaryText: string;
|
||||||
|
secondaryText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PieCenterLabel({ primaryText, secondaryText }: PieCenterLabelProps) {
|
||||||
|
const { width, height, left, top } = useDrawingArea();
|
||||||
|
const primaryY = top + height / 2 - 10;
|
||||||
|
const secondaryY = primaryY + 24;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<StyledText variant="primary" x={left + width / 2} y={primaryY}>
|
||||||
|
{primaryText}
|
||||||
|
</StyledText>
|
||||||
|
<StyledText variant="secondary" x={left + width / 2} y={secondaryY}>
|
||||||
|
{secondaryText}
|
||||||
|
</StyledText>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
'hsl(220, 20%, 65%)',
|
||||||
|
'hsl(220, 20%, 42%)',
|
||||||
|
'hsl(220, 20%, 35%)',
|
||||||
|
'hsl(220, 20%, 25%)',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ChartUserByCountry() {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ display: 'flex', flexDirection: 'column', gap: '8px', flexGrow: 1 }}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Typography component="h2" variant="subtitle2">
|
||||||
|
Users by country
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<PieChart
|
||||||
|
colors={colors}
|
||||||
|
margin={{
|
||||||
|
left: 80,
|
||||||
|
right: 80,
|
||||||
|
top: 80,
|
||||||
|
bottom: 80,
|
||||||
|
}}
|
||||||
|
series={[
|
||||||
|
{
|
||||||
|
data,
|
||||||
|
innerRadius: 75,
|
||||||
|
outerRadius: 100,
|
||||||
|
paddingAngle: 0,
|
||||||
|
highlightScope: { faded: 'global', highlighted: 'item' },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
height={260}
|
||||||
|
width={260}
|
||||||
|
slotProps={{
|
||||||
|
legend: { hidden: true },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PieCenterLabel primaryText="98.5K" secondaryText="Total" />
|
||||||
|
</PieChart>
|
||||||
|
</Box>
|
||||||
|
{countries.map((country, index) => (
|
||||||
|
<Stack
|
||||||
|
key={index}
|
||||||
|
direction="row"
|
||||||
|
sx={{ alignItems: 'center', gap: 2, pb: 2 }}
|
||||||
|
>
|
||||||
|
{country.flag}
|
||||||
|
<Stack sx={{ gap: 1, flexGrow: 1 }}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: '500' }}>
|
||||||
|
{country.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||||
|
{country.value}%
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<LinearProgress
|
||||||
|
variant="determinate"
|
||||||
|
aria-label="Number of users by country"
|
||||||
|
value={country.value}
|
||||||
|
sx={{
|
||||||
|
[`& .${linearProgressClasses.bar}`]: {
|
||||||
|
backgroundColor: country.color,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
77
src/dashboard/components/CustomDatePicker.tsx
Normal file
77
src/dashboard/components/CustomDatePicker.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import CalendarTodayRoundedIcon from '@mui/icons-material/CalendarTodayRounded';
|
||||||
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||||
|
import { UseDateFieldProps } from '@mui/x-date-pickers/DateField';
|
||||||
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||||
|
import {
|
||||||
|
BaseSingleInputFieldProps,
|
||||||
|
DateValidationError,
|
||||||
|
FieldSection,
|
||||||
|
} from '@mui/x-date-pickers/models';
|
||||||
|
|
||||||
|
interface ButtonFieldProps
|
||||||
|
extends UseDateFieldProps<Dayjs, false>,
|
||||||
|
BaseSingleInputFieldProps<
|
||||||
|
Dayjs | null,
|
||||||
|
Dayjs,
|
||||||
|
FieldSection,
|
||||||
|
false,
|
||||||
|
DateValidationError
|
||||||
|
> {
|
||||||
|
setOpen?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ButtonField(props: ButtonFieldProps) {
|
||||||
|
const {
|
||||||
|
setOpen,
|
||||||
|
label,
|
||||||
|
id,
|
||||||
|
disabled,
|
||||||
|
InputProps: { ref } = {},
|
||||||
|
inputProps: { 'aria-label': ariaLabel } = {},
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
id={id}
|
||||||
|
disabled={disabled}
|
||||||
|
ref={ref}
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
size="small"
|
||||||
|
onClick={() => setOpen?.((prev) => !prev)}
|
||||||
|
startIcon={<CalendarTodayRoundedIcon fontSize="small" />}
|
||||||
|
sx={{ minWidth: 'fit-content' }}
|
||||||
|
>
|
||||||
|
{label ? `${label}` : 'Pick a date'}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CustomDatePicker() {
|
||||||
|
const [value, setValue] = React.useState<Dayjs | null>(dayjs('2023-04-17'));
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||||
|
<DatePicker
|
||||||
|
value={value}
|
||||||
|
label={value == null ? null : value.format('MMM DD, YYYY')}
|
||||||
|
onChange={(newValue) => setValue(newValue)}
|
||||||
|
slots={{ field: ButtonField }}
|
||||||
|
slotProps={{
|
||||||
|
field: { setOpen } as any,
|
||||||
|
nextIconButton: { size: 'small' },
|
||||||
|
previousIconButton: { size: 'small' },
|
||||||
|
}}
|
||||||
|
open={open}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
onOpen={() => setOpen(true)}
|
||||||
|
views={['day', 'month', 'year']}
|
||||||
|
/>
|
||||||
|
</LocalizationProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
48
src/dashboard/components/CustomizedDataGrid.tsx
Normal file
48
src/dashboard/components/CustomizedDataGrid.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { DataGrid } from '@mui/x-data-grid';
|
||||||
|
import { columns, rows } from '../internals/data/gridData';
|
||||||
|
|
||||||
|
export default function CustomizedDataGrid() {
|
||||||
|
return (
|
||||||
|
<DataGrid
|
||||||
|
checkboxSelection
|
||||||
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
getRowClassName={(params) =>
|
||||||
|
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
|
||||||
|
}
|
||||||
|
initialState={{
|
||||||
|
pagination: { paginationModel: { pageSize: 20 } },
|
||||||
|
}}
|
||||||
|
pageSizeOptions={[10, 20, 50]}
|
||||||
|
disableColumnResize
|
||||||
|
density="compact"
|
||||||
|
slotProps={{
|
||||||
|
filterPanel: {
|
||||||
|
filterFormProps: {
|
||||||
|
logicOperatorInputProps: {
|
||||||
|
variant: 'outlined',
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
columnInputProps: {
|
||||||
|
variant: 'outlined',
|
||||||
|
size: 'small',
|
||||||
|
sx: { mt: 'auto' },
|
||||||
|
},
|
||||||
|
operatorInputProps: {
|
||||||
|
variant: 'outlined',
|
||||||
|
size: 'small',
|
||||||
|
sx: { mt: 'auto' },
|
||||||
|
},
|
||||||
|
valueInputProps: {
|
||||||
|
InputComponentProps: {
|
||||||
|
variant: 'outlined',
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
208
src/dashboard/components/CustomizedTreeView.tsx
Normal file
208
src/dashboard/components/CustomizedTreeView.tsx
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { animated, useSpring } from '@react-spring/web';
|
||||||
|
import { TransitionProps } from '@mui/material/transitions';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Collapse from '@mui/material/Collapse';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
|
||||||
|
import {
|
||||||
|
unstable_useTreeItem2 as useTreeItem2,
|
||||||
|
UseTreeItem2Parameters,
|
||||||
|
} from '@mui/x-tree-view/useTreeItem2';
|
||||||
|
import {
|
||||||
|
TreeItem2Content,
|
||||||
|
TreeItem2IconContainer,
|
||||||
|
TreeItem2Label,
|
||||||
|
TreeItem2Root,
|
||||||
|
} from '@mui/x-tree-view/TreeItem2';
|
||||||
|
import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon';
|
||||||
|
import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider';
|
||||||
|
import { TreeViewBaseItem } from '@mui/x-tree-view/models';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
type Color = 'blue' | 'green';
|
||||||
|
|
||||||
|
type ExtendedTreeItemProps = {
|
||||||
|
color?: Color;
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ITEMS: TreeViewBaseItem<ExtendedTreeItemProps>[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Website',
|
||||||
|
children: [
|
||||||
|
{ id: '1.1', label: 'Home', color: 'green' },
|
||||||
|
{ id: '1.2', label: 'Pricing', color: 'green' },
|
||||||
|
{ id: '1.3', label: 'About us', color: 'green' },
|
||||||
|
{
|
||||||
|
id: '1.4',
|
||||||
|
label: 'Blog',
|
||||||
|
children: [
|
||||||
|
{ id: '1.1.1', label: 'Announcements', color: 'blue' },
|
||||||
|
{ id: '1.1.2', label: 'April lookahead', color: 'blue' },
|
||||||
|
{ id: '1.1.3', label: "What's new", color: 'blue' },
|
||||||
|
{ id: '1.1.4', label: 'Meet the team', color: 'blue' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
label: 'Store',
|
||||||
|
children: [
|
||||||
|
{ id: '2.1', label: 'All products', color: 'green' },
|
||||||
|
{
|
||||||
|
id: '2.2',
|
||||||
|
label: 'Categories',
|
||||||
|
children: [
|
||||||
|
{ id: '2.2.1', label: 'Gadgets', color: 'blue' },
|
||||||
|
{ id: '2.2.2', label: 'Phones', color: 'blue' },
|
||||||
|
{ id: '2.2.3', label: 'Wearables', color: 'blue' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ id: '2.3', label: 'Bestsellers', color: 'green' },
|
||||||
|
{ id: '2.4', label: 'Sales', color: 'green' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ id: '4', label: 'Contact', color: 'blue' },
|
||||||
|
{ id: '5', label: 'Help', color: 'blue' },
|
||||||
|
];
|
||||||
|
|
||||||
|
function DotIcon({ color }: { color: string }) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ marginRight: 1, display: 'flex', alignItems: 'center' }}>
|
||||||
|
<svg width={6} height={6}>
|
||||||
|
<circle cx={3} cy={3} r={3} fill={color} />
|
||||||
|
</svg>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimatedCollapse = animated(Collapse);
|
||||||
|
|
||||||
|
function TransitionComponent(props: TransitionProps) {
|
||||||
|
const style = useSpring({
|
||||||
|
to: {
|
||||||
|
opacity: props.in ? 1 : 0,
|
||||||
|
transform: `translate3d(0,${props.in ? 0 : 20}px,0)`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <AnimatedCollapse style={style} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomLabelProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
color?: Color;
|
||||||
|
expandable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CustomLabel({ color, expandable, children, ...other }: CustomLabelProps) {
|
||||||
|
const theme = useTheme();
|
||||||
|
const colors = {
|
||||||
|
blue: (theme.vars || theme).palette.primary.main,
|
||||||
|
green: (theme.vars || theme).palette.success.main,
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconColor = color ? colors[color] : null;
|
||||||
|
return (
|
||||||
|
<TreeItem2Label {...other} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
{iconColor && <DotIcon color={iconColor} />}
|
||||||
|
<Typography
|
||||||
|
className="labelText"
|
||||||
|
variant="body2"
|
||||||
|
sx={{ color: 'text.primary' }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Typography>
|
||||||
|
</TreeItem2Label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomTreeItemProps
|
||||||
|
extends Omit<UseTreeItem2Parameters, 'rootRef'>,
|
||||||
|
Omit<React.HTMLAttributes<HTMLLIElement>, 'onFocus'> {}
|
||||||
|
|
||||||
|
const CustomTreeItem = React.forwardRef(function CustomTreeItem(
|
||||||
|
props: CustomTreeItemProps,
|
||||||
|
ref: React.Ref<HTMLLIElement>,
|
||||||
|
) {
|
||||||
|
const { id, itemId, label, disabled, children, ...other } = props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
getRootProps,
|
||||||
|
getContentProps,
|
||||||
|
getIconContainerProps,
|
||||||
|
getLabelProps,
|
||||||
|
getGroupTransitionProps,
|
||||||
|
status,
|
||||||
|
publicAPI,
|
||||||
|
} = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref });
|
||||||
|
|
||||||
|
const item = publicAPI.getItem(itemId);
|
||||||
|
const color = item?.color;
|
||||||
|
return (
|
||||||
|
<TreeItem2Provider itemId={itemId}>
|
||||||
|
<TreeItem2Root {...getRootProps(other)}>
|
||||||
|
<TreeItem2Content
|
||||||
|
{...getContentProps({
|
||||||
|
className: clsx('content', {
|
||||||
|
expanded: status.expanded,
|
||||||
|
selected: status.selected,
|
||||||
|
focused: status.focused,
|
||||||
|
disabled: status.disabled,
|
||||||
|
}),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{status.expandable && (
|
||||||
|
<TreeItem2IconContainer {...getIconContainerProps()}>
|
||||||
|
<TreeItem2Icon status={status} />
|
||||||
|
</TreeItem2IconContainer>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<CustomLabel {...getLabelProps({ color })} />
|
||||||
|
</TreeItem2Content>
|
||||||
|
{children && (
|
||||||
|
<TransitionComponent
|
||||||
|
{...getGroupTransitionProps({ className: 'groupTransition' })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TreeItem2Root>
|
||||||
|
</TreeItem2Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function CustomizedTreeView() {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ display: 'flex', flexDirection: 'column', gap: '8px', flexGrow: 1 }}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Typography component="h2" variant="subtitle2">
|
||||||
|
Product tree
|
||||||
|
</Typography>
|
||||||
|
<RichTreeView
|
||||||
|
items={ITEMS}
|
||||||
|
aria-label="pages"
|
||||||
|
multiSelect
|
||||||
|
defaultExpandedItems={['1', '1.1']}
|
||||||
|
defaultSelectedItems={['1.1', '1.1.1']}
|
||||||
|
sx={{
|
||||||
|
m: '0 -8px',
|
||||||
|
pb: '8px',
|
||||||
|
height: 'fit-content',
|
||||||
|
flexGrow: 1,
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
slots={{ item: CustomTreeItem }}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
36
src/dashboard/components/Header.tsx
Normal file
36
src/dashboard/components/Header.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import NotificationsRoundedIcon from '@mui/icons-material/NotificationsRounded';
|
||||||
|
import CustomDatePicker from './CustomDatePicker';
|
||||||
|
import NavbarBreadcrumbs from './NavbarBreadcrumbs';
|
||||||
|
import MenuButton from './MenuButton';
|
||||||
|
import ColorModeIconDropdown from '../../shared-theme/ColorModeIconDropdown';
|
||||||
|
|
||||||
|
import Search from './Search';
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{
|
||||||
|
display: { xs: 'none', md: 'flex' },
|
||||||
|
width: '100%',
|
||||||
|
alignItems: { xs: 'flex-start', md: 'center' },
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
maxWidth: { sm: '100%', md: '1700px' },
|
||||||
|
pt: 1.5,
|
||||||
|
}}
|
||||||
|
spacing={2}
|
||||||
|
>
|
||||||
|
<NavbarBreadcrumbs />
|
||||||
|
<Stack direction="row" sx={{ gap: 1 }}>
|
||||||
|
<Search />
|
||||||
|
<CustomDatePicker />
|
||||||
|
<MenuButton showBadge aria-label="Open notifications">
|
||||||
|
<NotificationsRoundedIcon />
|
||||||
|
</MenuButton>
|
||||||
|
<ColorModeIconDropdown />
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
src/dashboard/components/HighlightedCard.tsx
Normal file
42
src/dashboard/components/HighlightedCard.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';
|
||||||
|
import InsightsRoundedIcon from '@mui/icons-material/InsightsRounded';
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export default function HighlightedCard() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card sx={{ height: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<InsightsRoundedIcon />
|
||||||
|
<Typography
|
||||||
|
component="h2"
|
||||||
|
variant="subtitle2"
|
||||||
|
gutterBottom
|
||||||
|
sx={{ fontWeight: '600' }}
|
||||||
|
>
|
||||||
|
Explore your data
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ color: 'text.secondary', mb: '8px' }}>
|
||||||
|
Uncover performance and visitor insights with our data wizardry.
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
endIcon={<ChevronRightRoundedIcon />}
|
||||||
|
fullWidth={isSmallScreen}
|
||||||
|
>
|
||||||
|
Get insights
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
93
src/dashboard/components/MainGrid.tsx
Normal file
93
src/dashboard/components/MainGrid.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Grid from '@mui/material/Grid2';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Copyright from '../internals/components/Copyright';
|
||||||
|
import ChartUserByCountry from './ChartUserByCountry';
|
||||||
|
import CustomizedTreeView from './CustomizedTreeView';
|
||||||
|
import CustomizedDataGrid from './CustomizedDataGrid';
|
||||||
|
import HighlightedCard from './HighlightedCard';
|
||||||
|
import PageViewsBarChart from './PageViewsBarChart';
|
||||||
|
import SessionsChart from './SessionsChart';
|
||||||
|
import StatCard, { StatCardProps } from './StatCard';
|
||||||
|
|
||||||
|
const data: StatCardProps[] = [
|
||||||
|
{
|
||||||
|
title: 'Users',
|
||||||
|
value: '14k',
|
||||||
|
interval: 'Last 30 days',
|
||||||
|
trend: 'up',
|
||||||
|
data: [
|
||||||
|
200, 24, 220, 260, 240, 380, 100, 240, 280, 240, 300, 340, 320, 360, 340, 380,
|
||||||
|
360, 400, 380, 420, 400, 640, 340, 460, 440, 480, 460, 600, 880, 920,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Conversions',
|
||||||
|
value: '325',
|
||||||
|
interval: 'Last 30 days',
|
||||||
|
trend: 'down',
|
||||||
|
data: [
|
||||||
|
1640, 1250, 970, 1130, 1050, 900, 720, 1080, 900, 450, 920, 820, 840, 600, 820,
|
||||||
|
780, 800, 760, 380, 740, 660, 620, 840, 500, 520, 480, 400, 360, 300, 220,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Event count',
|
||||||
|
value: '200k',
|
||||||
|
interval: 'Last 30 days',
|
||||||
|
trend: 'neutral',
|
||||||
|
data: [
|
||||||
|
500, 400, 510, 530, 520, 600, 530, 520, 510, 730, 520, 510, 530, 620, 510, 530,
|
||||||
|
520, 410, 530, 520, 610, 530, 520, 610, 530, 420, 510, 430, 520, 510,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function MainGrid() {
|
||||||
|
return (
|
||||||
|
<Box sx={{ width: '100%', maxWidth: { sm: '100%', md: '1700px' } }}>
|
||||||
|
{/* cards */}
|
||||||
|
<Typography component="h2" variant="h6" sx={{ mb: 2 }}>
|
||||||
|
Overview
|
||||||
|
</Typography>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={2}
|
||||||
|
columns={12}
|
||||||
|
sx={{ mb: (theme) => theme.spacing(2) }}
|
||||||
|
>
|
||||||
|
{data.map((card, index) => (
|
||||||
|
<Grid key={index} size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||||
|
<StatCard {...card} />
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||||
|
<HighlightedCard />
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
|
<SessionsChart />
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
|
<PageViewsBarChart />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Typography component="h2" variant="h6" sx={{ mb: 2 }}>
|
||||||
|
Details
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={2} columns={12}>
|
||||||
|
<Grid size={{ xs: 12, lg: 9 }}>
|
||||||
|
<CustomizedDataGrid />
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, lg: 3 }}>
|
||||||
|
<Stack gap={2} direction={{ xs: 'column', sm: 'row', lg: 'column' }}>
|
||||||
|
<CustomizedTreeView />
|
||||||
|
<ChartUserByCountry />
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Copyright sx={{ my: 4 }} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/dashboard/components/MenuButton.tsx
Normal file
23
src/dashboard/components/MenuButton.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Badge, { badgeClasses } from '@mui/material/Badge';
|
||||||
|
import IconButton, { IconButtonProps } from '@mui/material/IconButton';
|
||||||
|
|
||||||
|
export interface MenuButtonProps extends IconButtonProps {
|
||||||
|
showBadge?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MenuButton({
|
||||||
|
showBadge = false,
|
||||||
|
...props
|
||||||
|
}: MenuButtonProps) {
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
color="error"
|
||||||
|
variant="dot"
|
||||||
|
invisible={!showBadge}
|
||||||
|
sx={{ [`& .${badgeClasses.badge}`]: { right: 2, top: 2 } }}
|
||||||
|
>
|
||||||
|
<IconButton size="small" {...props} />
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
}
|
||||||
54
src/dashboard/components/MenuContent.tsx
Normal file
54
src/dashboard/components/MenuContent.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import List from '@mui/material/List';
|
||||||
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import HomeRoundedIcon from '@mui/icons-material/HomeRounded';
|
||||||
|
import AnalyticsRoundedIcon from '@mui/icons-material/AnalyticsRounded';
|
||||||
|
import PeopleRoundedIcon from '@mui/icons-material/PeopleRounded';
|
||||||
|
import AssignmentRoundedIcon from '@mui/icons-material/AssignmentRounded';
|
||||||
|
import SettingsRoundedIcon from '@mui/icons-material/SettingsRounded';
|
||||||
|
import InfoRoundedIcon from '@mui/icons-material/InfoRounded';
|
||||||
|
import HelpRoundedIcon from '@mui/icons-material/HelpRounded';
|
||||||
|
|
||||||
|
const mainListItems = [
|
||||||
|
{ text: 'Home', icon: <HomeRoundedIcon /> },
|
||||||
|
{ text: 'Analytics', icon: <AnalyticsRoundedIcon /> },
|
||||||
|
{ text: 'Clients', icon: <PeopleRoundedIcon /> },
|
||||||
|
{ text: 'Tasks', icon: <AssignmentRoundedIcon /> },
|
||||||
|
];
|
||||||
|
|
||||||
|
const secondaryListItems = [
|
||||||
|
{ text: 'Settings', icon: <SettingsRoundedIcon /> },
|
||||||
|
{ text: 'About', icon: <InfoRoundedIcon /> },
|
||||||
|
{ text: 'Feedback', icon: <HelpRoundedIcon /> },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function MenuContent() {
|
||||||
|
return (
|
||||||
|
<Stack sx={{ flexGrow: 1, p: 1, justifyContent: 'space-between' }}>
|
||||||
|
<List dense>
|
||||||
|
{mainListItems.map((item, index) => (
|
||||||
|
<ListItem key={index} disablePadding sx={{ display: 'block' }}>
|
||||||
|
<ListItemButton selected={index === 0}>
|
||||||
|
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||||
|
<ListItemText primary={item.text} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
<List dense>
|
||||||
|
{secondaryListItems.map((item, index) => (
|
||||||
|
<ListItem key={index} disablePadding sx={{ display: 'block' }}>
|
||||||
|
<ListItemButton>
|
||||||
|
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||||
|
<ListItemText primary={item.text} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
src/dashboard/components/NavbarBreadcrumbs.tsx
Normal file
30
src/dashboard/components/NavbarBreadcrumbs.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Breadcrumbs, { breadcrumbsClasses } from '@mui/material/Breadcrumbs';
|
||||||
|
import NavigateNextRoundedIcon from '@mui/icons-material/NavigateNextRounded';
|
||||||
|
|
||||||
|
const StyledBreadcrumbs = styled(Breadcrumbs)(({ theme }) => ({
|
||||||
|
margin: theme.spacing(1, 0),
|
||||||
|
[`& .${breadcrumbsClasses.separator}`]: {
|
||||||
|
color: (theme.vars || theme).palette.action.disabled,
|
||||||
|
margin: 1,
|
||||||
|
},
|
||||||
|
[`& .${breadcrumbsClasses.ol}`]: {
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default function NavbarBreadcrumbs() {
|
||||||
|
return (
|
||||||
|
<StyledBreadcrumbs
|
||||||
|
aria-label="breadcrumb"
|
||||||
|
separator={<NavigateNextRoundedIcon fontSize="small" />}
|
||||||
|
>
|
||||||
|
<Typography variant="body1">Dashboard</Typography>
|
||||||
|
<Typography variant="body1" sx={{ color: 'text.primary', fontWeight: 600 }}>
|
||||||
|
Home
|
||||||
|
</Typography>
|
||||||
|
</StyledBreadcrumbs>
|
||||||
|
);
|
||||||
|
}
|
||||||
79
src/dashboard/components/OptionsMenu.tsx
Normal file
79
src/dashboard/components/OptionsMenu.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import Divider, { dividerClasses } from '@mui/material/Divider';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import MuiMenuItem from '@mui/material/MenuItem';
|
||||||
|
import { paperClasses } from '@mui/material/Paper';
|
||||||
|
import { listClasses } from '@mui/material/List';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import ListItemIcon, { listItemIconClasses } from '@mui/material/ListItemIcon';
|
||||||
|
import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded';
|
||||||
|
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded';
|
||||||
|
import MenuButton from './MenuButton';
|
||||||
|
|
||||||
|
const MenuItem = styled(MuiMenuItem)({
|
||||||
|
margin: '2px 0',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function OptionsMenu() {
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<MenuButton
|
||||||
|
aria-label="Open menu"
|
||||||
|
onClick={handleClick}
|
||||||
|
sx={{ borderColor: 'transparent' }}
|
||||||
|
>
|
||||||
|
<MoreVertRoundedIcon />
|
||||||
|
</MenuButton>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
id="menu"
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
onClick={handleClose}
|
||||||
|
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||||
|
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||||
|
sx={{
|
||||||
|
[`& .${listClasses.root}`]: {
|
||||||
|
padding: '4px',
|
||||||
|
},
|
||||||
|
[`& .${paperClasses.root}`]: {
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
[`& .${dividerClasses.root}`]: {
|
||||||
|
margin: '4px -4px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={handleClose}>Profile</MenuItem>
|
||||||
|
<MenuItem onClick={handleClose}>My account</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem onClick={handleClose}>Add another account</MenuItem>
|
||||||
|
<MenuItem onClick={handleClose}>Settings</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem
|
||||||
|
onClick={handleClose}
|
||||||
|
sx={{
|
||||||
|
[`& .${listItemIconClasses.root}`]: {
|
||||||
|
ml: 'auto',
|
||||||
|
minWidth: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemText>Logout</ListItemText>
|
||||||
|
<ListItemIcon>
|
||||||
|
<LogoutRoundedIcon fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
85
src/dashboard/components/PageViewsBarChart.tsx
Normal file
85
src/dashboard/components/PageViewsBarChart.tsx
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import { BarChart } from '@mui/x-charts/BarChart';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export default function PageViewsBarChart() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const colorPalette = [
|
||||||
|
(theme.vars || theme).palette.primary.dark,
|
||||||
|
(theme.vars || theme).palette.primary.main,
|
||||||
|
(theme.vars || theme).palette.primary.light,
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<Card variant="outlined" sx={{ width: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||||
|
Page views and downloads
|
||||||
|
</Typography>
|
||||||
|
<Stack sx={{ justifyContent: 'space-between' }}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{
|
||||||
|
alignContent: { xs: 'center', sm: 'flex-start' },
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h4" component="p">
|
||||||
|
1.3M
|
||||||
|
</Typography>
|
||||||
|
<Chip size="small" color="error" label="-8%" />
|
||||||
|
</Stack>
|
||||||
|
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
|
||||||
|
Page views and downloads for the last 6 months
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<BarChart
|
||||||
|
borderRadius={8}
|
||||||
|
colors={colorPalette}
|
||||||
|
xAxis={
|
||||||
|
[
|
||||||
|
{
|
||||||
|
scaleType: 'band',
|
||||||
|
categoryGapRatio: 0.5,
|
||||||
|
data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
|
||||||
|
},
|
||||||
|
] as any
|
||||||
|
}
|
||||||
|
series={[
|
||||||
|
{
|
||||||
|
id: 'page-views',
|
||||||
|
label: 'Page views',
|
||||||
|
data: [2234, 3872, 2998, 4125, 3357, 2789, 2998],
|
||||||
|
stack: 'A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'downloads',
|
||||||
|
label: 'Downloads',
|
||||||
|
data: [3098, 4215, 2384, 2101, 4752, 3593, 2384],
|
||||||
|
stack: 'A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'conversions',
|
||||||
|
label: 'Conversions',
|
||||||
|
data: [4051, 2275, 3129, 4693, 3904, 2038, 2275],
|
||||||
|
stack: 'A',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
height={250}
|
||||||
|
margin={{ left: 50, right: 0, top: 20, bottom: 20 }}
|
||||||
|
grid={{ horizontal: true }}
|
||||||
|
slotProps={{
|
||||||
|
legend: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
src/dashboard/components/Search.tsx
Normal file
26
src/dashboard/components/Search.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import FormControl from '@mui/material/FormControl';
|
||||||
|
import InputAdornment from '@mui/material/InputAdornment';
|
||||||
|
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||||
|
import SearchRoundedIcon from '@mui/icons-material/SearchRounded';
|
||||||
|
|
||||||
|
export default function Search() {
|
||||||
|
return (
|
||||||
|
<FormControl sx={{ width: { xs: '100%', md: '25ch' } }} variant="outlined">
|
||||||
|
<OutlinedInput
|
||||||
|
size="small"
|
||||||
|
id="search"
|
||||||
|
placeholder="Search…"
|
||||||
|
sx={{ flexGrow: 1 }}
|
||||||
|
startAdornment={
|
||||||
|
<InputAdornment position="start" sx={{ color: 'text.primary' }}>
|
||||||
|
<SearchRoundedIcon fontSize="small" />
|
||||||
|
</InputAdornment>
|
||||||
|
}
|
||||||
|
inputProps={{
|
||||||
|
'aria-label': 'search',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
102
src/dashboard/components/SelectContent.tsx
Normal file
102
src/dashboard/components/SelectContent.tsx
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import MuiAvatar from '@mui/material/Avatar';
|
||||||
|
import MuiListItemAvatar from '@mui/material/ListItemAvatar';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListSubheader from '@mui/material/ListSubheader';
|
||||||
|
import Select, { SelectChangeEvent, selectClasses } from '@mui/material/Select';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import AddRoundedIcon from '@mui/icons-material/AddRounded';
|
||||||
|
import DevicesRoundedIcon from '@mui/icons-material/DevicesRounded';
|
||||||
|
import SmartphoneRoundedIcon from '@mui/icons-material/SmartphoneRounded';
|
||||||
|
import ConstructionRoundedIcon from '@mui/icons-material/ConstructionRounded';
|
||||||
|
|
||||||
|
const Avatar = styled(MuiAvatar)(({ theme }) => ({
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
color: (theme.vars || theme).palette.text.secondary,
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ListItemAvatar = styled(MuiListItemAvatar)({
|
||||||
|
minWidth: 0,
|
||||||
|
marginRight: 12,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function SelectContent() {
|
||||||
|
const [company, setCompany] = React.useState('');
|
||||||
|
|
||||||
|
const handleChange = (event: SelectChangeEvent) => {
|
||||||
|
setCompany(event.target.value as string);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
labelId="company-select"
|
||||||
|
id="company-simple-select"
|
||||||
|
value={company}
|
||||||
|
onChange={handleChange}
|
||||||
|
displayEmpty
|
||||||
|
inputProps={{ 'aria-label': 'Select company' }}
|
||||||
|
fullWidth
|
||||||
|
sx={{
|
||||||
|
maxHeight: 56,
|
||||||
|
width: 215,
|
||||||
|
'&.MuiList-root': {
|
||||||
|
p: '8px',
|
||||||
|
},
|
||||||
|
[`& .${selectClasses.select}`]: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '2px',
|
||||||
|
pl: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListSubheader sx={{ pt: 0 }}>Production</ListSubheader>
|
||||||
|
<MenuItem value="">
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar alt="Sitemark web">
|
||||||
|
<DevicesRoundedIcon sx={{ fontSize: '1rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Sitemark-web" secondary="Web app" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={10}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar alt="Sitemark App">
|
||||||
|
<SmartphoneRoundedIcon sx={{ fontSize: '1rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Sitemark-app" secondary="Mobile application" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={20}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar alt="Sitemark Store">
|
||||||
|
<DevicesRoundedIcon sx={{ fontSize: '1rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Sitemark-Store" secondary="Web app" />
|
||||||
|
</MenuItem>
|
||||||
|
<ListSubheader>Development</ListSubheader>
|
||||||
|
<MenuItem value={30}>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar alt="Sitemark Store">
|
||||||
|
<ConstructionRoundedIcon sx={{ fontSize: '1rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Sitemark-Admin" secondary="Web app" />
|
||||||
|
</MenuItem>
|
||||||
|
<Divider sx={{ mx: -1 }} />
|
||||||
|
<MenuItem value={40}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<AddRoundedIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Add product" secondary="Web app" />
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
150
src/dashboard/components/SessionsChart.tsx
Normal file
150
src/dashboard/components/SessionsChart.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import { LineChart } from '@mui/x-charts/LineChart';
|
||||||
|
|
||||||
|
function AreaGradient({ color, id }: { color: string; id: string }) {
|
||||||
|
return (
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
|
||||||
|
<stop offset="0%" stopColor={color} stopOpacity={0.5} />
|
||||||
|
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDaysInMonth(month: number, year: number) {
|
||||||
|
const date = new Date(year, month, 0);
|
||||||
|
const monthName = date.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
});
|
||||||
|
const daysInMonth = date.getDate();
|
||||||
|
const days = [];
|
||||||
|
let i = 1;
|
||||||
|
while (days.length < daysInMonth) {
|
||||||
|
days.push(`${monthName} ${i}`);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SessionsChart() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const data = getDaysInMonth(4, 2024);
|
||||||
|
|
||||||
|
const colorPalette = [
|
||||||
|
theme.palette.primary.light,
|
||||||
|
theme.palette.primary.main,
|
||||||
|
theme.palette.primary.dark,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card variant="outlined" sx={{ width: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||||
|
Sessions
|
||||||
|
</Typography>
|
||||||
|
<Stack sx={{ justifyContent: 'space-between' }}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{
|
||||||
|
alignContent: { xs: 'center', sm: 'flex-start' },
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h4" component="p">
|
||||||
|
13,277
|
||||||
|
</Typography>
|
||||||
|
<Chip size="small" color="success" label="+35%" />
|
||||||
|
</Stack>
|
||||||
|
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
|
||||||
|
Sessions per day for the last 30 days
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<LineChart
|
||||||
|
colors={colorPalette}
|
||||||
|
xAxis={[
|
||||||
|
{
|
||||||
|
scaleType: 'point',
|
||||||
|
data,
|
||||||
|
tickInterval: (index, i) => (i + 1) % 5 === 0,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
series={[
|
||||||
|
{
|
||||||
|
id: 'direct',
|
||||||
|
label: 'Direct',
|
||||||
|
showMark: false,
|
||||||
|
curve: 'linear',
|
||||||
|
stack: 'total',
|
||||||
|
area: true,
|
||||||
|
stackOrder: 'ascending',
|
||||||
|
data: [
|
||||||
|
300, 900, 600, 1200, 1500, 1800, 2400, 2100, 2700, 3000, 1800, 3300,
|
||||||
|
3600, 3900, 4200, 4500, 3900, 4800, 5100, 5400, 4800, 5700, 6000,
|
||||||
|
6300, 6600, 6900, 7200, 7500, 7800, 8100,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'referral',
|
||||||
|
label: 'Referral',
|
||||||
|
showMark: false,
|
||||||
|
curve: 'linear',
|
||||||
|
stack: 'total',
|
||||||
|
area: true,
|
||||||
|
stackOrder: 'ascending',
|
||||||
|
data: [
|
||||||
|
500, 900, 700, 1400, 1100, 1700, 2300, 2000, 2600, 2900, 2300, 3200,
|
||||||
|
3500, 3800, 4100, 4400, 2900, 4700, 5000, 5300, 5600, 5900, 6200,
|
||||||
|
6500, 5600, 6800, 7100, 7400, 7700, 8000,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'organic',
|
||||||
|
label: 'Organic',
|
||||||
|
showMark: false,
|
||||||
|
curve: 'linear',
|
||||||
|
stack: 'total',
|
||||||
|
stackOrder: 'ascending',
|
||||||
|
data: [
|
||||||
|
1000, 1500, 1200, 1700, 1300, 2000, 2400, 2200, 2600, 2800, 2500,
|
||||||
|
3000, 3400, 3700, 3200, 3900, 4100, 3500, 4300, 4500, 4000, 4700,
|
||||||
|
5000, 5200, 4800, 5400, 5600, 5900, 6100, 6300,
|
||||||
|
],
|
||||||
|
area: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
height={250}
|
||||||
|
margin={{ left: 50, right: 20, top: 20, bottom: 20 }}
|
||||||
|
grid={{ horizontal: true }}
|
||||||
|
sx={{
|
||||||
|
'& .MuiAreaElement-series-organic': {
|
||||||
|
fill: "url('#organic')",
|
||||||
|
},
|
||||||
|
'& .MuiAreaElement-series-referral': {
|
||||||
|
fill: "url('#referral')",
|
||||||
|
},
|
||||||
|
'& .MuiAreaElement-series-direct': {
|
||||||
|
fill: "url('#direct')",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
slotProps={{
|
||||||
|
legend: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AreaGradient color={theme.palette.primary.dark} id="organic" />
|
||||||
|
<AreaGradient color={theme.palette.primary.main} id="referral" />
|
||||||
|
<AreaGradient color={theme.palette.primary.light} id="direct" />
|
||||||
|
</LineChart>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
87
src/dashboard/components/SideMenu.tsx
Normal file
87
src/dashboard/components/SideMenu.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import MuiDrawer, { drawerClasses } from '@mui/material/Drawer';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import SelectContent from './SelectContent';
|
||||||
|
import MenuContent from './MenuContent';
|
||||||
|
import CardAlert from './CardAlert';
|
||||||
|
import OptionsMenu from './OptionsMenu';
|
||||||
|
|
||||||
|
const drawerWidth = 240;
|
||||||
|
|
||||||
|
const Drawer = styled(MuiDrawer)({
|
||||||
|
width: drawerWidth,
|
||||||
|
flexShrink: 0,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
mt: 10,
|
||||||
|
[`& .${drawerClasses.paper}`]: {
|
||||||
|
width: drawerWidth,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function SideMenu() {
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
variant="permanent"
|
||||||
|
sx={{
|
||||||
|
display: { xs: 'none', md: 'block' },
|
||||||
|
[`& .${drawerClasses.paper}`]: {
|
||||||
|
backgroundColor: 'background.paper',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
mt: 'calc(var(--template-frame-height, 0px) + 4px)',
|
||||||
|
p: 1.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectContent />
|
||||||
|
</Box>
|
||||||
|
<Divider />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
overflow: 'auto',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuContent />
|
||||||
|
<CardAlert />
|
||||||
|
</Box>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
gap: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
borderTop: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
sizes="small"
|
||||||
|
alt="Riley Carter"
|
||||||
|
src="/static/images/avatar/7.jpg"
|
||||||
|
sx={{ width: 36, height: 36 }}
|
||||||
|
/>
|
||||||
|
<Box sx={{ mr: 'auto' }}>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 500, lineHeight: '16px' }}>
|
||||||
|
Riley Carter
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
|
||||||
|
riley@email.com
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<OptionsMenu />
|
||||||
|
</Stack>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
72
src/dashboard/components/SideMenuMobile.tsx
Normal file
72
src/dashboard/components/SideMenuMobile.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import Drawer, { drawerClasses } from '@mui/material/Drawer';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded';
|
||||||
|
import NotificationsRoundedIcon from '@mui/icons-material/NotificationsRounded';
|
||||||
|
import MenuButton from './MenuButton';
|
||||||
|
import MenuContent from './MenuContent';
|
||||||
|
import CardAlert from './CardAlert';
|
||||||
|
|
||||||
|
interface SideMenuMobileProps {
|
||||||
|
open: boolean | undefined;
|
||||||
|
toggleDrawer: (newOpen: boolean) => () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SideMenuMobile({ open, toggleDrawer }: SideMenuMobileProps) {
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
anchor="right"
|
||||||
|
open={open}
|
||||||
|
onClose={toggleDrawer(false)}
|
||||||
|
sx={{
|
||||||
|
zIndex: (theme) => theme.zIndex.drawer + 1,
|
||||||
|
[`& .${drawerClasses.paper}`]: {
|
||||||
|
backgroundImage: 'none',
|
||||||
|
backgroundColor: 'background.paper',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
sx={{
|
||||||
|
maxWidth: '70dvw',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack direction="row" sx={{ p: 2, pb: 0, gap: 1 }}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{ gap: 1, alignItems: 'center', flexGrow: 1, p: 1 }}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
sizes="small"
|
||||||
|
alt="Riley Carter"
|
||||||
|
src="/static/images/avatar/7.jpg"
|
||||||
|
sx={{ width: 24, height: 24 }}
|
||||||
|
/>
|
||||||
|
<Typography component="p" variant="h6">
|
||||||
|
Riley Carter
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<MenuButton showBadge>
|
||||||
|
<NotificationsRoundedIcon />
|
||||||
|
</MenuButton>
|
||||||
|
</Stack>
|
||||||
|
<Divider />
|
||||||
|
<Stack sx={{ flexGrow: 1 }}>
|
||||||
|
<MenuContent />
|
||||||
|
<Divider />
|
||||||
|
</Stack>
|
||||||
|
<CardAlert />
|
||||||
|
<Stack sx={{ p: 2 }}>
|
||||||
|
<Button variant="outlined" fullWidth startIcon={<LogoutRoundedIcon />}>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
129
src/dashboard/components/StatCard.tsx
Normal file
129
src/dashboard/components/StatCard.tsx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
|
||||||
|
import { areaElementClasses } from '@mui/x-charts/LineChart';
|
||||||
|
|
||||||
|
export type StatCardProps = {
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
interval: string;
|
||||||
|
trend: 'up' | 'down' | 'neutral';
|
||||||
|
data: number[];
|
||||||
|
};
|
||||||
|
|
||||||
|
function getDaysInMonth(month: number, year: number) {
|
||||||
|
const date = new Date(year, month, 0);
|
||||||
|
const monthName = date.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
});
|
||||||
|
const daysInMonth = date.getDate();
|
||||||
|
const days = [];
|
||||||
|
let i = 1;
|
||||||
|
while (days.length < daysInMonth) {
|
||||||
|
days.push(`${monthName} ${i}`);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AreaGradient({ color, id }: { color: string; id: string }) {
|
||||||
|
return (
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
|
||||||
|
<stop offset="0%" stopColor={color} stopOpacity={0.3} />
|
||||||
|
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function StatCard({
|
||||||
|
title,
|
||||||
|
value,
|
||||||
|
interval,
|
||||||
|
trend,
|
||||||
|
data,
|
||||||
|
}: StatCardProps) {
|
||||||
|
const theme = useTheme();
|
||||||
|
const daysInWeek = getDaysInMonth(4, 2024);
|
||||||
|
|
||||||
|
const trendColors = {
|
||||||
|
up:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? theme.palette.success.main
|
||||||
|
: theme.palette.success.dark,
|
||||||
|
down:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? theme.palette.error.main
|
||||||
|
: theme.palette.error.dark,
|
||||||
|
neutral:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? theme.palette.grey[400]
|
||||||
|
: theme.palette.grey[700],
|
||||||
|
};
|
||||||
|
|
||||||
|
const labelColors = {
|
||||||
|
up: 'success' as const,
|
||||||
|
down: 'error' as const,
|
||||||
|
neutral: 'default' as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
const color = labelColors[trend];
|
||||||
|
const chartColor = trendColors[trend];
|
||||||
|
const trendValues = { up: '+25%', down: '-25%', neutral: '+5%' };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card variant="outlined" sx={{ height: '100%', flexGrow: 1 }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
sx={{ justifyContent: 'space-between', flexGrow: '1', gap: 1 }}
|
||||||
|
>
|
||||||
|
<Stack sx={{ justifyContent: 'space-between' }}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
sx={{ justifyContent: 'space-between', alignItems: 'center' }}
|
||||||
|
>
|
||||||
|
<Typography variant="h4" component="p">
|
||||||
|
{value}
|
||||||
|
</Typography>
|
||||||
|
<Chip size="small" color={color} label={trendValues[trend]} />
|
||||||
|
</Stack>
|
||||||
|
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
|
||||||
|
{interval}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Box sx={{ width: '100%', height: 50 }}>
|
||||||
|
<SparkLineChart
|
||||||
|
colors={[chartColor]}
|
||||||
|
data={data}
|
||||||
|
area
|
||||||
|
showHighlight
|
||||||
|
showTooltip
|
||||||
|
xAxis={{
|
||||||
|
scaleType: 'band',
|
||||||
|
data: daysInWeek, // Use the correct property 'data' for xAxis
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
[`& .${areaElementClasses.root}`]: {
|
||||||
|
fill: `url(#area-gradient-${value})`,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AreaGradient color={chartColor} id={`area-gradient-${value}`} />
|
||||||
|
</SparkLineChart>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
src/dashboard/internals/components/Copyright.tsx
Normal file
26
src/dashboard/internals/components/Copyright.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
export default function Copyright(props: any) {
|
||||||
|
return (
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
align="center"
|
||||||
|
{...props}
|
||||||
|
sx={[
|
||||||
|
{
|
||||||
|
color: 'text.secondary',
|
||||||
|
},
|
||||||
|
...(Array.isArray(props.sx) ? props.sx : [props.sx]),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{'Copyright © '}
|
||||||
|
<Link color="inherit" href="https://mui.com/">
|
||||||
|
Sitemark
|
||||||
|
</Link>{' '}
|
||||||
|
{new Date().getFullYear()}
|
||||||
|
{'.'}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
326
src/dashboard/internals/components/CustomIcons.tsx
Normal file
326
src/dashboard/internals/components/CustomIcons.tsx
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import SvgIcon from '@mui/material/SvgIcon';
|
||||||
|
|
||||||
|
export function SitemarkIcon() {
|
||||||
|
return (
|
||||||
|
<SvgIcon sx={{ height: 21, width: 100 }}>
|
||||||
|
<svg
|
||||||
|
width={86}
|
||||||
|
height={19}
|
||||||
|
viewBox="0 0 86 19"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#B4C0D3"
|
||||||
|
d="m.787 12.567 6.055-2.675 3.485 2.006.704 6.583-4.295-.035.634-4.577-.74-.422-3.625 2.817-2.218-3.697Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#00D3AB"
|
||||||
|
d="m10.714 11.616 5.352 3.908 2.112-3.767-4.295-1.725v-.845l4.295-1.76-2.112-3.732-5.352 3.908v4.013Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4876EF"
|
||||||
|
d="m10.327 7.286.704-6.583-4.295.07.634 4.577-.74.422-3.66-2.816L.786 6.617l6.055 2.676 3.485-2.007Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4876EE"
|
||||||
|
d="M32.507 8.804v6.167h2.312v-7.86h-3.366v1.693h1.054ZM32.435 6.006c.212.22.535.33.968.33.434 0 .751-.11.953-.33.213-.23.318-.516.318-.86 0-.354-.105-.641-.318-.86-.202-.23-.52-.345-.953-.345-.433 0-.756.115-.968.344-.202.22-.303.507-.303.86 0 .345.101.632.303.861ZM24.46 14.799c.655.296 1.46.444 2.413.444.896 0 1.667-.139 2.312-.416.645-.277 1.141-.664 1.488-1.162.357-.506.535-1.094.535-1.764 0-.65-.169-1.2-.506-1.649-.328-.459-.785-.818-1.373-1.076-.587-.267-1.266-.435-2.037-.502l-.809-.071c-.481-.039-.828-.168-1.04-.388a1.08 1.08 0 0 1-.318-.774c0-.23.058-.44.173-.631.116-.201.29-.359.52-.474.241-.114.535-.172.882-.172.366 0 .67.067.91.201.053.029.104.059.15.09l.012.009.052.037c.146.111.263.243.35.395.125.21.188.444.188.703h2.311c0-.689-.159-1.286-.476-1.793-.318-.516-.776-.913-1.373-1.19-.588-.287-1.296-.43-2.124-.43-.79 0-1.474.133-2.052.4a3.131 3.131 0 0 0-1.358 1.12c-.318.487-.477 1.066-.477 1.735 0 .927.314 1.673.94 2.237.626.564 1.464.89 2.514.976l.794.071c.645.058 1.113.187 1.401.388a.899.899 0 0 1 .434.788 1.181 1.181 0 0 1-.231.717c-.154.201-.38.36-.68.474-.298.115-.669.172-1.112.172-.49 0-.89-.067-1.199-.2-.308-.144-.539-.33-.694-.56a1.375 1.375 0 0 1-.216-.746h-2.297c0 .679.168 1.281.505 1.807.337.517.834.928 1.489 1.234ZM39.977 15.07c-.8 0-1.445-.095-1.936-.286a2.03 2.03 0 0 1-1.084-.99c-.221-.469-.332-1.1-.332-1.893V8.789h-1.2V7.11h1.2V4.988h2.153V7.11h2.312V8.79h-2.312v3.198c0 .373.096.66.289.86.202.192.486.287.852.287h1.17v1.937h-1.112Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4876EE"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M43.873 14.899c.52.23 1.117.344 1.791.344.665 0 1.252-.115 1.763-.344.51-.23.934-.55 1.271-.96.337-.412.564-.88.679-1.407h-2.124c-.096.24-.279.44-.549.603-.27.162-.616.244-1.04.244-.262 0-.497-.031-.704-.093a1.572 1.572 0 0 1-.423-.194 1.662 1.662 0 0 1-.636-.803 3.159 3.159 0 0 1-.163-.645h5.784v-.775a4.28 4.28 0 0 0-.463-1.98 3.686 3.686 0 0 0-1.343-1.477c-.578-.382-1.291-.574-2.139-.574-.645 0-1.223.115-1.733.345-.501.22-.92.52-1.257.903a4.178 4.178 0 0 0-.78 1.305c-.174.478-.26.98-.26 1.506v.287c0 .507.086 1.004.26 1.492.183.478.443.913.78 1.305.347.382.775.688 1.286.918Zm-.094-4.674.02-.09a2.507 2.507 0 0 1 .117-.356c.145-.354.356-.622.636-.804.104-.067.217-.123.339-.165.204-.071.433-.107.686-.107.395 0 .723.09.983.272.27.173.472.426.607.76a2.487 2.487 0 0 1 .16.603h-3.57c.006-.038.013-.076.022-.113Z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4876EE"
|
||||||
|
d="M50.476 14.97V7.112h1.835v1.98a4.54 4.54 0 0 1 .173-.603c.202-.536.506-.937.91-1.205.405-.277.9-.416 1.488-.416h.101c.598 0 1.094.139 1.489.416.404.268.707.67.91 1.205l.016.04.013.037.028-.077c.212-.536.52-.937.925-1.205.405-.277.901-.416 1.489-.416h.1c.598 0 1.098.139 1.503.416.414.268.727.67.94 1.205.211.535.317 1.205.317 2.008v4.475h-2.312v-4.604c0-.43-.115-.78-.346-1.047-.222-.268-.54-.402-.954-.402-.414 0-.742.139-.982.416-.241.268-.362.626-.362 1.076v4.56h-2.326v-4.603c0-.43-.115-.78-.346-1.047-.222-.268-.535-.402-.94-.402-.423 0-.756.139-.996.416-.241.268-.362.626-.362 1.076v4.56h-2.311Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4876EE"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M68.888 13.456v1.515h1.834v-4.82c0-.726-.144-1.319-.433-1.778-.289-.468-.712-.817-1.271-1.047-.549-.23-1.228-.344-2.037-.344a27.76 27.76 0 0 0-.896.014c-.318.01-.626.024-.924.043l-.229.016a36.79 36.79 0 0 0-.552.042v1.936a81.998 81.998 0 0 1 1.733-.09 37.806 37.806 0 0 1 1.171-.025c.424 0 .732.1.925.301.193.201.289.502.289.904v.029h-1.43c-.704 0-1.325.09-1.864.272-.54.172-.959.445-1.257.818-.299.363-.448.832-.448 1.405 0 .526.12.98.361 1.363.24.373.573.66.997.86.433.201.934.302 1.502.302.55 0 1.012-.1 1.388-.302.385-.2.683-.487.895-.86a2.443 2.443 0 0 0 .228-.498l.018-.056Zm-.39-1.397v-.63h-1.445c-.405 0-.718.1-.939.3-.212.192-.318.455-.318.79 0 .157.026.3.08.429a.99.99 0 0 0 .238.345c.221.191.534.287.939.287a2.125 2.125 0 0 0 .394-.038c.106-.021.206-.052.3-.092.212-.095.385-.253.52-.473.135-.22.212-.526.23-.918Z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4876EE"
|
||||||
|
d="M72.106 14.97V7.11h1.835v2.595c.088-.74.31-1.338.665-1.791.481-.603 1.174-.904 2.08-.904h.303v1.98h-.578c-.635 0-1.127.172-1.473.516-.347.334-.52.822-.52 1.463v4.001h-2.312ZM79.92 11.298h.767l2.499 3.672h2.6l-3.169-4.51 2.606-3.35h-2.427l-2.875 3.737V4.5h-2.312v10.47h2.312v-3.672Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IndiaFlag() {
|
||||||
|
return (
|
||||||
|
<SvgIcon>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
||||||
|
<g clipPath="url(#a)">
|
||||||
|
<mask
|
||||||
|
id="b"
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
x="-4"
|
||||||
|
y="0"
|
||||||
|
width="32"
|
||||||
|
height="24"
|
||||||
|
>
|
||||||
|
<path d="M-4 0h32v24H-4V0Z" fill="#fff" />
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#b)">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 0v24h32V0H-4Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<mask
|
||||||
|
id="c"
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
x="-4"
|
||||||
|
y="0"
|
||||||
|
width="32"
|
||||||
|
height="24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 0v24h32V0H-4Z"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#c)" fillRule="evenodd" clipRule="evenodd">
|
||||||
|
<path d="M-4 0v8h32V0H-4Z" fill="#FF8C1A" />
|
||||||
|
<path d="M-4 16v8h32v-8H-4Z" fill="#5EAA22" />
|
||||||
|
<path
|
||||||
|
d="M8 12a4 4 0 1 0 8 0 4 4 0 0 0-8 0Zm7 0a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||||
|
fill="#3D58DB"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="m12 12.9-.6 3 .4-3-1.5 2.8 1.2-3L9.4 15l2-2.4-2.8 1.6 2.6-1.8-3 .7 3-1H8l3.2-.2-3-1 3 .8-2.6-1.9 2.8 1.7-2-2.5 2.1 2.3-1.2-3 1.5 2.9-.4-3.2.6 3.2.6-3.2-.4 3.2 1.5-2.8-1.2 2.9L14.6 9l-2 2.5 2.8-1.7-2.6 1.9 3-.8-3 1 3.2.1-3.2.1 3 1-3-.7 2.6 1.8-2.8-1.6 2 2.4-2.1-2.3 1.2 3-1.5-2.9.4 3.2-.6-3.1Z"
|
||||||
|
fill="#3D58DB"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<rect width="24" height="24" rx="12" fill="#fff" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UsaFlag() {
|
||||||
|
return (
|
||||||
|
<SvgIcon>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clipPath="url(#clip0_983_1725)">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 0H28V24H-4V0Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 14.6667V16.6667H28V14.6667H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 18.3333V20.3333H28V18.3333H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 7.33325V9.33325H28V7.33325H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 22V24H28V22H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 11V13H28V11H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 0V2H28V0H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 3.66675V5.66675H28V3.66675H-4Z"
|
||||||
|
fill="#E31D1C"
|
||||||
|
/>
|
||||||
|
<path d="M-4 0H16V13H-4V0Z" fill="#2E42A5" />
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-2.27876 2.93871L-3.00465 3.44759L-2.75958 2.54198L-3.4043 1.96807H-2.56221L-2.27978 1.229L-1.94861 1.96807H-1.23075L-1.79479 2.54198L-1.57643 3.44759L-2.27876 2.93871ZM1.72124 2.93871L0.995357 3.44759L1.24042 2.54198L0.595707 1.96807H1.43779L1.72022 1.229L2.05139 1.96807H2.76925L2.20521 2.54198L2.42357 3.44759L1.72124 2.93871ZM4.99536 3.44759L5.72124 2.93871L6.42357 3.44759L6.20517 2.54198L6.76927 1.96807H6.05137L5.72022 1.229L5.43779 1.96807H4.59571L5.24042 2.54198L4.99536 3.44759ZM9.72127 2.93871L8.99537 3.44759L9.24047 2.54198L8.59567 1.96807H9.43777L9.72027 1.229L10.0514 1.96807H10.7693L10.2052 2.54198L10.4236 3.44759L9.72127 2.93871ZM-3.00465 7.44759L-2.27876 6.93871L-1.57643 7.44759L-1.79479 6.54198L-1.23075 5.96807H-1.94861L-2.27978 5.229L-2.56221 5.96807H-3.4043L-2.75958 6.54198L-3.00465 7.44759ZM1.72124 6.93871L0.995357 7.44759L1.24042 6.54198L0.595707 5.96807H1.43779L1.72022 5.229L2.05139 5.96807H2.76925L2.20521 6.54198L2.42357 7.44759L1.72124 6.93871ZM4.99536 7.44759L5.72124 6.93871L6.42357 7.44759L6.20517 6.54198L6.76927 5.96807H6.05137L5.72022 5.229L5.43779 5.96807H4.59571L5.24042 6.54198L4.99536 7.44759ZM9.72127 6.93871L8.99537 7.44759L9.24047 6.54198L8.59567 5.96807H9.43777L9.72027 5.229L10.0514 5.96807H10.7693L10.2052 6.54198L10.4236 7.44759L9.72127 6.93871ZM-3.00465 11.4476L-2.27876 10.9387L-1.57643 11.4476L-1.79479 10.542L-1.23075 9.96807H-1.94861L-2.27978 9.229L-2.56221 9.96807H-3.4043L-2.75958 10.542L-3.00465 11.4476ZM1.72124 10.9387L0.995357 11.4476L1.24042 10.542L0.595707 9.96807H1.43779L1.72022 9.229L2.05139 9.96807H2.76925L2.20521 10.542L2.42357 11.4476L1.72124 10.9387ZM4.99536 11.4476L5.72124 10.9387L6.42357 11.4476L6.20517 10.542L6.76927 9.96807H6.05137L5.72022 9.229L5.43779 9.96807H4.59571L5.24042 10.542L4.99536 11.4476ZM9.72127 10.9387L8.99537 11.4476L9.24047 10.542L8.59567 9.96807H9.43777L9.72027 9.229L10.0514 9.96807H10.7693L10.2052 10.542L10.4236 11.4476L9.72127 10.9387ZM12.9954 3.44759L13.7213 2.93871L14.4236 3.44759L14.2052 2.54198L14.7693 1.96807H14.0514L13.7203 1.229L13.4378 1.96807H12.5957L13.2405 2.54198L12.9954 3.44759ZM13.7213 6.93871L12.9954 7.44759L13.2405 6.54198L12.5957 5.96807H13.4378L13.7203 5.229L14.0514 5.96807H14.7693L14.2052 6.54198L14.4236 7.44759L13.7213 6.93871ZM12.9954 11.4476L13.7213 10.9387L14.4236 11.4476L14.2052 10.542L14.7693 9.96807H14.0514L13.7203 9.229L13.4378 9.96807H12.5957L13.2405 10.542L12.9954 11.4476ZM-0.278763 4.93871L-1.00464 5.44759L-0.759583 4.54198L-1.40429 3.96807H-0.562213L-0.279783 3.229L0.0513873 3.96807H0.769247L0.205207 4.54198L0.423567 5.44759L-0.278763 4.93871ZM2.99536 5.44759L3.72124 4.93871L4.42357 5.44759L4.20521 4.54198L4.76925 3.96807H4.05139L3.72022 3.229L3.43779 3.96807H2.59571L3.24042 4.54198L2.99536 5.44759ZM7.72127 4.93871L6.99537 5.44759L7.24047 4.54198L6.59567 3.96807H7.43777L7.72027 3.229L8.05137 3.96807H8.76927L8.20517 4.54198L8.42357 5.44759L7.72127 4.93871ZM-1.00464 9.44759L-0.278763 8.93871L0.423567 9.44759L0.205207 8.54198L0.769247 7.96807H0.0513873L-0.279783 7.229L-0.562213 7.96807H-1.40429L-0.759583 8.54198L-1.00464 9.44759ZM3.72124 8.93871L2.99536 9.44759L3.24042 8.54198L2.59571 7.96807H3.43779L3.72022 7.229L4.05139 7.96807H4.76925L4.20521 8.54198L4.42357 9.44759L3.72124 8.93871ZM6.99537 9.44759L7.72127 8.93871L8.42357 9.44759L8.20517 8.54198L8.76927 7.96807H8.05137L7.72027 7.229L7.43777 7.96807H6.59567L7.24047 8.54198L6.99537 9.44759ZM11.7213 4.93871L10.9954 5.44759L11.2405 4.54198L10.5957 3.96807H11.4378L11.7203 3.229L12.0514 3.96807H12.7693L12.2052 4.54198L12.4236 5.44759L11.7213 4.93871ZM10.9954 9.44759L11.7213 8.93871L12.4236 9.44759L12.2052 8.54198L12.7693 7.96807H12.0514L11.7203 7.229L11.4378 7.96807H10.5957L11.2405 8.54198L10.9954 9.44759Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_983_1725">
|
||||||
|
<rect width="24" height="24" rx="12" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export function BrazilFlag() {
|
||||||
|
return (
|
||||||
|
<SvgIcon>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 24 25"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clipPath="url(#clip0_983_1741)">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M-4 0.5V24.5H28V0.5H-4Z"
|
||||||
|
fill="#009933"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M11.9265 4.20404L24.1283 12.7075L11.7605 20.6713L-0.191406 12.5427L11.9265 4.20404Z"
|
||||||
|
fill="#FFD221"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M11.9265 4.20404L24.1283 12.7075L11.7605 20.6713L-0.191406 12.5427L11.9265 4.20404Z"
|
||||||
|
fill="url(#paint0_linear_983_1741)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M12 17.7C14.7614 17.7 17 15.4614 17 12.7C17 9.93853 14.7614 7.69995 12 7.69995C9.2386 7.69995 7 9.93853 7 12.7C7 15.4614 9.2386 17.7 12 17.7Z"
|
||||||
|
fill="#2E42A5"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M10.379 15.07L10.1556 15.1874L10.1983 14.9387L10.0176 14.7626L10.2673 14.7263L10.379 14.5L10.4907 14.7263L10.7404 14.7626L10.5597 14.9387L10.6024 15.1874L10.379 15.07Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M12.379 15.07L12.1556 15.1874L12.1983 14.9387L12.0176 14.7626L12.2673 14.7263L12.379 14.5L12.4907 14.7263L12.7404 14.7626L12.5597 14.9387L12.6024 15.1874L12.379 15.07Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M12.379 16.27L12.1556 16.3874L12.1983 16.1387L12.0176 15.9625L12.2673 15.9262L12.379 15.7L12.4907 15.9262L12.7404 15.9625L12.5597 16.1387L12.6024 16.3874L12.379 16.27Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M11.379 12.07L11.1556 12.1874L11.1983 11.9387L11.0176 11.7626L11.2673 11.7263L11.379 11.5L11.4907 11.7263L11.7404 11.7626L11.5597 11.9387L11.6024 12.1874L11.379 12.07Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M11.379 14.07L11.1556 14.1874L11.1983 13.9387L11.0176 13.7626L11.2673 13.7263L11.379 13.5L11.4907 13.7263L11.7404 13.7626L11.5597 13.9387L11.6024 14.1874L11.379 14.07Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M9.97859 13.07L9.75519 13.1874L9.79789 12.9387L9.61719 12.7626L9.86689 12.7263L9.97859 12.5L10.0903 12.7263L10.34 12.7626L10.1593 12.9387L10.2019 13.1874L9.97859 13.07Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M8.5783 13.87L8.3549 13.9875L8.3976 13.7388L8.2168 13.5626L8.4666 13.5263L8.5783 13.3L8.6899 13.5263L8.9397 13.5626L8.759 13.7388L8.8016 13.9875L8.5783 13.87Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M13.1798 10.47L12.9565 10.5875L12.9991 10.3387L12.8184 10.1626L13.0682 10.1263L13.1798 9.90002L13.2915 10.1263L13.5413 10.1626L13.3605 10.3387L13.4032 10.5875L13.1798 10.47Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7 12L7.5 10C11.6854 10.2946 14.6201 11.2147 17 13.5L16.5 15C14.4373 13.0193 10.7839 12.2664 7 12Z"
|
||||||
|
fill="#F7FCFF"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_983_1741"
|
||||||
|
x1="27.9997"
|
||||||
|
y1="24.5"
|
||||||
|
x2="27.9997"
|
||||||
|
y2="0.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stopColor="#FFC600" />
|
||||||
|
<stop offset="1" stopColor="#FFDE42" />
|
||||||
|
</linearGradient>
|
||||||
|
<clipPath id="clip0_983_1741">
|
||||||
|
<rect y="0.5" width="24" height="24" rx="12" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GlobeFlag() {
|
||||||
|
return (
|
||||||
|
<SvgIcon>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 24 25"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clipPath="url(#clip0_986_1789)">
|
||||||
|
<circle cx="12" cy="12.5" r="12" fill="#007FFF" />
|
||||||
|
<path
|
||||||
|
d="M12 0.5C5.376 0.5 0 5.876 0 12.5C0 19.124 5.376 24.5 12 24.5C18.624 24.5 24 19.124 24 12.5C24 5.876 18.624 0.5 12 0.5ZM10.8 22.016C6.06 21.428 2.4 17.396 2.4 12.5C2.4 11.756 2.496 11.048 2.652 10.352L8.4 16.1V17.3C8.4 18.62 9.48 19.7 10.8 19.7V22.016ZM19.08 18.968C18.768 17.996 17.88 17.3 16.8 17.3H15.6V13.7C15.6 13.04 15.06 12.5 14.4 12.5H7.2V10.1H9.6C10.26 10.1 10.8 9.56 10.8 8.9V6.5H13.2C14.52 6.5 15.6 5.42 15.6 4.1V3.608C19.116 5.036 21.6 8.48 21.6 12.5C21.6 14.996 20.64 17.264 19.08 18.968Z"
|
||||||
|
fill="#3EE07F"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_986_1789">
|
||||||
|
<rect width="24" height="24" fill="white" transform="translate(0 0.5)" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
636
src/dashboard/internals/data/gridData.tsx
Normal file
636
src/dashboard/internals/data/gridData.tsx
Normal file
@ -0,0 +1,636 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import { GridCellParams, GridRowsProp, GridColDef } from '@mui/x-data-grid';
|
||||||
|
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
|
||||||
|
|
||||||
|
type SparkLineData = number[];
|
||||||
|
|
||||||
|
function getDaysInMonth(month: number, year: number) {
|
||||||
|
const date = new Date(year, month, 0);
|
||||||
|
const monthName = date.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
});
|
||||||
|
const daysInMonth = date.getDate();
|
||||||
|
const days = [];
|
||||||
|
let i = 1;
|
||||||
|
while (days.length < daysInMonth) {
|
||||||
|
days.push(`${monthName} ${i}`);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSparklineCell(params: GridCellParams<SparkLineData, any>) {
|
||||||
|
const data = getDaysInMonth(4, 2024);
|
||||||
|
const { value, colDef } = params;
|
||||||
|
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', height: '100%' }}>
|
||||||
|
<SparkLineChart
|
||||||
|
data={value}
|
||||||
|
width={colDef.computedWidth || 100}
|
||||||
|
height={32}
|
||||||
|
plotType="bar"
|
||||||
|
showHighlight
|
||||||
|
showTooltip
|
||||||
|
colors={['hsl(210, 98%, 42%)']}
|
||||||
|
xAxis={{
|
||||||
|
scaleType: 'band',
|
||||||
|
data,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderStatus(status: 'Online' | 'Offline') {
|
||||||
|
const colors: { [index: string]: 'success' | 'default' } = {
|
||||||
|
Online: 'success',
|
||||||
|
Offline: 'default',
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Chip label={status} color={colors[status]} size="small" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderAvatar(
|
||||||
|
params: GridCellParams<{ name: string; color: string }, any, any>,
|
||||||
|
) {
|
||||||
|
if (params.value == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
backgroundColor: params.value.color,
|
||||||
|
width: '24px',
|
||||||
|
height: '24px',
|
||||||
|
fontSize: '0.85rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{params.value.name.toUpperCase().substring(0, 1)}
|
||||||
|
</Avatar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const columns: GridColDef[] = [
|
||||||
|
{ field: 'pageTitle', headerName: 'Page Title', flex: 1.5, minWidth: 200 },
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
headerName: 'Status',
|
||||||
|
flex: 0.5,
|
||||||
|
minWidth: 80,
|
||||||
|
renderCell: (params) => renderStatus(params.value as any),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'users',
|
||||||
|
headerName: 'Users',
|
||||||
|
headerAlign: 'right',
|
||||||
|
align: 'right',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'eventCount',
|
||||||
|
headerName: 'Event Count',
|
||||||
|
headerAlign: 'right',
|
||||||
|
align: 'right',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'viewsPerUser',
|
||||||
|
headerName: 'Views per User',
|
||||||
|
headerAlign: 'right',
|
||||||
|
align: 'right',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'averageTime',
|
||||||
|
headerName: 'Average Time',
|
||||||
|
headerAlign: 'right',
|
||||||
|
align: 'right',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'conversions',
|
||||||
|
headerName: 'Daily Conversions',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 150,
|
||||||
|
renderCell: renderSparklineCell,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const rows: GridRowsProp = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
pageTitle: 'Homepage Overview',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 8345,
|
||||||
|
users: 212423,
|
||||||
|
viewsPerUser: 18.5,
|
||||||
|
averageTime: '2m 15s',
|
||||||
|
conversions: [
|
||||||
|
469172, 488506, 592287, 617401, 640374, 632751, 668638, 807246, 749198, 944863,
|
||||||
|
911787, 844815, 992022, 1143838, 1446926, 1267886, 1362511, 1348746, 1560533,
|
||||||
|
1670690, 1695142, 1916613, 1823306, 1683646, 2025965, 2529989, 3263473,
|
||||||
|
3296541, 3041524, 2599497,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
pageTitle: 'Product Details - Gadgets',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 5653,
|
||||||
|
users: 172240,
|
||||||
|
viewsPerUser: 9.7,
|
||||||
|
averageTime: '2m 30s',
|
||||||
|
conversions: [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
557488, 1341471, 2044561, 2206438,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
pageTitle: 'Checkout Process - Step 1',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 3455,
|
||||||
|
users: 58240,
|
||||||
|
viewsPerUser: 15.2,
|
||||||
|
averageTime: '2m 10s',
|
||||||
|
conversions: [
|
||||||
|
166896, 190041, 248686, 226746, 261744, 271890, 332176, 381123, 396435, 495620,
|
||||||
|
520278, 460839, 704158, 559134, 681089, 712384, 765381, 771374, 851314, 907947,
|
||||||
|
903675, 1049642, 1003160, 881573, 1072283, 1139115, 1382701, 1395655, 1355040,
|
||||||
|
1381571,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
pageTitle: 'User Profile Dashboard',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 112543,
|
||||||
|
users: 96240,
|
||||||
|
viewsPerUser: 4.5,
|
||||||
|
averageTime: '2m 40s',
|
||||||
|
conversions: [
|
||||||
|
264651, 311845, 436558, 439385, 520413, 533380, 562363, 533793, 558029, 791126,
|
||||||
|
649082, 566792, 723451, 737827, 890859, 935554, 1044397, 1022973, 1129827,
|
||||||
|
1145309, 1195630, 1358925, 1373160, 1172679, 1340106, 1396974, 1623641,
|
||||||
|
1687545, 1581634, 1550291,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
pageTitle: 'Article Listing - Tech News',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 3653,
|
||||||
|
users: 142240,
|
||||||
|
viewsPerUser: 3.1,
|
||||||
|
averageTime: '2m 55s',
|
||||||
|
conversions: [
|
||||||
|
251871, 262216, 402383, 396459, 378793, 406720, 447538, 451451, 457111, 589821,
|
||||||
|
640744, 504879, 626099, 662007, 754576, 768231, 833019, 851537, 972306,
|
||||||
|
1014831, 1027570, 1189068, 1119099, 987244, 1197954, 1310721, 1480816, 1577547,
|
||||||
|
1854053, 1791831,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
pageTitle: 'FAQs - Customer Support',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 106543,
|
||||||
|
users: 15240,
|
||||||
|
viewsPerUser: 7.2,
|
||||||
|
averageTime: '2m 20s',
|
||||||
|
conversions: [
|
||||||
|
13671, 16918, 27272, 34315, 42212, 56369, 64241, 77857, 70680, 91093, 108306,
|
||||||
|
94734, 132289, 133860, 147706, 158504, 192578, 207173, 220052, 233496, 250091,
|
||||||
|
285557, 268555, 259482, 274019, 321648, 359801, 399502, 447249, 497403,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
pageTitle: 'Product Comparison - Laptops',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 7853,
|
||||||
|
users: 32240,
|
||||||
|
viewsPerUser: 6.5,
|
||||||
|
averageTime: '2m 50s',
|
||||||
|
conversions: [
|
||||||
|
93682, 107901, 144919, 151769, 170804, 183736, 201752, 219792, 227887, 295382,
|
||||||
|
309600, 278050, 331964, 356826, 404896, 428090, 470245, 485582, 539056, 582112,
|
||||||
|
594289, 671915, 649510, 574911, 713843, 754965, 853020, 916793, 960158, 984265,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
pageTitle: 'Shopping Cart - Electronics',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 8563,
|
||||||
|
users: 48240,
|
||||||
|
viewsPerUser: 4.3,
|
||||||
|
averageTime: '3m 10s',
|
||||||
|
conversions: [
|
||||||
|
52394, 63357, 82800, 105466, 128729, 144472, 172148, 197919, 212302, 278153,
|
||||||
|
290499, 249824, 317499, 333024, 388925, 410576, 462099, 488477, 533956, 572307,
|
||||||
|
591019, 681506, 653332, 581234, 719038, 783496, 911609, 973328, 1056071,
|
||||||
|
1112940,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
pageTitle: 'Payment Confirmation - Bank Transfer',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 4563,
|
||||||
|
users: 18240,
|
||||||
|
viewsPerUser: 2.7,
|
||||||
|
averageTime: '3m 25s',
|
||||||
|
conversions: [
|
||||||
|
15372, 16901, 25489, 30148, 40857, 51136, 64627, 75804, 89633, 100407, 114908,
|
||||||
|
129957, 143568, 158509, 174822, 192488, 211512, 234702, 258812, 284328, 310431,
|
||||||
|
338186, 366582, 396749, 428788, 462880, 499125, 537723, 578884, 622825,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
pageTitle: 'Product Reviews - Smartphones',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 9863,
|
||||||
|
users: 28240,
|
||||||
|
viewsPerUser: 5.1,
|
||||||
|
averageTime: '3m 05s',
|
||||||
|
conversions: [
|
||||||
|
70211, 89234, 115676, 136021, 158744, 174682, 192890, 218073, 240926, 308190,
|
||||||
|
317552, 279834, 334072, 354955, 422153, 443911, 501486, 538091, 593724, 642882,
|
||||||
|
686539, 788615, 754813, 687955, 883645, 978347, 1142551, 1233074, 1278155,
|
||||||
|
1356724,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
pageTitle: 'Subscription Management - Services',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 6563,
|
||||||
|
users: 24240,
|
||||||
|
viewsPerUser: 4.8,
|
||||||
|
averageTime: '3m 15s',
|
||||||
|
conversions: [
|
||||||
|
49662, 58971, 78547, 93486, 108722, 124901, 146422, 167883, 189295, 230090,
|
||||||
|
249837, 217828, 266494, 287537, 339586, 363299, 412855, 440900, 490111, 536729,
|
||||||
|
580591, 671635, 655812, 576431, 741632, 819296, 971762, 1052605, 1099234,
|
||||||
|
1173591,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
pageTitle: 'Order Tracking - Shipments',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 12353,
|
||||||
|
users: 38240,
|
||||||
|
viewsPerUser: 3.5,
|
||||||
|
averageTime: '3m 20s',
|
||||||
|
conversions: [
|
||||||
|
29589, 37965, 55800, 64672, 77995, 91126, 108203, 128900, 148232, 177159,
|
||||||
|
193489, 164471, 210765, 229977, 273802, 299381, 341092, 371567, 413812, 457693,
|
||||||
|
495920, 564785, 541022, 491680, 618096, 704926, 833365, 904313, 974622,
|
||||||
|
1036567,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 13,
|
||||||
|
pageTitle: 'Customer Feedback - Surveys',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 5863,
|
||||||
|
users: 13240,
|
||||||
|
viewsPerUser: 2.3,
|
||||||
|
averageTime: '3m 30s',
|
||||||
|
conversions: [
|
||||||
|
8472, 9637, 14892, 19276, 23489, 28510, 33845, 39602, 45867, 52605, 59189,
|
||||||
|
65731, 76021, 85579, 96876, 108515, 119572, 131826, 145328, 160192, 176528,
|
||||||
|
196662, 217929, 239731, 262920, 289258, 315691, 342199, 370752, 402319,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
|
pageTitle: 'Account Settings - Preferences',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 7853,
|
||||||
|
users: 18240,
|
||||||
|
viewsPerUser: 3.2,
|
||||||
|
averageTime: '3m 15s',
|
||||||
|
conversions: [
|
||||||
|
15792, 16948, 22728, 25491, 28412, 31268, 34241, 37857, 42068, 46893, 51098,
|
||||||
|
55734, 60780, 66421, 72680, 79584, 87233, 95711, 105285, 115814, 127509,
|
||||||
|
140260, 154086, 169495, 186445, 205109, 225580, 247983, 272484, 299280,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 15,
|
||||||
|
pageTitle: 'Login Page - Authentication',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 9563,
|
||||||
|
users: 24240,
|
||||||
|
viewsPerUser: 2.5,
|
||||||
|
averageTime: '3m 35s',
|
||||||
|
conversions: [
|
||||||
|
25638, 28355, 42089, 53021, 66074, 80620, 97989, 118202, 142103, 166890,
|
||||||
|
193869, 225467, 264089, 307721, 358059, 417835, 488732, 573924, 674878, 794657,
|
||||||
|
938542, 1111291, 1313329, 1543835, 1812156, 2123349, 2484926, 2907023, 3399566,
|
||||||
|
3973545,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 16,
|
||||||
|
pageTitle: 'Promotions - Seasonal Sales',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 13423,
|
||||||
|
users: 54230,
|
||||||
|
viewsPerUser: 7.8,
|
||||||
|
averageTime: '2m 45s',
|
||||||
|
conversions: [
|
||||||
|
241732, 256384, 289465, 321423, 345672, 378294, 398472, 420364, 436278, 460192,
|
||||||
|
495374, 510283, 532489, 559672, 587312, 610982, 629385, 654732, 678925, 704362,
|
||||||
|
725182, 749384, 772361, 798234, 819472, 846291, 872183, 894673, 919283, 945672,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 17,
|
||||||
|
pageTitle: 'Tutorials - How to Guides',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 4234,
|
||||||
|
users: 19342,
|
||||||
|
viewsPerUser: 5.2,
|
||||||
|
averageTime: '3m 05s',
|
||||||
|
conversions: [
|
||||||
|
12345, 14567, 16789, 18901, 21023, 23145, 25267, 27389, 29501, 31623, 33745,
|
||||||
|
35867, 37989, 40101, 42223, 44345, 46467, 48589, 50701, 52823, 54945, 57067,
|
||||||
|
59189, 61301, 63423, 65545, 67667, 69789, 71901, 74023,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 18,
|
||||||
|
pageTitle: 'Blog Posts - Tech Insights',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 8567,
|
||||||
|
users: 34234,
|
||||||
|
viewsPerUser: 6.3,
|
||||||
|
averageTime: '2m 50s',
|
||||||
|
conversions: [
|
||||||
|
23456, 25678, 27890, 30102, 32324, 34546, 36768, 38980, 41202, 43424, 45646,
|
||||||
|
47868, 50080, 52302, 54524, 56746, 58968, 61180, 63402, 65624, 67846, 70068,
|
||||||
|
72290, 74502, 76724, 78946, 81168, 83380, 85602, 87824,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 19,
|
||||||
|
pageTitle: 'Events - Webinars',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 3456,
|
||||||
|
users: 19234,
|
||||||
|
viewsPerUser: 4.5,
|
||||||
|
averageTime: '3m 20s',
|
||||||
|
conversions: [
|
||||||
|
123456, 145678, 167890, 190012, 212324, 234546, 256768, 278980, 301202, 323424,
|
||||||
|
345646, 367868, 390080, 412302, 434524, 456746, 478968, 501180, 523402, 545624,
|
||||||
|
567846, 590068, 612290, 634502, 656724, 678946, 701168, 723380, 745602, 767824,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 20,
|
||||||
|
pageTitle: 'Support - Contact Us',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 6734,
|
||||||
|
users: 27645,
|
||||||
|
viewsPerUser: 3.9,
|
||||||
|
averageTime: '2m 55s',
|
||||||
|
conversions: [
|
||||||
|
234567, 256789, 278901, 301023, 323245, 345467, 367689, 389801, 412023, 434245,
|
||||||
|
456467, 478689, 500801, 523023, 545245, 567467, 589689, 611801, 634023, 656245,
|
||||||
|
678467, 700689, 722801, 745023, 767245, 789467, 811689, 833801, 856023, 878245,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
pageTitle: 'Case Studies - Success Stories',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 4567,
|
||||||
|
users: 19345,
|
||||||
|
viewsPerUser: 6.1,
|
||||||
|
averageTime: '3m 10s',
|
||||||
|
conversions: [
|
||||||
|
34567, 36789, 38901, 41023, 43145, 45267, 47389, 49501, 51623, 53745, 55867,
|
||||||
|
57989, 60101, 62223, 64345, 66467, 68589, 70701, 72823, 74945, 77067, 79189,
|
||||||
|
81301, 83423, 85545, 87667, 89789, 91901, 94023, 96145,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 22,
|
||||||
|
pageTitle: 'News - Industry Updates',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 7856,
|
||||||
|
users: 34567,
|
||||||
|
viewsPerUser: 5.7,
|
||||||
|
averageTime: '3m 05s',
|
||||||
|
conversions: [
|
||||||
|
45678, 47890, 50102, 52324, 54546, 56768, 58980, 61202, 63424, 65646, 67868,
|
||||||
|
70080, 72302, 74524, 76746, 78968, 81180, 83402, 85624, 87846, 90068, 92290,
|
||||||
|
94502, 96724, 98946, 101168, 103380, 105602, 107824, 110046,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 23,
|
||||||
|
pageTitle: 'Forum - User Discussions',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 5678,
|
||||||
|
users: 23456,
|
||||||
|
viewsPerUser: 4.2,
|
||||||
|
averageTime: '2m 40s',
|
||||||
|
conversions: [
|
||||||
|
56789, 58901, 61023, 63145, 65267, 67389, 69501, 71623, 73745, 75867, 77989,
|
||||||
|
80101, 82223, 84345, 86467, 88589, 90701, 92823, 94945, 97067, 99189, 101301,
|
||||||
|
103423, 105545, 107667, 109789, 111901, 114023, 116145, 118267,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 24,
|
||||||
|
pageTitle: 'Documentation - API Reference',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 6789,
|
||||||
|
users: 27689,
|
||||||
|
viewsPerUser: 5.0,
|
||||||
|
averageTime: '3m 00s',
|
||||||
|
conversions: [
|
||||||
|
67890, 70102, 72324, 74546, 76768, 78980, 81202, 83424, 85646, 87868, 90080,
|
||||||
|
92302, 94524, 96746, 98968, 101180, 103402, 105624, 107846, 110068, 112290,
|
||||||
|
114502, 116724, 118946, 121168, 123380, 125602, 127824, 130046, 132268,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 25,
|
||||||
|
pageTitle: 'Services - Consulting',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 4563,
|
||||||
|
users: 19240,
|
||||||
|
viewsPerUser: 6.4,
|
||||||
|
averageTime: '3m 25s',
|
||||||
|
conversions: [
|
||||||
|
345678, 367890, 390012, 412324, 434546, 456768, 478980, 501202, 523424, 545646,
|
||||||
|
567868, 590080, 612302, 634524, 656746, 678968, 701180, 723402, 745624, 767846,
|
||||||
|
790068, 812290, 834502, 856724, 878946, 901168, 923380, 945602, 967824, 990046,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 26,
|
||||||
|
pageTitle: 'Feedback - User Reviews',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 8564,
|
||||||
|
users: 34240,
|
||||||
|
viewsPerUser: 6.2,
|
||||||
|
averageTime: '3m 15s',
|
||||||
|
conversions: [
|
||||||
|
123478, 145690, 167912, 190134, 212356, 234578, 256790, 279012, 301234, 323456,
|
||||||
|
345678, 367890, 390012, 412234, 434456, 456678, 478890, 501012, 523234, 545456,
|
||||||
|
567678, 589890, 612012, 634234, 656456, 678678, 700890, 723012, 745234, 767456,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 27,
|
||||||
|
pageTitle: 'Profiles - Team Members',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 5634,
|
||||||
|
users: 23423,
|
||||||
|
viewsPerUser: 5.5,
|
||||||
|
averageTime: '2m 45s',
|
||||||
|
conversions: [
|
||||||
|
345123, 367345, 389567, 411789, 434012, 456234, 478456, 500678, 522901, 545123,
|
||||||
|
567345, 589567, 611789, 634012, 656234, 678456, 700678, 722901, 745123, 767345,
|
||||||
|
789567, 811789, 834012, 856234, 878456, 900678, 922901, 945123, 967345, 989567,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 28,
|
||||||
|
pageTitle: 'Notifications - Alerts',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 6745,
|
||||||
|
users: 27654,
|
||||||
|
viewsPerUser: 4.9,
|
||||||
|
averageTime: '3m 10s',
|
||||||
|
conversions: [
|
||||||
|
456123, 478345, 500567, 522789, 545012, 567234, 589456, 611678, 633901, 656123,
|
||||||
|
678345, 700567, 722789, 745012, 767234, 789456, 811678, 833901, 856123, 878345,
|
||||||
|
900567, 922789, 945012, 967234, 989456, 1011678, 1033901, 1056123, 1078345,
|
||||||
|
1100567,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 29,
|
||||||
|
pageTitle: 'Dashboard - Metrics',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 5678,
|
||||||
|
users: 23456,
|
||||||
|
viewsPerUser: 6.3,
|
||||||
|
averageTime: '2m 50s',
|
||||||
|
conversions: [
|
||||||
|
567890, 590112, 612334, 634556, 656778, 678990, 701212, 723434, 745656, 767878,
|
||||||
|
790100, 812322, 834544, 856766, 878988, 901210, 923432, 945654, 967876, 990098,
|
||||||
|
1012320, 1034542, 1056764, 1078986, 1101208, 1123430, 1145652, 1167874,
|
||||||
|
1190096, 1212318,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 30,
|
||||||
|
pageTitle: 'Reports - Monthly Analysis',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 7890,
|
||||||
|
users: 34567,
|
||||||
|
viewsPerUser: 5.9,
|
||||||
|
averageTime: '3m 20s',
|
||||||
|
conversions: [
|
||||||
|
678901, 701123, 723345, 745567, 767789, 790011, 812233, 834455, 856677, 878899,
|
||||||
|
901121, 923343, 945565, 967787, 990009, 1012231, 1034453, 1056675, 1078897,
|
||||||
|
1101119, 1123341, 1145563, 1167785, 1190007, 1212229, 1234451, 1256673,
|
||||||
|
1278895, 1301117, 1323339,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 31,
|
||||||
|
pageTitle: 'Training - Employee Onboarding',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 3456,
|
||||||
|
users: 19234,
|
||||||
|
viewsPerUser: 6.1,
|
||||||
|
averageTime: '3m 10s',
|
||||||
|
conversions: [
|
||||||
|
789012, 811234, 833456, 855678, 877890, 900112, 922334, 944556, 966778, 989000,
|
||||||
|
1011222, 1033444, 1055666, 1077888, 1100110, 1122332, 1144554, 1166776,
|
||||||
|
1188998, 1211220, 1233442, 1255664, 1277886, 1300108, 1322330, 1344552,
|
||||||
|
1366774, 1388996, 1411218, 1433440,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 32,
|
||||||
|
pageTitle: 'Resources - Knowledge Base',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 5678,
|
||||||
|
users: 23456,
|
||||||
|
viewsPerUser: 4.7,
|
||||||
|
averageTime: '3m 25s',
|
||||||
|
conversions: [
|
||||||
|
890123, 912345, 934567, 956789, 979012, 1001234, 1023456, 1045678, 1067890,
|
||||||
|
1090123, 1112345, 1134567, 1156789, 1179012, 1201234, 1223456, 1245678,
|
||||||
|
1267890, 1290123, 1312345, 1334567, 1356789, 1379012, 1401234, 1423456,
|
||||||
|
1445678, 1467890, 1490123, 1512345, 1534567,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 33,
|
||||||
|
pageTitle: 'Settings - Privacy Controls',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 6789,
|
||||||
|
users: 27689,
|
||||||
|
viewsPerUser: 5.8,
|
||||||
|
averageTime: '3m 05s',
|
||||||
|
conversions: [
|
||||||
|
901234, 923456, 945678, 967890, 990112, 1012334, 1034556, 1056778, 1079000,
|
||||||
|
1101222, 1123444, 1145666, 1167888, 1190110, 1212332, 1234554, 1256776,
|
||||||
|
1278998, 1301220, 1323442, 1345664, 1367886, 1390108, 1412330, 1434552,
|
||||||
|
1456774, 1478996, 1501218, 1523440, 1545662,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 34,
|
||||||
|
pageTitle: 'Integrations - Third-Party Services',
|
||||||
|
status: 'Online',
|
||||||
|
eventCount: 4567,
|
||||||
|
users: 19345,
|
||||||
|
viewsPerUser: 4.4,
|
||||||
|
averageTime: '2m 50s',
|
||||||
|
conversions: [
|
||||||
|
123457, 145679, 167891, 190113, 212335, 234557, 256779, 279001, 301223, 323445,
|
||||||
|
345667, 367889, 390011, 412233, 434455, 456677, 478899, 501121, 523343, 545565,
|
||||||
|
567787, 590009, 612231, 634453, 656675, 678897, 701119, 723341, 745563, 767785,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 35,
|
||||||
|
pageTitle: 'Account - Billing Information',
|
||||||
|
status: 'Offline',
|
||||||
|
eventCount: 7890,
|
||||||
|
users: 34567,
|
||||||
|
viewsPerUser: 5.4,
|
||||||
|
averageTime: '3m 00s',
|
||||||
|
conversions: [
|
||||||
|
234568, 256790, 278912, 301134, 323356, 345578, 367790, 390012, 412234, 434456,
|
||||||
|
456678, 478890, 501112, 523334, 545556, 567778, 590000, 612222, 634444, 656666,
|
||||||
|
678888, 701110, 723332, 745554, 767776, 789998, 812220, 834442, 856664, 878886,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
76
src/dashboard/theme/customizations/charts.ts
Normal file
76
src/dashboard/theme/customizations/charts.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { Theme } from '@mui/material/styles';
|
||||||
|
import { axisClasses, legendClasses, chartsGridClasses } from '@mui/x-charts';
|
||||||
|
import type { ChartsComponents } from '@mui/x-charts/themeAugmentation';
|
||||||
|
import { gray } from '../../../shared-theme/themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const chartsCustomizations: ChartsComponents<Theme> = {
|
||||||
|
MuiChartsAxis: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
[`& .${axisClasses.line}`]: {
|
||||||
|
stroke: gray[300],
|
||||||
|
},
|
||||||
|
[`& .${axisClasses.tick}`]: { stroke: gray[300] },
|
||||||
|
[`& .${axisClasses.tickLabel}`]: {
|
||||||
|
fill: gray[500],
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
[`& .${axisClasses.line}`]: {
|
||||||
|
stroke: gray[700],
|
||||||
|
},
|
||||||
|
[`& .${axisClasses.tick}`]: { stroke: gray[700] },
|
||||||
|
[`& .${axisClasses.tickLabel}`]: {
|
||||||
|
fill: gray[300],
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiChartsTooltip: {
|
||||||
|
styleOverrides: {
|
||||||
|
mark: ({ theme }) => ({
|
||||||
|
ry: 6,
|
||||||
|
boxShadow: 'none',
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
}),
|
||||||
|
table: ({ theme }) => ({
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
background: 'hsl(0, 0%, 100%)',
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
background: gray[900],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiChartsLegend: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
[`& .${legendClasses.mark}`]: {
|
||||||
|
ry: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiChartsGrid: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
[`& .${chartsGridClasses.line}`]: {
|
||||||
|
stroke: gray[200],
|
||||||
|
strokeDasharray: '4 2',
|
||||||
|
strokeWidth: 0.8,
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
[`& .${chartsGridClasses.line}`]: {
|
||||||
|
stroke: gray[700],
|
||||||
|
strokeDasharray: '4 2',
|
||||||
|
strokeWidth: 0.8,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
132
src/dashboard/theme/customizations/dataGrid.ts
Normal file
132
src/dashboard/theme/customizations/dataGrid.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { paperClasses } from '@mui/material/Paper';
|
||||||
|
import { alpha, Theme } from '@mui/material/styles';
|
||||||
|
import type { DataGridProComponents } from '@mui/x-data-grid-pro/themeAugmentation';
|
||||||
|
import { menuItemClasses } from '@mui/material/MenuItem';
|
||||||
|
import { listItemIconClasses } from '@mui/material/ListItemIcon';
|
||||||
|
import { iconButtonClasses } from '@mui/material/IconButton';
|
||||||
|
import { checkboxClasses } from '@mui/material/Checkbox';
|
||||||
|
import { listClasses } from '@mui/material/List';
|
||||||
|
import { gridClasses } from '@mui/x-data-grid';
|
||||||
|
import { tablePaginationClasses } from '@mui/material/TablePagination';
|
||||||
|
import { gray } from '../../../shared-theme/themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const dataGridCustomizations: DataGridProComponents<Theme> & DataGridProComponents<Theme> = {
|
||||||
|
MuiDataGrid: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
'--DataGrid-overlayHeight': '300px',
|
||||||
|
overflow: 'clip',
|
||||||
|
borderColor: (theme.vars || theme).palette.divider,
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||||
|
[`& .${gridClasses.columnHeader}`]: {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
},
|
||||||
|
[`& .${gridClasses.footerContainer}`]: {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
},
|
||||||
|
[`& .${checkboxClasses.root}`]: {
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
'& > svg': {
|
||||||
|
fontSize: '1rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`& .${tablePaginationClasses.root}`]: {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
'& .MuiIconButton-root': {
|
||||||
|
maxHeight: 32,
|
||||||
|
maxWidth: 32,
|
||||||
|
'& > svg': {
|
||||||
|
fontSize: '1rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
cell: ({ theme }) => ({ borderTopColor: (theme.vars || theme).palette.divider }),
|
||||||
|
menu: ({ theme }) => ({
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
backgroundImage: 'none',
|
||||||
|
[`& .${paperClasses.root}`]: {
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
[`& .${menuItemClasses.root}`]: {
|
||||||
|
margin: '0 4px',
|
||||||
|
},
|
||||||
|
[`& .${listItemIconClasses.root}`]: {
|
||||||
|
marginRight: 0,
|
||||||
|
},
|
||||||
|
[`& .${listClasses.root}`]: {
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
row: ({ theme }) => ({
|
||||||
|
'&:last-of-type': { borderBottom: `1px solid ${(theme.vars || theme).palette.divider}` },
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
'&.Mui-selected': {
|
||||||
|
background: (theme.vars || theme).palette.action.selected,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
iconButtonContainer: ({ theme }) => ({
|
||||||
|
[`& .${iconButtonClasses.root}`]: {
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: gray[50],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
menuIconButton: ({ theme }) => ({
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: gray[50],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
filterForm: ({ theme }) => ({
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
}),
|
||||||
|
columnsManagementHeader: ({ theme }) => ({
|
||||||
|
paddingRight: theme.spacing(3),
|
||||||
|
paddingLeft: theme.spacing(3),
|
||||||
|
}),
|
||||||
|
columnHeaderTitleContainer: {
|
||||||
|
flexGrow: 1,
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
columnHeaderDraggableContainer: { paddingRight: 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
173
src/dashboard/theme/customizations/datePickers.ts
Normal file
173
src/dashboard/theme/customizations/datePickers.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import { alpha, Theme } from '@mui/material/styles';
|
||||||
|
import type { PickersProComponents } from '@mui/x-date-pickers-pro/themeAugmentation';
|
||||||
|
import type { PickerComponents } from '@mui/x-date-pickers/themeAugmentation';
|
||||||
|
import { pickersYearClasses, pickersMonthClasses, pickersDayClasses } from '@mui/x-date-pickers';
|
||||||
|
import { menuItemClasses } from '@mui/material/MenuItem';
|
||||||
|
import { gray, brand } from '../../../shared-theme/themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const datePickersCustomizations: PickersProComponents<Theme> & PickerComponents<Theme> = {
|
||||||
|
MuiPickersPopper: {
|
||||||
|
styleOverrides: {
|
||||||
|
paper: ({ theme }) => ({
|
||||||
|
marginTop: 4,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
backgroundImage: 'none',
|
||||||
|
background: 'hsl(0, 0%, 100%)',
|
||||||
|
boxShadow:
|
||||||
|
'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
|
||||||
|
[`& .${menuItemClasses.root}`]: {
|
||||||
|
borderRadius: 6,
|
||||||
|
margin: '0 6px',
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
background: gray[900],
|
||||||
|
boxShadow:
|
||||||
|
'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPickersArrowSwitcher: {
|
||||||
|
styleOverrides: {
|
||||||
|
spacer: { width: 16 },
|
||||||
|
button: ({ theme }) => ({
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: (theme.vars || theme).palette.grey[500],
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: (theme.vars || theme).palette.grey[400],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPickersCalendarHeader: {
|
||||||
|
styleOverrides: {
|
||||||
|
switchViewButton: {
|
||||||
|
padding: 0,
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPickersMonth: {
|
||||||
|
styleOverrides: {
|
||||||
|
monthButton: ({ theme }) => ({
|
||||||
|
fontSize: theme.typography.body1.fontSize,
|
||||||
|
color: (theme.vars || theme).palette.grey[600],
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
[`&.${pickersMonthClasses.selected}`]: {
|
||||||
|
backgroundColor: gray[700],
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
[`&.${pickersMonthClasses.selected}`]: { backgroundColor: gray[700] },
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: (theme.vars || theme).palette.grey[300],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
[`&.${pickersMonthClasses.selected}`]: {
|
||||||
|
color: (theme.vars || theme).palette.common.black,
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
backgroundColor: gray[300],
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
[`&.${pickersMonthClasses.selected}`]: { backgroundColor: gray[300] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPickersYear: {
|
||||||
|
styleOverrides: {
|
||||||
|
yearButton: ({ theme }) => ({
|
||||||
|
fontSize: theme.typography.body1.fontSize,
|
||||||
|
color: (theme.vars || theme).palette.grey[600],
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
height: 'fit-content',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
[`&.${pickersYearClasses.selected}`]: {
|
||||||
|
backgroundColor: gray[700],
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
[`&.${pickersYearClasses.selected}`]: { backgroundColor: gray[700] },
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: (theme.vars || theme).palette.grey[300],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
[`&.${pickersYearClasses.selected}`]: {
|
||||||
|
color: (theme.vars || theme).palette.common.black,
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
backgroundColor: gray[300],
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
[`&.${pickersYearClasses.selected}`]: { backgroundColor: gray[300] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPickersDay: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
fontSize: theme.typography.body1.fontSize,
|
||||||
|
color: (theme.vars || theme).palette.grey[600],
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
[`&.${pickersDayClasses.selected}`]: {
|
||||||
|
backgroundColor: gray[700],
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
[`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[700] },
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: (theme.vars || theme).palette.grey[300],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||||
|
},
|
||||||
|
[`&.${pickersDayClasses.selected}`]: {
|
||||||
|
color: (theme.vars || theme).palette.common.black,
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
backgroundColor: gray[300],
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
[`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[300] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
4
src/dashboard/theme/customizations/index.ts
Normal file
4
src/dashboard/theme/customizations/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { chartsCustomizations } from './charts';
|
||||||
|
export { dataGridCustomizations } from './dataGrid';
|
||||||
|
export { datePickersCustomizations } from './datePickers';
|
||||||
|
export { treeViewCustomizations } from './treeView';
|
||||||
62
src/dashboard/theme/customizations/treeView.ts
Normal file
62
src/dashboard/theme/customizations/treeView.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { alpha, Theme } from '@mui/material/styles';
|
||||||
|
import type { TreeViewComponents } from '@mui/x-tree-view/themeAugmentation';
|
||||||
|
import { gray, brand } from '../../../shared-theme/themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const treeViewCustomizations: TreeViewComponents<Theme> = {
|
||||||
|
MuiTreeItem2: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
position: 'relative',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
padding: theme.spacing(0, 1),
|
||||||
|
'& .groupTransition': {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
padding: theme.spacing(0),
|
||||||
|
borderLeft: '1px solid',
|
||||||
|
borderColor: (theme.vars || theme).palette.divider,
|
||||||
|
},
|
||||||
|
'&:focus-visible .focused': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(gray[300], 0.2),
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
content: ({ theme }) => ({
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
padding: theme.spacing(0.5, 1),
|
||||||
|
overflow: 'clip',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(gray[300], 0.2),
|
||||||
|
},
|
||||||
|
|
||||||
|
'&.selected': {
|
||||||
|
backgroundColor: alpha(gray[300], 0.4),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(gray[300], 0.6),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(gray[500], 0.2),
|
||||||
|
},
|
||||||
|
'&:focus-visible': {
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(gray[500], 0.2),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&.selected': {
|
||||||
|
backgroundColor: alpha(gray[500], 0.4),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(gray[500], 0.6),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
13
src/index.tsx
Normal file
13
src/index.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
import type {} from '@mui/material/themeCssVarsAugmentation';
|
||||||
|
import { StyledEngineProvider } from '@mui/material/styles';
|
||||||
|
import App from './dashboard/Dashboard';
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.querySelector("#root")!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<StyledEngineProvider injectFirst>
|
||||||
|
<App />
|
||||||
|
</StyledEngineProvider>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
53
src/shared-theme/AppTheme.tsx
Normal file
53
src/shared-theme/AppTheme.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
|
import type { ThemeOptions } from '@mui/material/styles';
|
||||||
|
import { inputsCustomizations } from './customizations/inputs';
|
||||||
|
import { dataDisplayCustomizations } from './customizations/dataDisplay';
|
||||||
|
import { feedbackCustomizations } from './customizations/feedback';
|
||||||
|
import { navigationCustomizations } from './customizations/navigation';
|
||||||
|
import { surfacesCustomizations } from './customizations/surfaces';
|
||||||
|
import { colorSchemes, typography, shadows, shape } from './themePrimitives';
|
||||||
|
|
||||||
|
interface AppThemeProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* This is for the docs site. You can ignore it or remove it.
|
||||||
|
*/
|
||||||
|
disableCustomTheme?: boolean;
|
||||||
|
themeComponents?: ThemeOptions['components'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppTheme(props: AppThemeProps) {
|
||||||
|
const { children, disableCustomTheme, themeComponents } = props;
|
||||||
|
const theme = React.useMemo(() => {
|
||||||
|
return disableCustomTheme
|
||||||
|
? {}
|
||||||
|
: createTheme({
|
||||||
|
// For more details about CSS variables configuration, see https://mui.com/material-ui/customization/css-theme-variables/configuration/
|
||||||
|
cssVariables: {
|
||||||
|
colorSchemeSelector: 'data-mui-color-scheme',
|
||||||
|
cssVarPrefix: 'template',
|
||||||
|
},
|
||||||
|
colorSchemes, // Recently added in v6 for building light & dark mode app, see https://mui.com/material-ui/customization/palette/#color-schemes
|
||||||
|
typography,
|
||||||
|
shadows,
|
||||||
|
shape,
|
||||||
|
components: {
|
||||||
|
...inputsCustomizations,
|
||||||
|
...dataDisplayCustomizations,
|
||||||
|
...feedbackCustomizations,
|
||||||
|
...navigationCustomizations,
|
||||||
|
...surfacesCustomizations,
|
||||||
|
...themeComponents,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [disableCustomTheme, themeComponents]);
|
||||||
|
if (disableCustomTheme) {
|
||||||
|
return <React.Fragment>{children}</React.Fragment>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={theme} disableTransitionOnChange>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
89
src/shared-theme/ColorModeIconDropdown.tsx
Normal file
89
src/shared-theme/ColorModeIconDropdown.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import DarkModeIcon from '@mui/icons-material/DarkModeRounded';
|
||||||
|
import LightModeIcon from '@mui/icons-material/LightModeRounded';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import IconButton, { IconButtonOwnProps } from '@mui/material/IconButton';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import { useColorScheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export default function ColorModeIconDropdown(props: IconButtonOwnProps) {
|
||||||
|
const { mode, systemMode, setMode } = useColorScheme();
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
const handleMode = (targetMode: 'system' | 'light' | 'dark') => () => {
|
||||||
|
setMode(targetMode);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
if (!mode) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
data-screenshot="toggle-mode"
|
||||||
|
sx={(theme) => ({
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
display: 'inline-flex',
|
||||||
|
width: '2.25rem',
|
||||||
|
height: '2.25rem',
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: (theme.vars || theme).palette.divider,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const resolvedMode = (systemMode || mode) as 'light' | 'dark';
|
||||||
|
const icon = {
|
||||||
|
light: <LightModeIcon />,
|
||||||
|
dark: <DarkModeIcon />,
|
||||||
|
}[resolvedMode];
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<IconButton
|
||||||
|
data-screenshot="toggle-mode"
|
||||||
|
onClick={handleClick}
|
||||||
|
disableRipple
|
||||||
|
size="small"
|
||||||
|
aria-controls={open ? 'color-scheme-menu' : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={open ? 'true' : undefined}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</IconButton>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
id="account-menu"
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
onClick={handleClose}
|
||||||
|
slotProps={{
|
||||||
|
paper: {
|
||||||
|
variant: 'outlined',
|
||||||
|
elevation: 0,
|
||||||
|
sx: {
|
||||||
|
my: '4px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||||
|
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||||
|
>
|
||||||
|
<MenuItem selected={mode === 'system'} onClick={handleMode('system')}>
|
||||||
|
System
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem selected={mode === 'light'} onClick={handleMode('light')}>
|
||||||
|
Light
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem selected={mode === 'dark'} onClick={handleMode('dark')}>
|
||||||
|
Dark
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
28
src/shared-theme/ColorModeSelect.tsx
Normal file
28
src/shared-theme/ColorModeSelect.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useColorScheme } from '@mui/material/styles';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import Select, { SelectProps } from '@mui/material/Select';
|
||||||
|
|
||||||
|
export default function ColorModeSelect(props: SelectProps) {
|
||||||
|
const { mode, setMode } = useColorScheme();
|
||||||
|
if (!mode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
value={mode}
|
||||||
|
onChange={(event) =>
|
||||||
|
setMode(event.target.value as 'system' | 'light' | 'dark')
|
||||||
|
}
|
||||||
|
SelectDisplayProps={{
|
||||||
|
// @ts-ignore
|
||||||
|
'data-screenshot': 'toggle-mode',
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<MenuItem value="system">System</MenuItem>
|
||||||
|
<MenuItem value="light">Light</MenuItem>
|
||||||
|
<MenuItem value="dark">Dark</MenuItem>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
233
src/shared-theme/customizations/dataDisplay.tsx
Normal file
233
src/shared-theme/customizations/dataDisplay.tsx
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
import { Theme, alpha, Components } from '@mui/material/styles';
|
||||||
|
import { svgIconClasses } from '@mui/material/SvgIcon';
|
||||||
|
import { typographyClasses } from '@mui/material/Typography';
|
||||||
|
import { buttonBaseClasses } from '@mui/material/ButtonBase';
|
||||||
|
import { chipClasses } from '@mui/material/Chip';
|
||||||
|
import { iconButtonClasses } from '@mui/material/IconButton';
|
||||||
|
import { gray, red, green } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const dataDisplayCustomizations: Components<Theme> = {
|
||||||
|
MuiList: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: '8px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiListItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
[`& .${svgIconClasses.root}`]: {
|
||||||
|
width: '1rem',
|
||||||
|
height: '1rem',
|
||||||
|
color: (theme.vars || theme).palette.text.secondary,
|
||||||
|
},
|
||||||
|
[`& .${typographyClasses.root}`]: {
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
[`& .${buttonBaseClasses.root}`]: {
|
||||||
|
display: 'flex',
|
||||||
|
gap: 8,
|
||||||
|
padding: '2px 8px',
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
opacity: 0.7,
|
||||||
|
'&.Mui-selected': {
|
||||||
|
opacity: 1,
|
||||||
|
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||||
|
[`& .${svgIconClasses.root}`]: {
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
},
|
||||||
|
'&:focus-visible': {
|
||||||
|
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(theme.palette.action.selected, 0.5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&:focus-visible': {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiListItemText: {
|
||||||
|
styleOverrides: {
|
||||||
|
primary: ({ theme }) => ({
|
||||||
|
fontSize: theme.typography.body2.fontSize,
|
||||||
|
fontWeight: 500,
|
||||||
|
lineHeight: theme.typography.body2.lineHeight,
|
||||||
|
}),
|
||||||
|
secondary: ({ theme }) => ({
|
||||||
|
fontSize: theme.typography.caption.fontSize,
|
||||||
|
lineHeight: theme.typography.caption.lineHeight,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiListSubheader: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
padding: '4px 8px',
|
||||||
|
fontSize: theme.typography.caption.fontSize,
|
||||||
|
fontWeight: 500,
|
||||||
|
lineHeight: theme.typography.caption.lineHeight,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiListItemIcon: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
minWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiChip: {
|
||||||
|
defaultProps: {
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
border: '1px solid',
|
||||||
|
borderRadius: '999px',
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'default',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
borderColor: gray[200],
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
color: gray[500],
|
||||||
|
},
|
||||||
|
[`& .${chipClasses.icon}`]: {
|
||||||
|
color: gray[500],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
borderColor: gray[700],
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
color: gray[300],
|
||||||
|
},
|
||||||
|
[`& .${chipClasses.icon}`]: {
|
||||||
|
color: gray[300],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'success',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
borderColor: green[200],
|
||||||
|
backgroundColor: green[50],
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
color: green[500],
|
||||||
|
},
|
||||||
|
[`& .${chipClasses.icon}`]: {
|
||||||
|
color: green[500],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
borderColor: green[800],
|
||||||
|
backgroundColor: green[900],
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
color: green[300],
|
||||||
|
},
|
||||||
|
[`& .${chipClasses.icon}`]: {
|
||||||
|
color: green[300],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'error',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
borderColor: red[100],
|
||||||
|
backgroundColor: red[50],
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
color: red[500],
|
||||||
|
},
|
||||||
|
[`& .${chipClasses.icon}`]: {
|
||||||
|
color: red[500],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
borderColor: red[800],
|
||||||
|
backgroundColor: red[900],
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
color: red[200],
|
||||||
|
},
|
||||||
|
[`& .${chipClasses.icon}`]: {
|
||||||
|
color: red[300],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: { size: 'small' },
|
||||||
|
style: {
|
||||||
|
maxHeight: 20,
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
fontSize: theme.typography.caption.fontSize,
|
||||||
|
},
|
||||||
|
[`& .${svgIconClasses.root}`]: {
|
||||||
|
fontSize: theme.typography.caption.fontSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: { size: 'medium' },
|
||||||
|
style: {
|
||||||
|
[`& .${chipClasses.label}`]: {
|
||||||
|
fontSize: theme.typography.caption.fontSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTablePagination: {
|
||||||
|
styleOverrides: {
|
||||||
|
actions: {
|
||||||
|
display: 'flex',
|
||||||
|
gap: 8,
|
||||||
|
marginRight: 6,
|
||||||
|
[`& .${iconButtonClasses.root}`]: {
|
||||||
|
minWidth: 0,
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiIcon: {
|
||||||
|
defaultProps: {
|
||||||
|
fontSize: 'small',
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
fontSize: 'small',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontSize: '1rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
46
src/shared-theme/customizations/feedback.tsx
Normal file
46
src/shared-theme/customizations/feedback.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Theme, alpha, Components } from '@mui/material/styles';
|
||||||
|
import { gray, orange } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const feedbackCustomizations: Components<Theme> = {
|
||||||
|
MuiAlert: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
borderRadius: 10,
|
||||||
|
backgroundColor: orange[100],
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
border: `1px solid ${alpha(orange[300], 0.5)}`,
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: orange[500],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
backgroundColor: `${alpha(orange[900], 0.5)}`,
|
||||||
|
border: `1px solid ${alpha(orange[800], 0.5)}`,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiDialog: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
borderRadius: '10px',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: (theme.vars || theme).palette.divider,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiLinearProgress: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 8,
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
445
src/shared-theme/customizations/inputs.tsx
Normal file
445
src/shared-theme/customizations/inputs.tsx
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { alpha, Theme, Components } from '@mui/material/styles';
|
||||||
|
import { outlinedInputClasses } from '@mui/material/OutlinedInput';
|
||||||
|
import { svgIconClasses } from '@mui/material/SvgIcon';
|
||||||
|
import { toggleButtonGroupClasses } from '@mui/material/ToggleButtonGroup';
|
||||||
|
import { toggleButtonClasses } from '@mui/material/ToggleButton';
|
||||||
|
import CheckBoxOutlineBlankRoundedIcon from '@mui/icons-material/CheckBoxOutlineBlankRounded';
|
||||||
|
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||||
|
import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded';
|
||||||
|
import { gray, brand } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const inputsCustomizations: Components<Theme> = {
|
||||||
|
MuiButtonBase: {
|
||||||
|
defaultProps: {
|
||||||
|
disableTouchRipple: true,
|
||||||
|
disableRipple: true,
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
transition: 'all 100ms ease-in',
|
||||||
|
'&:focus-visible': {
|
||||||
|
outline: `3px solid ${alpha(theme.palette.primary.main, 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
boxShadow: 'none',
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
textTransform: 'none',
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
height: '2.25rem',
|
||||||
|
padding: '8px 12px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
size: 'medium',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
height: '2.5rem', // 40px
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'primary',
|
||||||
|
variant: 'contained',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: 'white',
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`,
|
||||||
|
boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`,
|
||||||
|
border: `1px solid ${gray[700]}`,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundImage: 'none',
|
||||||
|
backgroundColor: gray[700],
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: 'black',
|
||||||
|
backgroundColor: gray[50],
|
||||||
|
backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`,
|
||||||
|
boxShadow: 'inset 0 -1px 0 hsl(220, 30%, 80%)',
|
||||||
|
border: `1px solid ${gray[50]}`,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundImage: 'none',
|
||||||
|
backgroundColor: gray[300],
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[400],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'secondary',
|
||||||
|
variant: 'contained',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: 'white',
|
||||||
|
backgroundColor: brand[300],
|
||||||
|
backgroundImage: `linear-gradient(to bottom, ${alpha(brand[400], 0.8)}, ${brand[500]})`,
|
||||||
|
boxShadow: `inset 0 2px 0 ${alpha(brand[200], 0.2)}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`,
|
||||||
|
border: `1px solid ${brand[500]}`,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: brand[700],
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: brand[700],
|
||||||
|
backgroundImage: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'outlined',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: gray[200],
|
||||||
|
backgroundColor: alpha(gray[50], 0.3),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
borderColor: gray[300],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
borderColor: gray[700],
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
borderColor: gray[600],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'secondary',
|
||||||
|
variant: 'outlined',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: brand[700],
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: brand[200],
|
||||||
|
backgroundColor: brand[50],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: brand[100],
|
||||||
|
borderColor: brand[400],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: alpha(brand[200], 0.7),
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: brand[50],
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: brand[900],
|
||||||
|
backgroundColor: alpha(brand[900], 0.3),
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: brand[700],
|
||||||
|
backgroundColor: alpha(brand[900], 0.6),
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: alpha(brand[900], 0.5),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'text',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: gray[600],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: gray[50],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[700],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: alpha(gray[700], 0.7),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
color: 'secondary',
|
||||||
|
variant: 'text',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: brand[700],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(brand[100], 0.5),
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: alpha(brand[200], 0.7),
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: brand[100],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: alpha(brand[900], 0.5),
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: alpha(brand[900], 0.3),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiIconButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
boxShadow: 'none',
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
textTransform: 'none',
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
letterSpacing: 0,
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
border: '1px solid ',
|
||||||
|
borderColor: gray[200],
|
||||||
|
backgroundColor: alpha(gray[50], 0.3),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
borderColor: gray[300],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
borderColor: gray[700],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
borderColor: gray[600],
|
||||||
|
},
|
||||||
|
'&:active': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
width: '2.25rem',
|
||||||
|
height: '2.25rem',
|
||||||
|
padding: '0.25rem',
|
||||||
|
[`& .${svgIconClasses.root}`]: { fontSize: '1rem' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
size: 'medium',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
width: '2.5rem',
|
||||||
|
height: '2.5rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiToggleButtonGroup: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
borderRadius: '10px',
|
||||||
|
boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`,
|
||||||
|
[`& .${toggleButtonGroupClasses.selected}`]: {
|
||||||
|
color: brand[500],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
[`& .${toggleButtonGroupClasses.selected}`]: {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiToggleButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
padding: '12px 16px',
|
||||||
|
textTransform: 'none',
|
||||||
|
borderRadius: '10px',
|
||||||
|
fontWeight: 500,
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: gray[400],
|
||||||
|
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)',
|
||||||
|
[`&.${toggleButtonClasses.selected}`]: {
|
||||||
|
color: brand[300],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCheckbox: {
|
||||||
|
defaultProps: {
|
||||||
|
disableRipple: true,
|
||||||
|
icon: (
|
||||||
|
<CheckBoxOutlineBlankRoundedIcon sx={{ color: 'hsla(210, 0%, 0%, 0.0)' }} />
|
||||||
|
),
|
||||||
|
checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />,
|
||||||
|
indeterminateIcon: <RemoveRoundedIcon sx={{ height: 14, width: 14 }} />,
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
margin: 10,
|
||||||
|
height: 16,
|
||||||
|
width: 16,
|
||||||
|
borderRadius: 5,
|
||||||
|
border: '1px solid ',
|
||||||
|
borderColor: alpha(gray[300], 0.8),
|
||||||
|
boxShadow: '0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset',
|
||||||
|
backgroundColor: alpha(gray[100], 0.4),
|
||||||
|
transition: 'border-color, background-color, 120ms ease-in',
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: brand[300],
|
||||||
|
},
|
||||||
|
'&.Mui-focusVisible': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
borderColor: brand[400],
|
||||||
|
},
|
||||||
|
'&.Mui-checked': {
|
||||||
|
color: 'white',
|
||||||
|
backgroundColor: brand[500],
|
||||||
|
borderColor: brand[500],
|
||||||
|
boxShadow: `none`,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: brand[600],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
borderColor: alpha(gray[700], 0.8),
|
||||||
|
boxShadow: '0 0 0 1.5px hsl(210, 0%, 0%) inset',
|
||||||
|
backgroundColor: alpha(gray[900], 0.8),
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: brand[300],
|
||||||
|
},
|
||||||
|
'&.Mui-focusVisible': {
|
||||||
|
borderColor: brand[400],
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '2px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiInputBase: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
'&::placeholder': {
|
||||||
|
opacity: 0.7,
|
||||||
|
color: gray[500],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiOutlinedInput: {
|
||||||
|
styleOverrides: {
|
||||||
|
input: {
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
padding: '8px 12px',
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||||
|
transition: 'border 120ms ease-in',
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: gray[400],
|
||||||
|
},
|
||||||
|
[`&.${outlinedInputClasses.focused}`]: {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
borderColor: brand[400],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: gray[500],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
height: '2.25rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
size: 'medium',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
height: '2.5rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
notchedOutline: {
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiInputAdornment: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
color: (theme.vars || theme).palette.grey[500],
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
color: (theme.vars || theme).palette.grey[400],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiFormLabel: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
typography: theme.typography.caption,
|
||||||
|
marginBottom: 8,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
279
src/shared-theme/customizations/navigation.tsx
Normal file
279
src/shared-theme/customizations/navigation.tsx
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Theme, alpha, Components } from '@mui/material/styles';
|
||||||
|
import { SvgIconProps } from '@mui/material/SvgIcon';
|
||||||
|
import { buttonBaseClasses } from '@mui/material/ButtonBase';
|
||||||
|
import { dividerClasses } from '@mui/material/Divider';
|
||||||
|
import { menuItemClasses } from '@mui/material/MenuItem';
|
||||||
|
import { selectClasses } from '@mui/material/Select';
|
||||||
|
import { tabClasses } from '@mui/material/Tab';
|
||||||
|
import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded';
|
||||||
|
import { gray, brand } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const navigationCustomizations: Components<Theme> = {
|
||||||
|
MuiMenuItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
padding: '6px 8px',
|
||||||
|
[`&.${menuItemClasses.focusVisible}`]: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
[`&.${menuItemClasses.selected}`]: {
|
||||||
|
[`&.${menuItemClasses.focusVisible}`]: {
|
||||||
|
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiMenu: {
|
||||||
|
styleOverrides: {
|
||||||
|
list: {
|
||||||
|
gap: '0px',
|
||||||
|
[`&.${dividerClasses.root}`]: {
|
||||||
|
margin: '0 -8px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paper: ({ theme }) => ({
|
||||||
|
marginTop: '4px',
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
backgroundImage: 'none',
|
||||||
|
background: 'hsl(0, 0%, 100%)',
|
||||||
|
boxShadow:
|
||||||
|
'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
|
||||||
|
[`& .${buttonBaseClasses.root}`]: {
|
||||||
|
'&.Mui-selected': {
|
||||||
|
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
background: gray[900],
|
||||||
|
boxShadow:
|
||||||
|
'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiSelect: {
|
||||||
|
defaultProps: {
|
||||||
|
IconComponent: React.forwardRef<SVGSVGElement, SvgIconProps>((props, ref) => (
|
||||||
|
<UnfoldMoreRoundedIcon fontSize="small" {...props} ref={ref} />
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: gray[200],
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
boxShadow: `inset 0 1px 0 1px hsla(220, 0%, 100%, 0.6), inset 0 -1px 0 1px hsla(220, 35%, 90%, 0.5)`,
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: gray[300],
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
[`&.${selectClasses.focused}`]: {
|
||||||
|
outlineOffset: 0,
|
||||||
|
borderColor: gray[400],
|
||||||
|
},
|
||||||
|
'&:before, &:after': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
borderColor: gray[700],
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
boxShadow: `inset 0 1px 0 1px ${alpha(gray[700], 0.15)}, inset 0 -1px 0 1px hsla(220, 0%, 0%, 0.7)`,
|
||||||
|
'&:hover': {
|
||||||
|
borderColor: alpha(gray[700], 0.7),
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
[`&.${selectClasses.focused}`]: {
|
||||||
|
outlineOffset: 0,
|
||||||
|
borderColor: gray[900],
|
||||||
|
},
|
||||||
|
'&:before, &:after': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
select: ({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
'&:focus-visible': {
|
||||||
|
backgroundColor: gray[900],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiLink: {
|
||||||
|
defaultProps: {
|
||||||
|
underline: 'none',
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
fontWeight: 500,
|
||||||
|
position: 'relative',
|
||||||
|
textDecoration: 'none',
|
||||||
|
width: 'fit-content',
|
||||||
|
'&::before': {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '1px',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
backgroundColor: (theme.vars || theme).palette.text.secondary,
|
||||||
|
opacity: 0.3,
|
||||||
|
transition: 'width 0.3s ease, opacity 0.3s ease',
|
||||||
|
},
|
||||||
|
'&:hover::before': {
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
'&:focus-visible': {
|
||||||
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
|
outlineOffset: '4px',
|
||||||
|
borderRadius: '2px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiDrawer: {
|
||||||
|
styleOverrides: {
|
||||||
|
paper: ({ theme }) => ({
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPaginationItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
'&.Mui-selected': {
|
||||||
|
color: 'white',
|
||||||
|
backgroundColor: (theme.vars || theme).palette.grey[900],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
'&.Mui-selected': {
|
||||||
|
color: 'black',
|
||||||
|
backgroundColor: (theme.vars || theme).palette.grey[50],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTabs: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: { minHeight: 'fit-content' },
|
||||||
|
indicator: ({ theme }) => ({
|
||||||
|
backgroundColor: (theme.vars || theme).palette.grey[800],
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
backgroundColor: (theme.vars || theme).palette.grey[200],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTab: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
padding: '6px 8px',
|
||||||
|
marginBottom: '8px',
|
||||||
|
textTransform: 'none',
|
||||||
|
minWidth: 'fit-content',
|
||||||
|
minHeight: 'fit-content',
|
||||||
|
color: (theme.vars || theme).palette.text.secondary,
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'transparent',
|
||||||
|
':hover': {
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
borderColor: gray[200],
|
||||||
|
},
|
||||||
|
[`&.${tabClasses.selected}`]: {
|
||||||
|
color: gray[900],
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
':hover': {
|
||||||
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
borderColor: gray[700],
|
||||||
|
},
|
||||||
|
[`&.${tabClasses.selected}`]: {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiStepConnector: {
|
||||||
|
styleOverrides: {
|
||||||
|
line: ({ theme }) => ({
|
||||||
|
borderTop: '1px solid',
|
||||||
|
borderColor: (theme.vars || theme).palette.divider,
|
||||||
|
flex: 1,
|
||||||
|
borderRadius: '99px',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiStepIcon: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
color: 'transparent',
|
||||||
|
border: `1px solid ${gray[400]}`,
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: '50%',
|
||||||
|
'& text': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
'&.Mui-active': {
|
||||||
|
border: 'none',
|
||||||
|
color: (theme.vars || theme).palette.primary.main,
|
||||||
|
},
|
||||||
|
'&.Mui-completed': {
|
||||||
|
border: 'none',
|
||||||
|
color: (theme.vars || theme).palette.success.main,
|
||||||
|
},
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
border: `1px solid ${gray[700]}`,
|
||||||
|
'&.Mui-active': {
|
||||||
|
border: 'none',
|
||||||
|
color: (theme.vars || theme).palette.primary.light,
|
||||||
|
},
|
||||||
|
'&.Mui-completed': {
|
||||||
|
border: 'none',
|
||||||
|
color: (theme.vars || theme).palette.success.light,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: { completed: true },
|
||||||
|
style: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiStepLabel: {
|
||||||
|
styleOverrides: {
|
||||||
|
label: ({ theme }) => ({
|
||||||
|
'&.Mui-completed': {
|
||||||
|
opacity: 0.6,
|
||||||
|
...theme.applyStyles('dark', { opacity: 0.5 }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
113
src/shared-theme/customizations/surfaces.ts
Normal file
113
src/shared-theme/customizations/surfaces.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { alpha, Theme, Components } from '@mui/material/styles';
|
||||||
|
import { gray } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const surfacesCustomizations: Components<Theme> = {
|
||||||
|
MuiAccordion: {
|
||||||
|
defaultProps: {
|
||||||
|
elevation: 0,
|
||||||
|
disableGutters: true,
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
padding: 4,
|
||||||
|
overflow: 'clip',
|
||||||
|
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: (theme.vars || theme).palette.divider,
|
||||||
|
':before': {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
'&:not(:last-of-type)': {
|
||||||
|
borderBottom: 'none',
|
||||||
|
},
|
||||||
|
'&:first-of-type': {
|
||||||
|
borderTopLeftRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
borderTopRightRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
},
|
||||||
|
'&:last-of-type': {
|
||||||
|
borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
borderBottomRightRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiAccordionSummary: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: 8,
|
||||||
|
'&:hover': { backgroundColor: gray[50] },
|
||||||
|
'&:focus-visible': { backgroundColor: 'transparent' },
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
'&:hover': { backgroundColor: gray[800] },
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiAccordionDetails: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: { mb: 20, border: 'none' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPaper: {
|
||||||
|
defaultProps: {
|
||||||
|
elevation: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCard: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => {
|
||||||
|
return {
|
||||||
|
padding: 16,
|
||||||
|
gap: 16,
|
||||||
|
transition: 'all 100ms ease',
|
||||||
|
backgroundColor: gray[50],
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
boxShadow: 'none',
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
}),
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'outlined',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
|
boxShadow: 'none',
|
||||||
|
background: 'hsl(0, 0%, 100%)',
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
background: alpha(gray[900], 0.4),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCardContent: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: 0,
|
||||||
|
'&:last-child': { paddingBottom: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCardHeader: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCardActions: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
403
src/shared-theme/themePrimitives.ts
Normal file
403
src/shared-theme/themePrimitives.ts
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
import { createTheme, alpha, PaletteMode, Shadows } from '@mui/material/styles';
|
||||||
|
|
||||||
|
declare module '@mui/material/Paper' {
|
||||||
|
interface PaperPropsVariantOverrides {
|
||||||
|
highlighted: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare module '@mui/material/styles/createPalette' {
|
||||||
|
interface ColorRange {
|
||||||
|
50: string;
|
||||||
|
100: string;
|
||||||
|
200: string;
|
||||||
|
300: string;
|
||||||
|
400: string;
|
||||||
|
500: string;
|
||||||
|
600: string;
|
||||||
|
700: string;
|
||||||
|
800: string;
|
||||||
|
900: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaletteColor extends ColorRange {}
|
||||||
|
|
||||||
|
interface Palette {
|
||||||
|
baseShadow: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultTheme = createTheme();
|
||||||
|
|
||||||
|
const customShadows: Shadows = [...defaultTheme.shadows];
|
||||||
|
|
||||||
|
export const brand = {
|
||||||
|
50: 'hsl(210, 100%, 95%)',
|
||||||
|
100: 'hsl(210, 100%, 92%)',
|
||||||
|
200: 'hsl(210, 100%, 80%)',
|
||||||
|
300: 'hsl(210, 100%, 65%)',
|
||||||
|
400: 'hsl(210, 98%, 48%)',
|
||||||
|
500: 'hsl(210, 98%, 42%)',
|
||||||
|
600: 'hsl(210, 98%, 55%)',
|
||||||
|
700: 'hsl(210, 100%, 35%)',
|
||||||
|
800: 'hsl(210, 100%, 16%)',
|
||||||
|
900: 'hsl(210, 100%, 21%)',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const gray = {
|
||||||
|
50: 'hsl(220, 35%, 97%)',
|
||||||
|
100: 'hsl(220, 30%, 94%)',
|
||||||
|
200: 'hsl(220, 20%, 88%)',
|
||||||
|
300: 'hsl(220, 20%, 80%)',
|
||||||
|
400: 'hsl(220, 20%, 65%)',
|
||||||
|
500: 'hsl(220, 20%, 42%)',
|
||||||
|
600: 'hsl(220, 20%, 35%)',
|
||||||
|
700: 'hsl(220, 20%, 25%)',
|
||||||
|
800: 'hsl(220, 30%, 6%)',
|
||||||
|
900: 'hsl(220, 35%, 3%)',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const green = {
|
||||||
|
50: 'hsl(120, 80%, 98%)',
|
||||||
|
100: 'hsl(120, 75%, 94%)',
|
||||||
|
200: 'hsl(120, 75%, 87%)',
|
||||||
|
300: 'hsl(120, 61%, 77%)',
|
||||||
|
400: 'hsl(120, 44%, 53%)',
|
||||||
|
500: 'hsl(120, 59%, 30%)',
|
||||||
|
600: 'hsl(120, 70%, 25%)',
|
||||||
|
700: 'hsl(120, 75%, 16%)',
|
||||||
|
800: 'hsl(120, 84%, 10%)',
|
||||||
|
900: 'hsl(120, 87%, 6%)',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const orange = {
|
||||||
|
50: 'hsl(45, 100%, 97%)',
|
||||||
|
100: 'hsl(45, 92%, 90%)',
|
||||||
|
200: 'hsl(45, 94%, 80%)',
|
||||||
|
300: 'hsl(45, 90%, 65%)',
|
||||||
|
400: 'hsl(45, 90%, 40%)',
|
||||||
|
500: 'hsl(45, 90%, 35%)',
|
||||||
|
600: 'hsl(45, 91%, 25%)',
|
||||||
|
700: 'hsl(45, 94%, 20%)',
|
||||||
|
800: 'hsl(45, 95%, 16%)',
|
||||||
|
900: 'hsl(45, 93%, 12%)',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const red = {
|
||||||
|
50: 'hsl(0, 100%, 97%)',
|
||||||
|
100: 'hsl(0, 92%, 90%)',
|
||||||
|
200: 'hsl(0, 94%, 80%)',
|
||||||
|
300: 'hsl(0, 90%, 65%)',
|
||||||
|
400: 'hsl(0, 90%, 40%)',
|
||||||
|
500: 'hsl(0, 90%, 30%)',
|
||||||
|
600: 'hsl(0, 91%, 25%)',
|
||||||
|
700: 'hsl(0, 94%, 18%)',
|
||||||
|
800: 'hsl(0, 95%, 12%)',
|
||||||
|
900: 'hsl(0, 93%, 6%)',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDesignTokens = (mode: PaletteMode) => {
|
||||||
|
customShadows[1] =
|
||||||
|
mode === 'dark'
|
||||||
|
? 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px'
|
||||||
|
: 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px';
|
||||||
|
|
||||||
|
return {
|
||||||
|
palette: {
|
||||||
|
mode,
|
||||||
|
primary: {
|
||||||
|
light: brand[200],
|
||||||
|
main: brand[400],
|
||||||
|
dark: brand[700],
|
||||||
|
contrastText: brand[50],
|
||||||
|
...(mode === 'dark' && {
|
||||||
|
contrastText: brand[50],
|
||||||
|
light: brand[300],
|
||||||
|
main: brand[400],
|
||||||
|
dark: brand[700],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
light: brand[100],
|
||||||
|
main: brand[300],
|
||||||
|
dark: brand[600],
|
||||||
|
contrastText: gray[50],
|
||||||
|
...(mode === 'dark' && {
|
||||||
|
contrastText: brand[300],
|
||||||
|
light: brand[500],
|
||||||
|
main: brand[700],
|
||||||
|
dark: brand[900],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
light: orange[300],
|
||||||
|
main: orange[400],
|
||||||
|
dark: orange[800],
|
||||||
|
...(mode === 'dark' && {
|
||||||
|
light: orange[400],
|
||||||
|
main: orange[500],
|
||||||
|
dark: orange[700],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
light: red[300],
|
||||||
|
main: red[400],
|
||||||
|
dark: red[800],
|
||||||
|
...(mode === 'dark' && {
|
||||||
|
light: red[400],
|
||||||
|
main: red[500],
|
||||||
|
dark: red[700],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
light: green[300],
|
||||||
|
main: green[400],
|
||||||
|
dark: green[800],
|
||||||
|
...(mode === 'dark' && {
|
||||||
|
light: green[400],
|
||||||
|
main: green[500],
|
||||||
|
dark: green[700],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
grey: {
|
||||||
|
...gray,
|
||||||
|
},
|
||||||
|
divider: mode === 'dark' ? alpha(gray[700], 0.6) : alpha(gray[300], 0.4),
|
||||||
|
background: {
|
||||||
|
default: 'hsl(0, 0%, 99%)',
|
||||||
|
paper: 'hsl(220, 35%, 97%)',
|
||||||
|
...(mode === 'dark' && { default: gray[900], paper: 'hsl(220, 30%, 7%)' }),
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: gray[800],
|
||||||
|
secondary: gray[600],
|
||||||
|
warning: orange[400],
|
||||||
|
...(mode === 'dark' && { primary: 'hsl(0, 0%, 100%)', secondary: gray[400] }),
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
hover: alpha(gray[200], 0.2),
|
||||||
|
selected: `${alpha(gray[200], 0.3)}`,
|
||||||
|
...(mode === 'dark' && {
|
||||||
|
hover: alpha(gray[600], 0.2),
|
||||||
|
selected: alpha(gray[600], 0.3),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typography: {
|
||||||
|
fontFamily: 'Inter, sans-serif',
|
||||||
|
h1: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(48),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
},
|
||||||
|
h2: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(36),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
},
|
||||||
|
h3: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(30),
|
||||||
|
lineHeight: 1.2,
|
||||||
|
},
|
||||||
|
h4: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(24),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.5,
|
||||||
|
},
|
||||||
|
h5: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(20),
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
h6: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(18),
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
subtitle1: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(18),
|
||||||
|
},
|
||||||
|
subtitle2: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(14),
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
body1: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(14),
|
||||||
|
},
|
||||||
|
body2: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(14),
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
caption: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(12),
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
shadows: customShadows,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const colorSchemes = {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
light: brand[200],
|
||||||
|
main: brand[400],
|
||||||
|
dark: brand[700],
|
||||||
|
contrastText: brand[50],
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
light: brand[100],
|
||||||
|
main: brand[300],
|
||||||
|
dark: brand[600],
|
||||||
|
contrastText: gray[50],
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
light: orange[300],
|
||||||
|
main: orange[400],
|
||||||
|
dark: orange[800],
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
light: red[300],
|
||||||
|
main: red[400],
|
||||||
|
dark: red[800],
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
light: green[300],
|
||||||
|
main: green[400],
|
||||||
|
dark: green[800],
|
||||||
|
},
|
||||||
|
grey: {
|
||||||
|
...gray,
|
||||||
|
},
|
||||||
|
divider: alpha(gray[300], 0.4),
|
||||||
|
background: {
|
||||||
|
default: 'hsl(0, 0%, 99%)',
|
||||||
|
paper: 'hsl(220, 35%, 97%)',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: gray[800],
|
||||||
|
secondary: gray[600],
|
||||||
|
warning: orange[400],
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
hover: alpha(gray[200], 0.2),
|
||||||
|
selected: `${alpha(gray[200], 0.3)}`,
|
||||||
|
},
|
||||||
|
baseShadow:
|
||||||
|
'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
contrastText: brand[50],
|
||||||
|
light: brand[300],
|
||||||
|
main: brand[400],
|
||||||
|
dark: brand[700],
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
contrastText: brand[300],
|
||||||
|
light: brand[500],
|
||||||
|
main: brand[700],
|
||||||
|
dark: brand[900],
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
light: orange[400],
|
||||||
|
main: orange[500],
|
||||||
|
dark: orange[700],
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
light: red[400],
|
||||||
|
main: red[500],
|
||||||
|
dark: red[700],
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
light: green[400],
|
||||||
|
main: green[500],
|
||||||
|
dark: green[700],
|
||||||
|
},
|
||||||
|
grey: {
|
||||||
|
...gray,
|
||||||
|
},
|
||||||
|
divider: alpha(gray[700], 0.6),
|
||||||
|
background: {
|
||||||
|
default: gray[900],
|
||||||
|
paper: 'hsl(220, 30%, 7%)',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: 'hsl(0, 0%, 100%)',
|
||||||
|
secondary: gray[400],
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
hover: alpha(gray[600], 0.2),
|
||||||
|
selected: alpha(gray[600], 0.3),
|
||||||
|
},
|
||||||
|
baseShadow:
|
||||||
|
'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typography = {
|
||||||
|
fontFamily: 'Inter, sans-serif',
|
||||||
|
h1: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(48),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
},
|
||||||
|
h2: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(36),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
},
|
||||||
|
h3: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(30),
|
||||||
|
lineHeight: 1.2,
|
||||||
|
},
|
||||||
|
h4: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(24),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.5,
|
||||||
|
},
|
||||||
|
h5: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(20),
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
h6: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(18),
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
subtitle1: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(18),
|
||||||
|
},
|
||||||
|
subtitle2: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(14),
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
body1: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(14),
|
||||||
|
},
|
||||||
|
body2: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(14),
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
caption: {
|
||||||
|
fontSize: defaultTheme.typography.pxToRem(12),
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shape = {
|
||||||
|
borderRadius: 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const defaultShadows: Shadows = [
|
||||||
|
'none',
|
||||||
|
'var(--template-palette-baseShadow)',
|
||||||
|
...defaultTheme.shadows.slice(2),
|
||||||
|
];
|
||||||
|
export const shadows = defaultShadows;
|
||||||
20
src/theme.tsx
Normal file
20
src/theme.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { createTheme } from '@mui/material/styles';
|
||||||
|
import { red } from '@mui/material/colors';
|
||||||
|
|
||||||
|
// A custom theme for this app
|
||||||
|
const theme = createTheme({
|
||||||
|
cssVariables: true,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#556cd6',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#19857b',
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
main: red.A400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default theme;
|
||||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
"allowJs": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
||||||
9
tsconfig.node.json
Normal file
9
tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
7
vite.config.ts
Normal file
7
vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user