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