diff --git a/package-lock.json b/package-lock.json
index f319750..e999c19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"@anatine/zod-openapi": "^2.2.6",
"@dotenvx/dotenvx": "^1.45.2",
+ "@react-spring/web": "^10.0.1",
"@ts-rest/core": "^3.51.0",
"@ts-rest/express": "^3.51.0",
"@ts-rest/open-api": "^3.51.0",
@@ -25,10 +26,14 @@
},
"devDependencies": {
"@emotion/react": "^11.14.0",
- "@emotion/styled": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
"@fontsource/roboto": "^5.1.1",
"@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
+ "@mui/x-charts": "^8.7.0",
+ "@mui/x-data-grid": "^8.7.0",
+ "@mui/x-date-pickers": "^8.7.0",
+ "@mui/x-tree-view": "^8.7.0",
"@tanstack/react-query": "^5.0.0",
"@ts-rest/react-query": "^3.51.0",
"@types/cors": "^2.8.17",
@@ -41,6 +46,7 @@
"@vitejs/plugin-react": "^4.3.4",
"concurrently": "^9.1.2",
"date-fns": "^4.1.0",
+ "dayjs": "^1.11.13",
"jotai": "^2.11.0",
"lodash-es": "^4.17.21",
"material-react-table": "^3.1.0",
@@ -1341,18 +1347,119 @@
}
}
},
- "node_modules/@mui/x-date-pickers": {
- "version": "7.29.4",
- "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.4.tgz",
- "integrity": "sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw==",
+ "node_modules/@mui/x-charts": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-8.7.0.tgz",
+ "integrity": "sha512-HFUpJUvJfZlBD15WWhHzXin2MwToUwJaXeiQCqe4r6W4T7VQMM3qqm+Rb6OQ0QQ3tvykQuogHfSzhSUbKW0cHw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
- "@babel/runtime": "^7.25.7",
- "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0",
- "@mui/x-internals": "7.29.0",
- "@types/react-transition-group": "^4.4.11",
+ "@babel/runtime": "^7.27.6",
+ "@mui/utils": "^7.1.1",
+ "@mui/x-charts-vendor": "8.5.3",
+ "@mui/x-internal-gestures": "0.2.0",
+ "@mui/x-internals": "8.7.0",
+ "bezier-easing": "^2.1.0",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
+ "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-charts-vendor": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-8.5.3.tgz",
+ "integrity": "sha512-H05cb0c2qfRhWLPcwtiIU8BOcKTrMNvhgmRAvJJXpmlirOA1km8dUlR71VeUvJiCthhVIHKyFkPPzFYKgHAfng==",
+ "dev": true,
+ "license": "MIT AND ISC",
+ "dependencies": {
+ "@babel/runtime": "^7.27.6",
+ "@types/d3-color": "^3.1.3",
+ "@types/d3-delaunay": "^6.0.4",
+ "@types/d3-interpolate": "^3.0.4",
+ "@types/d3-scale": "^4.0.9",
+ "@types/d3-shape": "^3.1.7",
+ "@types/d3-time": "^3.0.4",
+ "@types/d3-timer": "^3.0.2",
+ "d3-color": "^3.1.0",
+ "d3-delaunay": "^6.0.4",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.2.0",
+ "d3-time": "^3.1.0",
+ "d3-timer": "^3.0.1",
+ "delaunator": "^5.0.1",
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/@mui/x-data-grid": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.7.0.tgz",
+ "integrity": "sha512-3hVjnADSBXEd/7f+CHlxTNhqJrnRL0XkTbvI+yfttPuqLHYQJwNUR7p7d/VtyRcR6QlaK19+Bu8Q6u0Ygmw/PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.6",
+ "@mui/utils": "^7.1.1",
+ "@mui/x-internals": "8.7.0",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
+ "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-date-pickers": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.7.0.tgz",
+ "integrity": "sha512-7fCRhhoE/2s7wsJWLoY2IoHlN5ZA+ev7ZzhIjLPAOzMXwIflzCgljq6iG/iXpATugsmlxWHhO/7wdDSD6zUNOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.6",
+ "@mui/utils": "^7.1.1",
+ "@mui/x-internals": "8.7.0",
+ "@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
@@ -1409,16 +1516,26 @@
}
}
},
- "node_modules/@mui/x-internals": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz",
- "integrity": "sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==",
+ "node_modules/@mui/x-internal-gestures": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internal-gestures/-/x-internal-gestures-0.2.0.tgz",
+ "integrity": "sha512-HXCKslmb17wfRRLZ20g2mzzvEcGd31Os8oNbW1IvodlZqJpHhwT25A1fN9Ss3GiJa/+miD/Y0Ad5o6OiMbI1Uw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
- "@babel/runtime": "^7.25.7",
- "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0"
+ "@babel/runtime": "^7.27.6"
+ }
+ },
+ "node_modules/@mui/x-internals": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.7.0.tgz",
+ "integrity": "sha512-1aduds7L2i6t0HIFNlqG4UB07SVEg+wcnJ9GGu8B/X8EVwO72Rt+rc8ZlqK10ooscq1AlTwi2dd0q+hz+aWk+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.6",
+ "@mui/utils": "^7.1.1",
+ "reselect": "^5.1.1"
},
"engines": {
"node": ">=14.0.0"
@@ -1428,9 +1545,51 @@
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
+ "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/@mui/x-tree-view": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-8.7.0.tgz",
+ "integrity": "sha512-a6qtrdtLXN/qrhIQnhSSw+hyX2r4DLAj8g0XCkbV8i7NKJIM5ko5OSOaZoFTOFRHylohGeo47Gg9CJUszZpM/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.6",
+ "@mui/utils": "^7.1.1",
+ "@mui/x-internals": "8.7.0",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
+ "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@noble/ciphers": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
@@ -1481,6 +1640,78 @@
"url": "https://opencollective.com/popperjs"
}
},
+ "node_modules/@react-spring/animated": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-10.0.1.tgz",
+ "integrity": "sha512-BGL3hA66Y8Qm3KmRZUlfG/mFbDPYajgil2/jOP0VXf2+o2WPVmcDps/eEgdDqgf5Pv9eBbyj7LschLMuSjlW3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-spring/shared": "~10.0.1",
+ "@react-spring/types": "~10.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-spring/core": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-10.0.1.tgz",
+ "integrity": "sha512-KaMMsN1qHuVTsFpg/5ajAVye7OEqhYbCq0g4aKM9bnSZlDBBYpO7Uf+9eixyXN8YEbF+YXaYj9eoWDs+npZ+sA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-spring/animated": "~10.0.1",
+ "@react-spring/shared": "~10.0.1",
+ "@react-spring/types": "~10.0.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-spring/donate"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-spring/rafz": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-10.0.1.tgz",
+ "integrity": "sha512-UrzG/d6Is+9i0aCAjsjWRqIlFFiC4lFqFHrH63zK935z2YDU95TOFio4VKGISJ5SG0xq4ULy7c1V3KU+XvL+Yg==",
+ "license": "MIT"
+ },
+ "node_modules/@react-spring/shared": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-10.0.1.tgz",
+ "integrity": "sha512-KR2tmjDShPruI/GGPfAZOOLvDgkhFseabjvxzZFFggJMPkyICLjO0J6mCIoGtdJSuHywZyc4Mmlgi+C88lS00g==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-spring/rafz": "~10.0.1",
+ "@react-spring/types": "~10.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-spring/types": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-10.0.1.tgz",
+ "integrity": "sha512-Fk1wYVAKL+ZTYK+4YFDpHf3Slsy59pfFFvnnTfRjQQFGlyIo4VejPtDs3CbDiuBjM135YztRyZjIH2VbycB+ZQ==",
+ "license": "MIT"
+ },
+ "node_modules/@react-spring/web": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-10.0.1.tgz",
+ "integrity": "sha512-FgQk02OqFrYyJBTTnBTWAU0WPzkHkKXauc6aeexcvATvLapUxwnfGuLlsLYF8BYjEVfkivPT04ziAue6zyRBtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-spring/animated": "~10.0.1",
+ "@react-spring/core": "~10.0.1",
+ "@react-spring/shared": "~10.0.1",
+ "@react-spring/types": "~10.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.19",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz",
@@ -2085,6 +2316,71 @@
"@types/node": "*"
}
},
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -2420,6 +2716,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/bezier-easing": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
+ "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -2879,6 +3182,141 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
@@ -2890,6 +3328,13 @@
"url": "https://github.com/sponsors/kossnocorp"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -2908,6 +3353,16 @@
}
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+ "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
@@ -3605,6 +4060,16 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -3746,7 +4211,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/jsesc": {
@@ -3822,7 +4286,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -4391,7 +4854,6 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -4404,7 +4866,6 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
@@ -4510,6 +4971,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -4555,6 +5023,13 @@
"rimraf": "bin.js"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
+ "dev": true,
+ "license": "Unlicense"
+ },
"node_modules/rollup": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz",
@@ -4635,7 +5110,6 @@
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -5292,6 +5766,16 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+ "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
diff --git a/package.json b/package.json
index 4987e72..7e4357f 100644
--- a/package.json
+++ b/package.json
@@ -19,10 +19,14 @@
"license": "ISC",
"devDependencies": {
"@emotion/react": "^11.14.0",
- "@emotion/styled": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
"@fontsource/roboto": "^5.1.1",
"@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
+ "@mui/x-charts": "^8.7.0",
+ "@mui/x-data-grid": "^8.7.0",
+ "@mui/x-date-pickers": "^8.7.0",
+ "@mui/x-tree-view": "^8.7.0",
"@tanstack/react-query": "^5.0.0",
"@ts-rest/react-query": "^3.51.0",
"@types/cors": "^2.8.17",
@@ -35,6 +39,7 @@
"@vitejs/plugin-react": "^4.3.4",
"concurrently": "^9.1.2",
"date-fns": "^4.1.0",
+ "dayjs": "^1.11.13",
"jotai": "^2.11.0",
"lodash-es": "^4.17.21",
"material-react-table": "^3.1.0",
@@ -49,6 +54,7 @@
"dependencies": {
"@anatine/zod-openapi": "^2.2.6",
"@dotenvx/dotenvx": "^1.45.2",
+ "@react-spring/web": "^10.0.1",
"@ts-rest/core": "^3.51.0",
"@ts-rest/express": "^3.51.0",
"@ts-rest/open-api": "^3.51.0",
diff --git a/src/client/App.tsx b/src/client/App.tsx
index b5a6ff8..574348b 100644
--- a/src/client/App.tsx
+++ b/src/client/App.tsx
@@ -12,6 +12,7 @@ import theme from "./theme";
import { tsr } from "./client";
import { SessionDetails } from "./SessionDetails";
import { Root } from "./Root";
+import { Root as V1Root } from "./v1/Root";
const queryClient = new QueryClient();
@@ -24,13 +25,14 @@ export function App() {
- }>
+ }>
} />
}
/>
+ }>
diff --git a/src/client/Root.tsx b/src/client/Root.tsx
index d7c3db4..74c115a 100644
--- a/src/client/Root.tsx
+++ b/src/client/Root.tsx
@@ -72,7 +72,6 @@ export function Root() {
)}
-
);
diff --git a/src/client/v1/Root/Root.tsx b/src/client/v1/Root/Root.tsx
new file mode 100644
index 0000000..f7f428c
--- /dev/null
+++ b/src/client/v1/Root/Root.tsx
@@ -0,0 +1,5 @@
+import Dashboard from "../Template/dashboard/Dashboard";
+
+export function Root() {
+ return ;
+}
diff --git a/src/client/v1/Root/index.ts b/src/client/v1/Root/index.ts
new file mode 100644
index 0000000..7542b9b
--- /dev/null
+++ b/src/client/v1/Root/index.ts
@@ -0,0 +1 @@
+export { Root } from "./Root";
diff --git a/src/client/v1/Template/dashboard/Dashboard.tsx b/src/client/v1/Template/dashboard/Dashboard.tsx
new file mode 100644
index 0000000..8a3f044
--- /dev/null
+++ b/src/client/v1/Template/dashboard/Dashboard.tsx
@@ -0,0 +1,60 @@
+import type {} from "@mui/x-date-pickers/themeAugmentation";
+import type {} from "@mui/x-charts/themeAugmentation";
+import type {} from "@mui/x-tree-view/themeAugmentation";
+import { alpha } from "@mui/material/styles";
+import CssBaseline from "@mui/material/CssBaseline";
+import Box from "@mui/material/Box";
+import Stack from "@mui/material/Stack";
+import AppNavbar from "./components/AppNavbar";
+import Header from "./components/Header";
+import SideMenu from "./components/SideMenu";
+import AppTheme from "../shared-theme/AppTheme";
+import {
+ chartsCustomizations,
+ dataGridCustomizations,
+ datePickersCustomizations,
+ treeViewCustomizations,
+} from "./theme/customizations";
+import { Outlet } from "react-router";
+
+const xThemeComponents = {
+ ...chartsCustomizations,
+ ...dataGridCustomizations,
+ ...datePickersCustomizations,
+ ...treeViewCustomizations,
+};
+
+export default function Dashboard(props: { disableCustomTheme?: boolean }) {
+ return (
+
+
+
+
+
+ ({
+ flexGrow: 1,
+ backgroundColor: theme.vars
+ ? `rgba(${theme.vars.palette.background.defaultChannel} / 1)`
+ : alpha(theme.palette.background.default, 1),
+ overflow: "auto",
+ })}
+ >
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/Title.tsx.preview b/src/client/v1/Template/dashboard/Title.tsx.preview
new file mode 100644
index 0000000..76fc02f
--- /dev/null
+++ b/src/client/v1/Template/dashboard/Title.tsx.preview
@@ -0,0 +1,3 @@
+
+ {props.children}
+
\ No newline at end of file
diff --git a/src/client/v1/Template/dashboard/components/AppNavbar.tsx b/src/client/v1/Template/dashboard/components/AppNavbar.tsx
new file mode 100644
index 0000000..eb7be48
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/AppNavbar.tsx
@@ -0,0 +1,105 @@
+import React from "react";
+import { styled } from '@mui/material/styles';
+import AppBar from '@mui/material/AppBar';
+import Box from '@mui/material/Box';
+import Stack from '@mui/material/Stack';
+import MuiToolbar from '@mui/material/Toolbar';
+import { tabsClasses } from '@mui/material/Tabs';
+import Typography from '@mui/material/Typography';
+import MenuRoundedIcon from '@mui/icons-material/MenuRounded';
+import DashboardRoundedIcon from '@mui/icons-material/DashboardRounded';
+import SideMenuMobile from './SideMenuMobile';
+import MenuButton from './MenuButton';
+import ColorModeIconDropdown from '../../shared-theme/ColorModeIconDropdown';
+
+const Toolbar = styled(MuiToolbar)({
+ width: '100%',
+ padding: '12px',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'start',
+ justifyContent: 'center',
+ gap: '12px',
+ flexShrink: 0,
+ [`& ${tabsClasses.flexContainer}`]: {
+ gap: '8px',
+ p: '8px',
+ pb: 0,
+ },
+});
+
+export default function AppNavbar() {
+ const [open, setOpen] = React.useState(false);
+
+ const toggleDrawer = (newOpen: boolean) => () => {
+ setOpen(newOpen);
+ };
+
+ return (
+
+
+
+
+
+
+ Dashboard
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export function CustomIcon() {
+ return (
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/CardAlert.tsx b/src/client/v1/Template/dashboard/components/CardAlert.tsx
new file mode 100644
index 0000000..ff4206c
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/CardAlert.tsx
@@ -0,0 +1,25 @@
+
+import Card from '@mui/material/Card';
+import CardContent from '@mui/material/CardContent';
+import Button from '@mui/material/Button';
+import Typography from '@mui/material/Typography';
+import AutoAwesomeRoundedIcon from '@mui/icons-material/AutoAwesomeRounded';
+
+export default function CardAlert() {
+ return (
+
+
+
+
+ Plan about to expire
+
+
+ Enjoy 10% off when renewing your plan today.
+
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/CustomDatePicker.tsx b/src/client/v1/Template/dashboard/components/CustomDatePicker.tsx
new file mode 100644
index 0000000..bbefda3
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/CustomDatePicker.tsx
@@ -0,0 +1,60 @@
+import React from "react";
+import dayjs, { Dayjs } from 'dayjs';
+import { useForkRef } from '@mui/material/utils';
+import Button from '@mui/material/Button';
+import CalendarTodayRoundedIcon from '@mui/icons-material/CalendarTodayRounded';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
+import { DatePicker, DatePickerFieldProps } from '@mui/x-date-pickers/DatePicker';
+import {
+ useParsedFormat,
+ usePickerContext,
+ useSplitFieldProps,
+} from '@mui/x-date-pickers';
+
+interface ButtonFieldProps extends DatePickerFieldProps {}
+
+function ButtonField(props: ButtonFieldProps) {
+ const { forwardedProps } = useSplitFieldProps(props, 'date');
+ const pickerContext = usePickerContext();
+ const handleRef = useForkRef(pickerContext.triggerRef, pickerContext.rootRef);
+ const parsedFormat = useParsedFormat();
+ const valueStr =
+ pickerContext.value == null
+ ? parsedFormat
+ : pickerContext.value.format(pickerContext.fieldFormat);
+
+ return (
+ }
+ sx={{ minWidth: 'fit-content' }}
+ onClick={() => pickerContext.setOpen((prev) => !prev)}
+ >
+ {pickerContext.label ?? valueStr}
+
+ );
+}
+
+export default function CustomDatePicker() {
+ const [value, setValue] = React.useState(dayjs('2023-04-17'));
+
+ return (
+
+ setValue(newValue)}
+ slots={{ field: ButtonField }}
+ slotProps={{
+ nextIconButton: { size: 'small' },
+ previousIconButton: { size: 'small' },
+ }}
+ views={['day', 'month', 'year']}
+ />
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/Header.tsx b/src/client/v1/Template/dashboard/components/Header.tsx
new file mode 100644
index 0000000..9cd73fd
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/Header.tsx
@@ -0,0 +1,36 @@
+
+import Stack from '@mui/material/Stack';
+import NotificationsRoundedIcon from '@mui/icons-material/NotificationsRounded';
+import CustomDatePicker from './CustomDatePicker';
+import NavbarBreadcrumbs from './NavbarBreadcrumbs';
+import MenuButton from './MenuButton';
+import ColorModeIconDropdown from '../../shared-theme/ColorModeIconDropdown';
+
+import Search from './Search';
+
+export default function Header() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/MenuButton.tsx b/src/client/v1/Template/dashboard/components/MenuButton.tsx
new file mode 100644
index 0000000..b6e019d
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/MenuButton.tsx
@@ -0,0 +1,23 @@
+
+import Badge, { badgeClasses } from '@mui/material/Badge';
+import IconButton, { IconButtonProps } from '@mui/material/IconButton';
+
+export interface MenuButtonProps extends IconButtonProps {
+ showBadge?: boolean;
+}
+
+export default function MenuButton({
+ showBadge = false,
+ ...props
+}: MenuButtonProps) {
+ return (
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/MenuContent.tsx b/src/client/v1/Template/dashboard/components/MenuContent.tsx
new file mode 100644
index 0000000..9053822
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/MenuContent.tsx
@@ -0,0 +1,54 @@
+
+import List from '@mui/material/List';
+import ListItem from '@mui/material/ListItem';
+import ListItemButton from '@mui/material/ListItemButton';
+import ListItemIcon from '@mui/material/ListItemIcon';
+import ListItemText from '@mui/material/ListItemText';
+import Stack from '@mui/material/Stack';
+import HomeRoundedIcon from '@mui/icons-material/HomeRounded';
+import AnalyticsRoundedIcon from '@mui/icons-material/AnalyticsRounded';
+import PeopleRoundedIcon from '@mui/icons-material/PeopleRounded';
+import AssignmentRoundedIcon from '@mui/icons-material/AssignmentRounded';
+import SettingsRoundedIcon from '@mui/icons-material/SettingsRounded';
+import InfoRoundedIcon from '@mui/icons-material/InfoRounded';
+import HelpRoundedIcon from '@mui/icons-material/HelpRounded';
+
+const mainListItems = [
+ { text: 'Home', icon: },
+ { text: 'Analytics', icon: },
+ { text: 'Clients', icon: },
+ { text: 'Tasks', icon: },
+];
+
+const secondaryListItems = [
+ { text: 'Settings', icon: },
+ { text: 'About', icon: },
+ { text: 'Feedback', icon: },
+];
+
+export default function MenuContent() {
+ return (
+
+
+ {mainListItems.map((item, index) => (
+
+
+ {item.icon}
+
+
+
+ ))}
+
+
+ {secondaryListItems.map((item, index) => (
+
+
+ {item.icon}
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/NavbarBreadcrumbs.tsx b/src/client/v1/Template/dashboard/components/NavbarBreadcrumbs.tsx
new file mode 100644
index 0000000..2005827
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/NavbarBreadcrumbs.tsx
@@ -0,0 +1,30 @@
+
+import { styled } from '@mui/material/styles';
+import Typography from '@mui/material/Typography';
+import Breadcrumbs, { breadcrumbsClasses } from '@mui/material/Breadcrumbs';
+import NavigateNextRoundedIcon from '@mui/icons-material/NavigateNextRounded';
+
+const StyledBreadcrumbs = styled(Breadcrumbs)(({ theme }) => ({
+ margin: theme.spacing(1, 0),
+ [`& .${breadcrumbsClasses.separator}`]: {
+ color: (theme.vars || theme).palette.action.disabled,
+ margin: 1,
+ },
+ [`& .${breadcrumbsClasses.ol}`]: {
+ alignItems: 'center',
+ },
+}));
+
+export default function NavbarBreadcrumbs() {
+ return (
+ }
+ >
+ Dashboard
+
+ Home
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/OptionsMenu.tsx b/src/client/v1/Template/dashboard/components/OptionsMenu.tsx
new file mode 100644
index 0000000..c011563
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/OptionsMenu.tsx
@@ -0,0 +1,80 @@
+
+import { styled } from '@mui/material/styles';
+import Divider, { dividerClasses } from '@mui/material/Divider';
+import Menu from '@mui/material/Menu';
+import MuiMenuItem from '@mui/material/MenuItem';
+import { paperClasses } from '@mui/material/Paper';
+import { listClasses } from '@mui/material/List';
+import ListItemText from '@mui/material/ListItemText';
+import ListItemIcon, { listItemIconClasses } from '@mui/material/ListItemIcon';
+import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded';
+import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded';
+import MenuButton from './MenuButton';
+import React from 'react';
+
+const MenuItem = styled(MuiMenuItem)({
+ margin: '2px 0',
+});
+
+export default function OptionsMenu() {
+ const [anchorEl, setAnchorEl] = React.useState(null);
+ const open = Boolean(anchorEl);
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/Search.tsx b/src/client/v1/Template/dashboard/components/Search.tsx
new file mode 100644
index 0000000..7e2def3
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/Search.tsx
@@ -0,0 +1,26 @@
+
+import FormControl from '@mui/material/FormControl';
+import InputAdornment from '@mui/material/InputAdornment';
+import OutlinedInput from '@mui/material/OutlinedInput';
+import SearchRoundedIcon from '@mui/icons-material/SearchRounded';
+
+export default function Search() {
+ return (
+
+
+
+
+ }
+ inputProps={{
+ 'aria-label': 'search',
+ }}
+ />
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/SelectContent.tsx b/src/client/v1/Template/dashboard/components/SelectContent.tsx
new file mode 100644
index 0000000..49fc6c4
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/SelectContent.tsx
@@ -0,0 +1,103 @@
+
+import MuiAvatar from '@mui/material/Avatar';
+import MuiListItemAvatar from '@mui/material/ListItemAvatar';
+import MenuItem from '@mui/material/MenuItem';
+import ListItemText from '@mui/material/ListItemText';
+import ListItemIcon from '@mui/material/ListItemIcon';
+import ListSubheader from '@mui/material/ListSubheader';
+import Select, { SelectChangeEvent, selectClasses } from '@mui/material/Select';
+import Divider from '@mui/material/Divider';
+import { styled } from '@mui/material/styles';
+import AddRoundedIcon from '@mui/icons-material/AddRounded';
+import DevicesRoundedIcon from '@mui/icons-material/DevicesRounded';
+import SmartphoneRoundedIcon from '@mui/icons-material/SmartphoneRounded';
+import ConstructionRoundedIcon from '@mui/icons-material/ConstructionRounded';
+import React from 'react';
+
+const Avatar = styled(MuiAvatar)(({ theme }) => ({
+ width: 28,
+ height: 28,
+ backgroundColor: (theme.vars || theme).palette.background.paper,
+ color: (theme.vars || theme).palette.text.secondary,
+ border: `1px solid ${(theme.vars || theme).palette.divider}`,
+}));
+
+const ListItemAvatar = styled(MuiListItemAvatar)({
+ minWidth: 0,
+ marginRight: 12,
+});
+
+export default function SelectContent() {
+ const [company, setCompany] = React.useState('');
+
+ const handleChange = (event: SelectChangeEvent) => {
+ setCompany(event.target.value as string);
+ };
+
+ return (
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/SideMenu.tsx b/src/client/v1/Template/dashboard/components/SideMenu.tsx
new file mode 100644
index 0000000..6370b2d
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/SideMenu.tsx
@@ -0,0 +1,87 @@
+
+import { styled } from '@mui/material/styles';
+import Avatar from '@mui/material/Avatar';
+import MuiDrawer, { drawerClasses } from '@mui/material/Drawer';
+import Box from '@mui/material/Box';
+import Divider from '@mui/material/Divider';
+import Stack from '@mui/material/Stack';
+import Typography from '@mui/material/Typography';
+import SelectContent from './SelectContent';
+import MenuContent from './MenuContent';
+import CardAlert from './CardAlert';
+import OptionsMenu from './OptionsMenu';
+
+const drawerWidth = 240;
+
+const Drawer = styled(MuiDrawer)({
+ width: drawerWidth,
+ flexShrink: 0,
+ boxSizing: 'border-box',
+ mt: 10,
+ [`& .${drawerClasses.paper}`]: {
+ width: drawerWidth,
+ boxSizing: 'border-box',
+ },
+});
+
+export default function SideMenu() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Riley Carter
+
+
+ riley@email.com
+
+
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/components/SideMenuMobile.tsx b/src/client/v1/Template/dashboard/components/SideMenuMobile.tsx
new file mode 100644
index 0000000..7604a7b
--- /dev/null
+++ b/src/client/v1/Template/dashboard/components/SideMenuMobile.tsx
@@ -0,0 +1,72 @@
+
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import Divider from '@mui/material/Divider';
+import Drawer, { drawerClasses } from '@mui/material/Drawer';
+import Stack from '@mui/material/Stack';
+import Typography from '@mui/material/Typography';
+import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded';
+import NotificationsRoundedIcon from '@mui/icons-material/NotificationsRounded';
+import MenuButton from './MenuButton';
+import MenuContent from './MenuContent';
+import CardAlert from './CardAlert';
+
+interface SideMenuMobileProps {
+ open: boolean | undefined;
+ toggleDrawer: (newOpen: boolean) => () => void;
+}
+
+export default function SideMenuMobile({ open, toggleDrawer }: SideMenuMobileProps) {
+ return (
+ theme.zIndex.drawer + 1,
+ [`& .${drawerClasses.paper}`]: {
+ backgroundImage: 'none',
+ backgroundColor: 'background.paper',
+ },
+ }}
+ >
+
+
+
+
+
+ Riley Carter
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }>
+ Logout
+
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/dashboard/theme/customizations/charts.ts b/src/client/v1/Template/dashboard/theme/customizations/charts.ts
new file mode 100644
index 0000000..9c2714c
--- /dev/null
+++ b/src/client/v1/Template/dashboard/theme/customizations/charts.ts
@@ -0,0 +1,76 @@
+import { Theme } from '@mui/material/styles';
+import { axisClasses, legendClasses, chartsGridClasses } from '@mui/x-charts';
+import type { ChartsComponents } from '@mui/x-charts/themeAugmentation';
+import { gray } from '../../../shared-theme/themePrimitives';
+
+/* eslint-disable import/prefer-default-export */
+export const chartsCustomizations: ChartsComponents = {
+ MuiChartsAxis: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ [`& .${axisClasses.line}`]: {
+ stroke: gray[300],
+ },
+ [`& .${axisClasses.tick}`]: { stroke: gray[300] },
+ [`& .${axisClasses.tickLabel}`]: {
+ fill: gray[500],
+ fontWeight: 500,
+ },
+ ...theme.applyStyles('dark', {
+ [`& .${axisClasses.line}`]: {
+ stroke: gray[700],
+ },
+ [`& .${axisClasses.tick}`]: { stroke: gray[700] },
+ [`& .${axisClasses.tickLabel}`]: {
+ fill: gray[300],
+ fontWeight: 500,
+ },
+ }),
+ }),
+ },
+ },
+ MuiChartsTooltip: {
+ styleOverrides: {
+ mark: ({ theme }) => ({
+ ry: 6,
+ boxShadow: 'none',
+ border: `1px solid ${(theme.vars || theme).palette.divider}`,
+ }),
+ table: ({ theme }) => ({
+ border: `1px solid ${(theme.vars || theme).palette.divider}`,
+ borderRadius: theme.shape.borderRadius,
+ background: 'hsl(0, 0%, 100%)',
+ ...theme.applyStyles('dark', {
+ background: gray[900],
+ }),
+ }),
+ },
+ },
+ MuiChartsLegend: {
+ styleOverrides: {
+ root: {
+ [`& .${legendClasses.mark}`]: {
+ ry: 6,
+ },
+ },
+ },
+ },
+ MuiChartsGrid: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ [`& .${chartsGridClasses.line}`]: {
+ stroke: gray[200],
+ strokeDasharray: '4 2',
+ strokeWidth: 0.8,
+ },
+ ...theme.applyStyles('dark', {
+ [`& .${chartsGridClasses.line}`]: {
+ stroke: gray[700],
+ strokeDasharray: '4 2',
+ strokeWidth: 0.8,
+ },
+ }),
+ }),
+ },
+ },
+};
diff --git a/src/client/v1/Template/dashboard/theme/customizations/dataGrid.ts b/src/client/v1/Template/dashboard/theme/customizations/dataGrid.ts
new file mode 100644
index 0000000..822b513
--- /dev/null
+++ b/src/client/v1/Template/dashboard/theme/customizations/dataGrid.ts
@@ -0,0 +1,132 @@
+import { paperClasses } from '@mui/material/Paper';
+import { alpha, Theme } from '@mui/material/styles';
+import type { DataGridProComponents } from '@mui/x-data-grid-pro/themeAugmentation';
+import { menuItemClasses } from '@mui/material/MenuItem';
+import { listItemIconClasses } from '@mui/material/ListItemIcon';
+import { iconButtonClasses } from '@mui/material/IconButton';
+import { checkboxClasses } from '@mui/material/Checkbox';
+import { listClasses } from '@mui/material/List';
+import { gridClasses } from '@mui/x-data-grid';
+import { tablePaginationClasses } from '@mui/material/TablePagination';
+import { gray } from '../../../shared-theme/themePrimitives';
+
+/* eslint-disable import/prefer-default-export */
+export const dataGridCustomizations: DataGridProComponents & DataGridProComponents = {
+ MuiDataGrid: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ '--DataGrid-overlayHeight': '300px',
+ overflow: 'clip',
+ borderColor: (theme.vars || theme).palette.divider,
+ backgroundColor: (theme.vars || theme).palette.background.default,
+ [`& .${gridClasses.columnHeader}`]: {
+ backgroundColor: (theme.vars || theme).palette.background.paper,
+ },
+ [`& .${gridClasses.footerContainer}`]: {
+ backgroundColor: (theme.vars || theme).palette.background.paper,
+ },
+ [`& .${checkboxClasses.root}`]: {
+ padding: theme.spacing(0.5),
+ '& > svg': {
+ fontSize: '1rem',
+ },
+ },
+ [`& .${tablePaginationClasses.root}`]: {
+ marginRight: theme.spacing(1),
+ '& .MuiIconButton-root': {
+ maxHeight: 32,
+ maxWidth: 32,
+ '& > svg': {
+ fontSize: '1rem',
+ },
+ },
+ },
+ }),
+ cell: ({ theme }) => ({ borderTopColor: (theme.vars || theme).palette.divider }),
+ menu: ({ theme }) => ({
+ borderRadius: theme.shape.borderRadius,
+ backgroundImage: 'none',
+ [`& .${paperClasses.root}`]: {
+ border: `1px solid ${(theme.vars || theme).palette.divider}`,
+ },
+
+ [`& .${menuItemClasses.root}`]: {
+ margin: '0 4px',
+ },
+ [`& .${listItemIconClasses.root}`]: {
+ marginRight: 0,
+ },
+ [`& .${listClasses.root}`]: {
+ paddingLeft: 0,
+ paddingRight: 0,
+ },
+ }),
+
+ row: ({ theme }) => ({
+ '&:last-of-type': { borderBottom: `1px solid ${(theme.vars || theme).palette.divider}` },
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ '&.Mui-selected': {
+ background: (theme.vars || theme).palette.action.selected,
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ },
+ }),
+ iconButtonContainer: ({ theme }) => ({
+ [`& .${iconButtonClasses.root}`]: {
+ border: 'none',
+ backgroundColor: 'transparent',
+ '&:hover': {
+ backgroundColor: alpha(theme.palette.action.selected, 0.3),
+ },
+ '&:active': {
+ backgroundColor: gray[200],
+ },
+ ...theme.applyStyles('dark', {
+ color: gray[50],
+ '&:hover': {
+ backgroundColor: gray[800],
+ },
+ '&:active': {
+ backgroundColor: gray[900],
+ },
+ }),
+ },
+ }),
+ menuIconButton: ({ theme }) => ({
+ border: 'none',
+ backgroundColor: 'transparent',
+ '&:hover': {
+ backgroundColor: gray[100],
+ },
+ '&:active': {
+ backgroundColor: gray[200],
+ },
+ ...theme.applyStyles('dark', {
+ color: gray[50],
+ '&:hover': {
+ backgroundColor: gray[800],
+ },
+ '&:active': {
+ backgroundColor: gray[900],
+ },
+ }),
+ }),
+ filterForm: ({ theme }) => ({
+ gap: theme.spacing(1),
+ alignItems: 'flex-end',
+ }),
+ columnsManagementHeader: ({ theme }) => ({
+ paddingRight: theme.spacing(3),
+ paddingLeft: theme.spacing(3),
+ }),
+ columnHeaderTitleContainer: {
+ flexGrow: 1,
+ justifyContent: 'space-between',
+ },
+ columnHeaderDraggableContainer: { paddingRight: 2 },
+ },
+ },
+};
diff --git a/src/client/v1/Template/dashboard/theme/customizations/datePickers.ts b/src/client/v1/Template/dashboard/theme/customizations/datePickers.ts
new file mode 100644
index 0000000..ca6f8d3
--- /dev/null
+++ b/src/client/v1/Template/dashboard/theme/customizations/datePickers.ts
@@ -0,0 +1,173 @@
+import { alpha, Theme } from '@mui/material/styles';
+import type { PickersProComponents } from '@mui/x-date-pickers-pro/themeAugmentation';
+import type { PickerComponents } from '@mui/x-date-pickers/themeAugmentation';
+import { menuItemClasses } from '@mui/material/MenuItem';
+import { pickersDayClasses, yearCalendarClasses } from '@mui/x-date-pickers';
+import { gray, brand } from '../../../shared-theme/themePrimitives';
+
+/* eslint-disable import/prefer-default-export */
+export const datePickersCustomizations: PickersProComponents & PickerComponents = {
+ MuiPickerPopper: {
+ styleOverrides: {
+ paper: ({ theme }) => ({
+ marginTop: 4,
+ borderRadius: theme.shape.borderRadius,
+ border: `1px solid ${(theme.vars || theme).palette.divider}`,
+ backgroundImage: 'none',
+ background: 'hsl(0, 0%, 100%)',
+ boxShadow:
+ 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
+ [`& .${menuItemClasses.root}`]: {
+ borderRadius: 6,
+ margin: '0 6px',
+ },
+ ...theme.applyStyles('dark', {
+ background: gray[900],
+ boxShadow:
+ 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
+ }),
+ }),
+ },
+ },
+ MuiPickersArrowSwitcher: {
+ styleOverrides: {
+ spacer: { width: 16 },
+ button: ({ theme }) => ({
+ backgroundColor: 'transparent',
+ color: (theme.vars || theme).palette.grey[500],
+ ...theme.applyStyles('dark', {
+ color: (theme.vars || theme).palette.grey[400],
+ }),
+ }),
+ },
+ },
+ MuiPickersCalendarHeader: {
+ styleOverrides: {
+ switchViewButton: {
+ padding: 0,
+ border: 'none',
+ },
+ },
+ },
+ MuiMonthCalendar: {
+ styleOverrides: {
+ button: ({ theme }) => ({
+ fontSize: theme.typography.body1.fontSize,
+ color: (theme.vars || theme).palette.grey[600],
+ padding: theme.spacing(0.5),
+ borderRadius: theme.shape.borderRadius,
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ [`&.${yearCalendarClasses.selected}`]: {
+ backgroundColor: gray[700],
+ fontWeight: theme.typography.fontWeightMedium,
+ },
+ '&:focus': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ backgroundColor: 'transparent',
+ [`&.${yearCalendarClasses.selected}`]: { backgroundColor: gray[700] },
+ },
+ ...theme.applyStyles('dark', {
+ color: (theme.vars || theme).palette.grey[300],
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ [`&.${yearCalendarClasses.selected}`]: {
+ color: (theme.vars || theme).palette.common.black,
+ fontWeight: theme.typography.fontWeightMedium,
+ backgroundColor: gray[300],
+ },
+ '&:focus': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ backgroundColor: 'transparent',
+ [`&.${yearCalendarClasses.selected}`]: { backgroundColor: gray[300] },
+ },
+ }),
+ }),
+ },
+ },
+ MuiYearCalendar: {
+ styleOverrides: {
+ button: ({ theme }) => ({
+ fontSize: theme.typography.body1.fontSize,
+ color: (theme.vars || theme).palette.grey[600],
+ padding: theme.spacing(0.5),
+ borderRadius: theme.shape.borderRadius,
+ height: 'fit-content',
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ [`&.${yearCalendarClasses.selected}`]: {
+ backgroundColor: gray[700],
+ fontWeight: theme.typography.fontWeightMedium,
+ },
+ '&:focus': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ backgroundColor: 'transparent',
+ [`&.${yearCalendarClasses.selected}`]: { backgroundColor: gray[700] },
+ },
+ ...theme.applyStyles('dark', {
+ color: (theme.vars || theme).palette.grey[300],
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ [`&.${yearCalendarClasses.selected}`]: {
+ color: (theme.vars || theme).palette.common.black,
+ fontWeight: theme.typography.fontWeightMedium,
+ backgroundColor: gray[300],
+ },
+ '&:focus': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ backgroundColor: 'transparent',
+ [`&.${yearCalendarClasses.selected}`]: { backgroundColor: gray[300] },
+ },
+ }),
+ }),
+ },
+ },
+ MuiPickersDay: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ fontSize: theme.typography.body1.fontSize,
+ color: (theme.vars || theme).palette.grey[600],
+ padding: theme.spacing(0.5),
+ borderRadius: theme.shape.borderRadius,
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ [`&.${pickersDayClasses.selected}`]: {
+ backgroundColor: gray[700],
+ fontWeight: theme.typography.fontWeightMedium,
+ },
+ '&:focus': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ backgroundColor: 'transparent',
+ [`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[700] },
+ },
+ ...theme.applyStyles('dark', {
+ color: (theme.vars || theme).palette.grey[300],
+ '&:hover': {
+ backgroundColor: (theme.vars || theme).palette.action.hover,
+ },
+ [`&.${pickersDayClasses.selected}`]: {
+ color: (theme.vars || theme).palette.common.black,
+ fontWeight: theme.typography.fontWeightMedium,
+ backgroundColor: gray[300],
+ },
+ '&:focus': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ backgroundColor: 'transparent',
+ [`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[300] },
+ },
+ }),
+ }),
+ },
+ },
+};
diff --git a/src/client/v1/Template/dashboard/theme/customizations/index.ts b/src/client/v1/Template/dashboard/theme/customizations/index.ts
new file mode 100644
index 0000000..ef97812
--- /dev/null
+++ b/src/client/v1/Template/dashboard/theme/customizations/index.ts
@@ -0,0 +1,4 @@
+export { chartsCustomizations } from './charts';
+export { dataGridCustomizations } from './dataGrid';
+export { datePickersCustomizations } from './datePickers';
+export { treeViewCustomizations } from './treeView';
diff --git a/src/client/v1/Template/dashboard/theme/customizations/treeView.ts b/src/client/v1/Template/dashboard/theme/customizations/treeView.ts
new file mode 100644
index 0000000..e16c574
--- /dev/null
+++ b/src/client/v1/Template/dashboard/theme/customizations/treeView.ts
@@ -0,0 +1,62 @@
+import { alpha, Theme } from '@mui/material/styles';
+import type { TreeViewComponents } from '@mui/x-tree-view/themeAugmentation';
+import { gray, brand } from '../../../shared-theme/themePrimitives';
+
+/* eslint-disable import/prefer-default-export */
+export const treeViewCustomizations: TreeViewComponents = {
+ MuiTreeItem: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ position: 'relative',
+ boxSizing: 'border-box',
+ padding: theme.spacing(0, 1),
+ '& .groupTransition': {
+ marginLeft: theme.spacing(2),
+ padding: theme.spacing(0),
+ borderLeft: '1px solid',
+ borderColor: (theme.vars || theme).palette.divider,
+ },
+ '&:focus-visible .focused': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ '&:hover': {
+ backgroundColor: alpha(gray[300], 0.2),
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ },
+ },
+ }),
+ content: ({ theme }) => ({
+ marginTop: theme.spacing(1),
+ padding: theme.spacing(0.5, 1),
+ overflow: 'clip',
+ '&:hover': {
+ backgroundColor: alpha(gray[300], 0.2),
+ },
+
+ '&.selected': {
+ backgroundColor: alpha(gray[300], 0.4),
+ '&:hover': {
+ backgroundColor: alpha(gray[300], 0.6),
+ },
+ },
+ ...theme.applyStyles('dark', {
+ '&:hover': {
+ backgroundColor: alpha(gray[500], 0.2),
+ },
+ '&:focus-visible': {
+ '&:hover': {
+ backgroundColor: alpha(gray[500], 0.2),
+ },
+ },
+ '&.selected': {
+ backgroundColor: alpha(gray[500], 0.4),
+ '&:hover': {
+ backgroundColor: alpha(gray[500], 0.6),
+ },
+ },
+ }),
+ }),
+ },
+ },
+};
diff --git a/src/client/v1/Template/shared-theme/AppTheme.tsx b/src/client/v1/Template/shared-theme/AppTheme.tsx
new file mode 100644
index 0000000..91ebd27
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/AppTheme.tsx
@@ -0,0 +1,53 @@
+import * as React from "react";
+import { ThemeProvider, createTheme } from '@mui/material/styles';
+import type { ThemeOptions } from '@mui/material/styles';
+import { inputsCustomizations } from './customizations/inputs';
+import { dataDisplayCustomizations } from './customizations/dataDisplay';
+import { feedbackCustomizations } from './customizations/feedback';
+import { navigationCustomizations } from './customizations/navigation';
+import { surfacesCustomizations } from './customizations/surfaces';
+import { colorSchemes, typography, shadows, shape } from './themePrimitives';
+
+interface AppThemeProps {
+ children: React.ReactNode;
+ /**
+ * This is for the docs site. You can ignore it or remove it.
+ */
+ disableCustomTheme?: boolean;
+ themeComponents?: ThemeOptions['components'];
+}
+
+export default function AppTheme(props: AppThemeProps) {
+ const { children, disableCustomTheme, themeComponents } = props;
+ const theme = React.useMemo(() => {
+ return disableCustomTheme
+ ? {}
+ : createTheme({
+ // For more details about CSS variables configuration, see https://mui.com/material-ui/customization/css-theme-variables/configuration/
+ cssVariables: {
+ colorSchemeSelector: 'data-mui-color-scheme',
+ cssVarPrefix: 'template',
+ },
+ colorSchemes, // Recently added in v6 for building light & dark mode app, see https://mui.com/material-ui/customization/palette/#color-schemes
+ typography,
+ shadows,
+ shape,
+ components: {
+ ...inputsCustomizations,
+ ...dataDisplayCustomizations,
+ ...feedbackCustomizations,
+ ...navigationCustomizations,
+ ...surfacesCustomizations,
+ ...themeComponents,
+ },
+ });
+ }, [disableCustomTheme, themeComponents]);
+ if (disableCustomTheme) {
+ return {children};
+ }
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/client/v1/Template/shared-theme/ColorModeIconDropdown.tsx b/src/client/v1/Template/shared-theme/ColorModeIconDropdown.tsx
new file mode 100644
index 0000000..89f16f7
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/ColorModeIconDropdown.tsx
@@ -0,0 +1,89 @@
+import 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);
+ const open = Boolean(anchorEl);
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget);
+ };
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+ const handleMode = (targetMode: 'system' | 'light' | 'dark') => () => {
+ setMode(targetMode);
+ handleClose();
+ };
+ if (!mode) {
+ return (
+ ({
+ 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: ,
+ dark: ,
+ }[resolvedMode];
+ return (
+
+
+ {icon}
+
+
+
+ );
+}
diff --git a/src/client/v1/Template/shared-theme/ColorModeSelect.tsx b/src/client/v1/Template/shared-theme/ColorModeSelect.tsx
new file mode 100644
index 0000000..acf0f7d
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/ColorModeSelect.tsx
@@ -0,0 +1,28 @@
+
+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 (
+
+ );
+}
diff --git a/src/client/v1/Template/shared-theme/customizations/dataDisplay.tsx b/src/client/v1/Template/shared-theme/customizations/dataDisplay.tsx
new file mode 100644
index 0000000..b6b2b46
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/customizations/dataDisplay.tsx
@@ -0,0 +1,233 @@
+import { Theme, alpha, Components } from '@mui/material/styles';
+import { svgIconClasses } from '@mui/material/SvgIcon';
+import { typographyClasses } from '@mui/material/Typography';
+import { buttonBaseClasses } from '@mui/material/ButtonBase';
+import { chipClasses } from '@mui/material/Chip';
+import { iconButtonClasses } from '@mui/material/IconButton';
+import { gray, red, green } from '../themePrimitives';
+
+/* eslint-disable import/prefer-default-export */
+export const dataDisplayCustomizations: Components = {
+ MuiList: {
+ styleOverrides: {
+ root: {
+ padding: '8px',
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 0,
+ },
+ },
+ },
+ MuiListItem: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ [`& .${svgIconClasses.root}`]: {
+ width: '1rem',
+ height: '1rem',
+ color: (theme.vars || theme).palette.text.secondary,
+ },
+ [`& .${typographyClasses.root}`]: {
+ fontWeight: 500,
+ },
+ [`& .${buttonBaseClasses.root}`]: {
+ display: 'flex',
+ gap: 8,
+ padding: '2px 8px',
+ borderRadius: (theme.vars || theme).shape.borderRadius,
+ opacity: 0.7,
+ '&.Mui-selected': {
+ opacity: 1,
+ backgroundColor: alpha(theme.palette.action.selected, 0.3),
+ [`& .${svgIconClasses.root}`]: {
+ color: (theme.vars || theme).palette.text.primary,
+ },
+ '&:focus-visible': {
+ backgroundColor: alpha(theme.palette.action.selected, 0.3),
+ },
+ '&:hover': {
+ backgroundColor: alpha(theme.palette.action.selected, 0.5),
+ },
+ },
+ '&:focus-visible': {
+ backgroundColor: 'transparent',
+ },
+ },
+ }),
+ },
+ },
+ MuiListItemText: {
+ styleOverrides: {
+ primary: ({ theme }) => ({
+ fontSize: theme.typography.body2.fontSize,
+ fontWeight: 500,
+ lineHeight: theme.typography.body2.lineHeight,
+ }),
+ secondary: ({ theme }) => ({
+ fontSize: theme.typography.caption.fontSize,
+ lineHeight: theme.typography.caption.lineHeight,
+ }),
+ },
+ },
+ MuiListSubheader: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ backgroundColor: 'transparent',
+ padding: '4px 8px',
+ fontSize: theme.typography.caption.fontSize,
+ fontWeight: 500,
+ lineHeight: theme.typography.caption.lineHeight,
+ }),
+ },
+ },
+ MuiListItemIcon: {
+ styleOverrides: {
+ root: {
+ minWidth: 0,
+ },
+ },
+ },
+ MuiChip: {
+ defaultProps: {
+ size: 'small',
+ },
+ styleOverrides: {
+ root: ({ theme }) => ({
+ border: '1px solid',
+ borderRadius: '999px',
+ [`& .${chipClasses.label}`]: {
+ fontWeight: 600,
+ },
+ variants: [
+ {
+ props: {
+ color: 'default',
+ },
+ style: {
+ borderColor: gray[200],
+ backgroundColor: gray[100],
+ [`& .${chipClasses.label}`]: {
+ color: gray[500],
+ },
+ [`& .${chipClasses.icon}`]: {
+ color: gray[500],
+ },
+ ...theme.applyStyles('dark', {
+ borderColor: gray[700],
+ backgroundColor: gray[800],
+ [`& .${chipClasses.label}`]: {
+ color: gray[300],
+ },
+ [`& .${chipClasses.icon}`]: {
+ color: gray[300],
+ },
+ }),
+ },
+ },
+ {
+ props: {
+ color: 'success',
+ },
+ style: {
+ borderColor: green[200],
+ backgroundColor: green[50],
+ [`& .${chipClasses.label}`]: {
+ color: green[500],
+ },
+ [`& .${chipClasses.icon}`]: {
+ color: green[500],
+ },
+ ...theme.applyStyles('dark', {
+ borderColor: green[800],
+ backgroundColor: green[900],
+ [`& .${chipClasses.label}`]: {
+ color: green[300],
+ },
+ [`& .${chipClasses.icon}`]: {
+ color: green[300],
+ },
+ }),
+ },
+ },
+ {
+ props: {
+ color: 'error',
+ },
+ style: {
+ borderColor: red[100],
+ backgroundColor: red[50],
+ [`& .${chipClasses.label}`]: {
+ color: red[500],
+ },
+ [`& .${chipClasses.icon}`]: {
+ color: red[500],
+ },
+ ...theme.applyStyles('dark', {
+ borderColor: red[800],
+ backgroundColor: red[900],
+ [`& .${chipClasses.label}`]: {
+ color: red[200],
+ },
+ [`& .${chipClasses.icon}`]: {
+ color: red[300],
+ },
+ }),
+ },
+ },
+ {
+ props: { size: 'small' },
+ style: {
+ maxHeight: 20,
+ [`& .${chipClasses.label}`]: {
+ fontSize: theme.typography.caption.fontSize,
+ },
+ [`& .${svgIconClasses.root}`]: {
+ fontSize: theme.typography.caption.fontSize,
+ },
+ },
+ },
+ {
+ props: { size: 'medium' },
+ style: {
+ [`& .${chipClasses.label}`]: {
+ fontSize: theme.typography.caption.fontSize,
+ },
+ },
+ },
+ ],
+ }),
+ },
+ },
+ MuiTablePagination: {
+ styleOverrides: {
+ actions: {
+ display: 'flex',
+ gap: 8,
+ marginRight: 6,
+ [`& .${iconButtonClasses.root}`]: {
+ minWidth: 0,
+ width: 36,
+ height: 36,
+ },
+ },
+ },
+ },
+ MuiIcon: {
+ defaultProps: {
+ fontSize: 'small',
+ },
+ styleOverrides: {
+ root: {
+ variants: [
+ {
+ props: {
+ fontSize: 'small',
+ },
+ style: {
+ fontSize: '1rem',
+ },
+ },
+ ],
+ },
+ },
+ },
+};
diff --git a/src/client/v1/Template/shared-theme/customizations/feedback.tsx b/src/client/v1/Template/shared-theme/customizations/feedback.tsx
new file mode 100644
index 0000000..6d475c9
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/customizations/feedback.tsx
@@ -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 = {
+ 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],
+ }),
+ }),
+ },
+ },
+};
diff --git a/src/client/v1/Template/shared-theme/customizations/inputs.tsx b/src/client/v1/Template/shared-theme/customizations/inputs.tsx
new file mode 100644
index 0000000..3b1cff7
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/customizations/inputs.tsx
@@ -0,0 +1,445 @@
+
+import { alpha, Theme, Components } from '@mui/material/styles';
+import { outlinedInputClasses } from '@mui/material/OutlinedInput';
+import { svgIconClasses } from '@mui/material/SvgIcon';
+import { toggleButtonGroupClasses } from '@mui/material/ToggleButtonGroup';
+import { toggleButtonClasses } from '@mui/material/ToggleButton';
+import CheckBoxOutlineBlankRoundedIcon from '@mui/icons-material/CheckBoxOutlineBlankRounded';
+import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
+import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded';
+import { gray, brand } from '../themePrimitives';
+
+/* eslint-disable import/prefer-default-export */
+export const inputsCustomizations: Components = {
+ 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: (
+
+ ),
+ checkedIcon: ,
+ indeterminateIcon: ,
+ },
+ styleOverrides: {
+ root: ({ theme }) => ({
+ margin: 10,
+ height: 16,
+ width: 16,
+ borderRadius: 5,
+ border: '1px solid ',
+ borderColor: alpha(gray[300], 0.8),
+ boxShadow: '0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset',
+ backgroundColor: alpha(gray[100], 0.4),
+ transition: 'border-color, background-color, 120ms ease-in',
+ '&:hover': {
+ borderColor: brand[300],
+ },
+ '&.Mui-focusVisible': {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ borderColor: brand[400],
+ },
+ '&.Mui-checked': {
+ color: 'white',
+ backgroundColor: brand[500],
+ borderColor: brand[500],
+ boxShadow: `none`,
+ '&:hover': {
+ backgroundColor: brand[600],
+ },
+ },
+ ...theme.applyStyles('dark', {
+ borderColor: alpha(gray[700], 0.8),
+ boxShadow: '0 0 0 1.5px hsl(210, 0%, 0%) inset',
+ backgroundColor: alpha(gray[900], 0.8),
+ '&:hover': {
+ borderColor: brand[300],
+ },
+ '&.Mui-focusVisible': {
+ borderColor: brand[400],
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ outlineOffset: '2px',
+ },
+ }),
+ }),
+ },
+ },
+ MuiInputBase: {
+ styleOverrides: {
+ root: {
+ border: 'none',
+ },
+ input: {
+ '&::placeholder': {
+ opacity: 0.7,
+ color: gray[500],
+ },
+ },
+ },
+ },
+ MuiOutlinedInput: {
+ styleOverrides: {
+ input: {
+ padding: 0,
+ },
+ root: ({ theme }) => ({
+ padding: '8px 12px',
+ color: (theme.vars || theme).palette.text.primary,
+ borderRadius: (theme.vars || theme).shape.borderRadius,
+ border: `1px solid ${(theme.vars || theme).palette.divider}`,
+ backgroundColor: (theme.vars || theme).palette.background.default,
+ transition: 'border 120ms ease-in',
+ '&:hover': {
+ borderColor: gray[400],
+ },
+ [`&.${outlinedInputClasses.focused}`]: {
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
+ borderColor: brand[400],
+ },
+ ...theme.applyStyles('dark', {
+ '&:hover': {
+ borderColor: gray[500],
+ },
+ }),
+ variants: [
+ {
+ props: {
+ size: 'small',
+ },
+ style: {
+ height: '2.25rem',
+ },
+ },
+ {
+ props: {
+ size: 'medium',
+ },
+ style: {
+ height: '2.5rem',
+ },
+ },
+ ],
+ }),
+ notchedOutline: {
+ border: 'none',
+ },
+ },
+ },
+ MuiInputAdornment: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ color: (theme.vars || theme).palette.grey[500],
+ ...theme.applyStyles('dark', {
+ color: (theme.vars || theme).palette.grey[400],
+ }),
+ }),
+ },
+ },
+ MuiFormLabel: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ typography: theme.typography.caption,
+ marginBottom: 8,
+ }),
+ },
+ },
+};
diff --git a/src/client/v1/Template/shared-theme/customizations/navigation.tsx b/src/client/v1/Template/shared-theme/customizations/navigation.tsx
new file mode 100644
index 0000000..f1666f3
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/customizations/navigation.tsx
@@ -0,0 +1,280 @@
+
+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';
+import React from 'react';
+
+/* eslint-disable import/prefer-default-export */
+export const navigationCustomizations: Components = {
+ 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((props, 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 }),
+ },
+ }),
+ },
+ },
+};
diff --git a/src/client/v1/Template/shared-theme/customizations/surfaces.ts b/src/client/v1/Template/shared-theme/customizations/surfaces.ts
new file mode 100644
index 0000000..f47a6d8
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/customizations/surfaces.ts
@@ -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 = {
+ 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,
+ },
+ },
+ },
+};
diff --git a/src/client/v1/Template/shared-theme/themePrimitives.ts b/src/client/v1/Template/shared-theme/themePrimitives.ts
new file mode 100644
index 0000000..97b2c3d
--- /dev/null
+++ b/src/client/v1/Template/shared-theme/themePrimitives.ts
@@ -0,0 +1,403 @@
+import { createTheme, alpha, PaletteMode, Shadows } from '@mui/material/styles';
+
+declare module '@mui/material/Paper' {
+ interface PaperPropsVariantOverrides {
+ highlighted: true;
+ }
+}
+declare module '@mui/material/styles' {
+ interface ColorRange {
+ 50: string;
+ 100: string;
+ 200: string;
+ 300: string;
+ 400: string;
+ 500: string;
+ 600: string;
+ 700: string;
+ 800: string;
+ 900: string;
+ }
+
+ interface PaletteColor extends ColorRange {}
+
+ interface Palette {
+ baseShadow: string;
+ }
+}
+
+const defaultTheme = createTheme();
+
+const customShadows: Shadows = [...defaultTheme.shadows];
+
+export const brand = {
+ 50: 'hsl(210, 100%, 95%)',
+ 100: 'hsl(210, 100%, 92%)',
+ 200: 'hsl(210, 100%, 80%)',
+ 300: 'hsl(210, 100%, 65%)',
+ 400: 'hsl(210, 98%, 48%)',
+ 500: 'hsl(210, 98%, 42%)',
+ 600: 'hsl(210, 98%, 55%)',
+ 700: 'hsl(210, 100%, 35%)',
+ 800: 'hsl(210, 100%, 16%)',
+ 900: 'hsl(210, 100%, 21%)',
+};
+
+export const gray = {
+ 50: 'hsl(220, 35%, 97%)',
+ 100: 'hsl(220, 30%, 94%)',
+ 200: 'hsl(220, 20%, 88%)',
+ 300: 'hsl(220, 20%, 80%)',
+ 400: 'hsl(220, 20%, 65%)',
+ 500: 'hsl(220, 20%, 42%)',
+ 600: 'hsl(220, 20%, 35%)',
+ 700: 'hsl(220, 20%, 25%)',
+ 800: 'hsl(220, 30%, 6%)',
+ 900: 'hsl(220, 35%, 3%)',
+};
+
+export const green = {
+ 50: 'hsl(120, 80%, 98%)',
+ 100: 'hsl(120, 75%, 94%)',
+ 200: 'hsl(120, 75%, 87%)',
+ 300: 'hsl(120, 61%, 77%)',
+ 400: 'hsl(120, 44%, 53%)',
+ 500: 'hsl(120, 59%, 30%)',
+ 600: 'hsl(120, 70%, 25%)',
+ 700: 'hsl(120, 75%, 16%)',
+ 800: 'hsl(120, 84%, 10%)',
+ 900: 'hsl(120, 87%, 6%)',
+};
+
+export const orange = {
+ 50: 'hsl(45, 100%, 97%)',
+ 100: 'hsl(45, 92%, 90%)',
+ 200: 'hsl(45, 94%, 80%)',
+ 300: 'hsl(45, 90%, 65%)',
+ 400: 'hsl(45, 90%, 40%)',
+ 500: 'hsl(45, 90%, 35%)',
+ 600: 'hsl(45, 91%, 25%)',
+ 700: 'hsl(45, 94%, 20%)',
+ 800: 'hsl(45, 95%, 16%)',
+ 900: 'hsl(45, 93%, 12%)',
+};
+
+export const red = {
+ 50: 'hsl(0, 100%, 97%)',
+ 100: 'hsl(0, 92%, 90%)',
+ 200: 'hsl(0, 94%, 80%)',
+ 300: 'hsl(0, 90%, 65%)',
+ 400: 'hsl(0, 90%, 40%)',
+ 500: 'hsl(0, 90%, 30%)',
+ 600: 'hsl(0, 91%, 25%)',
+ 700: 'hsl(0, 94%, 18%)',
+ 800: 'hsl(0, 95%, 12%)',
+ 900: 'hsl(0, 93%, 6%)',
+};
+
+export const getDesignTokens = (mode: PaletteMode) => {
+ customShadows[1] =
+ mode === 'dark'
+ ? 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px'
+ : 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px';
+
+ return {
+ palette: {
+ mode,
+ primary: {
+ light: brand[200],
+ main: brand[400],
+ dark: brand[700],
+ contrastText: brand[50],
+ ...(mode === 'dark' && {
+ contrastText: brand[50],
+ light: brand[300],
+ main: brand[400],
+ dark: brand[700],
+ }),
+ },
+ info: {
+ light: brand[100],
+ main: brand[300],
+ dark: brand[600],
+ contrastText: gray[50],
+ ...(mode === 'dark' && {
+ contrastText: brand[300],
+ light: brand[500],
+ main: brand[700],
+ dark: brand[900],
+ }),
+ },
+ warning: {
+ light: orange[300],
+ main: orange[400],
+ dark: orange[800],
+ ...(mode === 'dark' && {
+ light: orange[400],
+ main: orange[500],
+ dark: orange[700],
+ }),
+ },
+ error: {
+ light: red[300],
+ main: red[400],
+ dark: red[800],
+ ...(mode === 'dark' && {
+ light: red[400],
+ main: red[500],
+ dark: red[700],
+ }),
+ },
+ success: {
+ light: green[300],
+ main: green[400],
+ dark: green[800],
+ ...(mode === 'dark' && {
+ light: green[400],
+ main: green[500],
+ dark: green[700],
+ }),
+ },
+ grey: {
+ ...gray,
+ },
+ divider: mode === 'dark' ? alpha(gray[700], 0.6) : alpha(gray[300], 0.4),
+ background: {
+ default: 'hsl(0, 0%, 99%)',
+ paper: 'hsl(220, 35%, 97%)',
+ ...(mode === 'dark' && { default: gray[900], paper: 'hsl(220, 30%, 7%)' }),
+ },
+ text: {
+ primary: gray[800],
+ secondary: gray[600],
+ warning: orange[400],
+ ...(mode === 'dark' && { primary: 'hsl(0, 0%, 100%)', secondary: gray[400] }),
+ },
+ action: {
+ hover: alpha(gray[200], 0.2),
+ selected: `${alpha(gray[200], 0.3)}`,
+ ...(mode === 'dark' && {
+ hover: alpha(gray[600], 0.2),
+ selected: alpha(gray[600], 0.3),
+ }),
+ },
+ },
+ typography: {
+ fontFamily: 'Inter, sans-serif',
+ h1: {
+ fontSize: defaultTheme.typography.pxToRem(48),
+ fontWeight: 600,
+ lineHeight: 1.2,
+ letterSpacing: -0.5,
+ },
+ h2: {
+ fontSize: defaultTheme.typography.pxToRem(36),
+ fontWeight: 600,
+ lineHeight: 1.2,
+ },
+ h3: {
+ fontSize: defaultTheme.typography.pxToRem(30),
+ lineHeight: 1.2,
+ },
+ h4: {
+ fontSize: defaultTheme.typography.pxToRem(24),
+ fontWeight: 600,
+ lineHeight: 1.5,
+ },
+ h5: {
+ fontSize: defaultTheme.typography.pxToRem(20),
+ fontWeight: 600,
+ },
+ h6: {
+ fontSize: defaultTheme.typography.pxToRem(18),
+ fontWeight: 600,
+ },
+ subtitle1: {
+ fontSize: defaultTheme.typography.pxToRem(18),
+ },
+ subtitle2: {
+ fontSize: defaultTheme.typography.pxToRem(14),
+ fontWeight: 500,
+ },
+ body1: {
+ fontSize: defaultTheme.typography.pxToRem(14),
+ },
+ body2: {
+ fontSize: defaultTheme.typography.pxToRem(14),
+ fontWeight: 400,
+ },
+ caption: {
+ fontSize: defaultTheme.typography.pxToRem(12),
+ fontWeight: 400,
+ },
+ },
+ shape: {
+ borderRadius: 8,
+ },
+ shadows: customShadows,
+ };
+};
+
+export const colorSchemes = {
+ light: {
+ palette: {
+ primary: {
+ light: brand[200],
+ main: brand[400],
+ dark: brand[700],
+ contrastText: brand[50],
+ },
+ info: {
+ light: brand[100],
+ main: brand[300],
+ dark: brand[600],
+ contrastText: gray[50],
+ },
+ warning: {
+ light: orange[300],
+ main: orange[400],
+ dark: orange[800],
+ },
+ error: {
+ light: red[300],
+ main: red[400],
+ dark: red[800],
+ },
+ success: {
+ light: green[300],
+ main: green[400],
+ dark: green[800],
+ },
+ grey: {
+ ...gray,
+ },
+ divider: alpha(gray[300], 0.4),
+ background: {
+ default: 'hsl(0, 0%, 99%)',
+ paper: 'hsl(220, 35%, 97%)',
+ },
+ text: {
+ primary: gray[800],
+ secondary: gray[600],
+ warning: orange[400],
+ },
+ action: {
+ hover: alpha(gray[200], 0.2),
+ selected: `${alpha(gray[200], 0.3)}`,
+ },
+ baseShadow:
+ 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
+ },
+ },
+ dark: {
+ palette: {
+ primary: {
+ contrastText: brand[50],
+ light: brand[300],
+ main: brand[400],
+ dark: brand[700],
+ },
+ info: {
+ contrastText: brand[300],
+ light: brand[500],
+ main: brand[700],
+ dark: brand[900],
+ },
+ warning: {
+ light: orange[400],
+ main: orange[500],
+ dark: orange[700],
+ },
+ error: {
+ light: red[400],
+ main: red[500],
+ dark: red[700],
+ },
+ success: {
+ light: green[400],
+ main: green[500],
+ dark: green[700],
+ },
+ grey: {
+ ...gray,
+ },
+ divider: alpha(gray[700], 0.6),
+ background: {
+ default: gray[900],
+ paper: 'hsl(220, 30%, 7%)',
+ },
+ text: {
+ primary: 'hsl(0, 0%, 100%)',
+ secondary: gray[400],
+ },
+ action: {
+ hover: alpha(gray[600], 0.2),
+ selected: alpha(gray[600], 0.3),
+ },
+ baseShadow:
+ 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
+ },
+ },
+};
+
+export const typography = {
+ fontFamily: 'Inter, sans-serif',
+ h1: {
+ fontSize: defaultTheme.typography.pxToRem(48),
+ fontWeight: 600,
+ lineHeight: 1.2,
+ letterSpacing: -0.5,
+ },
+ h2: {
+ fontSize: defaultTheme.typography.pxToRem(36),
+ fontWeight: 600,
+ lineHeight: 1.2,
+ },
+ h3: {
+ fontSize: defaultTheme.typography.pxToRem(30),
+ lineHeight: 1.2,
+ },
+ h4: {
+ fontSize: defaultTheme.typography.pxToRem(24),
+ fontWeight: 600,
+ lineHeight: 1.5,
+ },
+ h5: {
+ fontSize: defaultTheme.typography.pxToRem(20),
+ fontWeight: 600,
+ },
+ h6: {
+ fontSize: defaultTheme.typography.pxToRem(18),
+ fontWeight: 600,
+ },
+ subtitle1: {
+ fontSize: defaultTheme.typography.pxToRem(18),
+ },
+ subtitle2: {
+ fontSize: defaultTheme.typography.pxToRem(14),
+ fontWeight: 500,
+ },
+ body1: {
+ fontSize: defaultTheme.typography.pxToRem(14),
+ },
+ body2: {
+ fontSize: defaultTheme.typography.pxToRem(14),
+ fontWeight: 400,
+ },
+ caption: {
+ fontSize: defaultTheme.typography.pxToRem(12),
+ fontWeight: 400,
+ },
+};
+
+export const shape = {
+ borderRadius: 8,
+};
+
+// @ts-ignore
+const defaultShadows: Shadows = [
+ 'none',
+ 'var(--template-palette-baseShadow)',
+ ...defaultTheme.shadows.slice(2),
+];
+export const shadows = defaultShadows;