Compare commits
	
		
			1 Commits
		
	
	
		
			d3848ac1aa
			...
			feat/web-c
		
	
	| Author | SHA256 | Date | |
|---|---|---|---|
| 8f7d2fd1e3 | 
							
								
								
									
										26
									
								
								webClient/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								webClient/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | # Logs | ||||||
|  | logs | ||||||
|  | *.log | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  | pnpm-debug.log* | ||||||
|  | lerna-debug.log* | ||||||
|  |  | ||||||
|  | node_modules | ||||||
|  | dist | ||||||
|  | dist-ssr | ||||||
|  | *.local | ||||||
|  |  | ||||||
|  | # Editor directories and files | ||||||
|  | .vscode/* | ||||||
|  | !.vscode/extensions.json | ||||||
|  | .idea | ||||||
|  | .DS_Store | ||||||
|  | *.suo | ||||||
|  | *.ntvs* | ||||||
|  | *.njsproj | ||||||
|  | *.sln | ||||||
|  | *.sw? | ||||||
|  |  | ||||||
|  | .env | ||||||
							
								
								
									
										50
									
								
								webClient/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								webClient/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | # React + TypeScript + Vite | ||||||
|  |  | ||||||
|  | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. | ||||||
|  |  | ||||||
|  | Currently, two official plugins are available: | ||||||
|  |  | ||||||
|  | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh | ||||||
|  | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh | ||||||
|  |  | ||||||
|  | ## Expanding the ESLint configuration | ||||||
|  |  | ||||||
|  | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: | ||||||
|  |  | ||||||
|  | - Configure the top-level `parserOptions` property like this: | ||||||
|  |  | ||||||
|  | ```js | ||||||
|  | export default tseslint.config({ | ||||||
|  |   languageOptions: { | ||||||
|  |     // other options... | ||||||
|  |     parserOptions: { | ||||||
|  |       project: ['./tsconfig.node.json', './tsconfig.app.json'], | ||||||
|  |       tsconfigRootDir: import.meta.dirname, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` | ||||||
|  | - Optionally add `...tseslint.configs.stylisticTypeChecked` | ||||||
|  | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: | ||||||
|  |  | ||||||
|  | ```js | ||||||
|  | // eslint.config.js | ||||||
|  | import react from 'eslint-plugin-react' | ||||||
|  |  | ||||||
|  | export default tseslint.config({ | ||||||
|  |   // Set the react version | ||||||
|  |   settings: { react: { version: '18.3' } }, | ||||||
|  |   plugins: { | ||||||
|  |     // Add the react plugin | ||||||
|  |     react, | ||||||
|  |   }, | ||||||
|  |   rules: { | ||||||
|  |     // other rules... | ||||||
|  |     // Enable its recommended rules | ||||||
|  |     ...react.configs.recommended.rules, | ||||||
|  |     ...react.configs['jsx-runtime'].rules, | ||||||
|  |   }, | ||||||
|  | }) | ||||||
|  | ``` | ||||||
							
								
								
									
										28
									
								
								webClient/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								webClient/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | import js from '@eslint/js' | ||||||
|  | import globals from 'globals' | ||||||
|  | import reactHooks from 'eslint-plugin-react-hooks' | ||||||
|  | import reactRefresh from 'eslint-plugin-react-refresh' | ||||||
|  | import tseslint from 'typescript-eslint' | ||||||
|  |  | ||||||
|  | export default tseslint.config( | ||||||
|  |   { ignores: ['dist'] }, | ||||||
|  |   { | ||||||
|  |     extends: [js.configs.recommended, ...tseslint.configs.recommended], | ||||||
|  |     files: ['**/*.{ts,tsx}'], | ||||||
|  |     languageOptions: { | ||||||
|  |       ecmaVersion: 2020, | ||||||
|  |       globals: globals.browser, | ||||||
|  |     }, | ||||||
|  |     plugins: { | ||||||
|  |       'react-hooks': reactHooks, | ||||||
|  |       'react-refresh': reactRefresh, | ||||||
|  |     }, | ||||||
|  |     rules: { | ||||||
|  |       ...reactHooks.configs.recommended.rules, | ||||||
|  |       'react-refresh/only-export-components': [ | ||||||
|  |         'warn', | ||||||
|  |         { allowConstantExport: true }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ) | ||||||
							
								
								
									
										13
									
								
								webClient/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								webClient/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | <!doctype html> | ||||||
|  | <html lang="en"> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="UTF-8" /> | ||||||
|  |     <link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||||
|  |     <title>Vite + React + TS</title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div id="root"></div> | ||||||
|  |     <script type="module" src="/src/main.tsx"></script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										4347
									
								
								webClient/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4347
									
								
								webClient/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										40
									
								
								webClient/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								webClient/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | { | ||||||
|  |   "name": "location_hub_ui", | ||||||
|  |   "private": true, | ||||||
|  |   "version": "0.0.0", | ||||||
|  |   "type": "module", | ||||||
|  |   "scripts": { | ||||||
|  |     "dev": "vite", | ||||||
|  |     "build": "tsc -b && vite build", | ||||||
|  |     "lint": "eslint .", | ||||||
|  |     "preview": "vite preview" | ||||||
|  |   }, | ||||||
|  |   "dependencies": { | ||||||
|  |     "@emotion/react": "^11.14.0", | ||||||
|  |     "@emotion/styled": "^11.14.0", | ||||||
|  |     "@mui/icons-material": "^6.4.3", | ||||||
|  |     "@mui/material": "^6.4.3", | ||||||
|  |     "@mui/x-charts": "^7.25.0", | ||||||
|  |     "@mui/x-data-grid": "^7.25.0", | ||||||
|  |     "@mui/x-date-pickers": "^7.25.0", | ||||||
|  |     "@mui/x-tree-view": "^7.25.0", | ||||||
|  |     "@reduxjs/toolkit": "^2.5.0", | ||||||
|  |     "dayjs": "^1.11.13", | ||||||
|  |     "react": "^18.3.1", | ||||||
|  |     "react-dom": "^18.3.1", | ||||||
|  |     "react-redux": "^9.2.0" | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@eslint/js": "^9.17.0", | ||||||
|  |     "@types/react": "^18.3.18", | ||||||
|  |     "@types/react-dom": "^18.3.5", | ||||||
|  |     "@vitejs/plugin-react-swc": "^3.5.0", | ||||||
|  |     "eslint": "^9.17.0", | ||||||
|  |     "eslint-plugin-react-hooks": "^5.0.0", | ||||||
|  |     "eslint-plugin-react-refresh": "^0.4.16", | ||||||
|  |     "globals": "^15.14.0", | ||||||
|  |     "typescript": "~5.6.2", | ||||||
|  |     "typescript-eslint": "^8.18.2", | ||||||
|  |     "vite": "^6.0.5" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								webClient/public/vite.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								webClient/public/vite.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> | ||||||
| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										56
									
								
								webClient/src/App.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								webClient/src/App.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | import Box from "@mui/material/Box"; | ||||||
|  | import CssBaseline from "@mui/material/CssBaseline"; | ||||||
|  | import Stack from "@mui/material/Stack"; | ||||||
|  | import { alpha } from "@mui/material/styles"; | ||||||
|  | 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"; | ||||||
|  |  | ||||||
|  | const xThemeComponents = { | ||||||
|  |   ...chartsCustomizations, | ||||||
|  |   ...dataGridCustomizations, | ||||||
|  |   ...datePickersCustomizations, | ||||||
|  |   ...treeViewCustomizations, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default function Dashboard(props: { disableCustomTheme?: boolean }) { | ||||||
|  |   return ( | ||||||
|  |     <AppTheme {...props} themeComponents={xThemeComponents}> | ||||||
|  |       <CssBaseline enableColorScheme /> | ||||||
|  |       <Box sx={{ display: "flex" }}> | ||||||
|  |         <SideMenu /> | ||||||
|  |         <AppNavbar /> | ||||||
|  |         {/* Main content */} | ||||||
|  |         <Box | ||||||
|  |           component="main" | ||||||
|  |           sx={(theme) => ({ | ||||||
|  |             flexGrow: 1, | ||||||
|  |             backgroundColor: theme.vars | ||||||
|  |               ? `rgba(${theme.vars.palette.background.defaultChannel} / 1)` | ||||||
|  |               : alpha(theme.palette.background.default, 1), | ||||||
|  |             overflow: "auto", | ||||||
|  |           })} | ||||||
|  |         > | ||||||
|  |           <Stack | ||||||
|  |             spacing={2} | ||||||
|  |             sx={{ | ||||||
|  |               alignItems: "center", | ||||||
|  |               mx: 3, | ||||||
|  |               pb: 5, | ||||||
|  |               mt: { xs: 8, md: 0 }, | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             <Header /> | ||||||
|  |           </Stack> | ||||||
|  |         </Box> | ||||||
|  |       </Box> | ||||||
|  |     </AppTheme> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								webClient/src/app/apiBaseQuery.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								webClient/src/app/apiBaseQuery.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | import { fetchBaseQuery } from "@reduxjs/toolkit/query/react"; | ||||||
|  |  | ||||||
|  | const BASE_URL = import.meta.env.VITE_BASE_URL; | ||||||
|  |  | ||||||
|  | export const baseQuery = fetchBaseQuery({ baseUrl: BASE_URL }); | ||||||
							
								
								
									
										14
									
								
								webClient/src/app/store.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								webClient/src/app/store.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | import { configureStore } from "@reduxjs/toolkit"; | ||||||
|  | import { locationApi } from "../features/locations/locationApi"; | ||||||
|  |  | ||||||
|  | export const store = configureStore({ | ||||||
|  |   reducer: { | ||||||
|  |     [locationApi.reducerPath]: locationApi.reducer, | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   middleware: (getDefaultMiddleware) => | ||||||
|  |     getDefaultMiddleware().concat(locationApi.middleware), | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export type RootState = ReturnType<typeof store.getState>; | ||||||
|  | export type AppDispatch = typeof store.dispatch; | ||||||
							
								
								
									
										107
									
								
								webClient/src/components/AppNavbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								webClient/src/components/AppNavbar.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | |||||||
|  | import DashboardRoundedIcon from "@mui/icons-material/DashboardRounded"; | ||||||
|  | import MenuRoundedIcon from "@mui/icons-material/MenuRounded"; | ||||||
|  | import AppBar from "@mui/material/AppBar"; | ||||||
|  | import Box from "@mui/material/Box"; | ||||||
|  | import Stack from "@mui/material/Stack"; | ||||||
|  | import { styled } from "@mui/material/styles"; | ||||||
|  | import { tabsClasses } from "@mui/material/Tabs"; | ||||||
|  | import MuiToolbar from "@mui/material/Toolbar"; | ||||||
|  | import Typography from "@mui/material/Typography"; | ||||||
|  | import * as React from "react"; | ||||||
|  | import MenuButton from "./MenuButton"; | ||||||
|  | import SideMenuMobile from "./SideMenuMobile"; | ||||||
|  |  | ||||||
|  | const Toolbar = styled(MuiToolbar)({ | ||||||
|  |   width: "100%", | ||||||
|  |   padding: "12px", | ||||||
|  |   display: "flex", | ||||||
|  |   flexDirection: "column", | ||||||
|  |   alignItems: "start", | ||||||
|  |   justifyContent: "center", | ||||||
|  |   gap: "12px", | ||||||
|  |   flexShrink: 0, | ||||||
|  |   [`& ${tabsClasses.flexContainer}`]: { | ||||||
|  |     gap: "8px", | ||||||
|  |     p: "8px", | ||||||
|  |     pb: 0, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export default function AppNavbar() { | ||||||
|  |   const [open, setOpen] = React.useState(false); | ||||||
|  |  | ||||||
|  |   const toggleDrawer = (newOpen: boolean) => () => { | ||||||
|  |     setOpen(newOpen); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <AppBar | ||||||
|  |       position="fixed" | ||||||
|  |       sx={{ | ||||||
|  |         display: { xs: "auto", md: "none" }, | ||||||
|  |         boxShadow: 0, | ||||||
|  |         bgcolor: "background.paper", | ||||||
|  |         backgroundImage: "none", | ||||||
|  |         borderBottom: "1px solid", | ||||||
|  |         borderColor: "divider", | ||||||
|  |         top: "var(--template-frame-height, 0px)", | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       <Toolbar variant="regular"> | ||||||
|  |         <Stack | ||||||
|  |           direction="row" | ||||||
|  |           sx={{ | ||||||
|  |             alignItems: "center", | ||||||
|  |             flexGrow: 1, | ||||||
|  |             width: "100%", | ||||||
|  |             gap: 1, | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           <Stack | ||||||
|  |             direction="row" | ||||||
|  |             spacing={1} | ||||||
|  |             sx={{ justifyContent: "center", mr: "auto" }} | ||||||
|  |           > | ||||||
|  |             <CustomIcon /> | ||||||
|  |             <Typography | ||||||
|  |               variant="h4" | ||||||
|  |               component="h1" | ||||||
|  |               sx={{ color: "text.primary" }} | ||||||
|  |             > | ||||||
|  |               Dashboard | ||||||
|  |             </Typography> | ||||||
|  |           </Stack> | ||||||
|  |           <MenuButton aria-label="menu" onClick={toggleDrawer(true)}> | ||||||
|  |             <MenuRoundedIcon /> | ||||||
|  |           </MenuButton> | ||||||
|  |           <SideMenuMobile open={open} toggleDrawer={toggleDrawer} /> | ||||||
|  |         </Stack> | ||||||
|  |       </Toolbar> | ||||||
|  |     </AppBar> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function CustomIcon() { | ||||||
|  |   return ( | ||||||
|  |     <Box | ||||||
|  |       sx={{ | ||||||
|  |         width: "1.5rem", | ||||||
|  |         height: "1.5rem", | ||||||
|  |         bgcolor: "black", | ||||||
|  |         borderRadius: "999px", | ||||||
|  |         display: "flex", | ||||||
|  |         justifyContent: "center", | ||||||
|  |         alignItems: "center", | ||||||
|  |         alignSelf: "center", | ||||||
|  |         backgroundImage: | ||||||
|  |           "linear-gradient(135deg, hsl(210, 98%, 60%) 0%, hsl(210, 100%, 35%) 100%)", | ||||||
|  |         color: "hsla(210, 100%, 95%, 0.9)", | ||||||
|  |         border: "1px solid", | ||||||
|  |         borderColor: "hsl(210, 100%, 55%)", | ||||||
|  |         boxShadow: "inset 0 2px 5px rgba(255, 255, 255, 0.3)", | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       <DashboardRoundedIcon color="inherit" sx={{ fontSize: "1rem" }} /> | ||||||
|  |     </Box> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								webClient/src/components/CardAlert.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								webClient/src/components/CardAlert.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | import AutoAwesomeRoundedIcon from "@mui/icons-material/AutoAwesomeRounded"; | ||||||
|  | import Button from "@mui/material/Button"; | ||||||
|  | import Card from "@mui/material/Card"; | ||||||
|  | import CardContent from "@mui/material/CardContent"; | ||||||
|  | import Typography from "@mui/material/Typography"; | ||||||
|  |  | ||||||
|  | export default function CardAlert() { | ||||||
|  |   return ( | ||||||
|  |     <Card variant="outlined" sx={{ m: 1.5, p: 1.5 }}> | ||||||
|  |       <CardContent> | ||||||
|  |         <AutoAwesomeRoundedIcon fontSize="small" /> | ||||||
|  |         <Typography gutterBottom sx={{ fontWeight: 600 }}> | ||||||
|  |           Plan about to expire | ||||||
|  |         </Typography> | ||||||
|  |         <Typography variant="body2" sx={{ mb: 2, color: "text.secondary" }}> | ||||||
|  |           Enjoy 10% off when renewing your plan today. | ||||||
|  |         </Typography> | ||||||
|  |         <Button variant="contained" size="small" fullWidth> | ||||||
|  |           Get the discount | ||||||
|  |         </Button> | ||||||
|  |       </CardContent> | ||||||
|  |     </Card> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								webClient/src/components/CustomDatePicker.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								webClient/src/components/CustomDatePicker.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | import * as React from "react"; | ||||||
|  | import dayjs, { Dayjs } from "dayjs"; | ||||||
|  | import Button from "@mui/material/Button"; | ||||||
|  | import CalendarTodayRoundedIcon from "@mui/icons-material/CalendarTodayRounded"; | ||||||
|  | import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | ||||||
|  | import { UseDateFieldProps } from "@mui/x-date-pickers/DateField"; | ||||||
|  | import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; | ||||||
|  | import { DatePicker } from "@mui/x-date-pickers/DatePicker"; | ||||||
|  | import { | ||||||
|  |   BaseSingleInputFieldProps, | ||||||
|  |   DateValidationError, | ||||||
|  |   FieldSection, | ||||||
|  | } from "@mui/x-date-pickers/models"; | ||||||
|  |  | ||||||
|  | interface ButtonFieldProps | ||||||
|  |   extends UseDateFieldProps<Dayjs, false>, | ||||||
|  |     BaseSingleInputFieldProps< | ||||||
|  |       Dayjs | null, | ||||||
|  |       Dayjs, | ||||||
|  |       FieldSection, | ||||||
|  |       false, | ||||||
|  |       DateValidationError | ||||||
|  |     > { | ||||||
|  |   setOpen?: React.Dispatch<React.SetStateAction<boolean>>; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function ButtonField(props: ButtonFieldProps) { | ||||||
|  |   const { | ||||||
|  |     setOpen, | ||||||
|  |     label, | ||||||
|  |     id, | ||||||
|  |     disabled, | ||||||
|  |     InputProps: { ref } = {}, | ||||||
|  |     inputProps: { "aria-label": ariaLabel } = {}, | ||||||
|  |   } = props; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <Button | ||||||
|  |       variant="outlined" | ||||||
|  |       id={id} | ||||||
|  |       disabled={disabled} | ||||||
|  |       ref={ref} | ||||||
|  |       aria-label={ariaLabel} | ||||||
|  |       size="small" | ||||||
|  |       onClick={() => setOpen?.((prev) => !prev)} | ||||||
|  |       startIcon={<CalendarTodayRoundedIcon fontSize="small" />} | ||||||
|  |       sx={{ minWidth: "fit-content" }} | ||||||
|  |     > | ||||||
|  |       {label ? `${label}` : "Pick a date"} | ||||||
|  |     </Button> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function CustomDatePicker() { | ||||||
|  |   const [value, setValue] = React.useState<Dayjs | null>(dayjs("2023-04-17")); | ||||||
|  |   const [open, setOpen] = React.useState(false); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <LocalizationProvider dateAdapter={AdapterDayjs}> | ||||||
|  |       <DatePicker | ||||||
|  |         value={value} | ||||||
|  |         label={value == null ? null : value.format("MMM DD, YYYY")} | ||||||
|  |         onChange={(newValue) => setValue(newValue)} | ||||||
|  |         slots={{ field: ButtonField }} | ||||||
|  |         slotProps={{ | ||||||
|  |           // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||||
|  |           field: { setOpen } as any, | ||||||
|  |           nextIconButton: { size: "small" }, | ||||||
|  |           previousIconButton: { size: "small" }, | ||||||
|  |         }} | ||||||
|  |         open={open} | ||||||
|  |         onClose={() => setOpen(false)} | ||||||
|  |         onOpen={() => setOpen(true)} | ||||||
|  |         views={["day", "month", "year"]} | ||||||
|  |       /> | ||||||
|  |     </LocalizationProvider> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								webClient/src/components/Header.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								webClient/src/components/Header.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | import NotificationsRoundedIcon from "@mui/icons-material/NotificationsRounded"; | ||||||
|  | import Stack from "@mui/material/Stack"; | ||||||
|  | import CustomDatePicker from "./CustomDatePicker"; | ||||||
|  | import MenuButton from "./MenuButton"; | ||||||
|  | import NavbarBreadcrumbs from "./NavbarBreadcrumbs"; | ||||||
|  |  | ||||||
|  | import ColorModeIconDropdown from "../shared-theme/ColorModeIconDropdown"; | ||||||
|  | import Search from "./Search"; | ||||||
|  |  | ||||||
|  | export default function Header() { | ||||||
|  |   return ( | ||||||
|  |     <Stack | ||||||
|  |       direction="row" | ||||||
|  |       sx={{ | ||||||
|  |         display: { xs: "none", md: "flex" }, | ||||||
|  |         width: "100%", | ||||||
|  |         alignItems: { xs: "flex-start", md: "center" }, | ||||||
|  |         justifyContent: "space-between", | ||||||
|  |         maxWidth: { sm: "100%", md: "1700px" }, | ||||||
|  |         pt: 1.5, | ||||||
|  |       }} | ||||||
|  |       spacing={2} | ||||||
|  |     > | ||||||
|  |       <NavbarBreadcrumbs /> | ||||||
|  |       <Stack direction="row" sx={{ gap: 1 }}> | ||||||
|  |         <Search /> | ||||||
|  |         <CustomDatePicker /> | ||||||
|  |         <MenuButton showBadge aria-label="Open notifications"> | ||||||
|  |           <NotificationsRoundedIcon /> | ||||||
|  |         </MenuButton> | ||||||
|  |         <ColorModeIconDropdown /> | ||||||
|  |       </Stack> | ||||||
|  |     </Stack> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								webClient/src/components/MenuButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								webClient/src/components/MenuButton.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | import Badge, { badgeClasses } from "@mui/material/Badge"; | ||||||
|  | import IconButton, { IconButtonProps } from "@mui/material/IconButton"; | ||||||
|  |  | ||||||
|  | export interface MenuButtonProps extends IconButtonProps { | ||||||
|  |   showBadge?: boolean; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function MenuButton({ | ||||||
|  |   showBadge = false, | ||||||
|  |   ...props | ||||||
|  | }: MenuButtonProps) { | ||||||
|  |   return ( | ||||||
|  |     <Badge | ||||||
|  |       color="error" | ||||||
|  |       variant="dot" | ||||||
|  |       invisible={!showBadge} | ||||||
|  |       sx={{ [`& .${badgeClasses.badge}`]: { right: 2, top: 2 } }} | ||||||
|  |     > | ||||||
|  |       <IconButton size="small" {...props} /> | ||||||
|  |     </Badge> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								webClient/src/components/MenuContent.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								webClient/src/components/MenuContent.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | import AnalyticsRoundedIcon from "@mui/icons-material/AnalyticsRounded"; | ||||||
|  | import AssignmentRoundedIcon from "@mui/icons-material/AssignmentRounded"; | ||||||
|  | import HelpRoundedIcon from "@mui/icons-material/HelpRounded"; | ||||||
|  | import HomeRoundedIcon from "@mui/icons-material/HomeRounded"; | ||||||
|  | import InfoRoundedIcon from "@mui/icons-material/InfoRounded"; | ||||||
|  | import PeopleRoundedIcon from "@mui/icons-material/PeopleRounded"; | ||||||
|  | import SettingsRoundedIcon from "@mui/icons-material/SettingsRounded"; | ||||||
|  | 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"; | ||||||
|  |  | ||||||
|  | const mainListItems = [ | ||||||
|  |   { text: "Home", icon: <HomeRoundedIcon /> }, | ||||||
|  |   { text: "Analytics", icon: <AnalyticsRoundedIcon /> }, | ||||||
|  |   { text: "Clients", icon: <PeopleRoundedIcon /> }, | ||||||
|  |   { text: "Tasks", icon: <AssignmentRoundedIcon /> }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | const secondaryListItems = [ | ||||||
|  |   { text: "Settings", icon: <SettingsRoundedIcon /> }, | ||||||
|  |   { text: "About", icon: <InfoRoundedIcon /> }, | ||||||
|  |   { text: "Feedback", icon: <HelpRoundedIcon /> }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | export default function MenuContent() { | ||||||
|  |   return ( | ||||||
|  |     <Stack sx={{ flexGrow: 1, p: 1, justifyContent: "space-between" }}> | ||||||
|  |       <List dense> | ||||||
|  |         {mainListItems.map((item, index) => ( | ||||||
|  |           <ListItem key={index} disablePadding sx={{ display: "block" }}> | ||||||
|  |             <ListItemButton selected={index === 0}> | ||||||
|  |               <ListItemIcon>{item.icon}</ListItemIcon> | ||||||
|  |               <ListItemText primary={item.text} /> | ||||||
|  |             </ListItemButton> | ||||||
|  |           </ListItem> | ||||||
|  |         ))} | ||||||
|  |       </List> | ||||||
|  |  | ||||||
|  |       <List dense> | ||||||
|  |         {secondaryListItems.map((item, index) => ( | ||||||
|  |           <ListItem key={index} disablePadding sx={{ display: "block" }}> | ||||||
|  |             <ListItemButton> | ||||||
|  |               <ListItemIcon>{item.icon}</ListItemIcon> | ||||||
|  |               <ListItemText primary={item.text} /> | ||||||
|  |             </ListItemButton> | ||||||
|  |           </ListItem> | ||||||
|  |         ))} | ||||||
|  |       </List> | ||||||
|  |     </Stack> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								webClient/src/components/NavbarBreadcrumbs.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								webClient/src/components/NavbarBreadcrumbs.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | import NavigateNextRoundedIcon from "@mui/icons-material/NavigateNextRounded"; | ||||||
|  | import Breadcrumbs, { breadcrumbsClasses } from "@mui/material/Breadcrumbs"; | ||||||
|  | import { styled } from "@mui/material/styles"; | ||||||
|  | import Typography from "@mui/material/Typography"; | ||||||
|  |  | ||||||
|  | const StyledBreadcrumbs = styled(Breadcrumbs)(({ theme }) => ({ | ||||||
|  |   margin: theme.spacing(1, 0), | ||||||
|  |   [`& .${breadcrumbsClasses.separator}`]: { | ||||||
|  |     color: (theme.vars || theme).palette.action.disabled, | ||||||
|  |     margin: 1, | ||||||
|  |   }, | ||||||
|  |   [`& .${breadcrumbsClasses.ol}`]: { | ||||||
|  |     alignItems: "center", | ||||||
|  |   }, | ||||||
|  | })); | ||||||
|  |  | ||||||
|  | export default function NavbarBreadcrumbs() { | ||||||
|  |   return ( | ||||||
|  |     <StyledBreadcrumbs | ||||||
|  |       aria-label="breadcrumb" | ||||||
|  |       separator={<NavigateNextRoundedIcon fontSize="small" />} | ||||||
|  |     > | ||||||
|  |       <Typography variant="body1">Dashboard</Typography> | ||||||
|  |       <Typography | ||||||
|  |         variant="body1" | ||||||
|  |         sx={{ color: "text.primary", fontWeight: 600 }} | ||||||
|  |       > | ||||||
|  |         Home | ||||||
|  |       </Typography> | ||||||
|  |     </StyledBreadcrumbs> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								webClient/src/components/OptionsMenu.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								webClient/src/components/OptionsMenu.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | import * as React from 'react'; | ||||||
|  | import { styled } from '@mui/material/styles'; | ||||||
|  | import Divider, { dividerClasses } from '@mui/material/Divider'; | ||||||
|  | import Menu from '@mui/material/Menu'; | ||||||
|  | import MuiMenuItem from '@mui/material/MenuItem'; | ||||||
|  | import { paperClasses } from '@mui/material/Paper'; | ||||||
|  | import { listClasses } from '@mui/material/List'; | ||||||
|  | import ListItemText from '@mui/material/ListItemText'; | ||||||
|  | import ListItemIcon, { listItemIconClasses } from '@mui/material/ListItemIcon'; | ||||||
|  | import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded'; | ||||||
|  | import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'; | ||||||
|  | import MenuButton from './MenuButton'; | ||||||
|  |  | ||||||
|  | const MenuItem = styled(MuiMenuItem)({ | ||||||
|  |   margin: '2px 0', | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export default function OptionsMenu() { | ||||||
|  |   const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); | ||||||
|  |   const open = Boolean(anchorEl); | ||||||
|  |   const handleClick = (event: React.MouseEvent<HTMLElement>) => { | ||||||
|  |     setAnchorEl(event.currentTarget); | ||||||
|  |   }; | ||||||
|  |   const handleClose = () => { | ||||||
|  |     setAnchorEl(null); | ||||||
|  |   }; | ||||||
|  |   return ( | ||||||
|  |     <React.Fragment> | ||||||
|  |       <MenuButton | ||||||
|  |         aria-label="Open menu" | ||||||
|  |         onClick={handleClick} | ||||||
|  |         sx={{ borderColor: 'transparent' }} | ||||||
|  |       > | ||||||
|  |         <MoreVertRoundedIcon /> | ||||||
|  |       </MenuButton> | ||||||
|  |       <Menu | ||||||
|  |         anchorEl={anchorEl} | ||||||
|  |         id="menu" | ||||||
|  |         open={open} | ||||||
|  |         onClose={handleClose} | ||||||
|  |         onClick={handleClose} | ||||||
|  |         transformOrigin={{ horizontal: 'right', vertical: 'top' }} | ||||||
|  |         anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} | ||||||
|  |         sx={{ | ||||||
|  |           [`& .${listClasses.root}`]: { | ||||||
|  |             padding: '4px', | ||||||
|  |           }, | ||||||
|  |           [`& .${paperClasses.root}`]: { | ||||||
|  |             padding: 0, | ||||||
|  |           }, | ||||||
|  |           [`& .${dividerClasses.root}`]: { | ||||||
|  |             margin: '4px -4px', | ||||||
|  |           }, | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <MenuItem onClick={handleClose}>Profile</MenuItem> | ||||||
|  |         <MenuItem onClick={handleClose}>My account</MenuItem> | ||||||
|  |         <Divider /> | ||||||
|  |         <MenuItem onClick={handleClose}>Add another account</MenuItem> | ||||||
|  |         <MenuItem onClick={handleClose}>Settings</MenuItem> | ||||||
|  |         <Divider /> | ||||||
|  |         <MenuItem | ||||||
|  |           onClick={handleClose} | ||||||
|  |           sx={{ | ||||||
|  |             [`& .${listItemIconClasses.root}`]: { | ||||||
|  |               ml: 'auto', | ||||||
|  |               minWidth: 0, | ||||||
|  |             }, | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           <ListItemText>Logout</ListItemText> | ||||||
|  |           <ListItemIcon> | ||||||
|  |             <LogoutRoundedIcon fontSize="small" /> | ||||||
|  |           </ListItemIcon> | ||||||
|  |         </MenuItem> | ||||||
|  |       </Menu> | ||||||
|  |     </React.Fragment> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								webClient/src/components/Search.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								webClient/src/components/Search.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | import SearchRoundedIcon from "@mui/icons-material/SearchRounded"; | ||||||
|  | import FormControl from "@mui/material/FormControl"; | ||||||
|  | import InputAdornment from "@mui/material/InputAdornment"; | ||||||
|  | import OutlinedInput from "@mui/material/OutlinedInput"; | ||||||
|  |  | ||||||
|  | export default function Search() { | ||||||
|  |   return ( | ||||||
|  |     <FormControl sx={{ width: { xs: "100%", md: "25ch" } }} variant="outlined"> | ||||||
|  |       <OutlinedInput | ||||||
|  |         size="small" | ||||||
|  |         id="search" | ||||||
|  |         placeholder="Search…" | ||||||
|  |         sx={{ flexGrow: 1 }} | ||||||
|  |         startAdornment={ | ||||||
|  |           <InputAdornment position="start" sx={{ color: "text.primary" }}> | ||||||
|  |             <SearchRoundedIcon fontSize="small" /> | ||||||
|  |           </InputAdornment> | ||||||
|  |         } | ||||||
|  |         inputProps={{ | ||||||
|  |           "aria-label": "search", | ||||||
|  |         }} | ||||||
|  |       /> | ||||||
|  |     </FormControl> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								webClient/src/components/SelectContent.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								webClient/src/components/SelectContent.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | |||||||
|  | import * as React from 'react'; | ||||||
|  | import MuiAvatar from '@mui/material/Avatar'; | ||||||
|  | import MuiListItemAvatar from '@mui/material/ListItemAvatar'; | ||||||
|  | import MenuItem from '@mui/material/MenuItem'; | ||||||
|  | import ListItemText from '@mui/material/ListItemText'; | ||||||
|  | import ListItemIcon from '@mui/material/ListItemIcon'; | ||||||
|  | import ListSubheader from '@mui/material/ListSubheader'; | ||||||
|  | import Select, { SelectChangeEvent, selectClasses } from '@mui/material/Select'; | ||||||
|  | import Divider from '@mui/material/Divider'; | ||||||
|  | import { styled } from '@mui/material/styles'; | ||||||
|  | import AddRoundedIcon from '@mui/icons-material/AddRounded'; | ||||||
|  | import DevicesRoundedIcon from '@mui/icons-material/DevicesRounded'; | ||||||
|  | import SmartphoneRoundedIcon from '@mui/icons-material/SmartphoneRounded'; | ||||||
|  | import ConstructionRoundedIcon from '@mui/icons-material/ConstructionRounded'; | ||||||
|  |  | ||||||
|  | const Avatar = styled(MuiAvatar)(({ theme }) => ({ | ||||||
|  |   width: 28, | ||||||
|  |   height: 28, | ||||||
|  |   backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |   color: (theme.vars || theme).palette.text.secondary, | ||||||
|  |   border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  | })); | ||||||
|  |  | ||||||
|  | const ListItemAvatar = styled(MuiListItemAvatar)({ | ||||||
|  |   minWidth: 0, | ||||||
|  |   marginRight: 12, | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export default function SelectContent() { | ||||||
|  |   const [company, setCompany] = React.useState(''); | ||||||
|  |  | ||||||
|  |   const handleChange = (event: SelectChangeEvent) => { | ||||||
|  |     setCompany(event.target.value as string); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <Select | ||||||
|  |       labelId="company-select" | ||||||
|  |       id="company-simple-select" | ||||||
|  |       value={company} | ||||||
|  |       onChange={handleChange} | ||||||
|  |       displayEmpty | ||||||
|  |       inputProps={{ 'aria-label': 'Select company' }} | ||||||
|  |       fullWidth | ||||||
|  |       sx={{ | ||||||
|  |         maxHeight: 56, | ||||||
|  |         width: 215, | ||||||
|  |         '&.MuiList-root': { | ||||||
|  |           p: '8px', | ||||||
|  |         }, | ||||||
|  |         [`& .${selectClasses.select}`]: { | ||||||
|  |           display: 'flex', | ||||||
|  |           alignItems: 'center', | ||||||
|  |           gap: '2px', | ||||||
|  |           pl: 1, | ||||||
|  |         }, | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       <ListSubheader sx={{ pt: 0 }}>Production</ListSubheader> | ||||||
|  |       <MenuItem value=""> | ||||||
|  |         <ListItemAvatar> | ||||||
|  |           <Avatar alt="Sitemark web"> | ||||||
|  |             <DevicesRoundedIcon sx={{ fontSize: '1rem' }} /> | ||||||
|  |           </Avatar> | ||||||
|  |         </ListItemAvatar> | ||||||
|  |         <ListItemText primary="Sitemark-web" secondary="Web app" /> | ||||||
|  |       </MenuItem> | ||||||
|  |       <MenuItem value={10}> | ||||||
|  |         <ListItemAvatar> | ||||||
|  |           <Avatar alt="Sitemark App"> | ||||||
|  |             <SmartphoneRoundedIcon sx={{ fontSize: '1rem' }} /> | ||||||
|  |           </Avatar> | ||||||
|  |         </ListItemAvatar> | ||||||
|  |         <ListItemText primary="Sitemark-app" secondary="Mobile application" /> | ||||||
|  |       </MenuItem> | ||||||
|  |       <MenuItem value={20}> | ||||||
|  |         <ListItemAvatar> | ||||||
|  |           <Avatar alt="Sitemark Store"> | ||||||
|  |             <DevicesRoundedIcon sx={{ fontSize: '1rem' }} /> | ||||||
|  |           </Avatar> | ||||||
|  |         </ListItemAvatar> | ||||||
|  |         <ListItemText primary="Sitemark-Store" secondary="Web app" /> | ||||||
|  |       </MenuItem> | ||||||
|  |       <ListSubheader>Development</ListSubheader> | ||||||
|  |       <MenuItem value={30}> | ||||||
|  |         <ListItemAvatar> | ||||||
|  |           <Avatar alt="Sitemark Store"> | ||||||
|  |             <ConstructionRoundedIcon sx={{ fontSize: '1rem' }} /> | ||||||
|  |           </Avatar> | ||||||
|  |         </ListItemAvatar> | ||||||
|  |         <ListItemText primary="Sitemark-Admin" secondary="Web app" /> | ||||||
|  |       </MenuItem> | ||||||
|  |       <Divider sx={{ mx: -1 }} /> | ||||||
|  |       <MenuItem value={40}> | ||||||
|  |         <ListItemIcon> | ||||||
|  |           <AddRoundedIcon /> | ||||||
|  |         </ListItemIcon> | ||||||
|  |         <ListItemText primary="Add product" secondary="Web app" /> | ||||||
|  |       </MenuItem> | ||||||
|  |     </Select> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								webClient/src/components/SideMenu.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								webClient/src/components/SideMenu.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | import Avatar from "@mui/material/Avatar"; | ||||||
|  | import Box from "@mui/material/Box"; | ||||||
|  | import Divider from "@mui/material/Divider"; | ||||||
|  | import MuiDrawer, { drawerClasses } from "@mui/material/Drawer"; | ||||||
|  | import Stack from "@mui/material/Stack"; | ||||||
|  | import { styled } from "@mui/material/styles"; | ||||||
|  | import Typography from "@mui/material/Typography"; | ||||||
|  | import CardAlert from "./CardAlert"; | ||||||
|  | import MenuContent from "./MenuContent"; | ||||||
|  | import OptionsMenu from "./OptionsMenu"; | ||||||
|  | import SelectContent from "./SelectContent"; | ||||||
|  |  | ||||||
|  | const drawerWidth = 240; | ||||||
|  |  | ||||||
|  | const Drawer = styled(MuiDrawer)({ | ||||||
|  |   width: drawerWidth, | ||||||
|  |   flexShrink: 0, | ||||||
|  |   boxSizing: "border-box", | ||||||
|  |   mt: 10, | ||||||
|  |   [`& .${drawerClasses.paper}`]: { | ||||||
|  |     width: drawerWidth, | ||||||
|  |     boxSizing: "border-box", | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export default function SideMenu() { | ||||||
|  |   return ( | ||||||
|  |     <Drawer | ||||||
|  |       variant="permanent" | ||||||
|  |       sx={{ | ||||||
|  |         display: { xs: "none", md: "block" }, | ||||||
|  |         [`& .${drawerClasses.paper}`]: { | ||||||
|  |           backgroundColor: "background.paper", | ||||||
|  |         }, | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       <Box | ||||||
|  |         sx={{ | ||||||
|  |           display: "flex", | ||||||
|  |           mt: "calc(var(--template-frame-height, 0px) + 4px)", | ||||||
|  |           p: 1.5, | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <SelectContent /> | ||||||
|  |       </Box> | ||||||
|  |       <Divider /> | ||||||
|  |       <MenuContent /> | ||||||
|  |       <CardAlert /> | ||||||
|  |       <Stack | ||||||
|  |         direction="row" | ||||||
|  |         sx={{ | ||||||
|  |           p: 2, | ||||||
|  |           gap: 1, | ||||||
|  |           alignItems: "center", | ||||||
|  |           borderTop: "1px solid", | ||||||
|  |           borderColor: "divider", | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <Avatar | ||||||
|  |           sizes="small" | ||||||
|  |           alt="Riley Carter" | ||||||
|  |           src="/static/images/avatar/7.jpg" | ||||||
|  |           sx={{ width: 36, height: 36 }} | ||||||
|  |         /> | ||||||
|  |         <Box sx={{ mr: "auto" }}> | ||||||
|  |           <Typography | ||||||
|  |             variant="body2" | ||||||
|  |             sx={{ fontWeight: 500, lineHeight: "16px" }} | ||||||
|  |           > | ||||||
|  |             Riley Carter | ||||||
|  |           </Typography> | ||||||
|  |           <Typography variant="caption" sx={{ color: "text.secondary" }}> | ||||||
|  |             riley@email.com | ||||||
|  |           </Typography> | ||||||
|  |         </Box> | ||||||
|  |         <OptionsMenu /> | ||||||
|  |       </Stack> | ||||||
|  |     </Drawer> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								webClient/src/components/SideMenuMobile.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								webClient/src/components/SideMenuMobile.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | import LogoutRoundedIcon from "@mui/icons-material/LogoutRounded"; | ||||||
|  | import NotificationsRoundedIcon from "@mui/icons-material/NotificationsRounded"; | ||||||
|  | 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 CardAlert from "./CardAlert"; | ||||||
|  | import MenuButton from "./MenuButton"; | ||||||
|  | import MenuContent from "./MenuContent"; | ||||||
|  |  | ||||||
|  | interface SideMenuMobileProps { | ||||||
|  |   open: boolean | undefined; | ||||||
|  |   toggleDrawer: (newOpen: boolean) => () => void; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function SideMenuMobile({ | ||||||
|  |   open, | ||||||
|  |   toggleDrawer, | ||||||
|  | }: SideMenuMobileProps) { | ||||||
|  |   return ( | ||||||
|  |     <Drawer | ||||||
|  |       anchor="right" | ||||||
|  |       open={open} | ||||||
|  |       onClose={toggleDrawer(false)} | ||||||
|  |       sx={{ | ||||||
|  |         zIndex: (theme) => theme.zIndex.drawer + 1, | ||||||
|  |         [`& .${drawerClasses.paper}`]: { | ||||||
|  |           backgroundImage: "none", | ||||||
|  |           backgroundColor: "background.paper", | ||||||
|  |         }, | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       <Stack | ||||||
|  |         sx={{ | ||||||
|  |           maxWidth: "70dvw", | ||||||
|  |           height: "100%", | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <Stack direction="row" sx={{ p: 2, pb: 0, gap: 1 }}> | ||||||
|  |           <Stack | ||||||
|  |             direction="row" | ||||||
|  |             sx={{ gap: 1, alignItems: "center", flexGrow: 1, p: 1 }} | ||||||
|  |           > | ||||||
|  |             <Avatar | ||||||
|  |               sizes="small" | ||||||
|  |               alt="Riley Carter" | ||||||
|  |               src="/static/images/avatar/7.jpg" | ||||||
|  |               sx={{ width: 24, height: 24 }} | ||||||
|  |             /> | ||||||
|  |             <Typography component="p" variant="h6"> | ||||||
|  |               Riley Carter | ||||||
|  |             </Typography> | ||||||
|  |           </Stack> | ||||||
|  |           <MenuButton showBadge> | ||||||
|  |             <NotificationsRoundedIcon /> | ||||||
|  |           </MenuButton> | ||||||
|  |         </Stack> | ||||||
|  |         <Divider /> | ||||||
|  |         <Stack sx={{ flexGrow: 1 }}> | ||||||
|  |           <MenuContent /> | ||||||
|  |           <Divider /> | ||||||
|  |         </Stack> | ||||||
|  |         <CardAlert /> | ||||||
|  |         <Stack sx={{ p: 2 }}> | ||||||
|  |           <Button | ||||||
|  |             variant="outlined" | ||||||
|  |             fullWidth | ||||||
|  |             startIcon={<LogoutRoundedIcon />} | ||||||
|  |           > | ||||||
|  |             Logout | ||||||
|  |           </Button> | ||||||
|  |         </Stack> | ||||||
|  |       </Stack> | ||||||
|  |     </Drawer> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								webClient/src/features/locations/locationApi.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								webClient/src/features/locations/locationApi.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | import { createApi } from "@reduxjs/toolkit/query/react"; | ||||||
|  | import { baseQuery } from "../../app/apiBaseQuery"; | ||||||
|  | import { Location } from "../../models/location"; | ||||||
|  |  | ||||||
|  | export const locationApi = createApi({ | ||||||
|  |   reducerPath: "locationApi", | ||||||
|  |   baseQuery, | ||||||
|  |   endpoints: (builder) => ({ | ||||||
|  |     getLocations: builder.query<Location[], void>({ | ||||||
|  |       query: () => "/locations", | ||||||
|  |     }), | ||||||
|  |     getLocationById: builder.query<Location, string>({ | ||||||
|  |       query: (id) => `/locations/${id}`, | ||||||
|  |     }), | ||||||
|  |     createLocation: builder.mutation<Location, Partial<Location>>({ | ||||||
|  |       query: (newLocation) => ({ | ||||||
|  |         url: "/locations", | ||||||
|  |         method: "POST", | ||||||
|  |         body: newLocation, | ||||||
|  |       }), | ||||||
|  |     }), | ||||||
|  |     updateLocation: builder.mutation< | ||||||
|  |       Location, | ||||||
|  |       { id: string; location: Partial<Location> } | ||||||
|  |     >({ | ||||||
|  |       query: ({ id, location }) => ({ | ||||||
|  |         url: `/locations/${id}`, | ||||||
|  |         method: "PUT", | ||||||
|  |         body: location, | ||||||
|  |       }), | ||||||
|  |     }), | ||||||
|  |     deleteLocation: builder.mutation<void, string>({ | ||||||
|  |       query: (id) => ({ | ||||||
|  |         url: `/locations/${id}`, | ||||||
|  |         method: "DELETE", | ||||||
|  |       }), | ||||||
|  |     }), | ||||||
|  |   }), | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export const { | ||||||
|  |   useGetLocationsQuery, | ||||||
|  |   useGetLocationByIdQuery, | ||||||
|  |   useCreateLocationMutation, | ||||||
|  |   useUpdateLocationMutation, | ||||||
|  |   useDeleteLocationMutation, | ||||||
|  | } = locationApi; | ||||||
							
								
								
									
										9
									
								
								webClient/src/main.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								webClient/src/main.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | import React from "react"; | ||||||
|  | import ReactDOM from "react-dom/client"; | ||||||
|  | import App from "./App.tsx"; | ||||||
|  |  | ||||||
|  | ReactDOM.createRoot(document.getElementById("root")!).render( | ||||||
|  |   <React.StrictMode> | ||||||
|  |     <App /> | ||||||
|  |   </React.StrictMode> | ||||||
|  | ); | ||||||
							
								
								
									
										12
									
								
								webClient/src/models/location.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								webClient/src/models/location.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | export interface Location { | ||||||
|  |   location_id: string; | ||||||
|  |   lp_ttn_end_device_uplinks_id: string; | ||||||
|  |   wifi_latitude: number; | ||||||
|  |   wifi_longitude: number; | ||||||
|  |   gnss_latitude: number; | ||||||
|  |   gnss_longitude: number; | ||||||
|  |   ttn_gw_latitude: number; | ||||||
|  |   ttn_gw_longitude: number; | ||||||
|  |   created_at_utc: string; | ||||||
|  |   updated_at_utc: string; | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								webClient/src/shared-theme/AppTheme.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								webClient/src/shared-theme/AppTheme.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | import type { ThemeOptions } from "@mui/material/styles"; | ||||||
|  | import { ThemeProvider, createTheme } from "@mui/material/styles"; | ||||||
|  | import * as React from "react"; | ||||||
|  | import { dataDisplayCustomizations } from "./customizations/dataDisplay"; | ||||||
|  | import { feedbackCustomizations } from "./customizations/feedback"; | ||||||
|  | import { inputsCustomizations } from "./customizations/inputs"; | ||||||
|  | import { navigationCustomizations } from "./customizations/navigation"; | ||||||
|  | import { surfacesCustomizations } from "./customizations/surfaces"; | ||||||
|  | import { colorSchemes, shadows, shape, typography } from "./themePrimitives"; | ||||||
|  |  | ||||||
|  | interface AppThemeProps { | ||||||
|  |   children: React.ReactNode; | ||||||
|  |   /** | ||||||
|  |    * This is for the docs site. You can ignore it or remove it. | ||||||
|  |    */ | ||||||
|  |   disableCustomTheme?: boolean; | ||||||
|  |   themeComponents?: ThemeOptions["components"]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function AppTheme(props: AppThemeProps) { | ||||||
|  |   const { children, disableCustomTheme, themeComponents } = props; | ||||||
|  |   const theme = React.useMemo(() => { | ||||||
|  |     return disableCustomTheme | ||||||
|  |       ? {} | ||||||
|  |       : createTheme({ | ||||||
|  |           // For more details about CSS variables configuration, see https://mui.com/material-ui/customization/css-theme-variables/configuration/ | ||||||
|  |           cssVariables: { | ||||||
|  |             colorSchemeSelector: "data-mui-color-scheme", | ||||||
|  |             cssVarPrefix: "template", | ||||||
|  |           }, | ||||||
|  |           colorSchemes, // Recently added in v6 for building light & dark mode app, see https://mui.com/material-ui/customization/palette/#color-schemes | ||||||
|  |           typography, | ||||||
|  |           shadows, | ||||||
|  |           shape, | ||||||
|  |           components: { | ||||||
|  |             ...inputsCustomizations, | ||||||
|  |             ...dataDisplayCustomizations, | ||||||
|  |             ...feedbackCustomizations, | ||||||
|  |             ...navigationCustomizations, | ||||||
|  |             ...surfacesCustomizations, | ||||||
|  |             ...themeComponents, | ||||||
|  |           }, | ||||||
|  |         }); | ||||||
|  |   }, [disableCustomTheme, themeComponents]); | ||||||
|  |   if (disableCustomTheme) { | ||||||
|  |     return <React.Fragment>{children}</React.Fragment>; | ||||||
|  |   } | ||||||
|  |   return ( | ||||||
|  |     <ThemeProvider theme={theme} disableTransitionOnChange> | ||||||
|  |       {children} | ||||||
|  |     </ThemeProvider> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										89
									
								
								webClient/src/shared-theme/ColorModeIconDropdown.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								webClient/src/shared-theme/ColorModeIconDropdown.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | |||||||
|  | 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"; | ||||||
|  | import * as React from "react"; | ||||||
|  |  | ||||||
|  | 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> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										233
									
								
								webClient/src/shared-theme/customizations/dataDisplay.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								webClient/src/shared-theme/customizations/dataDisplay.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,233 @@ | |||||||
|  | import { Theme, alpha, Components } from '@mui/material/styles'; | ||||||
|  | import { svgIconClasses } from '@mui/material/SvgIcon'; | ||||||
|  | import { typographyClasses } from '@mui/material/Typography'; | ||||||
|  | import { buttonBaseClasses } from '@mui/material/ButtonBase'; | ||||||
|  | import { chipClasses } from '@mui/material/Chip'; | ||||||
|  | import { iconButtonClasses } from '@mui/material/IconButton'; | ||||||
|  | import { gray, red, green } from '../themePrimitives'; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const dataDisplayCustomizations: Components<Theme> = { | ||||||
|  |   MuiList: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: { | ||||||
|  |         padding: '8px', | ||||||
|  |         display: 'flex', | ||||||
|  |         flexDirection: 'column', | ||||||
|  |         gap: 0, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiListItem: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         [`& .${svgIconClasses.root}`]: { | ||||||
|  |           width: '1rem', | ||||||
|  |           height: '1rem', | ||||||
|  |           color: (theme.vars || theme).palette.text.secondary, | ||||||
|  |         }, | ||||||
|  |         [`& .${typographyClasses.root}`]: { | ||||||
|  |           fontWeight: 500, | ||||||
|  |         }, | ||||||
|  |         [`& .${buttonBaseClasses.root}`]: { | ||||||
|  |           display: 'flex', | ||||||
|  |           gap: 8, | ||||||
|  |           padding: '2px 8px', | ||||||
|  |           borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |           opacity: 0.7, | ||||||
|  |           '&.Mui-selected': { | ||||||
|  |             opacity: 1, | ||||||
|  |             backgroundColor: alpha(theme.palette.action.selected, 0.3), | ||||||
|  |             [`& .${svgIconClasses.root}`]: { | ||||||
|  |               color: (theme.vars || theme).palette.text.primary, | ||||||
|  |             }, | ||||||
|  |             '&:focus-visible': { | ||||||
|  |               backgroundColor: alpha(theme.palette.action.selected, 0.3), | ||||||
|  |             }, | ||||||
|  |             '&:hover': { | ||||||
|  |               backgroundColor: alpha(theme.palette.action.selected, 0.5), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           '&:focus-visible': { | ||||||
|  |             backgroundColor: 'transparent', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiListItemText: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       primary: ({ theme }) => ({ | ||||||
|  |         fontSize: theme.typography.body2.fontSize, | ||||||
|  |         fontWeight: 500, | ||||||
|  |         lineHeight: theme.typography.body2.lineHeight, | ||||||
|  |       }), | ||||||
|  |       secondary: ({ theme }) => ({ | ||||||
|  |         fontSize: theme.typography.caption.fontSize, | ||||||
|  |         lineHeight: theme.typography.caption.lineHeight, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiListSubheader: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         backgroundColor: 'transparent', | ||||||
|  |         padding: '4px 8px', | ||||||
|  |         fontSize: theme.typography.caption.fontSize, | ||||||
|  |         fontWeight: 500, | ||||||
|  |         lineHeight: theme.typography.caption.lineHeight, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiListItemIcon: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: { | ||||||
|  |         minWidth: 0, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiChip: { | ||||||
|  |     defaultProps: { | ||||||
|  |       size: 'small', | ||||||
|  |     }, | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         border: '1px solid', | ||||||
|  |         borderRadius: '999px', | ||||||
|  |         [`& .${chipClasses.label}`]: { | ||||||
|  |           fontWeight: 600, | ||||||
|  |         }, | ||||||
|  |         variants: [ | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'default', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               borderColor: gray[200], | ||||||
|  |               backgroundColor: gray[100], | ||||||
|  |               [`& .${chipClasses.label}`]: { | ||||||
|  |                 color: gray[500], | ||||||
|  |               }, | ||||||
|  |               [`& .${chipClasses.icon}`]: { | ||||||
|  |                 color: gray[500], | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 borderColor: gray[700], | ||||||
|  |                 backgroundColor: gray[800], | ||||||
|  |                 [`& .${chipClasses.label}`]: { | ||||||
|  |                   color: gray[300], | ||||||
|  |                 }, | ||||||
|  |                 [`& .${chipClasses.icon}`]: { | ||||||
|  |                   color: gray[300], | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'success', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               borderColor: green[200], | ||||||
|  |               backgroundColor: green[50], | ||||||
|  |               [`& .${chipClasses.label}`]: { | ||||||
|  |                 color: green[500], | ||||||
|  |               }, | ||||||
|  |               [`& .${chipClasses.icon}`]: { | ||||||
|  |                 color: green[500], | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 borderColor: green[800], | ||||||
|  |                 backgroundColor: green[900], | ||||||
|  |                 [`& .${chipClasses.label}`]: { | ||||||
|  |                   color: green[300], | ||||||
|  |                 }, | ||||||
|  |                 [`& .${chipClasses.icon}`]: { | ||||||
|  |                   color: green[300], | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'error', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               borderColor: red[100], | ||||||
|  |               backgroundColor: red[50], | ||||||
|  |               [`& .${chipClasses.label}`]: { | ||||||
|  |                 color: red[500], | ||||||
|  |               }, | ||||||
|  |               [`& .${chipClasses.icon}`]: { | ||||||
|  |                 color: red[500], | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 borderColor: red[800], | ||||||
|  |                 backgroundColor: red[900], | ||||||
|  |                 [`& .${chipClasses.label}`]: { | ||||||
|  |                   color: red[200], | ||||||
|  |                 }, | ||||||
|  |                 [`& .${chipClasses.icon}`]: { | ||||||
|  |                   color: red[300], | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { size: 'small' }, | ||||||
|  |             style: { | ||||||
|  |               maxHeight: 20, | ||||||
|  |               [`& .${chipClasses.label}`]: { | ||||||
|  |                 fontSize: theme.typography.caption.fontSize, | ||||||
|  |               }, | ||||||
|  |               [`& .${svgIconClasses.root}`]: { | ||||||
|  |                 fontSize: theme.typography.caption.fontSize, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { size: 'medium' }, | ||||||
|  |             style: { | ||||||
|  |               [`& .${chipClasses.label}`]: { | ||||||
|  |                 fontSize: theme.typography.caption.fontSize, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiTablePagination: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       actions: { | ||||||
|  |         display: 'flex', | ||||||
|  |         gap: 8, | ||||||
|  |         marginRight: 6, | ||||||
|  |         [`& .${iconButtonClasses.root}`]: { | ||||||
|  |           minWidth: 0, | ||||||
|  |           width: 36, | ||||||
|  |           height: 36, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiIcon: { | ||||||
|  |     defaultProps: { | ||||||
|  |       fontSize: 'small', | ||||||
|  |     }, | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: { | ||||||
|  |         variants: [ | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               fontSize: 'small', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               fontSize: '1rem', | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										46
									
								
								webClient/src/shared-theme/customizations/feedback.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								webClient/src/shared-theme/customizations/feedback.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | import { Theme, alpha, Components } from '@mui/material/styles'; | ||||||
|  | import { gray, orange } from '../themePrimitives'; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const feedbackCustomizations: Components<Theme> = { | ||||||
|  |   MuiAlert: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         borderRadius: 10, | ||||||
|  |         backgroundColor: orange[100], | ||||||
|  |         color: (theme.vars || theme).palette.text.primary, | ||||||
|  |         border: `1px solid ${alpha(orange[300], 0.5)}`, | ||||||
|  |         '& .MuiAlert-icon': { | ||||||
|  |           color: orange[500], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           backgroundColor: `${alpha(orange[900], 0.5)}`, | ||||||
|  |           border: `1px solid ${alpha(orange[800], 0.5)}`, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiDialog: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         '& .MuiDialog-paper': { | ||||||
|  |           borderRadius: '10px', | ||||||
|  |           border: '1px solid', | ||||||
|  |           borderColor: (theme.vars || theme).palette.divider, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiLinearProgress: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         height: 8, | ||||||
|  |         borderRadius: 8, | ||||||
|  |         backgroundColor: gray[200], | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           backgroundColor: gray[800], | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										445
									
								
								webClient/src/shared-theme/customizations/inputs.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										445
									
								
								webClient/src/shared-theme/customizations/inputs.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,445 @@ | |||||||
|  | import * as React from 'react'; | ||||||
|  | import { alpha, Theme, Components } from '@mui/material/styles'; | ||||||
|  | import { outlinedInputClasses } from '@mui/material/OutlinedInput'; | ||||||
|  | import { svgIconClasses } from '@mui/material/SvgIcon'; | ||||||
|  | import { toggleButtonGroupClasses } from '@mui/material/ToggleButtonGroup'; | ||||||
|  | import { toggleButtonClasses } from '@mui/material/ToggleButton'; | ||||||
|  | import CheckBoxOutlineBlankRoundedIcon from '@mui/icons-material/CheckBoxOutlineBlankRounded'; | ||||||
|  | import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; | ||||||
|  | import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded'; | ||||||
|  | import { gray, brand } from '../themePrimitives'; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const inputsCustomizations: Components<Theme> = { | ||||||
|  |   MuiButtonBase: { | ||||||
|  |     defaultProps: { | ||||||
|  |       disableTouchRipple: true, | ||||||
|  |       disableRipple: true, | ||||||
|  |     }, | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         boxSizing: 'border-box', | ||||||
|  |         transition: 'all 100ms ease-in', | ||||||
|  |         '&:focus-visible': { | ||||||
|  |           outline: `3px solid ${alpha(theme.palette.primary.main, 0.5)}`, | ||||||
|  |           outlineOffset: '2px', | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiButton: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         boxShadow: 'none', | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         textTransform: 'none', | ||||||
|  |         variants: [ | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               size: 'small', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               height: '2.25rem', | ||||||
|  |               padding: '8px 12px', | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               size: 'medium', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               height: '2.5rem', // 40px | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'primary', | ||||||
|  |               variant: 'contained', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               color: 'white', | ||||||
|  |               backgroundColor: gray[900], | ||||||
|  |               backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`, | ||||||
|  |               boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`, | ||||||
|  |               border: `1px solid ${gray[700]}`, | ||||||
|  |               '&:hover': { | ||||||
|  |                 backgroundImage: 'none', | ||||||
|  |                 backgroundColor: gray[700], | ||||||
|  |                 boxShadow: 'none', | ||||||
|  |               }, | ||||||
|  |               '&:active': { | ||||||
|  |                 backgroundColor: gray[800], | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 color: 'black', | ||||||
|  |                 backgroundColor: gray[50], | ||||||
|  |                 backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`, | ||||||
|  |                 boxShadow: 'inset 0 -1px 0  hsl(220, 30%, 80%)', | ||||||
|  |                 border: `1px solid ${gray[50]}`, | ||||||
|  |                 '&:hover': { | ||||||
|  |                   backgroundImage: 'none', | ||||||
|  |                   backgroundColor: gray[300], | ||||||
|  |                   boxShadow: 'none', | ||||||
|  |                 }, | ||||||
|  |                 '&:active': { | ||||||
|  |                   backgroundColor: gray[400], | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'secondary', | ||||||
|  |               variant: 'contained', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               color: 'white', | ||||||
|  |               backgroundColor: brand[300], | ||||||
|  |               backgroundImage: `linear-gradient(to bottom, ${alpha(brand[400], 0.8)}, ${brand[500]})`, | ||||||
|  |               boxShadow: `inset 0 2px 0 ${alpha(brand[200], 0.2)}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`, | ||||||
|  |               border: `1px solid ${brand[500]}`, | ||||||
|  |               '&:hover': { | ||||||
|  |                 backgroundColor: brand[700], | ||||||
|  |                 boxShadow: 'none', | ||||||
|  |               }, | ||||||
|  |               '&:active': { | ||||||
|  |                 backgroundColor: brand[700], | ||||||
|  |                 backgroundImage: 'none', | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               variant: 'outlined', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               color: (theme.vars || theme).palette.text.primary, | ||||||
|  |               border: '1px solid', | ||||||
|  |               borderColor: gray[200], | ||||||
|  |               backgroundColor: alpha(gray[50], 0.3), | ||||||
|  |               '&:hover': { | ||||||
|  |                 backgroundColor: gray[100], | ||||||
|  |                 borderColor: gray[300], | ||||||
|  |               }, | ||||||
|  |               '&:active': { | ||||||
|  |                 backgroundColor: gray[200], | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 backgroundColor: gray[800], | ||||||
|  |                 borderColor: gray[700], | ||||||
|  |  | ||||||
|  |                 '&:hover': { | ||||||
|  |                   backgroundColor: gray[900], | ||||||
|  |                   borderColor: gray[600], | ||||||
|  |                 }, | ||||||
|  |                 '&:active': { | ||||||
|  |                   backgroundColor: gray[900], | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'secondary', | ||||||
|  |               variant: 'outlined', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               color: brand[700], | ||||||
|  |               border: '1px solid', | ||||||
|  |               borderColor: brand[200], | ||||||
|  |               backgroundColor: brand[50], | ||||||
|  |               '&:hover': { | ||||||
|  |                 backgroundColor: brand[100], | ||||||
|  |                 borderColor: brand[400], | ||||||
|  |               }, | ||||||
|  |               '&:active': { | ||||||
|  |                 backgroundColor: alpha(brand[200], 0.7), | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 color: brand[50], | ||||||
|  |                 border: '1px solid', | ||||||
|  |                 borderColor: brand[900], | ||||||
|  |                 backgroundColor: alpha(brand[900], 0.3), | ||||||
|  |                 '&:hover': { | ||||||
|  |                   borderColor: brand[700], | ||||||
|  |                   backgroundColor: alpha(brand[900], 0.6), | ||||||
|  |                 }, | ||||||
|  |                 '&:active': { | ||||||
|  |                   backgroundColor: alpha(brand[900], 0.5), | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               variant: 'text', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               color: gray[600], | ||||||
|  |               '&:hover': { | ||||||
|  |                 backgroundColor: gray[100], | ||||||
|  |               }, | ||||||
|  |               '&:active': { | ||||||
|  |                 backgroundColor: gray[200], | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 color: gray[50], | ||||||
|  |                 '&:hover': { | ||||||
|  |                   backgroundColor: gray[700], | ||||||
|  |                 }, | ||||||
|  |                 '&:active': { | ||||||
|  |                   backgroundColor: alpha(gray[700], 0.7), | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               color: 'secondary', | ||||||
|  |               variant: 'text', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               color: brand[700], | ||||||
|  |               '&:hover': { | ||||||
|  |                 backgroundColor: alpha(brand[100], 0.5), | ||||||
|  |               }, | ||||||
|  |               '&:active': { | ||||||
|  |                 backgroundColor: alpha(brand[200], 0.7), | ||||||
|  |               }, | ||||||
|  |               ...theme.applyStyles('dark', { | ||||||
|  |                 color: brand[100], | ||||||
|  |                 '&:hover': { | ||||||
|  |                   backgroundColor: alpha(brand[900], 0.5), | ||||||
|  |                 }, | ||||||
|  |                 '&:active': { | ||||||
|  |                   backgroundColor: alpha(brand[900], 0.3), | ||||||
|  |                 }, | ||||||
|  |               }), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiIconButton: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         boxShadow: 'none', | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         textTransform: 'none', | ||||||
|  |         fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |         letterSpacing: 0, | ||||||
|  |         color: (theme.vars || theme).palette.text.primary, | ||||||
|  |         border: '1px solid ', | ||||||
|  |         borderColor: gray[200], | ||||||
|  |         backgroundColor: alpha(gray[50], 0.3), | ||||||
|  |         '&:hover': { | ||||||
|  |           backgroundColor: gray[100], | ||||||
|  |           borderColor: gray[300], | ||||||
|  |         }, | ||||||
|  |         '&:active': { | ||||||
|  |           backgroundColor: gray[200], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           backgroundColor: gray[800], | ||||||
|  |           borderColor: gray[700], | ||||||
|  |           '&:hover': { | ||||||
|  |             backgroundColor: gray[900], | ||||||
|  |             borderColor: gray[600], | ||||||
|  |           }, | ||||||
|  |           '&:active': { | ||||||
|  |             backgroundColor: gray[900], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         variants: [ | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               size: 'small', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               width: '2.25rem', | ||||||
|  |               height: '2.25rem', | ||||||
|  |               padding: '0.25rem', | ||||||
|  |               [`& .${svgIconClasses.root}`]: { fontSize: '1rem' }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               size: 'medium', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               width: '2.5rem', | ||||||
|  |               height: '2.5rem', | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiToggleButtonGroup: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         borderRadius: '10px', | ||||||
|  |         boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`, | ||||||
|  |         [`& .${toggleButtonGroupClasses.selected}`]: { | ||||||
|  |           color: brand[500], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           [`& .${toggleButtonGroupClasses.selected}`]: { | ||||||
|  |             color: '#fff', | ||||||
|  |           }, | ||||||
|  |           boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiToggleButton: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         padding: '12px 16px', | ||||||
|  |         textTransform: 'none', | ||||||
|  |         borderRadius: '10px', | ||||||
|  |         fontWeight: 500, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           color: gray[400], | ||||||
|  |           boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)', | ||||||
|  |           [`&.${toggleButtonClasses.selected}`]: { | ||||||
|  |             color: brand[300], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiCheckbox: { | ||||||
|  |     defaultProps: { | ||||||
|  |       disableRipple: true, | ||||||
|  |       icon: ( | ||||||
|  |         <CheckBoxOutlineBlankRoundedIcon sx={{ color: 'hsla(210, 0%, 0%, 0.0)' }} /> | ||||||
|  |       ), | ||||||
|  |       checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />, | ||||||
|  |       indeterminateIcon: <RemoveRoundedIcon sx={{ height: 14, width: 14 }} />, | ||||||
|  |     }, | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         margin: 10, | ||||||
|  |         height: 16, | ||||||
|  |         width: 16, | ||||||
|  |         borderRadius: 5, | ||||||
|  |         border: '1px solid ', | ||||||
|  |         borderColor: alpha(gray[300], 0.8), | ||||||
|  |         boxShadow: '0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset', | ||||||
|  |         backgroundColor: alpha(gray[100], 0.4), | ||||||
|  |         transition: 'border-color, background-color, 120ms ease-in', | ||||||
|  |         '&:hover': { | ||||||
|  |           borderColor: brand[300], | ||||||
|  |         }, | ||||||
|  |         '&.Mui-focusVisible': { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           outlineOffset: '2px', | ||||||
|  |           borderColor: brand[400], | ||||||
|  |         }, | ||||||
|  |         '&.Mui-checked': { | ||||||
|  |           color: 'white', | ||||||
|  |           backgroundColor: brand[500], | ||||||
|  |           borderColor: brand[500], | ||||||
|  |           boxShadow: `none`, | ||||||
|  |           '&:hover': { | ||||||
|  |             backgroundColor: brand[600], | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           borderColor: alpha(gray[700], 0.8), | ||||||
|  |           boxShadow: '0 0 0 1.5px hsl(210, 0%, 0%) inset', | ||||||
|  |           backgroundColor: alpha(gray[900], 0.8), | ||||||
|  |           '&:hover': { | ||||||
|  |             borderColor: brand[300], | ||||||
|  |           }, | ||||||
|  |           '&.Mui-focusVisible': { | ||||||
|  |             borderColor: brand[400], | ||||||
|  |             outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |             outlineOffset: '2px', | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiInputBase: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: { | ||||||
|  |         border: 'none', | ||||||
|  |       }, | ||||||
|  |       input: { | ||||||
|  |         '&::placeholder': { | ||||||
|  |           opacity: 0.7, | ||||||
|  |           color: gray[500], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiOutlinedInput: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       input: { | ||||||
|  |         padding: 0, | ||||||
|  |       }, | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         padding: '8px 12px', | ||||||
|  |         color: (theme.vars || theme).palette.text.primary, | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |         backgroundColor: (theme.vars || theme).palette.background.default, | ||||||
|  |         transition: 'border 120ms ease-in', | ||||||
|  |         '&:hover': { | ||||||
|  |           borderColor: gray[400], | ||||||
|  |         }, | ||||||
|  |         [`&.${outlinedInputClasses.focused}`]: { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           borderColor: brand[400], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           '&:hover': { | ||||||
|  |             borderColor: gray[500], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         variants: [ | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               size: 'small', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               height: '2.25rem', | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             props: { | ||||||
|  |               size: 'medium', | ||||||
|  |             }, | ||||||
|  |             style: { | ||||||
|  |               height: '2.5rem', | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }), | ||||||
|  |       notchedOutline: { | ||||||
|  |         border: 'none', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiInputAdornment: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         color: (theme.vars || theme).palette.grey[500], | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           color: (theme.vars || theme).palette.grey[400], | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiFormLabel: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         typography: theme.typography.caption, | ||||||
|  |         marginBottom: 8, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										279
									
								
								webClient/src/shared-theme/customizations/navigation.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								webClient/src/shared-theme/customizations/navigation.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,279 @@ | |||||||
|  | import * as React from 'react'; | ||||||
|  | import { Theme, alpha, Components } from '@mui/material/styles'; | ||||||
|  | import { SvgIconProps } from '@mui/material/SvgIcon'; | ||||||
|  | import { buttonBaseClasses } from '@mui/material/ButtonBase'; | ||||||
|  | import { dividerClasses } from '@mui/material/Divider'; | ||||||
|  | import { menuItemClasses } from '@mui/material/MenuItem'; | ||||||
|  | import { selectClasses } from '@mui/material/Select'; | ||||||
|  | import { tabClasses } from '@mui/material/Tab'; | ||||||
|  | import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded'; | ||||||
|  | import { gray, brand } from '../themePrimitives'; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const navigationCustomizations: Components<Theme> = { | ||||||
|  |   MuiMenuItem: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         padding: '6px 8px', | ||||||
|  |         [`&.${menuItemClasses.focusVisible}`]: { | ||||||
|  |           backgroundColor: 'transparent', | ||||||
|  |         }, | ||||||
|  |         [`&.${menuItemClasses.selected}`]: { | ||||||
|  |           [`&.${menuItemClasses.focusVisible}`]: { | ||||||
|  |             backgroundColor: alpha(theme.palette.action.selected, 0.3), | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiMenu: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       list: { | ||||||
|  |         gap: '0px', | ||||||
|  |         [`&.${dividerClasses.root}`]: { | ||||||
|  |           margin: '0 -8px', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       paper: ({ theme }) => ({ | ||||||
|  |         marginTop: '4px', | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |         backgroundImage: 'none', | ||||||
|  |         background: 'hsl(0, 0%, 100%)', | ||||||
|  |         boxShadow: | ||||||
|  |           'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px', | ||||||
|  |         [`& .${buttonBaseClasses.root}`]: { | ||||||
|  |           '&.Mui-selected': { | ||||||
|  |             backgroundColor: alpha(theme.palette.action.selected, 0.3), | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           background: gray[900], | ||||||
|  |           boxShadow: | ||||||
|  |             'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px', | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiSelect: { | ||||||
|  |     defaultProps: { | ||||||
|  |       IconComponent: React.forwardRef<SVGSVGElement, SvgIconProps>((props, ref) => ( | ||||||
|  |         <UnfoldMoreRoundedIcon fontSize="small" {...props} ref={ref} /> | ||||||
|  |       )), | ||||||
|  |     }, | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         border: '1px solid', | ||||||
|  |         borderColor: gray[200], | ||||||
|  |         backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |         boxShadow: `inset 0 1px 0 1px hsla(220, 0%, 100%, 0.6), inset 0 -1px 0 1px hsla(220, 35%, 90%, 0.5)`, | ||||||
|  |         '&:hover': { | ||||||
|  |           borderColor: gray[300], | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |           boxShadow: 'none', | ||||||
|  |         }, | ||||||
|  |         [`&.${selectClasses.focused}`]: { | ||||||
|  |           outlineOffset: 0, | ||||||
|  |           borderColor: gray[400], | ||||||
|  |         }, | ||||||
|  |         '&:before, &:after': { | ||||||
|  |           display: 'none', | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |           borderColor: gray[700], | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |           boxShadow: `inset 0 1px 0 1px ${alpha(gray[700], 0.15)}, inset 0 -1px 0 1px hsla(220, 0%, 0%, 0.7)`, | ||||||
|  |           '&:hover': { | ||||||
|  |             borderColor: alpha(gray[700], 0.7), | ||||||
|  |             backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |             boxShadow: 'none', | ||||||
|  |           }, | ||||||
|  |           [`&.${selectClasses.focused}`]: { | ||||||
|  |             outlineOffset: 0, | ||||||
|  |             borderColor: gray[900], | ||||||
|  |           }, | ||||||
|  |           '&:before, &:after': { | ||||||
|  |             display: 'none', | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |       select: ({ theme }) => ({ | ||||||
|  |         display: 'flex', | ||||||
|  |         alignItems: 'center', | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           display: 'flex', | ||||||
|  |           alignItems: 'center', | ||||||
|  |           '&:focus-visible': { | ||||||
|  |             backgroundColor: gray[900], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiLink: { | ||||||
|  |     defaultProps: { | ||||||
|  |       underline: 'none', | ||||||
|  |     }, | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         color: (theme.vars || theme).palette.text.primary, | ||||||
|  |         fontWeight: 500, | ||||||
|  |         position: 'relative', | ||||||
|  |         textDecoration: 'none', | ||||||
|  |         width: 'fit-content', | ||||||
|  |         '&::before': { | ||||||
|  |           content: '""', | ||||||
|  |           position: 'absolute', | ||||||
|  |           width: '100%', | ||||||
|  |           height: '1px', | ||||||
|  |           bottom: 0, | ||||||
|  |           left: 0, | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.text.secondary, | ||||||
|  |           opacity: 0.3, | ||||||
|  |           transition: 'width 0.3s ease, opacity 0.3s ease', | ||||||
|  |         }, | ||||||
|  |         '&:hover::before': { | ||||||
|  |           width: 0, | ||||||
|  |         }, | ||||||
|  |         '&:focus-visible': { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           outlineOffset: '4px', | ||||||
|  |           borderRadius: '2px', | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiDrawer: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       paper: ({ theme }) => ({ | ||||||
|  |         backgroundColor: (theme.vars || theme).palette.background.default, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiPaginationItem: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         '&.Mui-selected': { | ||||||
|  |           color: 'white', | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.grey[900], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           '&.Mui-selected': { | ||||||
|  |             color: 'black', | ||||||
|  |             backgroundColor: (theme.vars || theme).palette.grey[50], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiTabs: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: { minHeight: 'fit-content' }, | ||||||
|  |       indicator: ({ theme }) => ({ | ||||||
|  |         backgroundColor: (theme.vars || theme).palette.grey[800], | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.grey[200], | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiTab: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         padding: '6px 8px', | ||||||
|  |         marginBottom: '8px', | ||||||
|  |         textTransform: 'none', | ||||||
|  |         minWidth: 'fit-content', | ||||||
|  |         minHeight: 'fit-content', | ||||||
|  |         color: (theme.vars || theme).palette.text.secondary, | ||||||
|  |         borderRadius: (theme.vars || theme).shape.borderRadius, | ||||||
|  |         border: '1px solid', | ||||||
|  |         borderColor: 'transparent', | ||||||
|  |         ':hover': { | ||||||
|  |           color: (theme.vars || theme).palette.text.primary, | ||||||
|  |           backgroundColor: gray[100], | ||||||
|  |           borderColor: gray[200], | ||||||
|  |         }, | ||||||
|  |         [`&.${tabClasses.selected}`]: { | ||||||
|  |           color: gray[900], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           ':hover': { | ||||||
|  |             color: (theme.vars || theme).palette.text.primary, | ||||||
|  |             backgroundColor: gray[800], | ||||||
|  |             borderColor: gray[700], | ||||||
|  |           }, | ||||||
|  |           [`&.${tabClasses.selected}`]: { | ||||||
|  |             color: '#fff', | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiStepConnector: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       line: ({ theme }) => ({ | ||||||
|  |         borderTop: '1px solid', | ||||||
|  |         borderColor: (theme.vars || theme).palette.divider, | ||||||
|  |         flex: 1, | ||||||
|  |         borderRadius: '99px', | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiStepIcon: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         color: 'transparent', | ||||||
|  |         border: `1px solid ${gray[400]}`, | ||||||
|  |         width: 12, | ||||||
|  |         height: 12, | ||||||
|  |         borderRadius: '50%', | ||||||
|  |         '& text': { | ||||||
|  |           display: 'none', | ||||||
|  |         }, | ||||||
|  |         '&.Mui-active': { | ||||||
|  |           border: 'none', | ||||||
|  |           color: (theme.vars || theme).palette.primary.main, | ||||||
|  |         }, | ||||||
|  |         '&.Mui-completed': { | ||||||
|  |           border: 'none', | ||||||
|  |           color: (theme.vars || theme).palette.success.main, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles('dark', { | ||||||
|  |           border: `1px solid ${gray[700]}`, | ||||||
|  |           '&.Mui-active': { | ||||||
|  |             border: 'none', | ||||||
|  |             color: (theme.vars || theme).palette.primary.light, | ||||||
|  |           }, | ||||||
|  |           '&.Mui-completed': { | ||||||
|  |             border: 'none', | ||||||
|  |             color: (theme.vars || theme).palette.success.light, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         variants: [ | ||||||
|  |           { | ||||||
|  |             props: { completed: true }, | ||||||
|  |             style: { | ||||||
|  |               width: 12, | ||||||
|  |               height: 12, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiStepLabel: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       label: ({ theme }) => ({ | ||||||
|  |         '&.Mui-completed': { | ||||||
|  |           opacity: 0.6, | ||||||
|  |           ...theme.applyStyles('dark', { opacity: 0.5 }), | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										113
									
								
								webClient/src/shared-theme/customizations/surfaces.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								webClient/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, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										410
									
								
								webClient/src/shared-theme/themePrimitives.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										410
									
								
								webClient/src/shared-theme/themePrimitives.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,410 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-empty-object-type */ | ||||||
|  | import { alpha, createTheme, PaletteMode, Shadows } from "@mui/material/styles"; | ||||||
|  |  | ||||||
|  | declare module "@mui/material/Paper" { | ||||||
|  |   interface PaperPropsVariantOverrides { | ||||||
|  |     highlighted: true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | declare module "@mui/material/styles/createPalette" { | ||||||
|  |   interface ColorRange { | ||||||
|  |     50: string; | ||||||
|  |     100: string; | ||||||
|  |     200: string; | ||||||
|  |     300: string; | ||||||
|  |     400: string; | ||||||
|  |     500: string; | ||||||
|  |     600: string; | ||||||
|  |     700: string; | ||||||
|  |     800: string; | ||||||
|  |     900: string; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   interface PaletteColor extends ColorRange {} | ||||||
|  |  | ||||||
|  |   interface Palette { | ||||||
|  |     baseShadow: string; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const defaultTheme = createTheme(); | ||||||
|  |  | ||||||
|  | const customShadows: Shadows = [...defaultTheme.shadows]; | ||||||
|  |  | ||||||
|  | export const brand = { | ||||||
|  |   50: "hsl(210, 100%, 95%)", | ||||||
|  |   100: "hsl(210, 100%, 92%)", | ||||||
|  |   200: "hsl(210, 100%, 80%)", | ||||||
|  |   300: "hsl(210, 100%, 65%)", | ||||||
|  |   400: "hsl(210, 98%, 48%)", | ||||||
|  |   500: "hsl(210, 98%, 42%)", | ||||||
|  |   600: "hsl(210, 98%, 55%)", | ||||||
|  |   700: "hsl(210, 100%, 35%)", | ||||||
|  |   800: "hsl(210, 100%, 16%)", | ||||||
|  |   900: "hsl(210, 100%, 21%)", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const gray = { | ||||||
|  |   50: "hsl(220, 35%, 97%)", | ||||||
|  |   100: "hsl(220, 30%, 94%)", | ||||||
|  |   200: "hsl(220, 20%, 88%)", | ||||||
|  |   300: "hsl(220, 20%, 80%)", | ||||||
|  |   400: "hsl(220, 20%, 65%)", | ||||||
|  |   500: "hsl(220, 20%, 42%)", | ||||||
|  |   600: "hsl(220, 20%, 35%)", | ||||||
|  |   700: "hsl(220, 20%, 25%)", | ||||||
|  |   800: "hsl(220, 30%, 6%)", | ||||||
|  |   900: "hsl(220, 35%, 3%)", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const green = { | ||||||
|  |   50: "hsl(120, 80%, 98%)", | ||||||
|  |   100: "hsl(120, 75%, 94%)", | ||||||
|  |   200: "hsl(120, 75%, 87%)", | ||||||
|  |   300: "hsl(120, 61%, 77%)", | ||||||
|  |   400: "hsl(120, 44%, 53%)", | ||||||
|  |   500: "hsl(120, 59%, 30%)", | ||||||
|  |   600: "hsl(120, 70%, 25%)", | ||||||
|  |   700: "hsl(120, 75%, 16%)", | ||||||
|  |   800: "hsl(120, 84%, 10%)", | ||||||
|  |   900: "hsl(120, 87%, 6%)", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const orange = { | ||||||
|  |   50: "hsl(45, 100%, 97%)", | ||||||
|  |   100: "hsl(45, 92%, 90%)", | ||||||
|  |   200: "hsl(45, 94%, 80%)", | ||||||
|  |   300: "hsl(45, 90%, 65%)", | ||||||
|  |   400: "hsl(45, 90%, 40%)", | ||||||
|  |   500: "hsl(45, 90%, 35%)", | ||||||
|  |   600: "hsl(45, 91%, 25%)", | ||||||
|  |   700: "hsl(45, 94%, 20%)", | ||||||
|  |   800: "hsl(45, 95%, 16%)", | ||||||
|  |   900: "hsl(45, 93%, 12%)", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const red = { | ||||||
|  |   50: "hsl(0, 100%, 97%)", | ||||||
|  |   100: "hsl(0, 92%, 90%)", | ||||||
|  |   200: "hsl(0, 94%, 80%)", | ||||||
|  |   300: "hsl(0, 90%, 65%)", | ||||||
|  |   400: "hsl(0, 90%, 40%)", | ||||||
|  |   500: "hsl(0, 90%, 30%)", | ||||||
|  |   600: "hsl(0, 91%, 25%)", | ||||||
|  |   700: "hsl(0, 94%, 18%)", | ||||||
|  |   800: "hsl(0, 95%, 12%)", | ||||||
|  |   900: "hsl(0, 93%, 6%)", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const getDesignTokens = (mode: PaletteMode) => { | ||||||
|  |   customShadows[1] = | ||||||
|  |     mode === "dark" | ||||||
|  |       ? "hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px" | ||||||
|  |       : "hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px"; | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     palette: { | ||||||
|  |       mode, | ||||||
|  |       primary: { | ||||||
|  |         light: brand[200], | ||||||
|  |         main: brand[400], | ||||||
|  |         dark: brand[700], | ||||||
|  |         contrastText: brand[50], | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           contrastText: brand[50], | ||||||
|  |           light: brand[300], | ||||||
|  |           main: brand[400], | ||||||
|  |           dark: brand[700], | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       info: { | ||||||
|  |         light: brand[100], | ||||||
|  |         main: brand[300], | ||||||
|  |         dark: brand[600], | ||||||
|  |         contrastText: gray[50], | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           contrastText: brand[300], | ||||||
|  |           light: brand[500], | ||||||
|  |           main: brand[700], | ||||||
|  |           dark: brand[900], | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       warning: { | ||||||
|  |         light: orange[300], | ||||||
|  |         main: orange[400], | ||||||
|  |         dark: orange[800], | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           light: orange[400], | ||||||
|  |           main: orange[500], | ||||||
|  |           dark: orange[700], | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       error: { | ||||||
|  |         light: red[300], | ||||||
|  |         main: red[400], | ||||||
|  |         dark: red[800], | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           light: red[400], | ||||||
|  |           main: red[500], | ||||||
|  |           dark: red[700], | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       success: { | ||||||
|  |         light: green[300], | ||||||
|  |         main: green[400], | ||||||
|  |         dark: green[800], | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           light: green[400], | ||||||
|  |           main: green[500], | ||||||
|  |           dark: green[700], | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       grey: { | ||||||
|  |         ...gray, | ||||||
|  |       }, | ||||||
|  |       divider: mode === "dark" ? alpha(gray[700], 0.6) : alpha(gray[300], 0.4), | ||||||
|  |       background: { | ||||||
|  |         default: "hsl(0, 0%, 99%)", | ||||||
|  |         paper: "hsl(220, 35%, 97%)", | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           default: gray[900], | ||||||
|  |           paper: "hsl(220, 30%, 7%)", | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       text: { | ||||||
|  |         primary: gray[800], | ||||||
|  |         secondary: gray[600], | ||||||
|  |         warning: orange[400], | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           primary: "hsl(0, 0%, 100%)", | ||||||
|  |           secondary: gray[400], | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |       action: { | ||||||
|  |         hover: alpha(gray[200], 0.2), | ||||||
|  |         selected: `${alpha(gray[200], 0.3)}`, | ||||||
|  |         ...(mode === "dark" && { | ||||||
|  |           hover: alpha(gray[600], 0.2), | ||||||
|  |           selected: alpha(gray[600], 0.3), | ||||||
|  |         }), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     typography: { | ||||||
|  |       fontFamily: "Inter, sans-serif", | ||||||
|  |       h1: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(48), | ||||||
|  |         fontWeight: 600, | ||||||
|  |         lineHeight: 1.2, | ||||||
|  |         letterSpacing: -0.5, | ||||||
|  |       }, | ||||||
|  |       h2: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(36), | ||||||
|  |         fontWeight: 600, | ||||||
|  |         lineHeight: 1.2, | ||||||
|  |       }, | ||||||
|  |       h3: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(30), | ||||||
|  |         lineHeight: 1.2, | ||||||
|  |       }, | ||||||
|  |       h4: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(24), | ||||||
|  |         fontWeight: 600, | ||||||
|  |         lineHeight: 1.5, | ||||||
|  |       }, | ||||||
|  |       h5: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(20), | ||||||
|  |         fontWeight: 600, | ||||||
|  |       }, | ||||||
|  |       h6: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(18), | ||||||
|  |         fontWeight: 600, | ||||||
|  |       }, | ||||||
|  |       subtitle1: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(18), | ||||||
|  |       }, | ||||||
|  |       subtitle2: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(14), | ||||||
|  |         fontWeight: 500, | ||||||
|  |       }, | ||||||
|  |       body1: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(14), | ||||||
|  |       }, | ||||||
|  |       body2: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(14), | ||||||
|  |         fontWeight: 400, | ||||||
|  |       }, | ||||||
|  |       caption: { | ||||||
|  |         fontSize: defaultTheme.typography.pxToRem(12), | ||||||
|  |         fontWeight: 400, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     shape: { | ||||||
|  |       borderRadius: 8, | ||||||
|  |     }, | ||||||
|  |     shadows: customShadows, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const colorSchemes = { | ||||||
|  |   light: { | ||||||
|  |     palette: { | ||||||
|  |       primary: { | ||||||
|  |         light: brand[200], | ||||||
|  |         main: brand[400], | ||||||
|  |         dark: brand[700], | ||||||
|  |         contrastText: brand[50], | ||||||
|  |       }, | ||||||
|  |       info: { | ||||||
|  |         light: brand[100], | ||||||
|  |         main: brand[300], | ||||||
|  |         dark: brand[600], | ||||||
|  |         contrastText: gray[50], | ||||||
|  |       }, | ||||||
|  |       warning: { | ||||||
|  |         light: orange[300], | ||||||
|  |         main: orange[400], | ||||||
|  |         dark: orange[800], | ||||||
|  |       }, | ||||||
|  |       error: { | ||||||
|  |         light: red[300], | ||||||
|  |         main: red[400], | ||||||
|  |         dark: red[800], | ||||||
|  |       }, | ||||||
|  |       success: { | ||||||
|  |         light: green[300], | ||||||
|  |         main: green[400], | ||||||
|  |         dark: green[800], | ||||||
|  |       }, | ||||||
|  |       grey: { | ||||||
|  |         ...gray, | ||||||
|  |       }, | ||||||
|  |       divider: alpha(gray[300], 0.4), | ||||||
|  |       background: { | ||||||
|  |         default: "hsl(0, 0%, 99%)", | ||||||
|  |         paper: "hsl(220, 35%, 97%)", | ||||||
|  |       }, | ||||||
|  |       text: { | ||||||
|  |         primary: gray[800], | ||||||
|  |         secondary: gray[600], | ||||||
|  |         warning: orange[400], | ||||||
|  |       }, | ||||||
|  |       action: { | ||||||
|  |         hover: alpha(gray[200], 0.2), | ||||||
|  |         selected: `${alpha(gray[200], 0.3)}`, | ||||||
|  |       }, | ||||||
|  |       baseShadow: | ||||||
|  |         "hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   dark: { | ||||||
|  |     palette: { | ||||||
|  |       primary: { | ||||||
|  |         contrastText: brand[50], | ||||||
|  |         light: brand[300], | ||||||
|  |         main: brand[400], | ||||||
|  |         dark: brand[700], | ||||||
|  |       }, | ||||||
|  |       info: { | ||||||
|  |         contrastText: brand[300], | ||||||
|  |         light: brand[500], | ||||||
|  |         main: brand[700], | ||||||
|  |         dark: brand[900], | ||||||
|  |       }, | ||||||
|  |       warning: { | ||||||
|  |         light: orange[400], | ||||||
|  |         main: orange[500], | ||||||
|  |         dark: orange[700], | ||||||
|  |       }, | ||||||
|  |       error: { | ||||||
|  |         light: red[400], | ||||||
|  |         main: red[500], | ||||||
|  |         dark: red[700], | ||||||
|  |       }, | ||||||
|  |       success: { | ||||||
|  |         light: green[400], | ||||||
|  |         main: green[500], | ||||||
|  |         dark: green[700], | ||||||
|  |       }, | ||||||
|  |       grey: { | ||||||
|  |         ...gray, | ||||||
|  |       }, | ||||||
|  |       divider: alpha(gray[700], 0.6), | ||||||
|  |       background: { | ||||||
|  |         default: gray[900], | ||||||
|  |         paper: "hsl(220, 30%, 7%)", | ||||||
|  |       }, | ||||||
|  |       text: { | ||||||
|  |         primary: "hsl(0, 0%, 100%)", | ||||||
|  |         secondary: gray[400], | ||||||
|  |       }, | ||||||
|  |       action: { | ||||||
|  |         hover: alpha(gray[600], 0.2), | ||||||
|  |         selected: alpha(gray[600], 0.3), | ||||||
|  |       }, | ||||||
|  |       baseShadow: | ||||||
|  |         "hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const typography = { | ||||||
|  |   fontFamily: "Inter, sans-serif", | ||||||
|  |   h1: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(48), | ||||||
|  |     fontWeight: 600, | ||||||
|  |     lineHeight: 1.2, | ||||||
|  |     letterSpacing: -0.5, | ||||||
|  |   }, | ||||||
|  |   h2: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(36), | ||||||
|  |     fontWeight: 600, | ||||||
|  |     lineHeight: 1.2, | ||||||
|  |   }, | ||||||
|  |   h3: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(30), | ||||||
|  |     lineHeight: 1.2, | ||||||
|  |   }, | ||||||
|  |   h4: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(24), | ||||||
|  |     fontWeight: 600, | ||||||
|  |     lineHeight: 1.5, | ||||||
|  |   }, | ||||||
|  |   h5: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(20), | ||||||
|  |     fontWeight: 600, | ||||||
|  |   }, | ||||||
|  |   h6: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(18), | ||||||
|  |     fontWeight: 600, | ||||||
|  |   }, | ||||||
|  |   subtitle1: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(18), | ||||||
|  |   }, | ||||||
|  |   subtitle2: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(14), | ||||||
|  |     fontWeight: 500, | ||||||
|  |   }, | ||||||
|  |   body1: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(14), | ||||||
|  |   }, | ||||||
|  |   body2: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(14), | ||||||
|  |     fontWeight: 400, | ||||||
|  |   }, | ||||||
|  |   caption: { | ||||||
|  |     fontSize: defaultTheme.typography.pxToRem(12), | ||||||
|  |     fontWeight: 400, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const shape = { | ||||||
|  |   borderRadius: 8, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // @ts-ignore | ||||||
|  | const defaultShadows: Shadows = [ | ||||||
|  |   "none", | ||||||
|  |   "var(--template-palette-baseShadow)", | ||||||
|  |   ...defaultTheme.shadows.slice(2), | ||||||
|  | ]; | ||||||
|  | export const shadows = defaultShadows; | ||||||
							
								
								
									
										76
									
								
								webClient/src/theme/customizations/charts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								webClient/src/theme/customizations/charts.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,76 @@ | |||||||
|  | import { Theme } from "@mui/material/styles"; | ||||||
|  | import { axisClasses, chartsGridClasses, legendClasses } from "@mui/x-charts"; | ||||||
|  | import type { ChartsComponents } from "@mui/x-charts/themeAugmentation"; | ||||||
|  | import { gray } from "../../shared-theme/themePrimitives"; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const chartsCustomizations: ChartsComponents<Theme> = { | ||||||
|  |   MuiChartsAxis: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         [`& .${axisClasses.line}`]: { | ||||||
|  |           stroke: gray[300], | ||||||
|  |         }, | ||||||
|  |         [`& .${axisClasses.tick}`]: { stroke: gray[300] }, | ||||||
|  |         [`& .${axisClasses.tickLabel}`]: { | ||||||
|  |           fill: gray[500], | ||||||
|  |           fontWeight: 500, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           [`& .${axisClasses.line}`]: { | ||||||
|  |             stroke: gray[700], | ||||||
|  |           }, | ||||||
|  |           [`& .${axisClasses.tick}`]: { stroke: gray[700] }, | ||||||
|  |           [`& .${axisClasses.tickLabel}`]: { | ||||||
|  |             fill: gray[300], | ||||||
|  |             fontWeight: 500, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiChartsTooltip: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       mark: ({ theme }) => ({ | ||||||
|  |         ry: 6, | ||||||
|  |         boxShadow: "none", | ||||||
|  |         border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |       }), | ||||||
|  |       table: ({ theme }) => ({ | ||||||
|  |         border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |         borderRadius: theme.shape.borderRadius, | ||||||
|  |         background: "hsl(0, 0%, 100%)", | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           background: gray[900], | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiChartsLegend: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: { | ||||||
|  |         [`& .${legendClasses.mark}`]: { | ||||||
|  |           ry: 6, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiChartsGrid: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         [`& .${chartsGridClasses.line}`]: { | ||||||
|  |           stroke: gray[200], | ||||||
|  |           strokeDasharray: "4 2", | ||||||
|  |           strokeWidth: 0.8, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           [`& .${chartsGridClasses.line}`]: { | ||||||
|  |             stroke: gray[700], | ||||||
|  |             strokeDasharray: "4 2", | ||||||
|  |             strokeWidth: 0.8, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										138
									
								
								webClient/src/theme/customizations/dataGrid.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								webClient/src/theme/customizations/dataGrid.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | |||||||
|  | import { checkboxClasses } from "@mui/material/Checkbox"; | ||||||
|  | import { iconButtonClasses } from "@mui/material/IconButton"; | ||||||
|  | import { listClasses } from "@mui/material/List"; | ||||||
|  | import { listItemIconClasses } from "@mui/material/ListItemIcon"; | ||||||
|  | import { menuItemClasses } from "@mui/material/MenuItem"; | ||||||
|  | import { paperClasses } from "@mui/material/Paper"; | ||||||
|  | import { alpha, Theme } from "@mui/material/styles"; | ||||||
|  | import { tablePaginationClasses } from "@mui/material/TablePagination"; | ||||||
|  | import { gridClasses } from "@mui/x-data-grid"; | ||||||
|  | import type { DataGridProComponents } from "@mui/x-data-grid-pro/themeAugmentation"; | ||||||
|  | import type { DataGridComponents } from "@mui/x-data-grid/themeAugmentation"; | ||||||
|  | import { gray } from "../../shared-theme/themePrimitives"; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const dataGridCustomizations: DataGridProComponents<Theme> & | ||||||
|  |   DataGridComponents<Theme> = { | ||||||
|  |   MuiDataGrid: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         "--DataGrid-overlayHeight": "300px", | ||||||
|  |         overflow: "clip", | ||||||
|  |         borderColor: (theme.vars || theme).palette.divider, | ||||||
|  |         backgroundColor: (theme.vars || theme).palette.background.default, | ||||||
|  |         [`& .${gridClasses.columnHeader}`]: { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |         }, | ||||||
|  |         [`& .${gridClasses.footerContainer}`]: { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.background.paper, | ||||||
|  |         }, | ||||||
|  |         [`& .${checkboxClasses.root}`]: { | ||||||
|  |           padding: theme.spacing(0.5), | ||||||
|  |           "& > svg": { | ||||||
|  |             fontSize: "1rem", | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         [`& .${tablePaginationClasses.root}`]: { | ||||||
|  |           marginRight: theme.spacing(1), | ||||||
|  |           "& .MuiIconButton-root": { | ||||||
|  |             maxHeight: 32, | ||||||
|  |             maxWidth: 32, | ||||||
|  |             "& > svg": { | ||||||
|  |               fontSize: "1rem", | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |       cell: ({ theme }) => ({ | ||||||
|  |         borderTopColor: (theme.vars || theme).palette.divider, | ||||||
|  |       }), | ||||||
|  |       menu: ({ theme }) => ({ | ||||||
|  |         borderRadius: theme.shape.borderRadius, | ||||||
|  |         backgroundImage: "none", | ||||||
|  |         [`& .${paperClasses.root}`]: { | ||||||
|  |           border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         [`& .${menuItemClasses.root}`]: { | ||||||
|  |           margin: "0 4px", | ||||||
|  |         }, | ||||||
|  |         [`& .${listItemIconClasses.root}`]: { | ||||||
|  |           marginRight: 0, | ||||||
|  |         }, | ||||||
|  |         [`& .${listClasses.root}`]: { | ||||||
|  |           paddingLeft: 0, | ||||||
|  |           paddingRight: 0, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |  | ||||||
|  |       row: ({ theme }) => ({ | ||||||
|  |         "&:last-of-type": { | ||||||
|  |           borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |         }, | ||||||
|  |         "&:hover": { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |         }, | ||||||
|  |         "&.Mui-selected": { | ||||||
|  |           background: (theme.vars || theme).palette.action.selected, | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |       iconButtonContainer: ({ theme }) => ({ | ||||||
|  |         [`& .${iconButtonClasses.root}`]: { | ||||||
|  |           border: "none", | ||||||
|  |           backgroundColor: "transparent", | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: alpha(theme.palette.action.selected, 0.3), | ||||||
|  |           }, | ||||||
|  |           "&:active": { | ||||||
|  |             backgroundColor: gray[200], | ||||||
|  |           }, | ||||||
|  |           ...theme.applyStyles("dark", { | ||||||
|  |             color: gray[50], | ||||||
|  |             "&:hover": { | ||||||
|  |               backgroundColor: gray[800], | ||||||
|  |             }, | ||||||
|  |             "&:active": { | ||||||
|  |               backgroundColor: gray[900], | ||||||
|  |             }, | ||||||
|  |           }), | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |       menuIconButton: ({ theme }) => ({ | ||||||
|  |         border: "none", | ||||||
|  |         backgroundColor: "transparent", | ||||||
|  |         "&:hover": { | ||||||
|  |           backgroundColor: gray[100], | ||||||
|  |         }, | ||||||
|  |         "&:active": { | ||||||
|  |           backgroundColor: gray[200], | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           color: gray[50], | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: gray[800], | ||||||
|  |           }, | ||||||
|  |           "&:active": { | ||||||
|  |             backgroundColor: gray[900], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |       filterForm: ({ theme }) => ({ | ||||||
|  |         gap: theme.spacing(1), | ||||||
|  |         alignItems: "flex-end", | ||||||
|  |       }), | ||||||
|  |       columnsManagementHeader: ({ theme }) => ({ | ||||||
|  |         paddingRight: theme.spacing(3), | ||||||
|  |         paddingLeft: theme.spacing(3), | ||||||
|  |       }), | ||||||
|  |       columnHeaderTitleContainer: { | ||||||
|  |         flexGrow: 1, | ||||||
|  |         justifyContent: "space-between", | ||||||
|  |       }, | ||||||
|  |       columnHeaderDraggableContainer: { paddingRight: 2 }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										182
									
								
								webClient/src/theme/customizations/datePickers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								webClient/src/theme/customizations/datePickers.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,182 @@ | |||||||
|  | import { menuItemClasses } from "@mui/material/MenuItem"; | ||||||
|  | import { alpha, Theme } from "@mui/material/styles"; | ||||||
|  | import { | ||||||
|  |   pickersDayClasses, | ||||||
|  |   pickersMonthClasses, | ||||||
|  |   pickersYearClasses, | ||||||
|  | } from "@mui/x-date-pickers"; | ||||||
|  | import type { PickersProComponents } from "@mui/x-date-pickers-pro/themeAugmentation"; | ||||||
|  | import type { PickerComponents } from "@mui/x-date-pickers/themeAugmentation"; | ||||||
|  | import { brand, gray } from "../../shared-theme/themePrimitives"; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const datePickersCustomizations: PickersProComponents<Theme> & | ||||||
|  |   PickerComponents<Theme> = { | ||||||
|  |   MuiPickersPopper: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       paper: ({ theme }) => ({ | ||||||
|  |         marginTop: 4, | ||||||
|  |         borderRadius: theme.shape.borderRadius, | ||||||
|  |         border: `1px solid ${(theme.vars || theme).palette.divider}`, | ||||||
|  |         backgroundImage: "none", | ||||||
|  |         background: "hsl(0, 0%, 100%)", | ||||||
|  |         boxShadow: | ||||||
|  |           "hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px", | ||||||
|  |         [`& .${menuItemClasses.root}`]: { | ||||||
|  |           borderRadius: 6, | ||||||
|  |           margin: "0 6px", | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           background: gray[900], | ||||||
|  |           boxShadow: | ||||||
|  |             "hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px", | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiPickersArrowSwitcher: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       spacer: { width: 16 }, | ||||||
|  |       button: ({ theme }) => ({ | ||||||
|  |         backgroundColor: "transparent", | ||||||
|  |         color: (theme.vars || theme).palette.grey[500], | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           color: (theme.vars || theme).palette.grey[400], | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiPickersCalendarHeader: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       switchViewButton: { | ||||||
|  |         padding: 0, | ||||||
|  |         border: "none", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiPickersMonth: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       monthButton: ({ theme }) => ({ | ||||||
|  |         fontSize: theme.typography.body1.fontSize, | ||||||
|  |         color: (theme.vars || theme).palette.grey[600], | ||||||
|  |         padding: theme.spacing(0.5), | ||||||
|  |         borderRadius: theme.shape.borderRadius, | ||||||
|  |         "&:hover": { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |         }, | ||||||
|  |         [`&.${pickersMonthClasses.selected}`]: { | ||||||
|  |           backgroundColor: gray[700], | ||||||
|  |           fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |         }, | ||||||
|  |         "&:focus": { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           outlineOffset: "2px", | ||||||
|  |           backgroundColor: "transparent", | ||||||
|  |           [`&.${pickersMonthClasses.selected}`]: { backgroundColor: gray[700] }, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           color: (theme.vars || theme).palette.grey[300], | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |           }, | ||||||
|  |           [`&.${pickersMonthClasses.selected}`]: { | ||||||
|  |             color: (theme.vars || theme).palette.common.black, | ||||||
|  |             fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |             backgroundColor: gray[300], | ||||||
|  |           }, | ||||||
|  |           "&:focus": { | ||||||
|  |             outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |             outlineOffset: "2px", | ||||||
|  |             backgroundColor: "transparent", | ||||||
|  |             [`&.${pickersMonthClasses.selected}`]: { | ||||||
|  |               backgroundColor: gray[300], | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiPickersYear: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       yearButton: ({ theme }) => ({ | ||||||
|  |         fontSize: theme.typography.body1.fontSize, | ||||||
|  |         color: (theme.vars || theme).palette.grey[600], | ||||||
|  |         padding: theme.spacing(0.5), | ||||||
|  |         borderRadius: theme.shape.borderRadius, | ||||||
|  |         height: "fit-content", | ||||||
|  |         "&:hover": { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |         }, | ||||||
|  |         [`&.${pickersYearClasses.selected}`]: { | ||||||
|  |           backgroundColor: gray[700], | ||||||
|  |           fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |         }, | ||||||
|  |         "&:focus": { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           outlineOffset: "2px", | ||||||
|  |           backgroundColor: "transparent", | ||||||
|  |           [`&.${pickersYearClasses.selected}`]: { backgroundColor: gray[700] }, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           color: (theme.vars || theme).palette.grey[300], | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |           }, | ||||||
|  |           [`&.${pickersYearClasses.selected}`]: { | ||||||
|  |             color: (theme.vars || theme).palette.common.black, | ||||||
|  |             fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |             backgroundColor: gray[300], | ||||||
|  |           }, | ||||||
|  |           "&:focus": { | ||||||
|  |             outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |             outlineOffset: "2px", | ||||||
|  |             backgroundColor: "transparent", | ||||||
|  |             [`&.${pickersYearClasses.selected}`]: { | ||||||
|  |               backgroundColor: gray[300], | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   MuiPickersDay: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         fontSize: theme.typography.body1.fontSize, | ||||||
|  |         color: (theme.vars || theme).palette.grey[600], | ||||||
|  |         padding: theme.spacing(0.5), | ||||||
|  |         borderRadius: theme.shape.borderRadius, | ||||||
|  |         "&:hover": { | ||||||
|  |           backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |         }, | ||||||
|  |         [`&.${pickersDayClasses.selected}`]: { | ||||||
|  |           backgroundColor: gray[700], | ||||||
|  |           fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |         }, | ||||||
|  |         "&:focus": { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           outlineOffset: "2px", | ||||||
|  |           backgroundColor: "transparent", | ||||||
|  |           [`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[700] }, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           color: (theme.vars || theme).palette.grey[300], | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: (theme.vars || theme).palette.action.hover, | ||||||
|  |           }, | ||||||
|  |           [`&.${pickersDayClasses.selected}`]: { | ||||||
|  |             color: (theme.vars || theme).palette.common.black, | ||||||
|  |             fontWeight: theme.typography.fontWeightMedium, | ||||||
|  |             backgroundColor: gray[300], | ||||||
|  |           }, | ||||||
|  |           "&:focus": { | ||||||
|  |             outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |             outlineOffset: "2px", | ||||||
|  |             backgroundColor: "transparent", | ||||||
|  |             [`&.${pickersDayClasses.selected}`]: { backgroundColor: gray[300] }, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										4
									
								
								webClient/src/theme/customizations/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								webClient/src/theme/customizations/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | export { chartsCustomizations } from './charts'; | ||||||
|  | export { dataGridCustomizations } from './dataGrid'; | ||||||
|  | export { datePickersCustomizations } from './datePickers'; | ||||||
|  | export { treeViewCustomizations } from './treeView'; | ||||||
							
								
								
									
										62
									
								
								webClient/src/theme/customizations/treeView.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								webClient/src/theme/customizations/treeView.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | import { alpha, Theme } from "@mui/material/styles"; | ||||||
|  | import type { TreeViewComponents } from "@mui/x-tree-view/themeAugmentation"; | ||||||
|  | import { brand, gray } from "../../shared-theme/themePrimitives"; | ||||||
|  |  | ||||||
|  | /* eslint-disable import/prefer-default-export */ | ||||||
|  | export const treeViewCustomizations: TreeViewComponents<Theme> = { | ||||||
|  |   MuiTreeItem2: { | ||||||
|  |     styleOverrides: { | ||||||
|  |       root: ({ theme }) => ({ | ||||||
|  |         position: "relative", | ||||||
|  |         boxSizing: "border-box", | ||||||
|  |         padding: theme.spacing(0, 1), | ||||||
|  |         "& .groupTransition": { | ||||||
|  |           marginLeft: theme.spacing(2), | ||||||
|  |           padding: theme.spacing(0), | ||||||
|  |           borderLeft: "1px solid", | ||||||
|  |           borderColor: (theme.vars || theme).palette.divider, | ||||||
|  |         }, | ||||||
|  |         "&:focus-visible .focused": { | ||||||
|  |           outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |           outlineOffset: "2px", | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: alpha(gray[300], 0.2), | ||||||
|  |             outline: `3px solid ${alpha(brand[500], 0.5)}`, | ||||||
|  |             outlineOffset: "2px", | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }), | ||||||
|  |       content: ({ theme }) => ({ | ||||||
|  |         marginTop: theme.spacing(1), | ||||||
|  |         padding: theme.spacing(0.5, 1), | ||||||
|  |         overflow: "clip", | ||||||
|  |         "&:hover": { | ||||||
|  |           backgroundColor: alpha(gray[300], 0.2), | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         "&.selected": { | ||||||
|  |           backgroundColor: alpha(gray[300], 0.4), | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: alpha(gray[300], 0.6), | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         ...theme.applyStyles("dark", { | ||||||
|  |           "&:hover": { | ||||||
|  |             backgroundColor: alpha(gray[500], 0.2), | ||||||
|  |           }, | ||||||
|  |           "&:focus-visible": { | ||||||
|  |             "&:hover": { | ||||||
|  |               backgroundColor: alpha(gray[500], 0.2), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           "&.selected": { | ||||||
|  |             backgroundColor: alpha(gray[500], 0.4), | ||||||
|  |             "&:hover": { | ||||||
|  |               backgroundColor: alpha(gray[500], 0.6), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |       }), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										1
									
								
								webClient/src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								webClient/src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | /// <reference types="vite/types/importMeta.d.ts" /> | ||||||
							
								
								
									
										26
									
								
								webClient/tsconfig.app.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								webClient/tsconfig.app.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | { | ||||||
|  |   "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"] | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								webClient/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								webClient/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | { | ||||||
|  |   "files": [], | ||||||
|  |   "references": [ | ||||||
|  |     { "path": "./tsconfig.app.json" }, | ||||||
|  |     { "path": "./tsconfig.node.json" } | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								webClient/tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								webClient/tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | { | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "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 | ||||||
|  |   }, | ||||||
|  |   "include": ["vite.config.ts"] | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								webClient/vite.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								webClient/vite.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | import { defineConfig } from 'vite' | ||||||
|  | import react from '@vitejs/plugin-react-swc' | ||||||
|  |  | ||||||
|  | // https://vite.dev/config/ | ||||||
|  | export default defineConfig({ | ||||||
|  |   plugins: [react()], | ||||||
|  | }) | ||||||
		Reference in New Issue
	
	Block a user