diff --git a/.gitignore b/.gitignore index 7259800..19e26e8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* -#Ignored +# Ignored node_modules dist dist-ssr @@ -24,4 +24,4 @@ dist-ssr *.njsproj *.sln *.sw? -.VSCodeCounter \ No newline at end of file +.VSCodeCounter diff --git a/README.md b/README.md index 24ab563..b38b5bf 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,56 @@ # University Frontend - -This project serves as the frontend for a University management system, designed to provide an intuitive interface for managing student data, course registrations, and other functionalities. Built with React and TypeScript, it focuses on delivering a responsive and user-friendly experience with modern frontend technologies. +This project offers frontend for a **University Management System**, offering an intuitive interface for managing student data, course registrations, and administrative functionalities. Built with **React** and **TypeScript**, it prioritizes responsiveness, scalability, and user experience with modern frontend technologies. ## Features -- **React with TypeScript**: Utilizes React for building dynamic user interfaces and TypeScript for strong typing and improved developer experience. -- **Component-Based Architecture**: Implements reusable and modular components for maintainability and scalability. -- **State Management**: Manages application state effectively using [state management library, e.g., Redux, Context API]. -- **API Integration**: Connects seamlessly with the backend API to handle data fetching and state synchronization. -- **Responsive Design**: Ensures a consistent user experience across different devices and screen sizes. +- **React with TypeScript**: Combines React's dynamic UI capabilities with TypeScript's type safety for a robust developer experience. +- **Modular Architecture**: Utilizes a component-based architecture for enhanced maintainability and scalability. +- **State Management**: Effectively handles application state using [Redux/Context API/etc.]. +- **Seamless API Integration**: Connects effortlessly with the backend to manage data fetching and synchronization. +- **Responsive Design**: Delivers a consistent and adaptive user experience across various devices and screen sizes. ## Getting Started ### Prerequisites -- Node.js (version >=18.0.0 or higher recommended) -- npm (version >=8.0.0 or higher) +- **Node.js**: Version 18.0.0 or higher +- **npm**: Version 8.0.0 or higher ### Installation -1. **Clone the repository:** - +1. **Clone the repository**: ```bash git clone https://github.com/Swiatlon/University-FN ``` -2. **Navigate to the project directory:** - +2. **Navigate to the project directory**: ```bash cd University-FN ``` -3. **Install NPM packages:** - +3. **Install dependencies**: ```bash npm install ``` ### Running the Application -- **Development mode with live reloads:** - - ```bash - npm run dev - ``` - -- **Build the application for production:** - - ```bash - npm run build - ``` - - The production build will be located in the `build` directory. - -## Contributing - -We welcome contributions to the University-FN frontend project! If you have suggestions, improvements, or bug fixes, please follow these steps: - -1. **Fork the repository** -2. **Create a new branch:** - +- **Development Mode**: ```bash - git checkout -b feature/your-feature-name + npm run dev ``` + This starts the application with live reloads for development. -3. **Commit your changes:** - +- **Production Build**: ```bash - git commit -m 'Add new feature' + npm run build ``` - -4. **Push to the branch:** - - ```bash - git push origin feature/your-feature-name - ``` - -5. **Submit a pull request** - -For detailed guidelines, please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) file. + The optimized production build will be available in the `build` directory. ## License -This project is open-source under the MIT license. See the [LICENSE](LICENSE) file for details. +This project is licensed under the **MIT License**. For more information, refer to the [LICENSE](LICENSE) file. ## Contact -For any questions or feedback, please reach out to [your email address] or create an issue on [GitHub Issues](https://github.com/Swiatlon/University-FN/issues). +For inquiries or feedback, please contact [your email address] or open an issue on [GitHub Issues](https://github.com/Swiatlon/University-FN/issues). diff --git a/.eslintrc.cjs b/eslint.config.js similarity index 78% rename from .eslintrc.cjs rename to eslint.config.js index fb93854..311c306 100644 --- a/.eslintrc.cjs +++ b/eslint.config.js @@ -1,44 +1,33 @@ -module.exports = { - root: true, - env: { - browser: true, - es2020: true, - node: true, - }, - extends: [ - 'eslint:all', - 'plugin:@typescript-eslint/all', - 'plugin:react/all', - 'plugin:jsx-a11y/strict', - 'plugin:security/recommended-legacy', - 'plugin:sonarjs/recommended-legacy', - 'plugin:import/errors', - 'plugin:import/warnings', - 'plugin:import/typescript', - 'plugin:prettier/recommended', - ], - parser: '@typescript-eslint/parser', - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - ecmaVersion: 2020, - sourceType: 'module', - ecmaFeatures: { - jsx: true, +import eslint from '@eslint/js'; +import eslintCommentsPlugin from 'eslint-plugin-eslint-comments'; +import importPlugin from 'eslint-plugin-import'; +import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; +import prettierPlugin from 'eslint-plugin-prettier'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import securityPlugin from 'eslint-plugin-security'; +import sonarjsPlugin from 'eslint-plugin-sonarjs'; +import unusedImportsPlugin from 'eslint-plugin-unused-imports'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config(eslint.configs.recommended, tseslint.configs.strict, tseslint.configs.stylistic, { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: './tsconfig.json', }, }, - plugins: [ - 'react', - '@typescript-eslint', - 'react-hooks', - 'jsx-a11y', - 'security', - 'sonarjs', - 'unused-imports', - 'eslint-comments', - 'react-refresh', - 'prettier', - ], + plugins: { + react: reactPlugin, + 'jsx-a11y': jsxA11yPlugin, + security: securityPlugin, + sonarjs: sonarjsPlugin, + 'unused-imports': unusedImportsPlugin, + 'eslint-comments': eslintCommentsPlugin, + 'react-hooks': reactHooksPlugin, + prettier: prettierPlugin, + import: importPlugin, + }, rules: { 'import/order': [ 'error', @@ -88,9 +77,10 @@ module.exports = { }, }, ], - "react/no-multi-comp": "off", - "no-multiple-empty-lines": ["error", { max: 1 }], - "max-lines": ["error", { "max": 500, "skipBlankLines": true }], + '@typescript-eslint/no-empty-object-type': 'off', + 'react/no-multi-comp': 'off', + 'no-multiple-empty-lines': ['error', { max: 1 }], + 'max-lines': ['error', { max: 500, skipBlankLines: true }], '@typescript-eslint/prefer-nullish-coalescing': 'off', 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': 'off', @@ -132,11 +122,9 @@ module.exports = { 'one-var': ['off'], 'sort-imports': 'off', 'sort-keys': 'off', - '@typescript-eslint/type-annotation-spacing': 'error', '@typescript-eslint/explicit-member-accessibility': ['error', { accessibility: 'explicit' }], '@typescript-eslint/no-for-in-array': 'error', '@typescript-eslint/no-misused-promises': 'off', - '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/promise-function-async': 'error', '@typescript-eslint/require-await': 'error', 'react/require-default-props': 'off', @@ -173,5 +161,5 @@ module.exports = { version: 'detect', }, }, - ignorePatterns: ['node_modules/', 'dist/', 'vite.config.ts'], -}; + ignores: ['node_modules/', 'dist/', 'vite.config.ts'], +}); diff --git a/package-lock.json b/package-lock.json index 22c2121..6cd93c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,6 @@ "name": "client", "version": "1.2.0 - Alfa", "dependencies": { - "@date-io/dayjs": "^3.0.0", - "@devexpress/dx-react-core": "^4.0.6", - "@devexpress/dx-react-grid": "^4.0.6", - "@devexpress/dx-react-grid-material-ui": "^4.0.8", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", "@fullcalendar/core": "^6.1.15", @@ -24,7 +20,6 @@ "@mui/icons-material": "^5.15.15", "@mui/material": "^5.16.7", "@mui/x-date-pickers": "^6.20.2", - "@mui/x-tree-view": "^7.23.0", "@reduxjs/toolkit": "^2.2.5", "ag-grid-community": "^32.0.0", "ag-grid-react": "^32.0.0", @@ -32,17 +27,12 @@ "date-fns": "^2.30.0", "dayjs": "^1.11.12", "file-saver": "^2.0.5", - "framer-motion": "^10.18.0", "i18next": "^23.11.3", - "i18next-http-backend": "^2.5.1", - "localforage": "^1.10.0", "lodash": "^4.17.21", - "match-sorter": "^6.3.4", "notistack": "^3.0.1", "qs": "^6.12.3", "randomcolor": "^0.6.2", "react": "^18.3.1", - "react-color": "^2.19.3", "react-datepicker": "^7.3.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.2", @@ -51,12 +41,12 @@ "react-router-dom": "^6.22.3", "recharts": "^2.13.3", "sass": "^1.74.1", - "sort-by": "^1.2.0", "uuid": "^9.0.1", "xlsx": "^0.18.5", "yup": "^1.4.0" }, "devDependencies": { + "@eslint/js": "^9.16.0", "@types/file-saver": "^2.0.7", "@types/lodash": "^4.17.7", "@types/qs": "^6.9.15", @@ -68,9 +58,8 @@ "@typescript-eslint/eslint-plugin": "^7.13.0", "@typescript-eslint/parser": "^7.13.0", "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", - "eslint-formatter-table": "^7.32.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", @@ -81,9 +70,9 @@ "eslint-plugin-security": "^3.0.0", "eslint-plugin-sonarjs": "^1.0.3", "eslint-plugin-unused-imports": "^3.1.0", - "husky": "^8.0.3", "prettier": "^3.2.5", - "typescript": "^5.4.5", + "typescript": "^5.7.2", + "typescript-eslint": "^8.17.0", "vite": "^4.5.3", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.3.2" @@ -456,84 +445,6 @@ "node": ">=6.9.0" } }, - "node_modules/@date-io/core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@date-io/core/-/core-3.0.0.tgz", - "integrity": "sha512-S3j+IAQVBYNkQzchVVhX40eBkGDreBpScy9RXwTS5j2+k07+62pMVPisQ44Gq76Rqy5AOG/EZXCwBpY/jbemvA==" - }, - "node_modules/@date-io/dayjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-3.0.0.tgz", - "integrity": "sha512-vy7DSwoQiPA2L0stRqW3le7lcEBMjoMMEmbpCNkyoX3xXizKInFvhbnOBmCyusIQ7tL/WsNC4X5bVgdNWX0JLA==", - "dependencies": { - "@date-io/core": "^3.0.0" - }, - "peerDependencies": { - "dayjs": "^1.8.17" - }, - "peerDependenciesMeta": { - "dayjs": { - "optional": true - } - } - }, - "node_modules/@devexpress/dx-core": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@devexpress/dx-core/-/dx-core-4.0.8.tgz", - "integrity": "sha512-H5RXg4WtBfJMD/2PzakdsB0KooycTsUzJGQ1mLedsVa9HEwJDggXRhL11/oZGb03pXdxdHcCzWMQyGj0pzu1OA==" - }, - "node_modules/@devexpress/dx-grid-core": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@devexpress/dx-grid-core/-/dx-grid-core-4.0.8.tgz", - "integrity": "sha512-JtoX7ec7OU6xqEt2uYBRVVslaA9f9EkzQoxUTth4SZQbqvzsiQlVRQ801TJAduYwzaWuMwhlAwjY0UuJMftG3w==", - "peerDependencies": { - "@devexpress/dx-core": "4.0.8" - } - }, - "node_modules/@devexpress/dx-react-core": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@devexpress/dx-react-core/-/dx-react-core-4.0.8.tgz", - "integrity": "sha512-+wlAnh73y1LCHhPOd9z05jV1qvzZQJIq6b4ByMbHArlhVpKEgiqE1lIF6n0Ex3q5vHqR11Djh63PUWng2m76GQ==", - "dependencies": { - "@devexpress/dx-core": "4.0.8", - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": ">=17.0.2", - "react-dom": ">=17.0.2" - } - }, - "node_modules/@devexpress/dx-react-grid": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@devexpress/dx-react-grid/-/dx-react-grid-4.0.8.tgz", - "integrity": "sha512-ezCAH+nRrbCJtoFUnaVmhc4uC7i7hsVg4okEol+zZRoprHZPGYjd7nAMFv09u3JvGlMF6jnW62mUdWx2z1Prtg==", - "dependencies": { - "@devexpress/dx-grid-core": "4.0.8" - }, - "peerDependencies": { - "@devexpress/dx-core": "4.0.8", - "@devexpress/dx-react-core": "4.0.8", - "react": ">=17.0.2", - "react-dom": ">=17.0.2" - } - }, - "node_modules/@devexpress/dx-react-grid-material-ui": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@devexpress/dx-react-grid-material-ui/-/dx-react-grid-material-ui-4.0.8.tgz", - "integrity": "sha512-c2aUhWXZCc/D7leCf6+d6ar8Kkrxok5xRf8XzVAJ8Y/hgRR7fOEozZL7gks0RaVPwCBrWUUL7tOL8a+uRDGM5Q==", - "dependencies": { - "clsx": "^1.0.4", - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "@devexpress/dx-grid-core": "4.0.8", - "@devexpress/dx-react-core": "4.0.8", - "@devexpress/dx-react-grid": "4.0.8", - "@mui/icons-material": ">=5.0.0", - "@mui/material": ">=5.0.0", - "react": ">=17.0.2" - } - }, "node_modules/@emotion/babel-plugin": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", @@ -1104,12 +1015,13 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@floating-ui/core": { @@ -1223,13 +1135,14 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -1242,6 +1155,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1252,6 +1166,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1277,16 +1192,8 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "node_modules/@icons/material": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", - "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", - "license": "MIT", - "peerDependencies": { - "react": "*" - } + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", @@ -1684,73 +1591,6 @@ "node": ">=6" } }, - "node_modules/@mui/x-internals": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.0.tgz", - "integrity": "sha512-bPclKpqUiJYIHqmTxSzMVZi6MH51cQsn5U+8jskaTlo3J4QiMeCYJn/gn7YbeR9GOZFp8hetyHjoQoVHKRXCig==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@mui/x-tree-view": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.23.0.tgz", - "integrity": "sha512-67e+FCVMD2A5IaNettHFLUg09j+mMOWlN9f0Uw+LGePA9vLCAMFBdPZIFa18J9F3hTurNLrFzjVA0O4QfHlvrQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.23.0", - "@types/react-transition-group": "^4.4.11", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14 || ^6.0.0", - "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/x-tree-view/node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2826,16 +2666,6 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3151,14 +2981,6 @@ "node": ">=0.8" } }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3736,16 +3558,18 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -3802,96 +3626,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-formatter-table": { - "version": "7.32.1", - "resolved": "https://registry.npmjs.org/eslint-formatter-table/-/eslint-formatter-table-7.32.1.tgz", - "integrity": "sha512-JYC49hAJMNjLfbgXVeQHU6ngP0M8ThgXCHLGrncYB+R/RHEhRPnLxHjolTJdb7RdQ8zcCt2F7Mrt6Ou3PwMOHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "table": "^6.0.9" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/eslint-formatter-table/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint-formatter-table/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint-formatter-table/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint-formatter-table/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-formatter-table/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-formatter-table/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -4326,6 +4060,16 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4565,13 +4309,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4669,44 +4406,6 @@ "node": ">=0.8" } }, - "node_modules/framer-motion": { - "version": "10.18.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", - "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", - "dependencies": { - "tslib": "^2.4.0" - }, - "optionalDependencies": { - "@emotion/is-prop-valid": "^0.8.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "optional": true, - "dependencies": { - "@emotion/memoize": "0.7.4" - } - }, - "node_modules/framer-motion/node_modules/@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "optional": true - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5032,21 +4731,6 @@ "void-elements": "3.1.0" } }, - "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, "node_modules/i18next": { "version": "23.11.5", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz", @@ -5069,14 +4753,6 @@ "@babel/runtime": "^7.23.2" } }, - "node_modules/i18next-http-backend": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz", - "integrity": "sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==", - "dependencies": { - "cross-fetch": "4.0.0" - } - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -5086,11 +4762,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, "node_modules/immer": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", @@ -5317,16 +4988,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -5680,27 +5341,11 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "dependencies": { - "lie": "3.1.1" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5722,25 +5367,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, - "license": "MIT" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5770,21 +5402,6 @@ "yallist": "^3.0.2" } }, - "node_modules/match-sorter": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", - "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", - "dependencies": { - "@babel/runtime": "^7.23.8", - "remove-accents": "0.5.0" - } - }, - "node_modules/material-colors": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", - "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==", - "license": "ISC" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5870,25 +5487,6 @@ "tslib": "^2.0.3" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -5949,14 +5547,6 @@ "node": ">= 0.4" } }, - "node_modules/object-path": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.6.0.tgz", - "integrity": "sha512-fxrwsCFi3/p+LeLOAwo/wyRMODZxdGBtUlWRzsEpsUVrisZbEfZ21arxLGfaWfcnqb8oHPNihIb4XPE8CQPN5A==", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/object.assign": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", @@ -6357,24 +5947,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-color": { - "version": "2.19.3", - "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", - "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", - "license": "MIT", - "dependencies": { - "@icons/material": "^0.2.4", - "lodash": "^4.17.15", - "lodash-es": "^4.17.15", - "material-colors": "^1.2.1", - "prop-types": "^15.5.10", - "reactcss": "^1.2.0", - "tinycolor2": "^1.4.1" - }, - "peerDependencies": { - "react": "*" - } - }, "node_modules/react-datepicker": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.3.0.tgz", @@ -6565,15 +6137,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/reactcss": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", - "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", - "license": "MIT", - "dependencies": { - "lodash": "^4.0.1" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6692,21 +6255,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/remove-accents": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", - "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -6959,60 +6507,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -7023,14 +6517,6 @@ "tslib": "^2.0.3" } }, - "node_modules/sort-by": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/sort-by/-/sort-by-1.2.0.tgz", - "integrity": "sha512-aRyW65r3xMnf4nxJRluCg0H/woJpksU1dQxRtXYzau30sNBOmf5HACpDd9MZDhKh7ALQ5FgSOfMPwZEtUmMqcg==", - "dependencies": { - "object-path": "0.6.0" - } - }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -7059,28 +6545,6 @@ "node": ">=0.8" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -7243,47 +6707,6 @@ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" }, - "node_modules/table": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", - "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7301,12 +6724,6 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, - "node_modules/tinycolor2": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", - "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", - "license": "MIT" - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -7331,11 +6748,6 @@ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -7395,7 +6807,8 @@ "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true }, "node_modules/type-check": { "version": "0.4.0", @@ -7495,10 +6908,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7507,6 +6921,244 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.17.0.tgz", + "integrity": "sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.17.0", + "@typescript-eslint/parser": "8.17.0", + "@typescript-eslint/utils": "8.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz", + "integrity": "sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/type-utils": "8.17.0", + "@typescript-eslint/utils": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.17.0.tgz", + "integrity": "sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz", + "integrity": "sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz", + "integrity": "sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.17.0", + "@typescript-eslint/utils": "8.17.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.17.0.tgz", + "integrity": "sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz", + "integrity": "sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.17.0.tgz", + "integrity": "sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz", + "integrity": "sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.17.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -7699,20 +7351,6 @@ "node": ">=0.10.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index d3d7cf2..38c0eaa 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,6 @@ "preview": "vite preview --port 5173" }, "dependencies": { - "@date-io/dayjs": "^3.0.0", - "@devexpress/dx-react-core": "^4.0.6", - "@devexpress/dx-react-grid": "^4.0.6", - "@devexpress/dx-react-grid-material-ui": "^4.0.8", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", "@fullcalendar/core": "^6.1.15", @@ -29,7 +25,6 @@ "@mui/icons-material": "^5.15.15", "@mui/material": "^5.16.7", "@mui/x-date-pickers": "^6.20.2", - "@mui/x-tree-view": "^7.23.0", "@reduxjs/toolkit": "^2.2.5", "ag-grid-community": "^32.0.0", "ag-grid-react": "^32.0.0", @@ -37,17 +32,12 @@ "date-fns": "^2.30.0", "dayjs": "^1.11.12", "file-saver": "^2.0.5", - "framer-motion": "^10.18.0", "i18next": "^23.11.3", - "i18next-http-backend": "^2.5.1", - "localforage": "^1.10.0", "lodash": "^4.17.21", - "match-sorter": "^6.3.4", "notistack": "^3.0.1", "qs": "^6.12.3", "randomcolor": "^0.6.2", "react": "^18.3.1", - "react-color": "^2.19.3", "react-datepicker": "^7.3.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.2", @@ -56,12 +46,12 @@ "react-router-dom": "^6.22.3", "recharts": "^2.13.3", "sass": "^1.74.1", - "sort-by": "^1.2.0", "uuid": "^9.0.1", "xlsx": "^0.18.5", "yup": "^1.4.0" }, "devDependencies": { + "@eslint/js": "^9.16.0", "@types/file-saver": "^2.0.7", "@types/lodash": "^4.17.7", "@types/qs": "^6.9.15", @@ -73,9 +63,8 @@ "@typescript-eslint/eslint-plugin": "^7.13.0", "@typescript-eslint/parser": "^7.13.0", "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", - "eslint-formatter-table": "^7.32.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", @@ -86,9 +75,9 @@ "eslint-plugin-security": "^3.0.0", "eslint-plugin-sonarjs": "^1.0.3", "eslint-plugin-unused-imports": "^3.1.0", - "husky": "^8.0.3", "prettier": "^3.2.5", - "typescript": "^5.4.5", + "typescript": "^5.7.2", + "typescript-eslint": "^8.17.0", "vite": "^4.5.3", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.3.2" diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 27341e1..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Outlet } from 'react-router-dom'; - -function App() { - return ; -} - -export default App; diff --git a/src/assets/styles/themeStyle.scss b/src/assets/styles/themeStyle.scss deleted file mode 100644 index b6629fd..0000000 --- a/src/assets/styles/themeStyle.scss +++ /dev/null @@ -1,20 +0,0 @@ -//Accesibility -.hide { - display: none; -} -.show { - display: unset; -} - -//Size ...etc -.fullWidth { - width: 100%; -} - -//Postion -.moveToRight { - margin-left: auto; -} -.moveToLeft { - margin-left: auto; -} diff --git a/src/components/viewsComponents/personalData/IconComponent.tsx b/src/components/shared/Icon/IconComponent.tsx similarity index 82% rename from src/components/viewsComponents/personalData/IconComponent.tsx rename to src/components/shared/Icon/IconComponent.tsx index 39e8cce..862b08f 100644 --- a/src/components/viewsComponents/personalData/IconComponent.tsx +++ b/src/components/shared/Icon/IconComponent.tsx @@ -8,7 +8,7 @@ interface IIconComponentProps extends SvgIconProps { name: MuiIconsNameType; } -const iconComponents: { [K in MuiIconsNameType]: React.ComponentType } = Icons; +const iconComponents: Record> = Icons; export function IconComponent({ name, ...props }: IIconComponentProps): React.ReactElement { const SpecificIcon = iconComponents[name]; diff --git a/src/components/shared/appBar/AppBar.scss b/src/components/shared/appBar/AppBar.scss deleted file mode 100644 index 5b438fa..0000000 --- a/src/components/shared/appBar/AppBar.scss +++ /dev/null @@ -1,37 +0,0 @@ -.AppBarBox { - display: flex; - align-items: center; - align-content: center; - height: 100%; - margin-right: 4rem; - margin-left: 4rem; - - @media (max-width: 600px) { - text-wrap: nowrap; - margin-right: 1rem; - margin-left: 1rem; - } -} - -.AppBarHamburger { - cursor: pointer; - margin-right: 24px; - - @media (max-width: 600px) { - margin-right: 16px; - } -} - -.ConfigAppBar { - display: flex; - margin-left: auto; - button { - min-width: min-content !important; - } -} - -@media (min-width: 910px) { - .AppBarHamburger { - display: none; - } -} diff --git a/src/components/shared/appBar/AppBar.tsx b/src/components/shared/appBar/AppBar.tsx index 796d221..49ceff1 100644 --- a/src/components/shared/appBar/AppBar.tsx +++ b/src/components/shared/appBar/AppBar.tsx @@ -1,47 +1,27 @@ -import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import MenuIcon from '@mui/icons-material/Menu'; -import { Box, Typography, useMediaQuery } from '@mui/material'; +import { Box, Typography } from '@mui/material'; +import { useTypedDispatch } from 'hooks/useStore.Hooks'; import { useTypedMatches } from 'hooks/useTypedMatches.Hook'; -import { toggleDrawer, setDrawerState } from 'redux/stateSlices/view/View.State.Slice'; -import './AppBar.scss'; +import { toggleDrawer } from 'redux/stateSlices/view/View.State.Slice'; import AppBarConfig from './elements/AppBarConfig'; - -const mobileBreakpoint = '(max-width:910px)'; +import './styles/AppBar.scss'; function AppBar() { - const dispatch = useDispatch(); + const dispatch = useTypedDispatch(); const matches = useTypedMatches(); - const currentMatch = matches.find(match => match.handle); + const currentMatch = matches.find(match => match.handle); const pageTitle = currentMatch?.handle?.navigation?.text; - const isMobile = useMediaQuery(mobileBreakpoint); - - useEffect(() => { - const shouldDrawerBeOpen = !isMobile; - dispatch(setDrawerState(shouldDrawerBeOpen)); - }, [isMobile, dispatch]); const handleToggleDrawer = () => { dispatch(toggleDrawer()); }; return ( - - - - {pageTitle} - - + + + {pageTitle} + ); } diff --git a/src/components/shared/appBar/constants/constants.tsx b/src/components/shared/appBar/constants/constants.tsx new file mode 100644 index 0000000..dbabea0 --- /dev/null +++ b/src/components/shared/appBar/constants/constants.tsx @@ -0,0 +1,23 @@ +import { ErrorOutline, WarningAmber } from '@mui/icons-material'; + +export enum TimerStatesEnum { + Critical = 'critical', + Warning = 'warning', + Normal = 'normal', +} + +export interface ITimerStylesKeys { + color: string; + icon?: JSX.Element; +} + +export const timerStylesByState: Record = { + [TimerStatesEnum.Critical]: { color: '#f70000', icon: }, + [TimerStatesEnum.Warning]: { color: '#ff9800', icon: }, + [TimerStatesEnum.Normal]: { color: 'black' }, +}; + +export const timeThresholds = [ + { minutes: 5, state: TimerStatesEnum.Critical }, + { minutes: 10, state: TimerStatesEnum.Warning }, +]; diff --git a/src/components/shared/appBar/elements/AppBarConfig.tsx b/src/components/shared/appBar/elements/AppBarConfig.tsx index 93baa27..79dbe23 100644 --- a/src/components/shared/appBar/elements/AppBarConfig.tsx +++ b/src/components/shared/appBar/elements/AppBarConfig.tsx @@ -1,38 +1,9 @@ -import { useTranslation } from 'react-i18next'; import { Box } from '@mui/material'; -import EnglandCircle from 'assets/icons/EnglandCircle.svg?react'; -import PolandCircle from 'assets/icons/PolandCircle.svg?react'; import AppBarTimer from './AppBarTimer'; -export function useLanguageMenuItems() { - const { t, i18n } = useTranslation(); - - const changeLanguage = (language: string) => { - i18n.changeLanguage(language); - }; - - return [ - { - label: t('polish'), - onClick: () => { - changeLanguage('pl'); - }, - icon: , - }, - { - label: t('english'), - onClick: () => { - changeLanguage('en'); - }, - icon: , - }, - ]; -} - function AppBarConfig() { return ( - {/* } hideLabelOnMobile /> */} ); diff --git a/src/components/shared/appBar/elements/AppBarTimer.tsx b/src/components/shared/appBar/elements/AppBarTimer.tsx index 32488bb..e06f505 100644 --- a/src/components/shared/appBar/elements/AppBarTimer.tsx +++ b/src/components/shared/appBar/elements/AppBarTimer.tsx @@ -1,77 +1,29 @@ -import { useLayoutEffect, useMemo, useState } from 'react'; -import { useSelector } from 'react-redux'; -import { ErrorOutline, WarningAmber } from '@mui/icons-material'; +import { useEffect } from 'react'; import { Box, Typography, useMediaQuery } from '@mui/material'; -import { parseISO, isAfter, intervalToDuration } from 'date-fns'; -import _ from 'lodash'; +import { isAfter, parseISO } from 'date-fns'; +import { useAppBarTimer } from 'hooks/useAppBarTimer'; +import { useTypedSelector } from 'hooks/useStore.Hooks'; import { useSendLogoutMutation } from 'redux/apiSlices/auth/Auth.Api.Slice'; import { selectTokenExpirationTime } from 'redux/stateSlices/auth/Auth.State.Slice'; +import { timerStylesByState } from '../constants/constants'; import AppBarExtSessionIcon from './AppBarExtSessionIcon'; -export enum TimerStatesEnum { - Critical = 'critical', - Warning = 'warning', - Normal = 'normal', -} - -interface ITimeThresholds { - minutes: number; - state: TimerStatesEnum; -} - -interface ITimerStylesKeys { - color: string; - icon?: JSX.Element; -} - -const timerStylesByState: Record = { - [TimerStatesEnum.Critical]: { color: '#f70000', icon: }, - [TimerStatesEnum.Warning]: { color: '#ff9800', icon: }, - [TimerStatesEnum.Normal]: { color: 'black' }, -}; - -const timeThresholds: ITimeThresholds[] = [ - { minutes: 5, state: TimerStatesEnum.Critical }, - { minutes: 10, state: TimerStatesEnum.Warning }, -]; - function AppBarTimer() { - const [sendLogout] = useSendLogoutMutation(); - const tokenExpirationTime = useSelector(selectTokenExpirationTime); - const [timeLeft, setTimeLeft] = useState(''); - const [timerState, setTimerState] = useState(TimerStatesEnum.Normal); + const tokenExpirationTime = useTypedSelector(selectTokenExpirationTime); const isMobile = useMediaQuery('(max-width:600px)'); + const { timeLeft, timerState } = useAppBarTimer(); + const { color, icon } = timerStylesByState[timerState]; + const [sendLogout] = useSendLogoutMutation(); - useLayoutEffect(() => { - const updateTimer = async () => { - const now = new Date(); - const expirationDate = parseISO(tokenExpirationTime ?? ''); - - if (isAfter(now, expirationDate)) { - clearInterval(timerId); - await sendLogout(); - return; - } - - const { minutes = 0, seconds = 0 } = intervalToDuration({ start: now, end: expirationDate }); - const matchingThreshold = _.find(timeThresholds, threshold => minutes < threshold.minutes); - const formattedTime = `${_.padStart(minutes.toString(), 2, '0')}:${_.padStart(seconds.toString(), 2, '0')}`; - - setTimeLeft(formattedTime); - setTimerState(matchingThreshold?.state ?? TimerStatesEnum.Normal); - }; - - const timerId = setInterval(updateTimer, 1000); - - return () => { - clearInterval(timerId); - }; - }, [tokenExpirationTime]); - - const { color, icon } = useMemo(() => timerStylesByState[timerState], [timerState]); + useEffect(() => { + if (!tokenExpirationTime || isAfter(new Date(), parseISO(tokenExpirationTime))) { + sendLogout(); + return; + } + }, [timeLeft]); return ( - + {icon} {!isMobile && 'Timer:'} {timeLeft} diff --git a/src/components/shared/appBar/styles/AppBar.scss b/src/components/shared/appBar/styles/AppBar.scss new file mode 100644 index 0000000..bb97bdc --- /dev/null +++ b/src/components/shared/appBar/styles/AppBar.scss @@ -0,0 +1,36 @@ +.AppBarBox { + height: 80px; + background: white; + padding: 0rem 4rem; + display: flex; + align-items: center; + align-content: center; + position: sticky; + top: 0; + z-index: 2; + + @media (max-width: 600px) { + padding: 0rem 1rem; + } + + .ConfigAppBar { + display: flex; + margin-left: auto; + + button { + min-width: min-content !important; + } + } + + .AppBarHamburger { + margin-right: 16px; + cursor: pointer; + + @media (min-width: 910px) { + display: none; + } + } +} + + + diff --git a/src/components/shared/appBar/utils/Timer.Utils.tsx b/src/components/shared/appBar/utils/Timer.Utils.tsx new file mode 100644 index 0000000..0a7221c --- /dev/null +++ b/src/components/shared/appBar/utils/Timer.Utils.tsx @@ -0,0 +1,20 @@ +import { intervalToDuration } from 'date-fns'; +import _ from 'lodash'; +import { TimerStatesEnum, timeThresholds } from '../constants/constants'; + +interface ICaluclateTimeLeft { + minutes: number; + seconds: number; + formattedTime: string; +} + +export const calculateTimeLeft = (expirationDate: Date): ICaluclateTimeLeft => { + const now = new Date(); + const { minutes = 0, seconds = 0 } = intervalToDuration({ start: now, end: expirationDate }); + const formattedTime = `${_.padStart(minutes.toString(), 2, '0')}:${_.padStart(seconds.toString(), 2, '0')}`; + return { minutes, seconds, formattedTime }; +}; + +export const getTimerState = (minutes: number): TimerStatesEnum => { + return _.find(timeThresholds, threshold => minutes < threshold.minutes)?.state ?? TimerStatesEnum.Normal; +}; diff --git a/src/components/shared/centeredLoader/CenteredLoader.tsx b/src/components/shared/centeredLoader/CenteredLoader.tsx index 5f4e373..a6d0279 100644 --- a/src/components/shared/centeredLoader/CenteredLoader.tsx +++ b/src/components/shared/centeredLoader/CenteredLoader.tsx @@ -1,6 +1,6 @@ import { CircularProgress, Box } from '@mui/material'; -function CenteredLoader() { +const CenteredLoader = () => { return ( ); -} +}; export default CenteredLoader; diff --git a/src/components/shared/dataGrid/elements/pagination/PageSizeSelect.tsx b/src/components/shared/dataGrid/elements/pagination/PageSizeSelect.tsx index 674482a..7af1fc1 100644 --- a/src/components/shared/dataGrid/elements/pagination/PageSizeSelect.tsx +++ b/src/components/shared/dataGrid/elements/pagination/PageSizeSelect.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Select, MenuItem, type SelectChangeEvent } from '@mui/material'; -import { beautifyNumbers } from 'routes/utils/Decorators'; +import { beautifyNumbers } from 'utils/general/Decorators'; interface IPageSizeSelectProps { value: number; diff --git a/src/components/shared/dataGrid/elements/pagination/PaginationOptions.tsx b/src/components/shared/dataGrid/elements/pagination/PaginationOptions.tsx index fed9e11..4e412cb 100644 --- a/src/components/shared/dataGrid/elements/pagination/PaginationOptions.tsx +++ b/src/components/shared/dataGrid/elements/pagination/PaginationOptions.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Box, Typography, type SelectChangeEvent } from '@mui/material'; -import { beautifyNumbers } from 'routes/utils/Decorators'; +import { beautifyNumbers } from 'utils/general/Decorators'; import PageSizeSelect from './PageSizeSelect'; interface IPaginationOptionsProps { diff --git a/src/routes/shared/error/ErrorPage.tsx b/src/components/shared/errorPage/ErrorPage.tsx similarity index 75% rename from src/routes/shared/error/ErrorPage.tsx rename to src/components/shared/errorPage/ErrorPage.tsx index aac979f..4f15901 100644 --- a/src/routes/shared/error/ErrorPage.tsx +++ b/src/components/shared/errorPage/ErrorPage.tsx @@ -4,22 +4,17 @@ import { useNavigate, useRouteError } from 'react-router-dom'; import { enqueueSnackbar } from 'notistack'; import { selectCurrentToken } from 'redux/stateSlices/auth/Auth.State.Slice'; -function ErrorPage() { - const isAuthenticated = useSelector(selectCurrentToken); +const ErrorPage = () => { const navigate = useNavigate(); const error = useRouteError(); + const isAuthenticated = useSelector(selectCurrentToken); useEffect(() => { - if (isAuthenticated) { - navigate('postAuth/dashboard'); - } else { - navigate('/login'); - } - + navigate(isAuthenticated ? 'postAuth/dashboard' : '/login'); enqueueSnackbar(`Something went wrong. Logs were sent to Admin`, { variant: 'error' }); - }, [isAuthenticated, navigate, error]); + }, [isAuthenticated, error]); return null; -} +}; export default ErrorPage; diff --git a/src/components/shared/navigation/Navigation.tsx b/src/components/shared/navigation/Navigation.tsx index ac75f46..45a1d23 100644 --- a/src/components/shared/navigation/Navigation.tsx +++ b/src/components/shared/navigation/Navigation.tsx @@ -1,12 +1,11 @@ -import { useState, useCallback } from 'react'; -import { useDispatch } from 'react-redux'; +import { useState, useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import MenuIcon from '@mui/icons-material/Menu'; import { Box, Divider, List, Collapse } from '@mui/material'; import useMediaQuery from '@mui/material/useMediaQuery'; -import { useTypedSelector } from 'hooks/useStore.Hooks'; +import { useTypedDispatch, useTypedSelector } from 'hooks/useStore.Hooks'; import { selectCurrentToken, selectUserRoles } from 'redux/stateSlices/auth/Auth.State.Slice'; -import { selectIsDrawerOpen, toggleDrawer } from 'redux/stateSlices/view/View.State.Slice'; +import { selectIsDrawerOpen, setDrawerState, toggleDrawer } from 'redux/stateSlices/view/View.State.Slice'; import Logo from 'assets/images/Logo.png'; import UserProfile from './elements/headerItems/UserProfile'; import MenuItemComponent from './elements/menuItems/MenuItem'; @@ -14,7 +13,7 @@ import { Drawer } from './Styled'; import type { INavigationProps, OpenMenuItemsStateType } from './types/types'; function Navigation({ menuItems }: INavigationProps) { - const dispatch = useDispatch(); + const dispatch = useTypedDispatch(); const navigate = useNavigate(); const isMenuFullWidth = useMediaQuery('(max-width:910px)'); @@ -43,6 +42,10 @@ function Navigation({ menuItems }: INavigationProps) { setOpenMenuItems(prevState => ({ ...prevState, [id]: !prevState[id] })); }, []); + useEffect(() => { + dispatch(setDrawerState(!isMenuFullWidth)); + }, [isMenuFullWidth, dispatch]); + return ( diff --git a/src/components/shared/todoListDrawer/TodoListDrawer.tsx b/src/components/shared/todoListDrawer/TodoListDrawer.tsx index de75bbd..2dcb957 100644 --- a/src/components/shared/todoListDrawer/TodoListDrawer.tsx +++ b/src/components/shared/todoListDrawer/TodoListDrawer.tsx @@ -7,13 +7,11 @@ import { useGetStudentTodosQuery } from 'redux/apiSlices/students/Students.Api.S import TaskCard from './elements/TaskCard'; import TodoCreateDialog from './elements/TodoCreateDialog'; -interface ITodoListDrawerProps { - maxDrawerWidth: number; -} - -const TodoListDrawer = ({ maxDrawerWidth }: ITodoListDrawerProps) => { +const TodoListDrawer = () => { const studentId = useSelector(selectId)!; + const isMobile = useMediaQuery('(max-width: 600px)'); const isEnoughSpaceForDrawer = useMediaQuery('(min-width: 1400px)'); + const maxDrawerWidth = isMobile ? 280 : 450; const [openDrawer, setOpenDrawer] = useState(isEnoughSpaceForDrawer); const [openDialog, setOpenDialog] = useState(false); const { data: tasks } = useGetStudentTodosQuery({ studentId }, { skip: !studentId }); diff --git a/src/components/viewsComponents/courses/Courses.tsx b/src/components/viewsComponents/courses/Courses.tsx index 1354df9..adfd17e 100644 --- a/src/components/viewsComponents/courses/Courses.tsx +++ b/src/components/viewsComponents/courses/Courses.tsx @@ -25,46 +25,6 @@ function Courses() { return ; } - const renderModules = (modules: IStudentCoursesModule[], level = 0) => - modules.map(module => ( - - - - ({ - id: subject.id, - name: subject.name, - grade: subject.grade, - }))} - columnDefs={gradesColumns} - /> - - - )); - - const renderDegreePath = (degreePath: IStudentCoursesDegreePath, level = 0) => ( - - - {renderModules(degreePath.modules, level + 1)} - - ); - - const renderCourseSubjects = (subjects: IStudentCourseSubject[]) => ( - - - - ({ - id: subject.id, - name: subject.name, - grade: subject.grade, - }))} - columnDefs={gradesColumns} - /> - - - ); - return ( {data ? ( @@ -81,3 +41,43 @@ function Courses() { } export default Courses; + +const renderModules = (modules: IStudentCoursesModule[], level = 0) => + modules.map(module => ( + + + + ({ + id: subject.id, + name: subject.name, + grade: subject.grade, + }))} + columnDefs={gradesColumns} + /> + + + )); + +const renderDegreePath = (degreePath: IStudentCoursesDegreePath, level = 0) => ( + + + {renderModules(degreePath.modules, level + 1)} + +); + +const renderCourseSubjects = (subjects: IStudentCourseSubject[]) => ( + + + + ({ + id: subject.id, + name: subject.name, + grade: subject.grade, + }))} + columnDefs={gradesColumns} + /> + + +); diff --git a/src/components/viewsComponents/courses/elements/AccordionSummaryWrapper.tsx b/src/components/viewsComponents/courses/elements/AccordionSummaryWrapper.tsx index 1897d70..09885b1 100644 --- a/src/components/viewsComponents/courses/elements/AccordionSummaryWrapper.tsx +++ b/src/components/viewsComponents/courses/elements/AccordionSummaryWrapper.tsx @@ -12,7 +12,6 @@ interface AccordionSummaryWrapperProps { const AccordionSummaryWrapper: React.FC = ({ level, title }) => { const theme = useTheme(); const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); - const marginLeft = isSmallScreen ? level * 1.2 : level * 4; return ( diff --git a/src/components/viewsComponents/dashboard/Dashboard.scss b/src/components/viewsComponents/dashboard/Dashboard.scss deleted file mode 100644 index 0b1c799..0000000 --- a/src/components/viewsComponents/dashboard/Dashboard.scss +++ /dev/null @@ -1,7 +0,0 @@ -.DashboardBasicInfoBox { - display: grid; - grid-template-columns: -webkit-min-content auto; - grid-template-columns: min-content auto; - gap: 16px; - margin-bottom: 16px; -} diff --git a/src/components/viewsComponents/dashboard/Dashboard.tsx b/src/components/viewsComponents/dashboard/Dashboard.tsx index 75c9223..7f1666a 100644 --- a/src/components/viewsComponents/dashboard/Dashboard.tsx +++ b/src/components/viewsComponents/dashboard/Dashboard.tsx @@ -1,5 +1,5 @@ import { useSelector } from 'react-redux'; -import { Box, useMediaQuery } from '@mui/material'; +import { Box } from '@mui/material'; import CenteredLoader from 'components/shared/centeredLoader/CenteredLoader'; import TodoListDrawer from 'components/shared/todoListDrawer/TodoListDrawer'; import { useGetStudentGradesQuery } from 'redux/apiSlices/academics/Grades.Api.Slice'; @@ -8,46 +8,34 @@ import Announcements from './elements/Announcements'; import ClosestEvents from './elements/ClosestEvents'; import GradesSection from './elements/GradesSection'; import type { IGetStudentGradesQueryParams } from 'contract/slices/academics/Grades.Interfaces'; -import './Dashboard.scss'; +import './styles/Dashboard.scss'; function Dashboard() { - const isMobile = useMediaQuery('(max-width: 500px)'); - const maxDrawerWidth = isMobile ? 280 : 450; - - const { data: userData, isFetching: isFetchingUserData } = useGetLoggedAccountBasicDataQuery(); - const studentId = useSelector(selectId); const initialQueryParams: IGetStudentGradesQueryParams = { studentId: studentId! }; - const { data: grades, isFetching: isFetchingGrades } = useGetStudentGradesQuery(initialQueryParams, { + const { data: userData, isFetching: isFetchingUserData } = useGetLoggedAccountBasicDataQuery(); + const { data: grades = [], isFetching: isFetchingGrades } = useGetStudentGradesQuery(initialQueryParams, { skip: !studentId, }); - const isFetching = isFetchingUserData || isFetchingGrades; - - if (isFetching || !userData) { + if (isFetchingUserData || isFetchingGrades) { return ; } - return ( - - - - - - + if (!userData) { + return; + } - + return ( + <> + + + - - - + {/* Need refactor */} + + + ); } diff --git a/src/components/viewsComponents/dashboard/elements/ClosestEvents.tsx b/src/components/viewsComponents/dashboard/elements/ClosestEvents.tsx index fe02b2f..564b449 100644 --- a/src/components/viewsComponents/dashboard/elements/ClosestEvents.tsx +++ b/src/components/viewsComponents/dashboard/elements/ClosestEvents.tsx @@ -1,10 +1,10 @@ -import React, { useMemo } from 'react'; +import { useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import EventIcon from '@mui/icons-material/Event'; import { Typography, List, ListItem, Paper, Box } from '@mui/material'; import { staticEvents } from 'routes/postAuth/events/EventsData'; -const ClosestEvents: React.FC = () => { +const ClosestEvents = () => { const now = new Date(); const navigate = useNavigate(); diff --git a/src/components/viewsComponents/dashboard/elements/CustomGradesDonutTooltip.tsx b/src/components/viewsComponents/dashboard/elements/CustomGradesDonutTooltip.tsx new file mode 100644 index 0000000..45b8c8d --- /dev/null +++ b/src/components/viewsComponents/dashboard/elements/CustomGradesDonutTooltip.tsx @@ -0,0 +1,44 @@ +import { Box } from '@mui/material'; + +interface CustomTooltipProps { + active?: boolean; + payload?: { + payload: { + grade: string; + count: number; + fill: string; + }; + }[]; +} + +const CustomGradesDonutTooltip: React.FC = ({ active, payload }) => { + if (active && payload?.length && payload[0]) { + const { grade, fill } = payload[0].payload; + + return ( + + + {grade} + + + ); + } + + return null; +}; + +export default CustomGradesDonutTooltip; diff --git a/src/components/viewsComponents/dashboard/elements/GradesDonutChart.tsx b/src/components/viewsComponents/dashboard/elements/GradesDonutChart.tsx index 453bfda..f8d5177 100644 --- a/src/components/viewsComponents/dashboard/elements/GradesDonutChart.tsx +++ b/src/components/viewsComponents/dashboard/elements/GradesDonutChart.tsx @@ -1,53 +1,16 @@ import React from 'react'; -import { Box } from '@mui/material'; import { PieChart, Pie, Cell, Tooltip } from 'recharts'; +import CustomGradesDonutTooltip from './CustomGradesDonutTooltip'; interface GradesDonutChartProps { - groupedData: { grade: string; count: number; fill: string }[]; - averageGrade: string; -} - -interface CustomTooltipProps { - active?: boolean; - payload?: { - payload: { - grade: string; - count: number; - fill: string; - }; + groupedData: { + grade: string; + count: number; + fill: string; }[]; + averageGrade: string; } -const CustomTooltip: React.FC = ({ active, payload }) => { - if (active && payload?.length && payload[0]) { - const { grade, fill } = payload[0].payload; - - return ( - - - {grade} - - - ); - } - - return null; -}; - const GradesDonutChart: React.FC = ({ groupedData, averageGrade }) => { return ( @@ -79,7 +42,7 @@ const GradesDonutChart: React.FC = ({ groupedData, averag Your Current GPA - } /> + } /> ); }; diff --git a/src/components/viewsComponents/dashboard/elements/GradesSection.tsx b/src/components/viewsComponents/dashboard/elements/GradesSection.tsx index 80b7822..0917821 100644 --- a/src/components/viewsComponents/dashboard/elements/GradesSection.tsx +++ b/src/components/viewsComponents/dashboard/elements/GradesSection.tsx @@ -1,21 +1,19 @@ import { useNavigate } from 'react-router-dom'; -import { Typography, Paper, Box, Button, useMediaQuery } from '@mui/material'; -import { calculateAverageGrade } from 'components/viewsComponents/grades/elements/gradesInformationBoxes/GradesInformationBoxes'; +import { Typography, Paper, Box, Button } from '@mui/material'; +import { calculateAverageGrade } from 'components/viewsComponents/grades/utils/Helpers'; import _ from 'lodash'; import GradesDonutChart from './GradesDonutChart'; import type { IGrade } from 'contract/interfaces/academics/Academics'; import type { IGetLoggedAccountBasicDataTransformedReponse } from 'contract/slices/loggedAccount/LoggedAccount'; interface GradesSectionProps { - grades?: IGrade[]; + grades: IGrade[]; userData: IGetLoggedAccountBasicDataTransformedReponse; } -const GradesSection: React.FC = ({ grades, userData }) => { +const GradesSection: React.FC = ({ grades, userData: { name, surname } }) => { const navigate = useNavigate(); - const isMobile = useMediaQuery('(max-width:800px)'); - - const averageGrade = calculateAverageGrade(grades ?? []); + const averageGrade = calculateAverageGrade(grades); const gradeGroups = _.groupBy(grades, 'grade'); const groupedData = [ { grade: '2', count: gradeGroups[2]?.length ?? 0, fill: '#c40101' }, @@ -25,27 +23,10 @@ const GradesSection: React.FC = ({ grades, userData }) => { ]; return ( - - + + - Hi, {`${userData.name} ${userData.surname}!`} + Hi, {`${name} ${surname}!`} Check your performance stats
to make sure you are on track
with your academic goals! @@ -53,9 +34,7 @@ const GradesSection: React.FC = ({ grades, userData }) => { diff --git a/src/components/viewsComponents/dashboard/styles/Dashboard.scss b/src/components/viewsComponents/dashboard/styles/Dashboard.scss new file mode 100644 index 0000000..695dcdf --- /dev/null +++ b/src/components/viewsComponents/dashboard/styles/Dashboard.scss @@ -0,0 +1,34 @@ +.DashboardBasicInfoBox { + display: grid; + grid-template-columns: -webkit-min-content auto; + grid-template-columns: min-content auto; + gap: 16px; + margin-bottom: 16px; +} + +.GradesSectionContainer { + width: fit-content; + padding: 32px; + display: flex; + flex-wrap: wrap; + gap: 16px; + flex-basis: 550px; + flex-grow: 1; + + .InfoBox { + display: flex; + flex-direction: column; + gap: 24px; + min-width: 200px; + } + + + @media (max-width: 900px) { + flex-basis: 350px; + flex-grow: 0; + padding: 16px; + } +} + + + diff --git a/src/components/viewsComponents/events/eventShowDialog/EventShowDialog.tsx b/src/components/viewsComponents/events/eventShowDialog/EventShowDialog.tsx index 4885fb0..3b4a894 100644 --- a/src/components/viewsComponents/events/eventShowDialog/EventShowDialog.tsx +++ b/src/components/viewsComponents/events/eventShowDialog/EventShowDialog.tsx @@ -1,6 +1,6 @@ import type { ReactElement } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, Box, Divider } from '@mui/material'; -import { formatFullDateTime } from 'routes/utils/Date.Utils'; +import { formatFullDateTime } from 'utils/general/Date.Utils'; import type { IEventShowDialog } from 'types/events/Events.Interfaces'; const EventShowDialog = ({ onClose, event }: IEventShowDialog): ReactElement => { diff --git a/src/components/viewsComponents/grades/Grades.tsx b/src/components/viewsComponents/grades/Grades.tsx index 1ce891a..f815130 100644 --- a/src/components/viewsComponents/grades/Grades.tsx +++ b/src/components/viewsComponents/grades/Grades.tsx @@ -11,7 +11,7 @@ const Grades = () => { const studentId = useSelector(selectId); const initialQueryParams: IGetStudentGradesQueryParams = { studentId: studentId! }; - const { data: grades, isFetching } = useGetStudentGradesQuery(initialQueryParams, { skip: !studentId }); + const { data: grades = [], isFetching } = useGetStudentGradesQuery(initialQueryParams, { skip: !studentId }); if (isFetching) { return ; @@ -20,8 +20,8 @@ const Grades = () => { return ( <> - - + + ); }; diff --git a/src/components/viewsComponents/grades/constants/Cards.tsx b/src/components/viewsComponents/grades/constants/Cards.tsx new file mode 100644 index 0000000..32b8986 --- /dev/null +++ b/src/components/viewsComponents/grades/constants/Cards.tsx @@ -0,0 +1,77 @@ +import { EqualizerRounded, ErrorOutline, CheckCircleOutline, Warning, InfoOutlined } from '@mui/icons-material'; +import { GradeValueEnum } from 'contract/enums/Enums'; +import _ from 'lodash'; +import { calculateAverageGrade, calculatePercentage } from '../utils/Helpers'; +import type { IGrade } from 'contract/interfaces/academics/Academics'; + +interface IGradeCard { + id: number; + icon: JSX.Element; + title: string; + text: string; + color: string; + hideWhen?: boolean; +} + +export const useGradesCards = (grades: IGrade[]): IGradeCard[] => { + const gradesCount = grades.length; + const highestGradesCount = _.filter(grades, { grade: GradeValueEnum.Excellent }).length; + const underPerformingGradesCount = _.filter(grades, { grade: GradeValueEnum.Fair }).length; + const notPassedGradesCount = _.filter(grades, { grade: GradeValueEnum.Poor }).length; + + return [ + { + id: 5, + icon: , + title: calculateAverageGrade(grades), + text: 'Average from your grades', + color: '#bbdefb', + }, + { + id: 1, + icon: , + title: calculatePercentage(notPassedGradesCount, gradesCount), + text: 'Not passed subjects', + color: '#ffc1b5', + hideWhen: notPassedGradesCount === 0, + }, + { + id: 6, + icon: , + title: '100%', + text: 'All subjects passed', + color: '#a5e8a7', + hideWhen: notPassedGradesCount > 0, + }, + { + id: 2, + icon: , + title: highestGradesCount.toString(), + text: 'Amount of highest rate grade', + color: '#a5e8a7', + }, + { + id: 3, + icon: , + title: calculatePercentage(underPerformingGradesCount, gradesCount), + text: 'Underperforming grades', + color: '#ffecb3', + hideWhen: underPerformingGradesCount === 0, + }, + { + id: 7, + icon: , + title: '0%', + text: 'No underperforming grades', + color: '#a5e8a7', + hideWhen: underPerformingGradesCount > 0, + }, + { + id: 4, + icon: , + title: gradesCount.toString(), + text: 'Total subjects you are enrolled in', + color: '#bbdefb', + }, + ].filter(card => !card.hideWhen); +}; diff --git a/src/components/viewsComponents/grades/elements/gradesDataGrid/Columns.tsx b/src/components/viewsComponents/grades/constants/Columns.tsx similarity index 70% rename from src/components/viewsComponents/grades/elements/gradesDataGrid/Columns.tsx rename to src/components/viewsComponents/grades/constants/Columns.tsx index c9d8a94..a873f91 100644 --- a/src/components/viewsComponents/grades/elements/gradesDataGrid/Columns.tsx +++ b/src/components/viewsComponents/grades/constants/Columns.tsx @@ -5,16 +5,21 @@ import { GradeValueEnum } from 'contract/enums/Enums'; import type { ColDef } from 'ag-grid-community'; import type { IGrade } from 'contract/interfaces/academics/Academics'; -const PassedChipRenderer = ({ value }: { value: 'Passed' | 'Not passed' | null }) => { +enum PassStatusEnum { + Passed = 'Passed', + NotPassed = 'Not Passed', +} + +const PassedChipRenderer = ({ value }: { value: PassStatusEnum | null }) => { if (value === null) { - return <> ; + return null; } - if (value === 'Passed') { - return } />; + if (value === PassStatusEnum.Passed) { + return } />; } - return } />; + return } />; }; interface IExtendedColumns extends IGrade { @@ -59,7 +64,7 @@ export const gradesColumns: ColDef[] = [ return null; } - return grade > GradeValueEnum.Poor ? 'Passed' : 'Not passed'; + return grade > GradeValueEnum.Poor ? PassStatusEnum.Passed : PassStatusEnum.NotPassed; }, cellRenderer: PassedChipRenderer, }, diff --git a/src/components/viewsComponents/grades/elements/gradesChartCustomTooltip/GradesChartCustomTooltip.tsx b/src/components/viewsComponents/grades/elements/gradesChartCustomTooltip/GradesChartCustomTooltip.tsx new file mode 100644 index 0000000..ea5fdf0 --- /dev/null +++ b/src/components/viewsComponents/grades/elements/gradesChartCustomTooltip/GradesChartCustomTooltip.tsx @@ -0,0 +1,32 @@ +import { Box, Typography } from '@mui/material'; + +interface ICustomTooltipProps { + active?: boolean; + payload?: { payload: { grade: number; count: number } }[]; +} + +const GradesChartCustomTooltip: React.FC = ({ active, payload }) => { + if (active && payload?.[0]) { + const { grade, count } = payload[0].payload; + + return ( + + + Grade: {grade} + + Amount: {count} + + ); + } + + return null; +}; + +export default GradesChartCustomTooltip; diff --git a/src/components/viewsComponents/grades/elements/gradesDataGrid/GradesDataGrid.tsx b/src/components/viewsComponents/grades/elements/gradesDataGrid/GradesDataGrid.tsx index 68f60b7..45f144a 100644 --- a/src/components/viewsComponents/grades/elements/gradesDataGrid/GradesDataGrid.tsx +++ b/src/components/viewsComponents/grades/elements/gradesDataGrid/GradesDataGrid.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import DataGrid, { SearchBarVariantEnum } from 'components/shared/dataGrid/DataGrid'; -import { gradesColumns } from './Columns'; +import { gradesColumns } from '../../constants/Columns'; import type { IGrade } from 'contract/interfaces/academics/Academics'; interface IGradesDataGridProps { diff --git a/src/components/viewsComponents/grades/elements/gradesInformationBoxes/GradesInformationBoxes.tsx b/src/components/viewsComponents/grades/elements/gradesInformationBoxes/GradesInformationBoxes.tsx index e8752a5..60f5a24 100644 --- a/src/components/viewsComponents/grades/elements/gradesInformationBoxes/GradesInformationBoxes.tsx +++ b/src/components/viewsComponents/grades/elements/gradesInformationBoxes/GradesInformationBoxes.tsx @@ -1,76 +1,14 @@ -import { ErrorOutline, CheckCircleOutline, Warning, InfoOutlined, EqualizerRounded } from '@mui/icons-material'; import { Box } from '@mui/material'; import SummaryCard from 'components/shared/summaryCard/SummaryCard'; -import { GradeValueEnum } from 'contract/enums/Enums'; -import _ from 'lodash'; +import { useGradesCards } from '../../constants/Cards'; import type { IGrade } from 'contract/interfaces/academics/Academics'; interface IGradesInformationBoxes { - grades?: IGrade[]; + grades: IGrade[]; } const GradesInformationBoxes = ({ grades }: IGradesInformationBoxes) => { - const data = grades ?? []; - const gradesCount = data.length; - const highestGradesCount = _.filter(grades, { grade: GradeValueEnum.Excellent }).length; - const underPerformingGradesCount = _.filter(grades, { grade: GradeValueEnum.Fair }).length; - const notPassedGradesCount = _.filter(grades, { grade: GradeValueEnum.Poor }).length; - - const cards = [ - { - id: 5, - icon: , - title: calculateAverageGrade(data), - text: 'Average from your grades', - color: '#bbdefb', - }, - { - id: 1, - icon: , - title: calculatePercentage(notPassedGradesCount, gradesCount), - text: 'Not passed subjects', - color: '#ffc1b5', - hideWhen: notPassedGradesCount === 0, - }, - { - id: 6, - icon: , - title: '100%', - text: 'All subjects passed', - color: '#a5e8a7', - hideWhen: notPassedGradesCount > 0, - }, - { - id: 2, - icon: , - title: highestGradesCount.toString(), - text: 'Amount of highest rate grade', - color: '#a5e8a7', - }, - { - id: 3, - icon: , - title: calculatePercentage(underPerformingGradesCount, gradesCount), - text: 'Underperforming grades', - color: '#ffecb3', - hideWhen: underPerformingGradesCount === 0, - }, - { - id: 7, - icon: , - title: '0%', - text: 'No underperforming grades', - color: '#a5e8a7', - hideWhen: underPerformingGradesCount > 0, - }, - { - id: 4, - icon: , - title: gradesCount.toString(), - text: 'Total subjects you are enrolled in', - color: '#bbdefb', - }, - ].filter(card => card.hideWhen === false || card.hideWhen === undefined); + const cards = useGradesCards(grades); return ( { width: '100%', }} > - {cards.map(card => ( - + {cards.map(({ id, icon, title, text, color }) => ( + ))} ); }; export default GradesInformationBoxes; - -export const calculateAverageGrade = (gradesList: IGrade[]): string => { - if (gradesList.length === 0) { - return '0.00'; - } - - const validGrades = _.map(gradesList, 'grade').filter(Number); - const average = _.mean(validGrades); - - return average.toFixed(2); -}; - -const calculatePercentage = (count: number, gradesCount: number): string => { - if (gradesCount === 0) { - return '0%'; - } - - return `${Math.round((count / gradesCount) * 100)}%`; -}; diff --git a/src/components/viewsComponents/grades/elements/gradesSummarizeChart/GradesSummarizeChart.tsx b/src/components/viewsComponents/grades/elements/gradesSummarizeChart/GradesSummarizeChart.tsx index 2ea321c..1643194 100644 --- a/src/components/viewsComponents/grades/elements/gradesSummarizeChart/GradesSummarizeChart.tsx +++ b/src/components/viewsComponents/grades/elements/gradesSummarizeChart/GradesSummarizeChart.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Box, Typography } from '@mui/material'; import _ from 'lodash'; import { Bar, BarChart, LabelList, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; +import GradesChartCustomTooltip from '../gradesChartCustomTooltip/GradesChartCustomTooltip'; import type { IGrade } from 'contract/interfaces/academics/Academics'; interface GradesSummarizeChartProps { @@ -51,39 +52,11 @@ const GradesSummarizeChart: React.FC = ({ grades }) = angle: -90, }} /> - } cursor={{ fill: 'transparent' }} /> + } cursor={{ fill: 'transparent' }} />
); }; -interface ICustomTooltipProps { - active?: boolean; - payload?: { payload: { grade: number; count: number } }[]; -} - -const CustomTooltip: React.FC = ({ active, payload }) => { - if (active && payload?.[0]) { - const { grade, count } = payload[0].payload; - - return ( - - - Grade: {grade} - - Amount: {count} - - ); - } - return null; -}; - export default GradesSummarizeChart; diff --git a/src/components/viewsComponents/grades/utils/Helpers.tsx b/src/components/viewsComponents/grades/utils/Helpers.tsx new file mode 100644 index 0000000..1b8efa8 --- /dev/null +++ b/src/components/viewsComponents/grades/utils/Helpers.tsx @@ -0,0 +1,21 @@ +import { IGrade } from 'contract/interfaces/academics/Academics'; +import _ from 'lodash'; + +export const calculateAverageGrade = (gradesList: IGrade[]): string => { + if (gradesList.length === 0) { + return '0.00'; + } + + const validGrades = _.map(gradesList, 'grade').filter(Number); + const average = _.mean(validGrades); + + return average.toFixed(2); +}; + +export const calculatePercentage = (count: number, gradesCount: number): string => { + if (gradesCount === 0) { + return '0%'; + } + + return `${Math.round((count / gradesCount) * 100)}%`; +}; diff --git a/src/routes/preAuth/login/Login.tsx b/src/components/viewsComponents/login/Login.tsx similarity index 70% rename from src/routes/preAuth/login/Login.tsx rename to src/components/viewsComponents/login/Login.tsx index 75f6900..c1ff5db 100644 --- a/src/routes/preAuth/login/Login.tsx +++ b/src/components/viewsComponents/login/Login.tsx @@ -3,12 +3,11 @@ import { useTranslation } from 'react-i18next'; import { Box, Typography, Button, Paper } from '@mui/material'; import { yupResolver } from '@hookform/resolvers/yup'; import RHFTextField from 'components/shared/formComponents/textField/RHFTextField'; -import LoginAdditionalActions from 'components/viewsComponents/login/LoginAdditionalActions'; -import PasswordField from 'components/viewsComponents/login/PasswordField'; +import LoginAdditionalActions from 'components/viewsComponents/login/elements/LoginAdditionalActions'; +import PasswordField from 'components/viewsComponents/login/elements/PasswordField'; import { useLoginMutation } from 'redux/apiSlices/auth/Auth.Api.Slice'; import ExampleUserIcon from 'assets/icons/exampleUserIcon.png'; -import { loginValidationSchema } from './Login.Yup'; -import type { ILoginFields } from './types/Login.Types'; +import { loginValidationSchema } from './schema/Login.Yup'; import './styles/AuthPanel.scss'; const Login: React.FC = () => { @@ -23,29 +22,25 @@ const Login: React.FC = () => { rememberMe: false, }, }); - const { handleSubmit } = methods; - const onSubmit: SubmitHandler = async data => { - const sessionUUID = sessionStorage.getItem('sessionUUID')!; const { login, password, rememberMe } = data; await loginUser({ identifier: login, password, rememberMe, - sessionID: sessionUUID, }); }; return ( - - - + + + {t('Login Form')} - - + +