first commit
58
.gitignore
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# Node.js dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
.cache/
|
||||||
|
.docker/
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# IDE-specific files
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.vscode/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
*.lock
|
||||||
|
*.env.local
|
||||||
|
*.env.development.local
|
||||||
|
*.env.test.local
|
||||||
|
*.env.production.local
|
||||||
|
|
||||||
|
# MacOS specific files
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Windows specific files
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
!.gitkeep
|
8
.prettierrc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": true,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"bracketSpacing": true
|
||||||
|
}
|
||||||
|
|
46
README.md
|
@ -1,2 +1,46 @@
|
||||||
# bulk-email
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.\
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
16096
package-lock.json
generated
Normal file
76
package.json
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"name": "digi-ev-admin-dashboard",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"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.1",
|
||||||
|
"@mui/x-data-grid": "^7.27.0",
|
||||||
|
"@mui/x-date-pickers": "^7.27.0",
|
||||||
|
"@react-spring/web": "^9.7.5",
|
||||||
|
"@reduxjs/toolkit": "^2.5.0",
|
||||||
|
"@types/babel__core": "^7.20.5",
|
||||||
|
"add": "^2.0.6",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"cra-template-typescript": "1.2.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"highcharts": "^12.1.2",
|
||||||
|
"mui-phone-number": "^3.0.3",
|
||||||
|
"mui-tel-input": "^7.0.0",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-cookie": "^7.2.2",
|
||||||
|
"react-dom": "^18.0.0",
|
||||||
|
"react-dropzone": "^14.3.5",
|
||||||
|
"react-hook-form": "^7.54.2",
|
||||||
|
"react-minimal-pie-chart": "^9.1.0",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
|
"react-router-dom": "^7.1.1",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"recharts": "^2.15.1",
|
||||||
|
"sonner": "^1.7.4",
|
||||||
|
"uuid": "^11.1.0",
|
||||||
|
"web-vitals": "^4.2.4",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.10.5",
|
||||||
|
"@types/react": "^19.0.4",
|
||||||
|
"@types/react-dom": "^19.0.2",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"eslint-config-react-app": "7.0.1"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": []
|
||||||
|
}
|
||||||
|
}
|
21011
pnpm-lock.yaml
Normal file
BIN
public/Bell.jpg
Normal file
After Width: | Height: | Size: 1,008 B |
BIN
public/DigiEVLogo.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
public/Digiev.png
Normal file
After Width: | Height: | Size: 246 KiB |
BIN
public/Digilogo.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
4
public/Group 14.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="535" height="226" viewBox="0 0 535 226" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M43.561 68.1C55.753 68.1 65.913 72.3757 74.041 80.927C82.2537 89.4783 86.36 100.019 86.36 112.55C86.36 125.081 82.2537 135.622 74.041 144.173C65.913 152.724 55.753 157 43.561 157H8.255V68.1H43.561ZM43.561 140.236C51.2657 140.236 57.531 137.696 62.357 132.616C67.183 127.451 69.596 120.763 69.596 112.55C69.596 104.337 67.183 97.691 62.357 92.611C57.531 87.4463 51.2657 84.864 43.561 84.864H25.781V140.236H43.561ZM98.7921 68.1H116.318V157H98.7921V68.1ZM220.024 109.248V116.106C220.024 128.806 215.96 139.093 207.832 146.967C199.704 154.841 189.205 158.778 176.336 158.778C162.62 158.778 151.232 154.333 142.173 145.443C133.198 136.468 128.711 125.546 128.711 112.677C128.711 99.723 133.156 88.7587 142.046 79.784C151.02 70.8093 162.112 66.322 175.32 66.322C183.617 66.322 191.195 68.227 198.053 72.037C204.911 75.7623 210.245 80.7577 214.055 87.023L199.069 95.659C196.952 92.0183 193.735 89.055 189.417 86.769C185.183 84.483 180.442 83.34 175.193 83.34C166.726 83.34 159.741 86.134 154.238 91.722C148.819 97.2253 146.11 104.21 146.11 112.677C146.11 121.059 148.861 128.002 154.365 133.505C159.953 138.924 167.319 141.633 176.463 141.633C183.236 141.633 188.824 140.151 193.227 137.188C197.714 134.14 200.762 129.991 202.371 124.742H175.447V109.248H220.024ZM232.489 68.1H250.015V157H232.489V68.1ZM410.998 140.236H448.463V157H393.472V68.1H447.828V84.864H410.998V103.787H444.653V120.297H410.998V140.236ZM482.788 157L452.943 68.1H471.993L493.71 136.426L515.3 68.1H534.477L504.505 157H482.788Z" fill="white"/>
|
||||||
|
<path d="M374.268 108.459L324.874 94.3623L347.782 14.0969L264.907 117.311L314.302 131.408L291.394 211.673L374.268 108.459Z" fill="#52ACDF"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/Jobopro.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
public/Jobopro1.png
Normal file
After Width: | Height: | Size: 24 KiB |
27
public/Login.svg
Normal file
After Width: | Height: | Size: 14 MiB |
BIN
public/apple_store.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
public/arrow-top-right.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/avatar.png
Normal file
After Width: | Height: | Size: 3.9 MiB |
28
public/bg.svg
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<svg width="1920" height="751" viewBox="0 0 1920 751" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g opacity="0.1" filter="url(#filter0_f_1407_4696)">
|
||||||
|
<path d="M357.765 483.542C426.213 434.499 1144.46 549.989 1258.37 579.451C1372.28 608.913 1757.57 743.868 1689.13 792.911C1620.68 841.954 527.424 923.713 413.507 894.245C299.59 864.776 289.318 532.588 357.765 483.542Z" fill="#0DB3D5"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.2" filter="url(#filter1_f_1407_4696)">
|
||||||
|
<path d="M605.311 371.098C596.35 427.875 656.872 561.013 526.007 556.64C395.141 552.267 122.44 412.046 131.401 355.264C140.363 298.481 198.295 172.294 329.163 176.666C460.031 181.038 614.27 314.317 605.311 371.098Z" fill="#EAF1B8"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.2" filter="url(#filter2_f_1407_4696)">
|
||||||
|
<path d="M1222 456.032C1222 409.041 1493.74 156 1551.77 156C1609.81 156 1882 384.378 1882 431.369C1882 478.359 1515.47 735 1457.43 735C1399.39 735 1222 503.022 1222 456.032Z" fill="#B59EDB"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_f_1407_4696" x="6.99048" y="163.563" width="1998.22" height="1044.92" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur stdDeviation="154" result="effect1_foregroundBlur_1407_4696"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter1_f_1407_4696" x="-176.814" y="-131.445" width="1096.48" height="996.19" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur stdDeviation="154" result="effect1_foregroundBlur_1407_4696"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter2_f_1407_4696" x="914" y="-152" width="1276" height="1195" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur stdDeviation="154" result="effect1_foregroundBlur_1407_4696"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/bgBottom.png
Normal file
After Width: | Height: | Size: 316 KiB |
BIN
public/bgEV.png
Normal file
After Width: | Height: | Size: 316 KiB |
34
public/bgev.svg
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<svg width="1920" height="1425" viewBox="0 0 1920 1425" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_1407_4268)">
|
||||||
|
<rect width="1920" height="1425" fill="#111111"/>
|
||||||
|
<g opacity="0.1" filter="url(#filter0_f_1407_4268)">
|
||||||
|
<path d="M357.765 483.542C426.213 434.499 1144.46 549.989 1258.37 579.451C1372.28 608.913 1757.57 743.868 1689.13 792.911C1620.68 841.955 527.425 923.713 413.508 894.245C299.591 864.776 289.318 532.588 357.765 483.542Z" fill="#0DB3D5"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.2" filter="url(#filter1_f_1407_4268)">
|
||||||
|
<path d="M605.311 371.098C596.349 427.875 656.872 561.013 526.007 556.64C395.141 552.267 122.439 412.046 131.401 355.264C140.363 298.481 198.295 172.294 329.163 176.666C460.031 181.038 614.27 314.317 605.311 371.098Z" fill="#EAF1B8"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.2" filter="url(#filter2_f_1407_4268)">
|
||||||
|
<path d="M1222 456.032C1222 409.042 1493.74 156 1551.77 156C1609.81 156 1882 384.379 1882 431.369C1882 478.359 1515.47 735 1457.43 735C1399.39 735 1222 503.022 1222 456.032Z" fill="#B59EDB"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_f_1407_4268" x="6.99072" y="163.564" width="1998.22" height="1044.92" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur stdDeviation="154" result="effect1_foregroundBlur_1407_4268"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter1_f_1407_4268" x="-176.814" y="-131.445" width="1096.48" height="996.19" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur stdDeviation="154" result="effect1_foregroundBlur_1407_4268"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter2_f_1407_4268" x="914" y="-152" width="1276" height="1195" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur stdDeviation="154" result="effect1_foregroundBlur_1407_4268"/>
|
||||||
|
</filter>
|
||||||
|
<clipPath id="clip0_1407_4268">
|
||||||
|
<rect width="1920" height="1425" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/dev-bg.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
public/developer.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
public/digimantra_labs_logo.jpeg
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
public/evLogo.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
public/google_play.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
public/hero.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
public/home-img.png
Normal file
After Width: | Height: | Size: 1 MiB |
BIN
public/home.png
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
public/iMockup - iPhone.png
Normal file
After Width: | Height: | Size: 221 KiB |
26
public/index.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Gilroy:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../src/index.css">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
|
||||||
|
<title>DigiEV - Eco-friendly Charge</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
public/jobo.png
Normal file
After Width: | Height: | Size: 975 B |
BIN
public/left-bottom-arrow.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
public/logo.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
public/logo192.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logo512.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
public/mainPageLogo.png
Normal file
After Width: | Height: | Size: 566 KiB |
25
public/manifest.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
0
public/mobileLogo.png
Normal file
BIN
public/model-s-exterior-front-view.webp
Normal file
After Width: | Height: | Size: 8.4 KiB |
3
public/robots.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
BIN
public/tablet-img.png
Normal file
After Width: | Height: | Size: 148 KiB |
BIN
public/vector-arrows.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
50
src/App.css
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&display=swap");
|
||||||
|
/* @import url("https://fonts.googleapis.com/css2?family=Baloo+2:wght@400..800&display=swap"); */
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden; /* Prevents scrolling */
|
||||||
|
font-family: "Fredoka";
|
||||||
|
}
|
||||||
|
|
||||||
|
.App {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-logo {
|
||||||
|
height: 40vmin;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.App-logo {
|
||||||
|
animation: App-logo-spin infinite 20s linear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-header {
|
||||||
|
background-color: #282c34;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: calc(10px + 2vmin);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-link {
|
||||||
|
color: #61dafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes App-logo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
8
src/App.test.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
test('renders learn react link', () => {
|
||||||
|
render(<App />);
|
||||||
|
const linkElement = screen.getByText(/learn react/i);
|
||||||
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
});
|
13
src/App.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { BrowserRouter as Router } from "react-router-dom";
|
||||||
|
import AppRouter from "./router";
|
||||||
|
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<AppRouter />
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
BIN
src/assets/images/exercise.webp
Normal file
After Width: | Height: | Size: 138 KiB |
381
src/components/CustomTable/customTable.tsx
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { styled } from "@mui/material/styles";
|
||||||
|
import Table from "@mui/material/Table";
|
||||||
|
import TableBody from "@mui/material/TableBody";
|
||||||
|
import TableCell, { tableCellClasses } from "@mui/material/TableCell";
|
||||||
|
import TableContainer from "@mui/material/TableContainer";
|
||||||
|
import TableHead from "@mui/material/TableHead";
|
||||||
|
import TableRow from "@mui/material/TableRow";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Pagination,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
InputAdornment,
|
||||||
|
} from "@mui/material";
|
||||||
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
|
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
|
||||||
|
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
||||||
|
|
||||||
|
// Styled Components
|
||||||
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||||
|
[`&.${tableCellClasses.head}`]: {
|
||||||
|
backgroundColor: "#000000",
|
||||||
|
color: "#D0E1E9",
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "16px",
|
||||||
|
padding: "12px 16px",
|
||||||
|
borderBottom: "none",
|
||||||
|
position: "sticky",
|
||||||
|
top: 0,
|
||||||
|
zIndex: 10,
|
||||||
|
transition: "background-color 0.2s ease",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#2A2A2A",
|
||||||
|
cursor: "pointer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`&.${tableCellClasses.body}`]: {
|
||||||
|
fontSize: "16px",
|
||||||
|
padding: "12px 16px",
|
||||||
|
borderBottom: "none",
|
||||||
|
color: "#333333",
|
||||||
|
transition: "background-color 0.2s ease",
|
||||||
|
fontWeight: 500,
|
||||||
|
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
||||||
|
backgroundColor: "#DFECF1",
|
||||||
|
transition: "background-color 0.2s ease",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#D0E1E9",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTableContainer = styled(TableContainer)(({ theme }) => ({
|
||||||
|
borderRadius: "12px",
|
||||||
|
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
|
||||||
|
"&::-webkit-scrollbar": {
|
||||||
|
height: "6px",
|
||||||
|
},
|
||||||
|
"&::-webkit-scrollbar-track": {
|
||||||
|
background: "#D0E1E9",
|
||||||
|
},
|
||||||
|
"&::-webkit-scrollbar-thumb": {
|
||||||
|
background: "#000000",
|
||||||
|
borderRadius: "3px",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Interfaces
|
||||||
|
export interface Column {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
align?: "left" | "center" | "right";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Row {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomTableProps {
|
||||||
|
columns: Column[];
|
||||||
|
rows: Row[];
|
||||||
|
tableType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting State Interface
|
||||||
|
interface SortConfig {
|
||||||
|
key: string;
|
||||||
|
direction: "asc" | "desc" | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
|
columns,
|
||||||
|
rows,
|
||||||
|
tableType,
|
||||||
|
}) => {
|
||||||
|
const [searchQuery, setSearchQuery] = React.useState("");
|
||||||
|
const [currentPage, setCurrentPage] = React.useState(1);
|
||||||
|
const [sortConfig, setSortConfig] = React.useState<SortConfig>({
|
||||||
|
key: "",
|
||||||
|
direction: null,
|
||||||
|
});
|
||||||
|
const usersPerPage = 10;
|
||||||
|
|
||||||
|
// Helper Functions
|
||||||
|
const getTitleByType = (type: string) => {
|
||||||
|
const titles: { [key: string]: string } = {
|
||||||
|
backlog: "Student Backlog",
|
||||||
|
tools: "Tools",
|
||||||
|
};
|
||||||
|
return titles[type] || "List";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEmptyMessage = (type: string) => {
|
||||||
|
const messages: { [key: string]: string } = {
|
||||||
|
backlog: "No students available",
|
||||||
|
tools: "No tools available",
|
||||||
|
};
|
||||||
|
return messages[type] || "No data available";
|
||||||
|
};
|
||||||
|
|
||||||
|
const isImage = (value: any) => {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
return value.startsWith("http") || value.startsWith("data:image");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sorting Logic
|
||||||
|
const handleSort = (key: string) => {
|
||||||
|
setSortConfig((prev) => {
|
||||||
|
if (prev.key === key) {
|
||||||
|
if (prev.direction === "asc") return { key, direction: "desc" };
|
||||||
|
if (prev.direction === "desc") return { key, direction: null };
|
||||||
|
return { key, direction: "asc" };
|
||||||
|
}
|
||||||
|
return { key, direction: "asc" };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedRows = React.useMemo(() => {
|
||||||
|
if (!sortConfig.key || !sortConfig.direction) return [...rows];
|
||||||
|
|
||||||
|
return [...rows].sort((a, b) => {
|
||||||
|
const aValue = a[sortConfig.key];
|
||||||
|
const bValue = b[sortConfig.key];
|
||||||
|
|
||||||
|
if (aValue == null && bValue == null) return 0;
|
||||||
|
if (aValue == null) return sortConfig.direction === "asc" ? 1 : -1;
|
||||||
|
if (bValue == null) return sortConfig.direction === "asc" ? -1 : 1;
|
||||||
|
|
||||||
|
if (typeof aValue === "number" && typeof bValue === "number") {
|
||||||
|
return sortConfig.direction === "asc"
|
||||||
|
? aValue - bValue
|
||||||
|
: bValue - aValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aStr = String(aValue).toLowerCase();
|
||||||
|
const bStr = String(bValue).toLowerCase();
|
||||||
|
return sortConfig.direction === "asc"
|
||||||
|
? aStr.localeCompare(bStr)
|
||||||
|
: bStr.localeCompare(aStr);
|
||||||
|
});
|
||||||
|
}, [rows, sortConfig]);
|
||||||
|
|
||||||
|
// Filtering Logic
|
||||||
|
const filteredRows = sortedRows.filter((row) => {
|
||||||
|
if (!searchQuery.trim()) return true;
|
||||||
|
const lowerCaseQuery = searchQuery.toLowerCase().trim();
|
||||||
|
return (
|
||||||
|
(row.name && row.name.toLowerCase().includes(lowerCaseQuery)) ||
|
||||||
|
(row.email && row.email.toLowerCase().includes(lowerCaseQuery))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pagination Logic
|
||||||
|
const indexOfLastRow = currentPage * usersPerPage;
|
||||||
|
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
||||||
|
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
|
||||||
|
|
||||||
|
const handlePageChange = (
|
||||||
|
_event: React.ChangeEvent<unknown>,
|
||||||
|
value: number
|
||||||
|
) => {
|
||||||
|
setCurrentPage(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
padding: "20px 8px",
|
||||||
|
backgroundColor: "#D0E1E9",
|
||||||
|
borderRadius: "12px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: { xs: "column", md: "row" },
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: { xs: "flex-start", md: "center" },
|
||||||
|
flexWrap: "wrap",
|
||||||
|
mb: 3,
|
||||||
|
gap: 2,
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "30px",
|
||||||
|
letterSpacing: "-0.5px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getTitleByType(tableType)}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="Search"
|
||||||
|
sx={{
|
||||||
|
width: { xs: "100%", sm: "300px" },
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
borderRadius: "8px",
|
||||||
|
height: "40px",
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
"& fieldset": { borderColor: "#D1D5DB" },
|
||||||
|
"&:hover fieldset": { borderColor: "#9CA3AF" },
|
||||||
|
"&.Mui-focused fieldset": {
|
||||||
|
borderColor: "#3B82F6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
fontSize: "14px",
|
||||||
|
color: "#1A1A1A",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon sx={{ color: "#6B7280" }} />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<StyledTableContainer>
|
||||||
|
<Table sx={{ minWidth: "750px", tableLayout: "auto" }}>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<StyledTableCell
|
||||||
|
key={column.id}
|
||||||
|
align={column.align}
|
||||||
|
onClick={
|
||||||
|
column.id !== "select" &&
|
||||||
|
column.id !== "status"
|
||||||
|
? () => handleSort(column.id)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
sx={{
|
||||||
|
cursor:
|
||||||
|
column.id !== "select" &&
|
||||||
|
column.id !== "status"
|
||||||
|
? "pointer"
|
||||||
|
: "default",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{column.label}
|
||||||
|
{column.id !== "select" &&
|
||||||
|
column.id !== "status" &&
|
||||||
|
sortConfig.key === column.id &&
|
||||||
|
sortConfig.direction &&
|
||||||
|
(sortConfig.direction === "asc" ? (
|
||||||
|
<ArrowUpwardIcon
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ArrowDownwardIcon
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</StyledTableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{currentRows.length === 0 ? (
|
||||||
|
<StyledTableRow>
|
||||||
|
<StyledTableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
sx={{ textAlign: "center", py: 4 }}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
color: "#6B7280",
|
||||||
|
fontSize: "16px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getEmptyMessage(tableType)}
|
||||||
|
</Typography>
|
||||||
|
</StyledTableCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
) : (
|
||||||
|
currentRows.map((row, rowIndex) => (
|
||||||
|
<StyledTableRow key={rowIndex}>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<StyledTableCell
|
||||||
|
key={column.id}
|
||||||
|
align={column.align}
|
||||||
|
>
|
||||||
|
{isImage(row[column.id]) ? (
|
||||||
|
<img
|
||||||
|
src={row[column.id]}
|
||||||
|
alt="Row"
|
||||||
|
style={{
|
||||||
|
width: "40px",
|
||||||
|
height: "40px",
|
||||||
|
borderRadius: "50%",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
row[column.id]
|
||||||
|
)}
|
||||||
|
</StyledTableCell>
|
||||||
|
))}
|
||||||
|
</StyledTableRow>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</StyledTableContainer>
|
||||||
|
|
||||||
|
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}>
|
||||||
|
<Pagination
|
||||||
|
count={Math.ceil(filteredRows.length / usersPerPage)}
|
||||||
|
page={currentPage}
|
||||||
|
onChange={handlePageChange}
|
||||||
|
sx={{
|
||||||
|
"& .MuiPaginationItem-root": {
|
||||||
|
color: "#1A1A1A",
|
||||||
|
fontSize: "16px",
|
||||||
|
},
|
||||||
|
"& .Mui-selected": {
|
||||||
|
backgroundColor: "#000000",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#000000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomTable;
|
24
src/components/Loading/index.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { CircularProgress, Box, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
function LoadingComponent() {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100vh',
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CircularProgress size={50} color="primary" />
|
||||||
|
<Typography variant="h6" sx={{ marginTop: 2 }}>
|
||||||
|
Loading...
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadingComponent;
|
8
src/global.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
declare module "*.css";
|
||||||
|
|
||||||
|
declare module "@mui/styles/defaultTheme" {
|
||||||
|
interface DefaultTheme extends Theme {
|
||||||
|
vars: object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
24
src/hooks/useAuth.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { checkUserAuth } from '../redux/slices/authSlice';
|
||||||
|
|
||||||
|
const useAuth = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isAuthorized, setIsAuthorized] = React.useState(false);
|
||||||
|
const { isAuthenticated } = useSelector((state) => ({
|
||||||
|
isAuthenticated: state.authReducer.isAuthenticated,
|
||||||
|
}));
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
dispatch(checkUserAuth());
|
||||||
|
} else {
|
||||||
|
setIsAuthorized(false);
|
||||||
|
}
|
||||||
|
}, [dispatch, isAuthenticated]);
|
||||||
|
|
||||||
|
return { isAuthenticated, isAuthorized };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAuth;
|
35
src/index.css
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&display=swap");
|
||||||
|
|
||||||
|
/* @import url("https://fonts.googleapis.com/css2?family=Baloo+2:wght@400..800&display=swap"); */
|
||||||
|
/* @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap'); */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Fredoka";
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mui-typography {
|
||||||
|
font-family: "Fredoka";
|
||||||
|
background-color: rgb(7, 127, 233);
|
||||||
|
}
|
||||||
|
|
||||||
|
.css-1w8ddxu-MuiBarElement-root {
|
||||||
|
width: 19px !important;
|
||||||
|
border-radius: 50px !important;
|
||||||
|
rx: 8;
|
||||||
|
ry: 8;
|
||||||
|
}
|
||||||
|
/* @font-face {
|
||||||
|
font-family: '"Publica Sans Round Medium", sans-serif';
|
||||||
|
src: url("../public/fonts/PublicaSansRound-Md.otf") format("otf");
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
} */
|
28
src/index.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import "./index.css";
|
||||||
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
import App from "./App";
|
||||||
|
import { Provider } from "react-redux";
|
||||||
|
import store from "./redux/store/store.ts";
|
||||||
|
import { Toaster } from "sonner";
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<Provider store={store}>
|
||||||
|
<App />
|
||||||
|
<Toaster
|
||||||
|
position="top-right"
|
||||||
|
richColors
|
||||||
|
closeButton
|
||||||
|
duration={3000}
|
||||||
|
/>
|
||||||
|
</Provider>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||||
|
reportWebVitals();
|
82
src/layouts/DashboardLayout/index.tsx
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { Box, Stack } from "@mui/material";
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import SideMenu from "../../components/SideMenu/sideMenu";
|
||||||
|
import AppNavbar from "../../components/AppNavbar";
|
||||||
|
import Header from "../../components/Header";
|
||||||
|
import AppTheme from "../../shared-theme/AppTheme";
|
||||||
|
|
||||||
|
interface LayoutProps {
|
||||||
|
customStyles?: React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DashboardLayout: React.FC<LayoutProps> = ({ customStyles }) => {
|
||||||
|
return (
|
||||||
|
<AppTheme>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
// height: "auto",
|
||||||
|
flexDirection: { xs: "column", md: "row" },
|
||||||
|
width: "100%",
|
||||||
|
height: "100vh", // Full height from root, not auto
|
||||||
|
overflow: "hidden",
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* SideMenu - Responsive, shown only on large screens */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: { xs: "none", md: "block" },
|
||||||
|
// width: 250,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SideMenu />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Navbar - Always visible */}
|
||||||
|
<AppNavbar />
|
||||||
|
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={(theme) => ({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: "100vh",
|
||||||
|
flexGrow: 1,
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
overflow: "auto",
|
||||||
|
overflowX: "hidden",
|
||||||
|
...customStyles,
|
||||||
|
mt: { xs: 8, md: 0 },
|
||||||
|
padding: 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Box sx={{ height: "84px", flex: "0 0 84px" }}>
|
||||||
|
<Header />
|
||||||
|
</Box>
|
||||||
|
<Stack
|
||||||
|
spacing={2}
|
||||||
|
sx={{
|
||||||
|
padding: "30px",
|
||||||
|
display: "flex",
|
||||||
|
flex: 1,
|
||||||
|
justifyItems: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
// mx: { xs: 1, sm: 3 },
|
||||||
|
// pb: 5,
|
||||||
|
mt: { xs: 3, md: 0 },
|
||||||
|
width: "100%",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Outlet />
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</AppTheme>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashboardLayout;
|
38
src/lib/https.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const http = axios.create({
|
||||||
|
baseURL: process.env.REACT_APP_BACKEND_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
http.interceptors.request.use((config) => {
|
||||||
|
const authToken = localStorage.getItem("authToken");
|
||||||
|
if (authToken) {
|
||||||
|
config.headers.Authorization = `Bearer ${authToken}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
http.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error.response) {
|
||||||
|
const status = error.response.status;
|
||||||
|
const requestUrl = error.config.url; // Get the API route
|
||||||
|
|
||||||
|
// Handle token expiration (401) but NOT for login failures
|
||||||
|
if (status === 401 && !requestUrl.includes("/login")) {
|
||||||
|
localStorage.removeItem("authToken");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle forbidden access
|
||||||
|
if (status === 403) {
|
||||||
|
localStorage.removeItem("authToken");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default http;
|
1
src/logo.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
175
src/pages/BacklogUploadPage/index.tsx
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import * as XLSX from "xlsx";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||||
|
import { loadStudents } from "../../redux/slices/backlogSlice";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
|
import CustomTable, { Column } from "../../components/CustomTable/customTable";
|
||||||
|
|
||||||
|
export default function BacklogUploadPage() {
|
||||||
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const students = useSelector(
|
||||||
|
(state: RootState) => state.backlogReducer.students
|
||||||
|
);
|
||||||
|
|
||||||
|
const [fileUploaded, setFileUploaded] = useState(students.length > 0);
|
||||||
|
const [selectedStudentIds, setSelectedStudentIds] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (evt) => {
|
||||||
|
const data = new Uint8Array(evt.target.result as ArrayBuffer);
|
||||||
|
const workbook = XLSX.read(data, { type: "array" });
|
||||||
|
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||||
|
const parsedData: { name: string; email: string }[] =
|
||||||
|
XLSX.utils.sheet_to_json(sheet);
|
||||||
|
|
||||||
|
const studentsWithId = parsedData.map((student) => ({
|
||||||
|
...student,
|
||||||
|
id: uuidv4(),
|
||||||
|
status: "Pending",
|
||||||
|
}));
|
||||||
|
|
||||||
|
dispatch(loadStudents(studentsWithId));
|
||||||
|
setFileUploaded(true);
|
||||||
|
setSelectedStudentIds([]); // Reset selection after upload
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCheckboxChange = (id: string) => {
|
||||||
|
setSelectedStudentIds((prev) =>
|
||||||
|
prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNavigateToEmail = () => {
|
||||||
|
if (selectedStudentIds.length === 0) {
|
||||||
|
toast.warning("Please select at least one student.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigate("/email", { state: { selectedIds: selectedStudentIds } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: Column[] = [
|
||||||
|
{ id: "select", label: "Select", align: "center" },
|
||||||
|
{ id: "name", label: "Name", align: "left" },
|
||||||
|
{ id: "email", label: "Email", align: "left" },
|
||||||
|
{ id: "status", label: "Status", align: "center" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const rows = students.map((student) => ({
|
||||||
|
id: student.id,
|
||||||
|
select: (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedStudentIds.includes(student.id)}
|
||||||
|
onChange={() => handleCheckboxChange(student.id)}
|
||||||
|
disabled={student.status === "Sent"}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
name: student.name,
|
||||||
|
email: student.email,
|
||||||
|
status:
|
||||||
|
student.status === "Sent" ? (
|
||||||
|
<span style={{ color: "#10B981" }}>✔️ Sent</span>
|
||||||
|
) : (
|
||||||
|
<span style={{ color: "#F59E0B" }}>⏳ Pending</span>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100vw",
|
||||||
|
minHeight: "100vh",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
p: { xs: 2, sm: 4 },
|
||||||
|
backgroundColor: "#F5F8FA",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: { xs: "20px", sm: "24px" },
|
||||||
|
mb: 3,
|
||||||
|
color: "#1A1A1A",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
📂 Upload Student Backlog File
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{!fileUploaded ? (
|
||||||
|
<Box sx={{ maxWidth: "600px" }}>
|
||||||
|
<label htmlFor="upload-input">
|
||||||
|
<input
|
||||||
|
id="upload-input"
|
||||||
|
type="file"
|
||||||
|
accept=".xlsx, .xls"
|
||||||
|
onChange={handleFileUpload}
|
||||||
|
style={{ display: "none" }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
component="span"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#1F2937",
|
||||||
|
color: "#fff",
|
||||||
|
borderRadius: "8px",
|
||||||
|
textTransform: "none",
|
||||||
|
fontWeight: "500",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
📁 Choose Excel File
|
||||||
|
</Button>
|
||||||
|
</label>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
color: "#6B7280",
|
||||||
|
mt: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
* Excel must contain columns: <b>name</b>, <b>email</b>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Box sx={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
||||||
|
<CustomTable
|
||||||
|
columns={columns}
|
||||||
|
rows={rows}
|
||||||
|
tableType="backlog"
|
||||||
|
/>
|
||||||
|
<Box sx={{ display: "flex", gap: 2, mt: 3 }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleNavigateToEmail}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#111827",
|
||||||
|
color: "#fff",
|
||||||
|
borderRadius: "8px",
|
||||||
|
fontSize: "16px",
|
||||||
|
padding: "8px 24px",
|
||||||
|
textTransform: "none",
|
||||||
|
"&:hover": { backgroundColor: "#1F2937" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send Email
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
140
src/pages/EmailPage/index.tsx
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import { Box, Button, TextField, Typography, Paper } from "@mui/material";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||||
|
import { markStudentAsSent } from "../../redux/slices/backlogSlice";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
export default function EmailPage() {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
|
||||||
|
const { selectedIds } = location.state || { selectedIds: [] };
|
||||||
|
const students = useSelector(
|
||||||
|
(state: RootState) => state.backlogReducer.students
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedStudents = students.filter((student) =>
|
||||||
|
selectedIds.includes(student.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
const [subject, setSubject] = useState("");
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
const [attachment, setAttachment] = useState<File | null>(null);
|
||||||
|
|
||||||
|
const handleSend = () => {
|
||||||
|
if (!subject || !message) {
|
||||||
|
toast.warning("Subject and Message are required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Sending email to:",
|
||||||
|
selectedStudents.map((s) => s.email)
|
||||||
|
);
|
||||||
|
console.log("Subject:", subject);
|
||||||
|
console.log("Message:", message);
|
||||||
|
console.log("Attachment:", attachment?.name);
|
||||||
|
|
||||||
|
dispatch(markStudentAsSent(selectedIds));
|
||||||
|
toast.success("Emails sent successfully.");
|
||||||
|
navigate("/");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
navigate("/");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
minHeight: "100vh",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: "#F5F8FA",
|
||||||
|
p: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paper
|
||||||
|
elevation={3}
|
||||||
|
sx={{ p: 4, maxWidth: "600px", width: "100%" }}
|
||||||
|
>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
📧 Compose Email
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography sx={{ mb: 2 }}>
|
||||||
|
<b>To:</b> {selectedStudents.map((s) => s.name).join(", ")}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Subject"
|
||||||
|
variant="outlined"
|
||||||
|
value={subject}
|
||||||
|
onChange={(e) => setSubject(e.target.value)}
|
||||||
|
sx={{ mb: 2 }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Message"
|
||||||
|
variant="outlined"
|
||||||
|
multiline
|
||||||
|
rows={6}
|
||||||
|
value={message}
|
||||||
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
|
sx={{ mb: 2 }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
component="label"
|
||||||
|
sx={{ mb: 2, textTransform: "none" }}
|
||||||
|
>
|
||||||
|
📎 Upload Attachment
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
hidden
|
||||||
|
onChange={(e) =>
|
||||||
|
setAttachment(e.target.files?.[0] || null)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{attachment && (
|
||||||
|
<Typography variant="body2" sx={{ mb: 2 }}>
|
||||||
|
Selected: {attachment.name}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
mt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSend}
|
||||||
|
sx={{ textTransform: "none" }}
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={handleCancel}
|
||||||
|
sx={{ textTransform: "none" }}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
115
src/pages/NotFoundPage/index.tsx
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Box, Typography, Button, Card, Grid } from "@mui/material";
|
||||||
|
import ElectricCarIcon from "@mui/icons-material/ElectricCar";
|
||||||
|
import { keyframes } from "@emotion/react";
|
||||||
|
|
||||||
|
// Animation for the car icon
|
||||||
|
const pulse = keyframes`
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const NotFoundPage = () => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: "100vh",
|
||||||
|
background:
|
||||||
|
"linear-gradient(135deg,rgb(12, 14, 15),rgb(10, 10, 10))",
|
||||||
|
padding: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#272727",
|
||||||
|
padding: "40px",
|
||||||
|
borderRadius: "20px",
|
||||||
|
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.7)",
|
||||||
|
textAlign: "center",
|
||||||
|
maxWidth: "500px",
|
||||||
|
width: "100%",
|
||||||
|
color: "#E0E0E0",
|
||||||
|
animation: `${pulse} 1.5s infinite`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ElectricCarIcon
|
||||||
|
sx={{
|
||||||
|
fontSize: 100,
|
||||||
|
color: "#52ACDF",
|
||||||
|
marginBottom: 2,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
variant="h3"
|
||||||
|
sx={{ fontWeight: 700, color: "#FFFFFF", marginBottom: 1 }}
|
||||||
|
>
|
||||||
|
404
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="h5"
|
||||||
|
sx={{ fontWeight: 500, color: "#FFFFFF", marginBottom: 3 }}
|
||||||
|
>
|
||||||
|
Oops! Page Not Found
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{ color: "#B0B0B0", marginBottom: 4 }}
|
||||||
|
>
|
||||||
|
The path you’re looking for seems to be off the grid. Maybe
|
||||||
|
the charging station is offline? ⚡
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Grid container spacing={2} justifyContent="center">
|
||||||
|
<Grid item>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
padding: "12px 32px",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
opacity: 0.9,
|
||||||
|
},
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
onClick={() => (window.location.href = "/")}
|
||||||
|
>
|
||||||
|
Back to Login
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
sx={{
|
||||||
|
borderColor: "#52ACDF",
|
||||||
|
color: "#52ACDF",
|
||||||
|
padding: "12px 32px",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onClick={() => window.history.back()}
|
||||||
|
>
|
||||||
|
Go Back
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFoundPage;
|
12
src/redux/reducers.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { combineReducers } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
import backlogReducer from "../redux/slices/backlogSlice.ts";
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
|
||||||
|
backlogReducer,
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof rootReducer>;
|
||||||
|
|
||||||
|
export default rootReducer;
|
123
src/redux/slices/backlogSlice.ts
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import http from "../../lib/https";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
export interface Student {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
status: "Pending" | "Sent";
|
||||||
|
}
|
||||||
|
|
||||||
|
// State shape
|
||||||
|
interface BacklogState {
|
||||||
|
students: Student[];
|
||||||
|
isLoading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
const initialState: BacklogState = {
|
||||||
|
students: [],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const fetchBacklogStudents = createAsyncThunk<
|
||||||
|
Student[],
|
||||||
|
void,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("backlog/fetchBacklogStudents", async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.get("/students/backlog");
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "Failed to fetch students"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeStudentFromServer = createAsyncThunk<
|
||||||
|
string, // returning student ID
|
||||||
|
string, // input student ID
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("backlog/removeStudentFromServer", async (studentId, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
await http.delete(`/students/${studentId}`);
|
||||||
|
toast.success("Student removed from backlog.");
|
||||||
|
return studentId;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Failed to remove student.");
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "Remove operation failed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const backlogSlice = createSlice({
|
||||||
|
name: "backlog",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
loadStudents: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<Omit<Student, "status">[]>
|
||||||
|
) => {
|
||||||
|
state.students = action.payload.map((student) => ({
|
||||||
|
...student,
|
||||||
|
status: "Pending",
|
||||||
|
}));
|
||||||
|
toast.success("Students loaded from file.");
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
removeStudent: (state, action: PayloadAction<string>) => {
|
||||||
|
state.students = state.students.filter(
|
||||||
|
(student) => student.id !== action.payload
|
||||||
|
);
|
||||||
|
toast.success("Student removed from local backlog.");
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
markStudentAsSent: (state, action: PayloadAction<string[]>) => {
|
||||||
|
action.payload.forEach((id) => {
|
||||||
|
const student = state.students.find((s) => s.id === id);
|
||||||
|
if (student) {
|
||||||
|
student.status = "Sent";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(fetchBacklogStudents.pending, (state) => {
|
||||||
|
state.isLoading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
fetchBacklogStudents.fulfilled,
|
||||||
|
(state, action: PayloadAction<Student[]>) => {
|
||||||
|
state.students = action.payload;
|
||||||
|
state.isLoading = false;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(
|
||||||
|
fetchBacklogStudents.rejected,
|
||||||
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.error = action.payload || "Failed to fetch";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(removeStudentFromServer.fulfilled, (state, action) => {
|
||||||
|
state.students = state.students.filter(
|
||||||
|
(student) => student.id !== action.payload
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { loadStudents, removeStudent, markStudentAsSent } =
|
||||||
|
backlogSlice.actions;
|
||||||
|
export default backlogSlice.reducer;
|
9
src/redux/store/store.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
|
import rootReducer from "../reducers.ts";
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: rootReducer,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
export default store;
|
13
src/reportWebVitals.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const reportWebVitals = onPerfEntry => {
|
||||||
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
|
getCLS(onPerfEntry);
|
||||||
|
getFID(onPerfEntry);
|
||||||
|
getFCP(onPerfEntry);
|
||||||
|
getLCP(onPerfEntry);
|
||||||
|
getTTFB(onPerfEntry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reportWebVitals;
|
24
src/router.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom";
|
||||||
|
import React, { lazy, Suspense } from "react";
|
||||||
|
import LoadingComponent from "./components/Loading";
|
||||||
|
import BacklogUploadPage from "./pages/BacklogUploadPage/index.tsx";
|
||||||
|
|
||||||
|
import EmailPage from "./pages/EmailPage/index.tsx";
|
||||||
|
const NotFoundPage = lazy(() => import("./pages/NotFoundPage"));
|
||||||
|
|
||||||
|
|
||||||
|
// Combined Router Component
|
||||||
|
export default function AppRouter() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<LoadingComponent />}>
|
||||||
|
<BaseRoutes>
|
||||||
|
{/* Default Route */}
|
||||||
|
<Route path="" element={<BacklogUploadPage />} />
|
||||||
|
<Route path="/email" element={<EmailPage />} />
|
||||||
|
|
||||||
|
{/* Catch-all Route */}
|
||||||
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
|
</BaseRoutes>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
5
src/setupTests.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
88
src/shared-theme/AppTheme.tsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ThemeProvider, Theme, 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, shadows, shape } from "./themePrimitives";
|
||||||
|
|
||||||
|
interface AppThemeProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
disableCustomTheme?: boolean;
|
||||||
|
themeComponents?: ThemeOptions["components"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppTheme(props: AppThemeProps) {
|
||||||
|
const { children, disableCustomTheme, themeComponents } = props;
|
||||||
|
const theme = React.useMemo(() => {
|
||||||
|
return disableCustomTheme
|
||||||
|
? {}
|
||||||
|
: createTheme({
|
||||||
|
palette: {
|
||||||
|
mode: "light",
|
||||||
|
background: {
|
||||||
|
default: "#DFECF1",
|
||||||
|
paper: "#D0E1E9",
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#000000",
|
||||||
|
secondary: "#454545",
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
main: "#00C853",
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
main: "#F44336",
|
||||||
|
},
|
||||||
|
divider: "#E0E0E0",
|
||||||
|
},
|
||||||
|
typography: {
|
||||||
|
fontFamily: "Fredoka",
|
||||||
|
h4: {
|
||||||
|
fontSize: "2rem",
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
h6: {
|
||||||
|
fontSize: "16px",
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
body1: {
|
||||||
|
fontSize: "1rem",
|
||||||
|
},
|
||||||
|
body2: {
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
color: "#b0b0b0",
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cssVariables: {
|
||||||
|
colorSchemeSelector: "data-mui-color-scheme",
|
||||||
|
cssVarPrefix: "template",
|
||||||
|
},
|
||||||
|
shadows,
|
||||||
|
shape: {
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
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.js
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 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) {
|
||||||
|
const { mode, systemMode, setMode } = useColorScheme();
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
const handleMode = (targetMode) => () => {
|
||||||
|
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;
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
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>
|
||||||
|
);
|
||||||
|
}
|
25
src/shared-theme/ColorModeSelect.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { useColorScheme } from '@mui/material/styles';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
|
||||||
|
export default function ColorModeSelect(props) {
|
||||||
|
const { mode, setMode } = useColorScheme();
|
||||||
|
if (!mode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
value={mode}
|
||||||
|
onChange={(event) => setMode(event.target.value)}
|
||||||
|
SelectDisplayProps={{
|
||||||
|
'data-screenshot': 'toggle-mode',
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<MenuItem value="system">System</MenuItem>
|
||||||
|
<MenuItem value="light">Light</MenuItem>
|
||||||
|
<MenuItem value="dark">Dark</MenuItem>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
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>
|
||||||
|
);
|
||||||
|
}
|
26
src/shared-theme/customizations/autoFill.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
export const autofillFix = {
|
||||||
|
"& input:-webkit-autofill": {
|
||||||
|
WebkitBoxShadow: "0 0 0 1000px transparent inset !important",
|
||||||
|
WebkitTextFillColor: "#000000 !important",
|
||||||
|
transition: "background-color 5000s ease-in-out 0s",
|
||||||
|
},
|
||||||
|
"& input:-webkit-autofill:hover": {
|
||||||
|
WebkitBoxShadow: "0 0 0 1000px transparent inset !important",
|
||||||
|
},
|
||||||
|
"& input:-webkit-autofill:focus": {
|
||||||
|
WebkitBoxShadow: "0 0 0 1000px transparent inset !important",
|
||||||
|
},
|
||||||
|
"& input:-webkit-autofill:active": {
|
||||||
|
WebkitBoxShadow: "0 0 0 1000px transparent inset !important",
|
||||||
|
},
|
||||||
|
"& input:-moz-autofill": {
|
||||||
|
boxShadow: "0 0 0 1000px transparent inset !important",
|
||||||
|
MozTextFillColor: "#000000 !important",
|
||||||
|
transition: "background-color 5000s ease-in-out 0s",
|
||||||
|
},
|
||||||
|
"& input:autofill": {
|
||||||
|
boxShadow: "0 0 0 1000px transparent inset !important",
|
||||||
|
textFillColor: "#000000 !important",
|
||||||
|
transition: "background-color 5000s ease-in-out 0s",
|
||||||
|
},
|
||||||
|
};
|
237
src/shared-theme/customizations/dataDisplay.js
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
import { alpha } 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 = {
|
||||||
|
MuiList: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
// padding: '8px',
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
|
||||||
|
gap: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiListItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ theme }) => ({
|
||||||
|
[`& .${svgIconClasses.root}`]: {
|
||||||
|
width: "3rem",
|
||||||
|
height: "2rem",
|
||||||
|
color: (theme.vars || theme).palette.text.secondary,
|
||||||
|
},
|
||||||
|
[`& .${typographyClasses.root}`]: {
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: "16px",
|
||||||
|
},
|
||||||
|
[`& .${buttonBaseClasses.root}`]: {
|
||||||
|
display: "flex",
|
||||||
|
// gap: 8,
|
||||||
|
// padding: '2px 8px',
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
opacity: 0.7,
|
||||||
|
"&.Mui-selected": {
|
||||||
|
opacity: 2,
|
||||||
|
// backgroundColor: "rgb(66, 64, 64)",
|
||||||
|
|
||||||
|
[`& .${svgIconClasses.root}`]: {
|
||||||
|
//color: (theme.vars || theme).palette.text.primary,
|
||||||
|
color: " #FFFFFF",
|
||||||
|
},
|
||||||
|
"&: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: "16px",
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
237
src/shared-theme/customizations/dataDisplay.tsx
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
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: {
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
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.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { alpha } from '@mui/material/styles';
|
||||||
|
import { gray, orange } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const feedbackCustomizations = {
|
||||||
|
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],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
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],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
460
src/shared-theme/customizations/inputs.js
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { alpha } 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';
|
||||||
|
import {autofillFix} from './autoFill';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const inputsCustomizations = {
|
||||||
|
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,
|
||||||
|
...autofillFix,
|
||||||
|
},
|
||||||
|
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: "#272727",
|
||||||
|
backgroundColor: "#D9E7ED",
|
||||||
|
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,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
461
src/shared-theme/customizations/inputs.tsx
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
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";
|
||||||
|
import { autofillFix } from "./autoFill";
|
||||||
|
|
||||||
|
/* 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[600],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiOutlinedInput: {
|
||||||
|
styleOverrides: {
|
||||||
|
input: {
|
||||||
|
padding: 0,
|
||||||
|
...autofillFix,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
294
src/shared-theme/customizations/navigation.js
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { alpha } from '@mui/material/styles';
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
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
|
||||||
|
// ),
|
||||||
|
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiMenu: {
|
||||||
|
styleOverrides: {
|
||||||
|
list: {
|
||||||
|
gap: "0px",
|
||||||
|
[`&.${dividerClasses.root}`]: {
|
||||||
|
margin: "0 -8px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paper: ({ theme }) => ({
|
||||||
|
marginTop: "4px",
|
||||||
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
|
border: `1px solidrgb(141, 135, 135)`,
|
||||||
|
backgroundImage: "none",
|
||||||
|
background: "#D9E7ED",
|
||||||
|
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: "transparent",
|
||||||
|
},
|
||||||
|
"&.Mui-selected:hover": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...theme.applyStyles("dark", {
|
||||||
|
background: "#272727",
|
||||||
|
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((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 }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
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 }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
114
src/shared-theme/customizations/surfaces.js
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import { alpha } from '@mui/material/styles';
|
||||||
|
import { gray } from '../themePrimitives';
|
||||||
|
|
||||||
|
/* eslint-disable import/prefer-default-export */
|
||||||
|
export const surfacesCustomizations = {
|
||||||
|
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:"#1C1C1C",
|
||||||
|
}),
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
384
src/shared-theme/themePrimitives.js
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
import { createTheme, alpha } from "@mui/material/styles";
|
||||||
|
|
||||||
|
const defaultTheme = createTheme();
|
||||||
|
|
||||||
|
const customShadows = [...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) => {
|
||||||
|
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: "Gilory",
|
||||||
|
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: "Gilory",
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultShadows = [
|
||||||
|
"none",
|
||||||
|
"var(--template-palette-baseShadow)",
|
||||||
|
...defaultTheme.shadows.slice(2),
|
||||||
|
];
|
||||||
|
|
||||||
|
export const shadows = defaultShadows;
|
410
src/shared-theme/themePrimitives.ts
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
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: "Gilroy",
|
||||||
|
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: "Gilroy",
|
||||||
|
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;
|
30
tsconfig.app.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
32
tsconfig.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"types": [
|
||||||
|
"react",
|
||||||
|
"react-dom"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
tsconfig.node.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react",
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
}
|
||||||
|
}
|