commit d9db46abcad00b3da56ca19b3aef8d43ccf4eced Author: deepvasoya Date: Tue May 6 18:33:01 2025 +0530 feat: initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..7059a96 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..ec2b712 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,33 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' + +export default [ + { ignores: ['dist'] }, + { + files: ['**/*.{js,jsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...js.configs.recommended.rules, + ...reactHooks.configs.recommended.rules, + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +] diff --git a/index.html b/index.html new file mode 100644 index 0000000..0759bd3 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + + Health Apps + + + +
+ + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2aabaa7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7800 @@ +{ + "name": "health-apps-admin", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "health-apps-admin", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@mui/icons-material": "^5.11.0", + "@mui/lab": "^5.0.0-alpha.117", + "@mui/material": "^5.11.7", + "@mui/styles": "^5.11.7", + "@mui/system": "^5.11.7", + "@mui/x-date-pickers": "^8.2.0", + "axios": "^1.8.4", + "date-fns": "^4.1.0", + "firebase": "^11.6.0", + "formik": "^2.4.6", + "i": "^0.3.7", + "lodash": "^4.17.21", + "material-react-table": "^3.2.1", + "npm": "^11.3.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-dropzone": "^14.3.8", + "react-image-crop": "^11.0.10", + "react-infinite-scroll-component": "^6.1.0", + "react-phone-input-2": "^2.15.1", + "react-redux": "^8.1.3", + "react-router-dom": "^6.20.0", + "react-toastify": "^9.1.3", + "react-zoom-pan-pinch": "^3.7.0", + "redux": "^5.0.1", + "redux-localstorage": "^0.4.1", + "redux-promise-middleware": "^6.2.0", + "redux-thunk": "^3.1.0", + "yup": "^1.6.1" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@types/react": "^18.2.38", + "@types/react-dom": "^18.2.15", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", + "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz", + "integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.12", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.12.tgz", + "integrity": "sha512-iDCGnw6qdFqwI5ywkgece99WADJNoymu+nLIQI4fZM/vCZ3bEo4wlpEetW71s1HqGpI0hQStiPhqVjFxDb2yyw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.18.tgz", + "integrity": "sha512-Hw9mzsSMZaQu6wrTbi3kYYwGw9nBqOHr47pVLxfr5v8CalsdrG5gfs9XUlPOZjHRVISp3oQrh1j7d3E+ulHPjQ==", + "dependencies": { + "@firebase/analytics": "0.10.12", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==" + }, + "node_modules/@firebase/app": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.11.4.tgz", + "integrity": "sha512-GPREsZjfSaHzwyC6cI/Cqvzf6zxqMzya+25tSpUstdqC2w0IdfxEfOMjfdW7bDfVEf4Rb4Nb6gfoOAgVSp4c4g==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.13.tgz", + "integrity": "sha512-ONsgml8/dplUOAP42JQO6hhiWDEwR9+RUTLenxAN9S8N6gel/sDQ9Ci721Py1oASMGdDU8v9R7xAZxzvOX5lPg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.20.tgz", + "integrity": "sha512-/twgmlnNAaZ/wbz3kcQrL/26b+X+zUX+lBmu5LwwEcWcpnb+mrVEAKhD7/ttm52dxYiSWtLDeuXy3FXBhqBC5A==", + "dependencies": { + "@firebase/app-check": "0.8.13", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.53", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.53.tgz", + "integrity": "sha512-vDeZSit0q4NyaDIVcaiJF3zhLgguP6yc0JwQAfpTyllgt8XMtkMFyY/MxJtFrK2ocpQX/yCbV2DXwvpY2NVuJw==", + "dependencies": { + "@firebase/app": "0.11.4", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.0.tgz", + "integrity": "sha512-S7SqBsN7sIQsftNE3bitLlK+4bWrTHY+Rx2JFlNitgVYu2nK8W8ZQrkG8GCEwiFPq0B2vZ9pO5kVTFfq2sP96A==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.20.tgz", + "integrity": "sha512-8FwODTSBnaqGQbKfML7LcpzGGPyouB7YHg3dZq+CZMziVc7oBY1jJeNvpnM1hAQoVuTjWPXoRrCltdGeOlkKfQ==", + "dependencies": { + "@firebase/auth": "1.10.0", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.13.tgz", + "integrity": "sha512-I/Eg1NpAtZ8AAfq8mpdfXnuUpcLxIDdCDtTzWSh+FXnp/9eCKJ3SNbOCKrUCyhLzNa2SiPJYruei0sxVjaOTeg==", + "dependencies": { + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.3.tgz", + "integrity": "sha512-JsgppNX1wcQYP5bg4Sg6WTS7S0XazklSjr1fG3ox9DHtt4LOQwJ3X1/c81mKMIZxocV22ujiwLYQWG6Y9D1FiQ==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.14.tgz", + "integrity": "sha512-9nxYtkHAG02/Nh2Ssms1T4BbWPPjiwohCvkHDUl4hNxnki1kPgsLo5xe9kXNzbacOStmVys+RUXvwzynQSKmUQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.5.tgz", + "integrity": "sha512-CNf1UbvWh6qIaSf4sn6sx2DTDz/em/D7QxULH1LTxxDQHr9+CeYGvlAqrKnk4ZH0P0eIHyQFQU7RwkUJI0B9gQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/database": "1.0.14", + "@firebase/database-types": "1.0.10", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.10.tgz", + "integrity": "sha512-mH6RC1E9/Pv8jf1/p+M8YFTX+iu+iHDN89hecvyO7wHrI4R1V0TXjxOHvX3nLJN1sfh0CWG6CHZ0VlrSmK/cwg==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.11.0" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.10", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.10.tgz", + "integrity": "sha512-6nKsyo2U+jYSCcSE5sjMdDNA23DMUvYPUvsYGg09CNvcTO8GGKsPs7SpOhspsB91mbacq+u627CDAx3FUhPSSQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.45", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.45.tgz", + "integrity": "sha512-uRvi7AYPmsDl7UZwPyV7jgDGYusEZ2+U2g7MndbQHKIA8fNHpYC6QrzMs58+/IjX+kF/lkUn67Vrr0AkVjlY+Q==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/firestore": "4.7.10", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.3.tgz", + "integrity": "sha512-Wv7JZMUkKLb1goOWRtsu3t7m97uK6XQvjQLPvn8rncY91+VgdU72crqnaYCDI/ophNuBEmuK8mn0/pAnjUeA6A==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.13", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.20.tgz", + "integrity": "sha512-iIudmYDAML6n3c7uXO2YTlzra2/J6lnMzmJTXNthvrKVMgNMaseNoQP1wKfchK84hMuSF8EkM4AvufwbJ+Juew==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/functions": "0.12.3", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.13.tgz", + "integrity": "sha512-6ZpkUiaygPFwgVneYxuuOuHnSPnTA4KefLEaw/sKk/rNYgC7X6twaGfYb0sYLpbi9xV4i5jXsqZ3WO+yaguNgg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.13.tgz", + "integrity": "sha512-f/o6MqCI7LD/ulY9gvgkv6w5k6diaReD8BFHd/y/fEdpsXmFWYS/g28GXCB72bRVBOgPpkOUNl+VsMvDwlRKmw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.17", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.17.tgz", + "integrity": "sha512-W3CnGhTm6Nx8XGb6E5/+jZTuxX/EK8Vur4QXvO1DwZta/t0xqWMRgO9vNsZFMYBqFV4o3j4F9qK/iddGYwWS6g==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.11.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.17.tgz", + "integrity": "sha512-5Q+9IG7FuedusdWHVQRjpA3OVD9KUWp/IPegcv0s5qSqRLBjib7FlAeWxN+VL0Ew43tuPJBY2HKhEecuizmO1Q==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/messaging": "0.12.17", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==" + }, + "node_modules/@firebase/performance": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.2.tgz", + "integrity": "sha512-DXLLp0R0jdxH/yTmv+WTkOzsLl8YYecXh4lGZE0dzqC0IV8k+AxpLSSWvOTCkAETze8yEU/iF+PtgYVlGjfMMQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.15.tgz", + "integrity": "sha512-wUxsw7hGBEMN6XfvYQqwPIQp5LcJXawWM5tmYp6L7ClCoTQuEiCKHWWVurJgN8Q1YHzoHVgjNfPQAOVu29iMVg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.2", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.0.tgz", + "integrity": "sha512-Yrk4l5+6FJLPHC6irNHMzgTtJ3NfHXlAXVChCBdNFtgmzyGmufNs/sr8oA0auEfIJ5VpXCaThRh3P4OdQxiAlQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.13.tgz", + "integrity": "sha512-UmHoO7TxAEJPIZf8e1Hy6CeFGMeyjqSCpgoBkQZYXFI2JHhzxIyDpr8jVKJJN1dmAePKZ5EX7dC13CmcdTOl7Q==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.0", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.7.tgz", + "integrity": "sha512-FkRyc24rK+Y6EaQ1tYFm3TevBnnfSNA0VyTfew2hrYyL/aYfatBg7HOgktUdB4kWMHNA9VoTotzZTGoLuK92wg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.17.tgz", + "integrity": "sha512-CBlODWEZ5b6MJWVh21VZioxwxNwVfPA9CAdsk+ZgVocJQQbE2oDW1XJoRcgthRY1HOitgbn4cVrM+NlQtuUYhw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/storage": "0.13.7", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.11.0.tgz", + "integrity": "sha512-PzSrhIr++KI6y4P6C/IdgBNMkEx0Ex6554/cYd0Hm+ovyFSJtJXqb/3OSIdnBoa2cpwZT1/GW56EmRc5qEc5fQ==", + "hasInstallScript": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/vertexai": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.2.1.tgz", + "integrity": "sha512-cukZ5ne2RsOWB4PB1EO6nTXgOLxPMKDJfEn+XnSV5ZKWM0ID5o0DvbyS59XihFaBzmy2SwJldP5ap7/xUnW4jA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", + "dependencies": { + "@floating-ui/core": "^1.7.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.40-1", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40-1.tgz", + "integrity": "sha512-agKXuNNy0bHUmeU7pNmoZwNFr7Hiyhojkb9+2PVyDG5+6RafYuyMgbrav8CndsB7KUc/U51JAw9vKNDLYBzaUA==", + "deprecated": "This package has been replaced by @base-ui-components/react", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.17.1.tgz", + "integrity": "sha512-OcZj+cs6EfUD39IoPBOgN61zf1XFVY+imsGoBDwXeSq2UHJZE3N59zzBOVjclck91Ne3e9gudONOeILvHCIhUA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.17.1.tgz", + "integrity": "sha512-CN86LocjkunFGG0yPlO4bgqHkNGgaEOEc3X/jG5Bzm401qYw79/SaLrofA7yAKCCXAGdIGnLoMHohc3+ubs95A==", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab": { + "version": "5.0.0-alpha.176", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.176.tgz", + "integrity": "sha512-DcZt1BAz4CDMUFGUvKqRh6W0sehmPj5luVHPx4vzSNnXj8xFvOdHwvNZ0bzNXy/Ol+81bkxcHQoIG2VOJuLnbw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40-1", + "@mui/system": "^5.17.1", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": ">=5.15.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.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 + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab/node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.17.1.tgz", + "integrity": "sha512-2B33kQf+GmPnrvXXweWAx+crbiUEsxCdCN979QDYnlH9ox4pd+0/IBriWLV+l6ORoBF60w39cWjFnJYGFdzXcw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/core-downloads-tracker": "^5.17.1", + "@mui/system": "^5.17.1", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.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 + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz", + "integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.17.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming/node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz", + "integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.13.5", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/styles": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.17.1.tgz", + "integrity": "sha512-GxNtcD1jXjj1i81vyuaeNxCpph/ApxSxgJ+G8A2jUY5/bMOxXSmgUdupbB0JLexsDIqmaSqTePVN0jnMZc1iZQ==", + "deprecated": "Deprecated, check the migration instruction in https://mui.com/material-ui/migration/migrating-from-jss/", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/hash": "^0.9.1", + "@mui/private-theming": "^5.17.1", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.10.0", + "jss-plugin-camel-case": "^10.10.0", + "jss-plugin-default-unit": "^10.10.0", + "jss-plugin-global": "^10.10.0", + "jss-plugin-nested": "^10.10.0", + "jss-plugin-props-sort": "^10.10.0", + "jss-plugin-rule-value-function": "^10.10.0", + "jss-plugin-vendor-prefixer": "^10.10.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styles/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styles/node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.17.1.tgz", + "integrity": "sha512-aJrmGfQpyF0U4D4xYwA6ueVtQcEMebET43CUmKMP7e7iFh3sMIF3sBR0l8Urb4pqx1CBjHAaWgB0ojpND4Q3Jg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.17.1", + "@mui/styled-engine": "^5.16.14", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/@mui/utils": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.1.tgz", + "integrity": "sha512-gUL8IIAI52CRXP/MixT1tJKt3SI6tVv4U/9soFsTtAsHzaJQptZ42ffdHZV3niX1ei0aUgMvOxBBN0KYqdG39g==", + "dependencies": { + "@babel/runtime": "^7.27.0" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-72gcuQjPzhj/MLmPHLCgZjy2VjOH4KniR/4qRtXTTXIEwbkgcN+Y5W/rC90rWtMmZbjt9svZev/z+QHUI4j74w==", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/types": "^7.4.1", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.2.0.tgz", + "integrity": "sha512-nkFzKkaDr4a9uto4pSOsNBMh+M2YQWiW9iYyBDOi5wOa983e2xMeb7zFjHGJ0xaycg1kfxNcbvJd7tm4Ub+RhA==", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/utils": "^7.0.2", + "@mui/x-internals": "8.2.0", + "@types/react-transition-group": "^4.4.12", + "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 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2 || ^3.0.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.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 + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-internals": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.2.0.tgz", + "integrity": "sha512-qV4Qr+m4sAPBSuqu8/Ofi5m+nMMvIybGno6cp757bHSmwxkqrn5SKaGyFnH5kB58fOhYA9hG1UivFp7mO1dE4A==", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/utils": "^7.0.2" + }, + "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/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@swc/core": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.22.tgz", + "integrity": "sha512-mjPYbqq8XjwqSE0hEPT9CzaJDyxql97LgK4iyvYlwVSQhdN1uK0DBG4eP9PxYzCS2MUGAXB34WFLegdUj5HGpg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.21" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.11.22", + "@swc/core-darwin-x64": "1.11.22", + "@swc/core-linux-arm-gnueabihf": "1.11.22", + "@swc/core-linux-arm64-gnu": "1.11.22", + "@swc/core-linux-arm64-musl": "1.11.22", + "@swc/core-linux-x64-gnu": "1.11.22", + "@swc/core-linux-x64-musl": "1.11.22", + "@swc/core-win32-arm64-msvc": "1.11.22", + "@swc/core-win32-ia32-msvc": "1.11.22", + "@swc/core-win32-x64-msvc": "1.11.22" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.22.tgz", + "integrity": "sha512-upSiFQfo1TE2QM3+KpBcp5SrOdKKjoc+oUoD1mmBDU2Wv4Bjjv16Z2I5ADvIqMV+b87AhYW+4Qu6iVrQD7j96Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.22.tgz", + "integrity": "sha512-8PEuF/gxIMJVK21DjuCOtzdqstn2DqnxVhpAYfXEtm3WmMqLIOIZBypF/xafAozyaHws4aB/5xmz8/7rPsjavw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.22.tgz", + "integrity": "sha512-NIPTXvqtn9e7oQHgdaxM9Z/anHoXC3Fg4ZAgw5rSGa1OlnKKupt5sdfJamNggSi+eAtyoFcyfkgqHnfe2u63HA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.22.tgz", + "integrity": "sha512-xZ+bgS60c5r8kAeYsLNjJJhhQNkXdidQ277pUabSlu5GjR0CkQUPQ+L9hFeHf8DITEqpPBPRiAiiJsWq5eqMBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.22.tgz", + "integrity": "sha512-JhrP/q5VqQl2eJR0xKYIkKTPjgf8CRsAmRnjJA2PtZhfQ543YbYvUqxyXSRyBOxdyX8JwzuAxIPEAlKlT7PPuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.22.tgz", + "integrity": "sha512-htmAVL+U01gk9GyziVUP0UWYaUQBgrsiP7Ytf6uDffrySyn/FclUS3MDPocNydqYsOpj3OpNKPxkaHK+F+X5fg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.22.tgz", + "integrity": "sha512-PL0VHbduWPX+ANoyOzr58jBiL2VnD0xGSFwPy7NRZ1Pr6SNWm4jw3x2u6RjLArGhS5EcWp64BSk9ZxqmTV3FEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.22.tgz", + "integrity": "sha512-moJvFhhTVGoMeEThtdF7hQog80Q00CS06v5uB+32VRuv+I31+4WPRyGlTWHO+oY4rReNcXut/mlDHPH7p0LdFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.22.tgz", + "integrity": "sha512-/jnsPJJz89F1aKHIb5ScHkwyzBciz2AjEq2m9tDvQdIdVufdJ4SpEDEN9FqsRNRLcBHjtbLs6bnboA+B+pRFXw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.22.tgz", + "integrity": "sha512-lc93Y8Mku7LCFGqIxJ91coXZp2HeoDcFZSHCL90Wttg5xhk5xVM9uUCP+OdQsSsEixLF34h5DbT9ObzP8rAdRw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/types": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", + "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tanstack/match-sorter-utils": { + "version": "8.19.4", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz", + "integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==", + "dependencies": { + "remove-accents": "0.5.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.20.6", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.6.tgz", + "integrity": "sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ==", + "dependencies": { + "@tanstack/table-core": "8.20.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz", + "integrity": "sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==", + "dependencies": { + "@tanstack/virtual-core": "3.11.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", + "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz", + "integrity": "sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "node_modules/@types/react": { + "version": "18.3.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", + "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz", + "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", + "dev": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.9.0.tgz", + "integrity": "sha512-jYFUSXhwMCYsh/aQTgSGLIN3Foz5wMbH9ahb0Zva//UzwZYbMiZd7oT3AU9jHT9DLswYDswsRwPU9jVF3yA48Q==", + "dev": true, + "dependencies": { + "@swc/core": "^1.11.21" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "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, + "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/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/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, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/firebase": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.6.0.tgz", + "integrity": "sha512-Xqm6j6zszIEmI5nW1MPR8yTafoRTSrW3mWG9Lk9elCJtQDQSiTEkKZiNtUm9y6XfOPl8xoF1TNpxZe8HjgA0Og==", + "dependencies": { + "@firebase/analytics": "0.10.12", + "@firebase/analytics-compat": "0.2.18", + "@firebase/app": "0.11.4", + "@firebase/app-check": "0.8.13", + "@firebase/app-check-compat": "0.3.20", + "@firebase/app-compat": "0.2.53", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.0", + "@firebase/auth-compat": "0.5.20", + "@firebase/data-connect": "0.3.3", + "@firebase/database": "1.0.14", + "@firebase/database-compat": "2.0.5", + "@firebase/firestore": "4.7.10", + "@firebase/firestore-compat": "0.3.45", + "@firebase/functions": "0.12.3", + "@firebase/functions-compat": "0.3.20", + "@firebase/installations": "0.6.13", + "@firebase/installations-compat": "0.2.13", + "@firebase/messaging": "0.12.17", + "@firebase/messaging-compat": "0.2.17", + "@firebase/performance": "0.7.2", + "@firebase/performance-compat": "0.2.15", + "@firebase/remote-config": "0.6.0", + "@firebase/remote-config-compat": "0.2.13", + "@firebase/storage": "0.13.7", + "@firebase/storage-compat": "0.3.17", + "@firebase/util": "1.11.0", + "@firebase/vertexai": "1.2.1" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formik": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", + "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight-words": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/highlight-words/-/highlight-words-2.0.0.tgz", + "integrity": "sha512-If5n+IhSBRXTScE7wl16VPmd+44Vy7kof24EdqhjsZsDuHikpv1OCagVcJFpB4fS4UPUniedlWqrjIO8vWOsIQ==", + "engines": { + "node": ">= 20", + "npm": ">= 9" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==" + }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==" + }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "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==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "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.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" + }, + "node_modules/lodash.startswith": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz", + "integrity": "sha512-XClYR1h4/fJ7H+mmCKppbiBmljN/nGs73iq2SjCT9SF4CBPoUHzLvWmH1GtZMhMBZSiRkHXfeA2RY1eIlJ75ww==" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/material-react-table": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/material-react-table/-/material-react-table-3.2.1.tgz", + "integrity": "sha512-sQtTf7bETpkPN2Hm5BVtz89wrfXCVQguz6XlwMChSnfKFO5QCKAJJC5aSIKnUc3S0AvTz/k/ILi00FnnY1Gixw==", + "dependencies": { + "@tanstack/match-sorter-utils": "8.19.4", + "@tanstack/react-table": "8.20.6", + "@tanstack/react-virtual": "3.11.2", + "highlight-words": "2.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kevinvandy" + }, + "peerDependencies": { + "@emotion/react": ">=11.13", + "@emotion/styled": ">=11.13", + "@mui/icons-material": ">=6", + "@mui/material": ">=6", + "@mui/x-date-pickers": ">=7.15", + "react": ">=18.0", + "react-dom": ">=18.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/npm": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.3.0.tgz", + "integrity": "sha512-luthFIP0nFX3+nTfYbWI3p4hP4CiVnKOZ5jdxnF2x7B+Shz8feiSJCLLzgJUNxQ2cDdTaVUiH6RRsMT++vIMZg==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which" + ], + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^9.0.2", + "@npmcli/config": "^10.2.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.1", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.1.1", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.2.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^8.0.0", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^10.0.0", + "libnpmdiff": "^8.0.2", + "libnpmexec": "^10.1.1", + "libnpmfund": "^7.0.2", + "libnpmorg": "^8.0.0", + "libnpmpack": "^9.0.2", + "libnpmpublish": "^11.0.0", + "libnpmsearch": "^9.0.0", + "libnpmteam": "^8.0.0", + "libnpmversion": "^8.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.2.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.1", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^10.0.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "9.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^9.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "10.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.1.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "2.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.1.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.2", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^3.0.0", + "diff": "^7.0.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "10.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.2", + "@npmcli/package-json": "^6.1.1", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "7.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.2" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "9.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.2", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "11.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "21.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^10.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.1", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": 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/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": 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/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "10.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.12", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, + "node_modules/protobufjs": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.0.tgz", + "integrity": "sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-dropzone": { + "version": "14.3.8", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz", + "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==", + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "node_modules/react-image-crop": { + "version": "11.0.10", + "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-11.0.10.tgz", + "integrity": "sha512-+5FfDXUgYLLqBh1Y/uQhIycpHCbXkI50a+nbfkB1C0xXXUTwkisHDo2QCB1SQJyHCqIuia4FeyReqXuMDKWQTQ==", + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-infinite-scroll-component": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", + "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", + "dependencies": { + "throttle-debounce": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==" + }, + "node_modules/react-phone-input-2": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/react-phone-input-2/-/react-phone-input-2-2.15.1.tgz", + "integrity": "sha512-W03abwhXcwUoq+vUFvC6ch2+LJYMN8qSOiO889UH6S7SyMCQvox/LF3QWt+cZagZrRdi5z2ON3omnjoCUmlaYw==", + "dependencies": { + "classnames": "^2.2.6", + "lodash.debounce": "^4.0.8", + "lodash.memoize": "^4.1.2", + "lodash.reduce": "^4.6.0", + "lodash.startswith": "^4.2.1", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0", + "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0" + } + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-toastify/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-zoom-pan-pinch": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.7.0.tgz", + "integrity": "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "node_modules/redux-localstorage": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/redux-localstorage/-/redux-localstorage-0.4.1.tgz", + "integrity": "sha512-dUha0YoH+BSZ2q15pakB+JWeqiuXUf3Ir4rObOpNrZ96HEdciGAjkL10k3KGdLI7qvQw/c096asw/SQ6TPjU/A==" + }, + "node_modules/redux-promise-middleware": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/redux-promise-middleware/-/redux-promise-middleware-6.2.0.tgz", + "integrity": "sha512-TEzfMeLX63gju2WqkdFQlQMvUGYzFvJNePIJJsBlbPHs3Txsbc/5Rjhmtha1XdMU6lkeiIlp1Qx7AR3Zo9he9g==", + "peerDependencies": { + "redux": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "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-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz", + "integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==", + "dev": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8cbaeb2 --- /dev/null +++ b/package.json @@ -0,0 +1,56 @@ +{ + "name": "health-apps-admin", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@mui/icons-material": "^5.11.0", + "@mui/lab": "^5.0.0-alpha.117", + "@mui/material": "^5.11.7", + "@mui/styles": "^5.11.7", + "@mui/system": "^5.11.7", + "@mui/x-date-pickers": "^8.2.0", + "axios": "^1.8.4", + "date-fns": "^4.1.0", + "firebase": "^11.6.0", + "formik": "^2.4.6", + "i": "^0.3.7", + "lodash": "^4.17.21", + "material-react-table": "^3.2.1", + "npm": "^11.3.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-dropzone": "^14.3.8", + "react-image-crop": "^11.0.10", + "react-infinite-scroll-component": "^6.1.0", + "react-phone-input-2": "^2.15.1", + "react-redux": "^8.1.3", + "react-router-dom": "^6.20.0", + "react-toastify": "^9.1.3", + "react-zoom-pan-pinch": "^3.7.0", + "redux": "^5.0.1", + "redux-localstorage": "^0.4.1", + "redux-promise-middleware": "^6.2.0", + "redux-thunk": "^3.1.0", + "yup": "^1.6.1" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@types/react": "^18.2.38", + "@types/react-dom": "^18.2.15", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.1" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..c435289 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,65 @@ +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import { routesData } from "./routes"; +import { withAuth } from "./routes/withAuth"; +import withPermission from "./routes/withPermission"; +import { CssBaseline } from "@mui/material"; +import FirebaseProvider from "./context/FirebaseProvider"; +import { ToastContainer } from "react-toastify"; +import ThemeProvider from './theme'; +import 'react-toastify/dist/ReactToastify.css'; + +function App() { + const renderRoutes = (routes) => + routes.map((route) => + route.routes ? ( + }> + {route.routes.map((childRoute) => { + let Component = childRoute.component; + + if (route.isProtected) { + Component = withAuth( + childRoute.component, + childRoute.permission, + route.isProtected + ); + } + + if ( + (childRoute.permission && childRoute.withPermission !== false) || + childRoute.withPermission === true + ) { + Component = withPermission(Component); + } + + return ( + } + /> + ); + })} + + ) : ( + } + > + ) + ); + + return ( + + + + + {renderRoutes(routesData)} + + + + + ); +} + +export default App; diff --git a/src/assets/fonts/Gilroy-Bold.ttf b/src/assets/fonts/Gilroy-Bold.ttf new file mode 100644 index 0000000..abd0b09 Binary files /dev/null and b/src/assets/fonts/Gilroy-Bold.ttf differ diff --git a/src/assets/fonts/Gilroy-Heavy.ttf b/src/assets/fonts/Gilroy-Heavy.ttf new file mode 100644 index 0000000..726e371 Binary files /dev/null and b/src/assets/fonts/Gilroy-Heavy.ttf differ diff --git a/src/assets/fonts/Gilroy-Medium.ttf b/src/assets/fonts/Gilroy-Medium.ttf new file mode 100644 index 0000000..06d6a94 Binary files /dev/null and b/src/assets/fonts/Gilroy-Medium.ttf differ diff --git a/src/assets/fonts/Gilroy-SemiBold.ttf b/src/assets/fonts/Gilroy-SemiBold.ttf new file mode 100644 index 0000000..cb3cbb6 Binary files /dev/null and b/src/assets/fonts/Gilroy-SemiBold.ttf differ diff --git a/src/assets/fonts/GraphikMedium.otf b/src/assets/fonts/GraphikMedium.otf new file mode 100644 index 0000000..e03a100 Binary files /dev/null and b/src/assets/fonts/GraphikMedium.otf differ diff --git a/src/assets/fonts/Inter-Black.ttf b/src/assets/fonts/Inter-Black.ttf new file mode 100644 index 0000000..b27822b Binary files /dev/null and b/src/assets/fonts/Inter-Black.ttf differ diff --git a/src/assets/fonts/Inter-Bold.ttf b/src/assets/fonts/Inter-Bold.ttf new file mode 100644 index 0000000..fe23eeb Binary files /dev/null and b/src/assets/fonts/Inter-Bold.ttf differ diff --git a/src/assets/fonts/Inter-ExtraBold.ttf b/src/assets/fonts/Inter-ExtraBold.ttf new file mode 100644 index 0000000..874b1b0 Binary files /dev/null and b/src/assets/fonts/Inter-ExtraBold.ttf differ diff --git a/src/assets/fonts/Inter-ExtraLight.ttf b/src/assets/fonts/Inter-ExtraLight.ttf new file mode 100644 index 0000000..c993e82 Binary files /dev/null and b/src/assets/fonts/Inter-ExtraLight.ttf differ diff --git a/src/assets/fonts/Inter-Light.ttf b/src/assets/fonts/Inter-Light.ttf new file mode 100644 index 0000000..71188f5 Binary files /dev/null and b/src/assets/fonts/Inter-Light.ttf differ diff --git a/src/assets/fonts/Inter-Medium.ttf b/src/assets/fonts/Inter-Medium.ttf new file mode 100644 index 0000000..a01f377 Binary files /dev/null and b/src/assets/fonts/Inter-Medium.ttf differ diff --git a/src/assets/fonts/Inter-Regular.ttf b/src/assets/fonts/Inter-Regular.ttf new file mode 100644 index 0000000..5e4851f Binary files /dev/null and b/src/assets/fonts/Inter-Regular.ttf differ diff --git a/src/assets/fonts/Inter-SemiBold.ttf b/src/assets/fonts/Inter-SemiBold.ttf new file mode 100644 index 0000000..ecc7041 Binary files /dev/null and b/src/assets/fonts/Inter-SemiBold.ttf differ diff --git a/src/assets/fonts/Inter-Thin.ttf b/src/assets/fonts/Inter-Thin.ttf new file mode 100644 index 0000000..fe77243 Binary files /dev/null and b/src/assets/fonts/Inter-Thin.ttf differ diff --git a/src/assets/fonts/NotoSans_Condensed-Medium.ttf b/src/assets/fonts/NotoSans_Condensed-Medium.ttf new file mode 100644 index 0000000..582b88a Binary files /dev/null and b/src/assets/fonts/NotoSans_Condensed-Medium.ttf differ diff --git a/src/assets/images/background/NoJobPost.svg b/src/assets/images/background/NoJobPost.svg new file mode 100644 index 0000000..a995b5c --- /dev/null +++ b/src/assets/images/background/NoJobPost.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/background/background.jpg b/src/assets/images/background/background.jpg new file mode 100644 index 0000000..9a158b4 Binary files /dev/null and b/src/assets/images/background/background.jpg differ diff --git a/src/assets/images/card/thankYou.png b/src/assets/images/card/thankYou.png new file mode 100644 index 0000000..151d2f3 Binary files /dev/null and b/src/assets/images/card/thankYou.png differ diff --git a/src/assets/images/icon/Actions.svg b/src/assets/images/icon/Actions.svg new file mode 100644 index 0000000..eb26227 --- /dev/null +++ b/src/assets/images/icon/Actions.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/Add button Outlined.svg b/src/assets/images/icon/Add button Outlined.svg new file mode 100644 index 0000000..f6ba3ce --- /dev/null +++ b/src/assets/images/icon/Add button Outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/AddToIcon.svg b/src/assets/images/icon/AddToIcon.svg new file mode 100644 index 0000000..71ee1ce --- /dev/null +++ b/src/assets/images/icon/AddToIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/Approve.svg b/src/assets/images/icon/Approve.svg new file mode 100644 index 0000000..cf17a40 --- /dev/null +++ b/src/assets/images/icon/Approve.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/icon/Avatar add button.svg b/src/assets/images/icon/Avatar add button.svg new file mode 100644 index 0000000..c41dfcb --- /dev/null +++ b/src/assets/images/icon/Avatar add button.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/AvatarBackground.svg b/src/assets/images/icon/AvatarBackground.svg new file mode 100644 index 0000000..3a1ec59 --- /dev/null +++ b/src/assets/images/icon/AvatarBackground.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/Bookmark.svg b/src/assets/images/icon/Bookmark.svg new file mode 100644 index 0000000..31d15f9 --- /dev/null +++ b/src/assets/images/icon/Bookmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/CalenderActive.svg b/src/assets/images/icon/CalenderActive.svg new file mode 100644 index 0000000..f1de204 --- /dev/null +++ b/src/assets/images/icon/CalenderActive.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/CalenderDisable.svg b/src/assets/images/icon/CalenderDisable.svg new file mode 100644 index 0000000..30d01f3 --- /dev/null +++ b/src/assets/images/icon/CalenderDisable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/CandidateFiltersIcon.svg b/src/assets/images/icon/CandidateFiltersIcon.svg new file mode 100644 index 0000000..df8cd90 --- /dev/null +++ b/src/assets/images/icon/CandidateFiltersIcon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/icon/CandidateSearchIcon.svg b/src/assets/images/icon/CandidateSearchIcon.svg new file mode 100644 index 0000000..c06f711 --- /dev/null +++ b/src/assets/images/icon/CandidateSearchIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/Chevron-right.svg b/src/assets/images/icon/Chevron-right.svg new file mode 100644 index 0000000..5541016 --- /dev/null +++ b/src/assets/images/icon/Chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/CompanyProfile.svg b/src/assets/images/icon/CompanyProfile.svg new file mode 100644 index 0000000..10b6b96 --- /dev/null +++ b/src/assets/images/icon/CompanyProfile.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/CompanyRegisteredEdit.svg b/src/assets/images/icon/CompanyRegisteredEdit.svg new file mode 100644 index 0000000..f4e3273 --- /dev/null +++ b/src/assets/images/icon/CompanyRegisteredEdit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/Company_Default_Icon.svg b/src/assets/images/icon/Company_Default_Icon.svg new file mode 100644 index 0000000..343d610 --- /dev/null +++ b/src/assets/images/icon/Company_Default_Icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/CrossIcon.svg b/src/assets/images/icon/CrossIcon.svg new file mode 100644 index 0000000..316133a --- /dev/null +++ b/src/assets/images/icon/CrossIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/DeactivatePlan.svg b/src/assets/images/icon/DeactivatePlan.svg new file mode 100644 index 0000000..05cbbb7 --- /dev/null +++ b/src/assets/images/icon/DeactivatePlan.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/DefaultProfilePicture.svg b/src/assets/images/icon/DefaultProfilePicture.svg new file mode 100644 index 0000000..3a6a606 --- /dev/null +++ b/src/assets/images/icon/DefaultProfilePicture.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/icon/DownloadIcon.svg b/src/assets/images/icon/DownloadIcon.svg new file mode 100644 index 0000000..9b5ac0a --- /dev/null +++ b/src/assets/images/icon/DownloadIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/Edit.svg b/src/assets/images/icon/Edit.svg new file mode 100644 index 0000000..3f4e49c --- /dev/null +++ b/src/assets/images/icon/Edit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/EmployeeStock.svg b/src/assets/images/icon/EmployeeStock.svg new file mode 100644 index 0000000..7043a94 --- /dev/null +++ b/src/assets/images/icon/EmployeeStock.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/Expired.svg b/src/assets/images/icon/Expired.svg new file mode 100644 index 0000000..70c77c4 --- /dev/null +++ b/src/assets/images/icon/Expired.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/icon/Folder.svg b/src/assets/images/icon/Folder.svg new file mode 100644 index 0000000..89fcd72 --- /dev/null +++ b/src/assets/images/icon/Folder.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/FolderActive.svg b/src/assets/images/icon/FolderActive.svg new file mode 100644 index 0000000..99315af --- /dev/null +++ b/src/assets/images/icon/FolderActive.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/LockIcon.svg b/src/assets/images/icon/LockIcon.svg new file mode 100644 index 0000000..dd6d249 --- /dev/null +++ b/src/assets/images/icon/LockIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/MoveTo.svg b/src/assets/images/icon/MoveTo.svg new file mode 100644 index 0000000..9c9a2c0 --- /dev/null +++ b/src/assets/images/icon/MoveTo.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/NotBookmark.svg b/src/assets/images/icon/NotBookmark.svg new file mode 100644 index 0000000..5abb608 --- /dev/null +++ b/src/assets/images/icon/NotBookmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/OfflinePayment.svg b/src/assets/images/icon/OfflinePayment.svg new file mode 100644 index 0000000..f4f55f1 --- /dev/null +++ b/src/assets/images/icon/OfflinePayment.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/Path.svg b/src/assets/images/icon/Path.svg new file mode 100644 index 0000000..2ad5ada --- /dev/null +++ b/src/assets/images/icon/Path.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/PostAnInternshipIcon.svg b/src/assets/images/icon/PostAnInternshipIcon.svg new file mode 100644 index 0000000..867ebe9 --- /dev/null +++ b/src/assets/images/icon/PostAnInternshipIcon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/PostFullTimeJobIcon.svg b/src/assets/images/icon/PostFullTimeJobIcon.svg new file mode 100644 index 0000000..11c30ac --- /dev/null +++ b/src/assets/images/icon/PostFullTimeJobIcon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/ReactivatePlan.svg b/src/assets/images/icon/ReactivatePlan.svg new file mode 100644 index 0000000..d2dec9e --- /dev/null +++ b/src/assets/images/icon/ReactivatePlan.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/Receipt.svg b/src/assets/images/icon/Receipt.svg new file mode 100644 index 0000000..9705188 --- /dev/null +++ b/src/assets/images/icon/Receipt.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/icon/ResetPasswordSuccessIcon.png b/src/assets/images/icon/ResetPasswordSuccessIcon.png new file mode 100644 index 0000000..f0193a2 Binary files /dev/null and b/src/assets/images/icon/ResetPasswordSuccessIcon.png differ diff --git a/src/assets/images/icon/Search.svg b/src/assets/images/icon/Search.svg new file mode 100644 index 0000000..f3f11ae --- /dev/null +++ b/src/assets/images/icon/Search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/SearchActive.svg b/src/assets/images/icon/SearchActive.svg new file mode 100644 index 0000000..063b1bc --- /dev/null +++ b/src/assets/images/icon/SearchActive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/SearchCandidateIcon.svg b/src/assets/images/icon/SearchCandidateIcon.svg new file mode 100644 index 0000000..f5a3e12 --- /dev/null +++ b/src/assets/images/icon/SearchCandidateIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/SearchFieldIcon.svg b/src/assets/images/icon/SearchFieldIcon.svg new file mode 100644 index 0000000..85643d5 --- /dev/null +++ b/src/assets/images/icon/SearchFieldIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/Settings_Future.svg b/src/assets/images/icon/Settings_Future.svg new file mode 100644 index 0000000..74d7552 --- /dev/null +++ b/src/assets/images/icon/Settings_Future.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/Settings_FutureActive.svg b/src/assets/images/icon/Settings_FutureActive.svg new file mode 100644 index 0000000..3283d13 --- /dev/null +++ b/src/assets/images/icon/Settings_FutureActive.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/Show.svg b/src/assets/images/icon/Show.svg new file mode 100644 index 0000000..0231fa7 --- /dev/null +++ b/src/assets/images/icon/Show.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/Star.svg b/src/assets/images/icon/Star.svg new file mode 100644 index 0000000..9fffdbe --- /dev/null +++ b/src/assets/images/icon/Star.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/StarActive.svg b/src/assets/images/icon/StarActive.svg new file mode 100644 index 0000000..65e9f73 --- /dev/null +++ b/src/assets/images/icon/StarActive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/SubscriptionExpiredIcon.svg b/src/assets/images/icon/SubscriptionExpiredIcon.svg new file mode 100644 index 0000000..3d2c9e3 --- /dev/null +++ b/src/assets/images/icon/SubscriptionExpiredIcon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/SubscriptionIcon.svg b/src/assets/images/icon/SubscriptionIcon.svg new file mode 100644 index 0000000..43524b7 --- /dev/null +++ b/src/assets/images/icon/SubscriptionIcon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/icon/TickSquare.svg b/src/assets/images/icon/TickSquare.svg new file mode 100644 index 0000000..3491c3e --- /dev/null +++ b/src/assets/images/icon/TickSquare.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/TickSquareRed.svg b/src/assets/images/icon/TickSquareRed.svg new file mode 100644 index 0000000..213ab08 --- /dev/null +++ b/src/assets/images/icon/TickSquareRed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/TransactionHistory.svg b/src/assets/images/icon/TransactionHistory.svg new file mode 100644 index 0000000..db8eec1 --- /dev/null +++ b/src/assets/images/icon/TransactionHistory.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/UnreadBadge.svg b/src/assets/images/icon/UnreadBadge.svg new file mode 100644 index 0000000..5690daa --- /dev/null +++ b/src/assets/images/icon/UnreadBadge.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/Vector.png b/src/assets/images/icon/Vector.png new file mode 100644 index 0000000..931296d Binary files /dev/null and b/src/assets/images/icon/Vector.png differ diff --git a/src/assets/images/icon/addPlanSuccessIcon.svg b/src/assets/images/icon/addPlanSuccessIcon.svg new file mode 100644 index 0000000..65fbf08 --- /dev/null +++ b/src/assets/images/icon/addPlanSuccessIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icon/arrow-down.svg b/src/assets/images/icon/arrow-down.svg new file mode 100644 index 0000000..4735f8f --- /dev/null +++ b/src/assets/images/icon/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/arrow-left.svg b/src/assets/images/icon/arrow-left.svg new file mode 100644 index 0000000..82a11d9 --- /dev/null +++ b/src/assets/images/icon/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/arrow-right.svg b/src/assets/images/icon/arrow-right.svg new file mode 100644 index 0000000..725feba --- /dev/null +++ b/src/assets/images/icon/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/avatar.svg b/src/assets/images/icon/avatar.svg new file mode 100644 index 0000000..7121cda --- /dev/null +++ b/src/assets/images/icon/avatar.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/icon/bell.svg b/src/assets/images/icon/bell.svg new file mode 100644 index 0000000..e70f9b5 --- /dev/null +++ b/src/assets/images/icon/bell.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/briefcase.svg b/src/assets/images/icon/briefcase.svg new file mode 100644 index 0000000..1862151 --- /dev/null +++ b/src/assets/images/icon/briefcase.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/icon/briefcaseActive.svg b/src/assets/images/icon/briefcaseActive.svg new file mode 100644 index 0000000..6505518 --- /dev/null +++ b/src/assets/images/icon/briefcaseActive.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/bsglobal.webp b/src/assets/images/icon/bsglobal.webp new file mode 100644 index 0000000..3a19ae5 Binary files /dev/null and b/src/assets/images/icon/bsglobal.webp differ diff --git a/src/assets/images/icon/calendar.svg b/src/assets/images/icon/calendar.svg new file mode 100644 index 0000000..4b4313b --- /dev/null +++ b/src/assets/images/icon/calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/check.svg b/src/assets/images/icon/check.svg new file mode 100644 index 0000000..7f8c6ff --- /dev/null +++ b/src/assets/images/icon/check.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/clinic-active.svg b/src/assets/images/icon/clinic-active.svg new file mode 100644 index 0000000..596a669 --- /dev/null +++ b/src/assets/images/icon/clinic-active.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/clinic.svg b/src/assets/images/icon/clinic.svg new file mode 100644 index 0000000..a4c6ace --- /dev/null +++ b/src/assets/images/icon/clinic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/clock.svg b/src/assets/images/icon/clock.svg new file mode 100644 index 0000000..f4f11ef --- /dev/null +++ b/src/assets/images/icon/clock.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/compantListInactive.svg b/src/assets/images/icon/compantListInactive.svg new file mode 100644 index 0000000..f256a03 --- /dev/null +++ b/src/assets/images/icon/compantListInactive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/companyListActive.svg b/src/assets/images/icon/companyListActive.svg new file mode 100644 index 0000000..1a1ff2f --- /dev/null +++ b/src/assets/images/icon/companyListActive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/defaultProfileIcon.svg b/src/assets/images/icon/defaultProfileIcon.svg new file mode 100644 index 0000000..526ef1b --- /dev/null +++ b/src/assets/images/icon/defaultProfileIcon.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/icon/delete.svg b/src/assets/images/icon/delete.svg new file mode 100644 index 0000000..842b9d0 --- /dev/null +++ b/src/assets/images/icon/delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/deleteIcon.svg b/src/assets/images/icon/deleteIcon.svg new file mode 100644 index 0000000..27e1c52 --- /dev/null +++ b/src/assets/images/icon/deleteIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/doc.png b/src/assets/images/icon/doc.png new file mode 100644 index 0000000..e7418db Binary files /dev/null and b/src/assets/images/icon/doc.png differ diff --git a/src/assets/images/icon/download.svg b/src/assets/images/icon/download.svg new file mode 100644 index 0000000..d5479c0 --- /dev/null +++ b/src/assets/images/icon/download.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/downloadActivityGray.svg b/src/assets/images/icon/downloadActivityGray.svg new file mode 100644 index 0000000..92dd7e2 --- /dev/null +++ b/src/assets/images/icon/downloadActivityGray.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/icon/downloadPdfGreen.svg b/src/assets/images/icon/downloadPdfGreen.svg new file mode 100644 index 0000000..e74ca30 --- /dev/null +++ b/src/assets/images/icon/downloadPdfGreen.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/downloadreciept.svg b/src/assets/images/icon/downloadreciept.svg new file mode 100644 index 0000000..101a787 --- /dev/null +++ b/src/assets/images/icon/downloadreciept.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/icon/editIcon.svg b/src/assets/images/icon/editIcon.svg new file mode 100644 index 0000000..af9567a --- /dev/null +++ b/src/assets/images/icon/editIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/educationCap.svg b/src/assets/images/icon/educationCap.svg new file mode 100644 index 0000000..5936d3c --- /dev/null +++ b/src/assets/images/icon/educationCap.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/exportFile.svg b/src/assets/images/icon/exportFile.svg new file mode 100644 index 0000000..5df7333 --- /dev/null +++ b/src/assets/images/icon/exportFile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/help.svg b/src/assets/images/icon/help.svg new file mode 100644 index 0000000..ccdc45e --- /dev/null +++ b/src/assets/images/icon/help.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/home(1).svg b/src/assets/images/icon/home(1).svg new file mode 100644 index 0000000..dc84dc8 --- /dev/null +++ b/src/assets/images/icon/home(1).svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/home-active.svg b/src/assets/images/icon/home-active.svg new file mode 100644 index 0000000..854098a --- /dev/null +++ b/src/assets/images/icon/home-active.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/home.svg b/src/assets/images/icon/home.svg new file mode 100644 index 0000000..e2d9b44 --- /dev/null +++ b/src/assets/images/icon/home.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/inactive.svg b/src/assets/images/icon/inactive.svg new file mode 100644 index 0000000..3d287c6 --- /dev/null +++ b/src/assets/images/icon/inactive.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/icon/onHoldDisable.svg b/src/assets/images/icon/onHoldDisable.svg new file mode 100644 index 0000000..acd918b --- /dev/null +++ b/src/assets/images/icon/onHoldDisable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/onHoldEnable.svg b/src/assets/images/icon/onHoldEnable.svg new file mode 100644 index 0000000..bd765ba --- /dev/null +++ b/src/assets/images/icon/onHoldEnable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/pdf.png b/src/assets/images/icon/pdf.png new file mode 100644 index 0000000..4889953 Binary files /dev/null and b/src/assets/images/icon/pdf.png differ diff --git a/src/assets/images/icon/pdfIcon.svg b/src/assets/images/icon/pdfIcon.svg new file mode 100644 index 0000000..6f8b835 --- /dev/null +++ b/src/assets/images/icon/pdfIcon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/planListActive.svg b/src/assets/images/icon/planListActive.svg new file mode 100644 index 0000000..0e5aad3 --- /dev/null +++ b/src/assets/images/icon/planListActive.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/planListInactive.svg b/src/assets/images/icon/planListInactive.svg new file mode 100644 index 0000000..c3077d5 --- /dev/null +++ b/src/assets/images/icon/planListInactive.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/redDeleteIcon.svg b/src/assets/images/icon/redDeleteIcon.svg new file mode 100644 index 0000000..96f3308 --- /dev/null +++ b/src/assets/images/icon/redDeleteIcon.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/assets/images/icon/replace.svg b/src/assets/images/icon/replace.svg new file mode 100644 index 0000000..343ec27 --- /dev/null +++ b/src/assets/images/icon/replace.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/src/assets/images/icon/revokAccess.svg b/src/assets/images/icon/revokAccess.svg new file mode 100644 index 0000000..c7531f7 --- /dev/null +++ b/src/assets/images/icon/revokAccess.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/icon/settings.svg b/src/assets/images/icon/settings.svg new file mode 100644 index 0000000..f0ab8fa --- /dev/null +++ b/src/assets/images/icon/settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/signout.svg b/src/assets/images/icon/signout.svg new file mode 100644 index 0000000..38f5b18 --- /dev/null +++ b/src/assets/images/icon/signout.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/stepDelete.svg b/src/assets/images/icon/stepDelete.svg new file mode 100644 index 0000000..9dbf0df --- /dev/null +++ b/src/assets/images/icon/stepDelete.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon/upload.svg b/src/assets/images/icon/upload.svg new file mode 100644 index 0000000..fda6705 --- /dev/null +++ b/src/assets/images/icon/upload.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/assets/images/icon/user-followed.svg b/src/assets/images/icon/user-followed.svg new file mode 100644 index 0000000..2b6eebf --- /dev/null +++ b/src/assets/images/icon/user-followed.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/icon/user.svg b/src/assets/images/icon/user.svg new file mode 100644 index 0000000..128828e --- /dev/null +++ b/src/assets/images/icon/user.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/user1.svg b/src/assets/images/icon/user1.svg new file mode 100644 index 0000000..c91c833 --- /dev/null +++ b/src/assets/images/icon/user1.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/icon/userAvatar.svg b/src/assets/images/icon/userAvatar.svg new file mode 100644 index 0000000..5aa0dfd --- /dev/null +++ b/src/assets/images/icon/userAvatar.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/icon/viewSimilarJobIcon.svg b/src/assets/images/icon/viewSimilarJobIcon.svg new file mode 100644 index 0000000..4290fbf --- /dev/null +++ b/src/assets/images/icon/viewSimilarJobIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/icon/waiting.svg b/src/assets/images/icon/waiting.svg new file mode 100644 index 0000000..bafefc4 --- /dev/null +++ b/src/assets/images/icon/waiting.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/logo/365-days-medical-centre-group-m 1.svg b/src/assets/images/logo/365-days-medical-centre-group-m 1.svg new file mode 100644 index 0000000..8c5bf2a --- /dev/null +++ b/src/assets/images/logo/365-days-medical-centre-group-m 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/logo/BSmart_Logo_1b 2.svg b/src/assets/images/logo/BSmart_Logo_1b 2.svg new file mode 100644 index 0000000..8c5bf2a --- /dev/null +++ b/src/assets/images/logo/BSmart_Logo_1b 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/logo/app.svg b/src/assets/images/logo/app.svg new file mode 100644 index 0000000..8c5bf2a --- /dev/null +++ b/src/assets/images/logo/app.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/logo/credit_card.png b/src/assets/images/logo/credit_card.png new file mode 100644 index 0000000..772c65e Binary files /dev/null and b/src/assets/images/logo/credit_card.png differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/common/action.js b/src/common/action.js new file mode 100644 index 0000000..53175bd --- /dev/null +++ b/src/common/action.js @@ -0,0 +1,6 @@ +import { RESET_REDUX } from './actionTypes'; + +export const resetReduxState = (reducerKey, key, value) => ({ + type: RESET_REDUX, + payload: { reducerKey, key, value }, +}); diff --git a/src/common/actionTypes.js b/src/common/actionTypes.js new file mode 100644 index 0000000..dede8a4 --- /dev/null +++ b/src/common/actionTypes.js @@ -0,0 +1,2 @@ +export const RESET_REDUX = 'RESET_REDUX'; +export const EMPTY_STORE_STATE = 'EMPTY_STORE_STATE'; diff --git a/src/common/envVariables.js b/src/common/envVariables.js new file mode 100644 index 0000000..8427590 --- /dev/null +++ b/src/common/envVariables.js @@ -0,0 +1,21 @@ +export const API_BASE_URL = + import.meta.env.VITE_API_BASE_URL ?? '/api/v1/recruitments'; +export const IMAGE_LOCATION_BASE_URL = import.meta.env + .VITE_IMAGE_LOCATION_BASE_URL; +export const BILLDESK_URL = import.meta.env.VITE_BILLDESK_URL; +export const BILLDESK_MERCHANT_ID = import.meta.env.VITE_BILLDESK_MERCHANT_ID; +export const API_BILLDESK_BASE_URL = import.meta.env.VITE_API_BILLDESK_BASE_URL; +// export const HIDE_FUNCTIONALITY = +// import.meta.env.VITE_HIDE_FUNCTIONALITY === 'true' || false; +export const HIDE_FUNCTIONALITY = + import.meta.env.VITE_HIDE_FUNCTIONALITY === 'show' || false; +export const RECRUITMENT_URL = import.meta.env.VITE_ADMIN_URL; + +export const FB_API_KEY = import.meta.env.VITE_FB_API_KEY; +export const FB_AUTH_DOMAIN = import.meta.env.VITE_FB_AUTH_DOMAIN; +export const FB_PROJECT_ID = import.meta.env.VITE_FB_PROJECT_ID; +export const FB_STORAGE_BUCKET = import.meta.env.VITE_FB_STORAGE_BUCKET; +export const FB_MESSAGING_SENDER_ID = import.meta.env + .VITE_FB_MESSAGING_SENDER_ID; +export const FB_APP_ID = import.meta.env.VITE_FB_APP_ID; +export const FB_VAPID_KEY = import.meta.env.VITE_FB_VAPID_KEY; diff --git a/src/components/Checkbox.jsx b/src/components/Checkbox.jsx new file mode 100644 index 0000000..8d0869b --- /dev/null +++ b/src/components/Checkbox.jsx @@ -0,0 +1,65 @@ +import Checkbox from '@mui/material/Checkbox'; +import { styled } from '@mui/material/styles'; + +function BpCheckbox(props) { + const BpIcon = styled('span')(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + width: props?.size ?? '13px', + height: props?.size ?? '13px', + boxShadow: + 'inset 0 0 0 1px rgba(16,22,26,.2), inset 0 -1px 0 rgba(16,22,26,.1)', + backgroundColor: theme.palette.grey[11], + backgroundImage: + 'linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0))', + '.Mui-focusVisible &': { + outline: 'none', + }, + 'input:hover ~ &': { + backgroundColor: theme.palette.grey[12], + }, + 'input:disabled ~ &': { + boxShadow: 'none', + background: theme.palette.grey[13], + }, + })); + + const BpCheckedIcon = styled(BpIcon)(({ theme }) => ({ + backgroundColor: theme.palette.primary.main, + backgroundImage: + 'linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0))', + '&:before': { + display: 'block', + width: props?.size ?? '13px', + height: props?.size ?? '13px', + backgroundImage: + "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath" + + " fill-rule='evenodd' clip-rule='evenodd' d='M12 5c-.28 0-.53.11-.71.29L7 9.59l-2.29-2.3a1.003 " + + "1.003 0 00-1.42 1.42l3 3c.18.18.43.29.71.29s.53-.11.71-.29l5-5A1.003 1.003 0 0012 5z' fill='%23fff'/%3E%3C/svg%3E\")", + content: '""', + }, + 'input:hover ~ &': { + backgroundColor: theme.palette.primary.main, + }, + })); + return ( + } + icon={} + inputProps={{ 'aria-label': 'Checkbox demo' }} + {...props} + /> + ); +} + +export default function CustomizedCheckbox(props) { + return ( +
+ +
+ ); +} diff --git a/src/components/CustomBreadcrumbs.jsx b/src/components/CustomBreadcrumbs.jsx new file mode 100644 index 0000000..d50dcf2 --- /dev/null +++ b/src/components/CustomBreadcrumbs.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { useStyles } from './styles/customBreadcrumbsStyles'; +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; +import { Breadcrumbs } from '@mui/material'; +import { Link } from 'react-router-dom'; + +function CustomBreadcrumbs({ breadcrumbs }) { + const classes = useStyles(); + const crums = [...breadcrumbs]; + return ( + } + > + {crums.map((breadcrumb, index) => ( + + {breadcrumb.label} + + ))} + + ); +} + +export default CustomBreadcrumbs; diff --git a/src/components/CustomFileUpload.jsx b/src/components/CustomFileUpload.jsx new file mode 100644 index 0000000..6283ed6 --- /dev/null +++ b/src/components/CustomFileUpload.jsx @@ -0,0 +1,489 @@ +import React, { + forwardRef, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react'; +import { useStyles } from './styles/customFileUploadStyles'; +import { + Box, + Button, + CircularProgress, + Grid, + InputLabel, + Modal, + Typography, +} from '@mui/material'; +import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'; +import Dropzone from 'react-dropzone'; +import SaveAltIcon from '@mui/icons-material/SaveAlt'; +import RemoveRedEyeOutlinedIcon from '@mui/icons-material/RemoveRedEyeOutlined'; +import RemoveOutlinedIcon from '@mui/icons-material/RemoveOutlined'; +import AddOutlinedIcon from '@mui/icons-material/AddOutlined'; +import ZoomOutOutlinedIcon from '@mui/icons-material/ZoomOutOutlined'; +import CloseIcon from '@mui/icons-material/Close'; +import downloadIcon from '../assets/images/icon/download.svg'; +import { pushNotification } from '../utils/notification'; +import { FILE_TYPE, NOTIFICATION } from '../constants'; +import uploadIcon from '../assets/images/icon/upload.svg'; +import PdfIcon from '../assets/images/icon/pdf.png'; +import DocIcon from '../assets/images/icon/doc.png'; +import { fileUpload } from '../services/file.upload.services'; +import { IMAGE_LOCATION_BASE_URL } from '../common/envVariables'; + +const CustomFileUpload = forwardRef(function CustomFileUpload( + { + documentName, + label, + saveFileName, + onUploadDone, + maxFileSizeInMb, + maxFiles, + uploadedFileUrl, + errorMessage, + isDisabled = false, + }, + ref +) { + const classes = useStyles(); + const imageRef = useRef(null); + const fileInputRef = useRef(null); + const [image, setImage] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [openPreview, setOpenPreview] = useState(false); + const [imageName, setImageName] = useState( + uploadedFileUrl ? uploadedFileUrl.split('/').pop() : '' + ); + const [fileExtension, setFileExtension] = useState( + uploadedFileUrl ? uploadedFileUrl.split('.').pop() : '' + ); + const [oldUploadedFileUrl, setOldUploadedFileUrl] = useState( + uploadedFileUrl ? uploadedFileUrl : '' + ); + + useImperativeHandle(ref, () => ({ + focus: () => { + if (fileInputRef.current) { + fileInputRef.current.focus(); + } + }, + scrollIntoView: (...args) => { + if (fileInputRef.current) { + fileInputRef.current.scrollIntoView(...args); + } + }, + })); + + useEffect(() => { + const makeFullUrlIfNeeded = (url) => { + const isHttp = url.startsWith('http://') || url.startsWith('https://'); + if (!isHttp) { + setOldUploadedFileUrl(url ? `${IMAGE_LOCATION_BASE_URL}${url}` : ''); + setFileExtension( + uploadedFileUrl ? uploadedFileUrl.split('.').pop() : '' + ); + setImageName(uploadedFileUrl ? uploadedFileUrl.split('/').pop() : ''); + return; + } + const urlObject = new URL(url); + const fileName = urlObject.pathname.split('/').pop(); + setImageName(fileName); + setFileExtension(fileName.split('.').pop()); + setOldUploadedFileUrl(url); + return url; + }; + makeFullUrlIfNeeded(uploadedFileUrl); + }, [uploadedFileUrl]); + + const handleClearImage = () => { + setImage(null); + setImageName(''); + setFileExtension(''); + setOldUploadedFileUrl(''); + onUploadDone(documentName, ''); + }; + + const handleFileUpload = async (value) => { + if (!value) { + return; + } + let formData = new FormData(); + formData.append('file', value); + formData.append('fileName', value?.name); + try { + setIsLoading(true); + const data = await fileUpload(formData); + onUploadDone(documentName, data?.data?.data?.Key); + return; + } catch (error) { + // console.error(error); + pushNotification('Error while uploading file', NOTIFICATION.ERROR); + } finally { + setIsLoading(false); + } + }; + + const handleDrop = async (acceptedFiles) => { + const file = acceptedFiles[0]; + await handleFileUpload(file); + setImage(file); + setImageName(file.name); + setOldUploadedFileUrl(''); + setFileExtension(''); + }; + + const handelRejection = (rejectedFiles) => { + rejectedFiles.forEach((rejection) => { + pushNotification( + `${rejection?.file?.name} : ${rejection?.errors?.[0]?.message}`, + NOTIFICATION.ERROR + ); + }); + }; + + const handlePreview = () => { + setOpenPreview(true); + }; + + const handleClose = () => { + setOpenPreview(false); + }; + + const handleDownload = () => { + if (image) { + const imageUrl = URL.createObjectURL(image); + const anchor = document.createElement('a'); + anchor.href = imageUrl; + anchor.download = saveFileName || imageName || 'Image'; + anchor.click(); + } + if (oldUploadedFileUrl) { + const imageUrl = oldUploadedFileUrl; + const anchor = document.createElement('a'); + anchor.href = imageUrl; + anchor.download = saveFileName || imageName || 'Image'; + anchor.click(); + } + }; + + const Controls = ({ zoomIn, zoomOut, resetTransform }) => ( + <> + + + + + + + ); + const FileUploadComponent = ({ + isLoading, + fileUrl, + fileExtension, + maxFileSizeInMb, + handlePreview, + handleDownload, + classes, + }) => ( + <> + + + {isLoading ? ( + + ) : ( + <> + {fileUrl ? ( + <> + {fileExtension === 'pdf' ? ( + Uploaded file + ) : fileExtension === 'doc' || + fileExtension === 'docx' || + fileExtension === + 'vnd.openxmlformats-officedocument.wordprocessingml.document' || + fileExtension === 'msword' ? ( + Uploaded file + ) : ( + Uploaded file + )} + + + + + + ) : ( + <> + + + Drag and drop file here, or click add + + + Allowed file types are jpeg, png, pdf, svg, doc to a maximum + size of {maxFileSizeInMb} MB + + + )} + + )} + + + {errorMessage && ( + {errorMessage} + )} + + ); + + const PreviewComponent = ({ + fileUrl, + fileExtension, + handleDownload, + classes, + }) => ( + <> + {fileExtension === 'pdf' ? ( + <> + + Uploaded file + + + + ) : fileExtension === 'doc' || + fileExtension === 'docx' || + fileExtension === + 'vnd.openxmlformats-officedocument.wordprocessingml.document' || + fileExtension === 'msword' ? ( + <> + + Uploaded file + + + + ) : ( + + {(utils) => ( + + + + Uploaded file + + + + + )} + + )} + + ); + + return ( + <> + + + {label} + {(image || oldUploadedFileUrl) && ( +
+ {!isDisabled && ( + + )} +
+ )} +
+ + + + + +
+ + + + + + + + + + + + + {imageName} + + + + + {!oldUploadedFileUrl ? ( + <> + + + ) : ( + <> + + + )} + + + + + ); +}); + +export default CustomFileUpload; diff --git a/src/components/CustomStepper.jsx b/src/components/CustomStepper.jsx new file mode 100644 index 0000000..7037a02 --- /dev/null +++ b/src/components/CustomStepper.jsx @@ -0,0 +1,102 @@ +import { Check } from '@mui/icons-material'; +import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'; +import { Box, Stack, Step, StepLabel, Stepper } from '@mui/material'; +import StepConnector, { + stepConnectorClasses, +} from '@mui/material/StepConnector'; +import { styled } from '@mui/styles'; +import React from 'react'; +import { useStyles } from './styles/customStepperStyles'; + +const CustomStepper = ({ activeStep }) => { + const classes = useStyles(); + const steps = ['Your details', 'Clinic details']; + + const ColorlibConnector = styled(StepConnector)(({ theme }) => ({ + [`&.${stepConnectorClasses.active}`]: { + [`& .${stepConnectorClasses.line}`]: { + backgroundColor: theme.palette.primary.main, + }, + }, + [`&.${stepConnectorClasses.completed}`]: { + [`& .${stepConnectorClasses.line}`]: { + backgroundColor: theme.palette.primary.main, + }, + }, + [`& .${stepConnectorClasses.line}`]: { + height: 2, + zIndex: 1, + marginTop: theme.spacing(-1.2), + width: 'calc(inherit +100%)', + marginLeft: theme.spacing(-2.2), + marginRight: theme.spacing(-4.5), + backgroundColor: theme.palette.grey[29], + }, + })); + + // Create a styled component that doesn't pass ownerState to DOM + const ColorlibStepIconRoot = styled('div')(({ theme, completed, active }) => ({ + backgroundColor: theme.palette.grey[29], + color: theme.palette.common.white, + width: 24, + height: 24, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + ...(active && { + backgroundColor: theme.palette.primary.main, + }), + ...(completed && { + backgroundColor: theme.palette.primary.main, + }), + })); + + function ColorlibStepIcon(props) { + const { active, completed } = props; + + const icons = { + 1: completed ? ( + + ) : ( + + ), + 2: completed ? ( + + ) : ( + + ), + }; + + return ( + + {icons[String(props.icon)]} + + ); + } + return ( + + + } + > + {steps.map((label) => ( + + + {label} + + + ))} + + + + ); +}; + +export default CustomStepper; diff --git a/src/components/FormFooterMessage.jsx b/src/components/FormFooterMessage.jsx new file mode 100644 index 0000000..378baa5 --- /dev/null +++ b/src/components/FormFooterMessage.jsx @@ -0,0 +1,52 @@ +import { Alert } from '@mui/material'; +import React from 'react'; +import { useStyles } from './styles/formFooterMessageStyles'; +import { LoadingButton } from '@mui/lab'; +import CheckBoxOutlinedIcon from '@mui/icons-material/CheckBoxOutlined'; + +const FormFooterMessage = ({ + mandatoryFieldText, + nextButtonTitle, + nextButtonOnClick, + isSubmitting, +}) => { + const classes = useStyles(); + return ( +
+
+ + {mandatoryFieldText} + +
+ {nextButtonTitle && ( +
+ + + {nextButtonTitle} + +
+ )} +
+ ); +}; + +export default FormFooterMessage; diff --git a/src/components/FormPageHeader.jsx b/src/components/FormPageHeader.jsx new file mode 100644 index 0000000..3224c09 --- /dev/null +++ b/src/components/FormPageHeader.jsx @@ -0,0 +1,62 @@ +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import { Box, Grid, InputLabel, Typography } from '@mui/material'; +import { useNavigate } from 'react-router'; +import { pxToRem } from '../theme/typography'; +import { useStyles } from './styles/formPageHeaderStyles'; + +function FormPageHeader({ + showGoBack, + pageTitle, + isDetailPage, + handleGoBack, + wrapHeader = false, + titleAlignItems, + showSubHeading = true, +}) { + const classes = useStyles(); + const navigate = useNavigate(); + return ( + + + + {showGoBack && ( + { + handleGoBack ? handleGoBack() : navigate(-1); + }} + /> + )} +
+ + {pageTitle} + +
+
+
+ {showSubHeading && ( + + + To sign up to 24x7 AI Healthcare Receptionist, please fill the + details below. + + + )} +
+ ); +} + +export default FormPageHeader; diff --git a/src/components/FormSectionHeading.jsx b/src/components/FormSectionHeading.jsx new file mode 100644 index 0000000..364c348 --- /dev/null +++ b/src/components/FormSectionHeading.jsx @@ -0,0 +1,29 @@ +import { Grid, Typography } from '@mui/material'; +import React from 'react'; +import { useStyles } from './styles/formSectionHeadingStyles'; + +const FormSectionHeading = ({ + title, + className = '', + bracketText = '', + bracketTextClassName = '', +}) => { + const classes = useStyles(); + return ( + <> + + + {title} + {bracketText && ( + + {' '} + ({bracketText}) + + )} + + + + ); +}; + +export default FormSectionHeading; diff --git a/src/components/Loader.jsx b/src/components/Loader.jsx new file mode 100644 index 0000000..6210a86 --- /dev/null +++ b/src/components/Loader.jsx @@ -0,0 +1,16 @@ +import { Backdrop, Box, CircularProgress } from '@mui/material'; +import React from 'react'; +import { useStyles } from './styles/loaderStyles'; + +function Loader() { + const classes = useStyles(); + return ( + + + + + + ); +} + +export default Loader; diff --git a/src/components/MultiSelect.jsx b/src/components/MultiSelect.jsx new file mode 100644 index 0000000..c55256f --- /dev/null +++ b/src/components/MultiSelect.jsx @@ -0,0 +1,258 @@ +import { Clear } from '@mui/icons-material'; +import ExpandMoreOutlinedIcon from '@mui/icons-material/ExpandMoreOutlined'; +import { + Autocomplete, + Box, + Checkbox, + Chip, + CircularProgress, + ListItemText, + Stack, + TextField +} from '@mui/material'; +import debounce from 'lodash/debounce'; +import React, { useEffect, useMemo, useState } from 'react'; +import { NOTIFICATION } from '../constants'; +import { pushNotification } from '../utils/notification'; +import { useStyles } from './styles/multiSelectStyles'; + +const MultiSelect = ({ + apiCall, + searchParams = {}, + searchDebounceTime = 500, + maxSelections = Infinity, + formik, + fieldValue, + fieldObjectValue, + getOptionLabel, + customErrorMessage = null, + hideEndAdornment = false, + onError = (error) => console.error(error), + onOptionsChange = () => {}, + fieldName, + styleForSelectorParent, + styleForSelector, + placeholderText = '', + inputRef, +}) => { + const classes = useStyles(); + const [data, setData] = useState([]); + const [page, setPage] = useState(0); + const [searchQuery, setSearchQuery] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [hasMore, setHasMore] = useState(true); + const [dropdownOpen, setDropdownOpen] = useState(false); + + useEffect(() => { + const fetchData = async () => { + try { + setIsLoading(true); + const response = await apiCall({ + ...searchParams, + page, + size: 30, + search: searchQuery, + }); + const newData = response?.data || []; + if (page === 0) { + setData(newData); + } else { + setData((prevData) => [...prevData, ...newData]); + } + setHasMore(newData.length > 0); + onOptionsChange(newData); + } catch (error) { + onError(error); + } finally { + setIsLoading(false); + } + }; + fetchData(); + }, [searchQuery, page]); + + const debouncedSearch = useMemo( + () => + debounce((value) => { + setSearchQuery(value); + setPage(0); + }, searchDebounceTime), + [] + ); + + const handleToggleDropdown = (dropdownStatus) => { + setDropdownOpen(() => dropdownStatus); + }; + + const handleOptionScroll = (event) => { + const threshold = 100; + const bottom = + event.target.scrollHeight - + event.target.scrollTop - + event.target.clientHeight <= + threshold; + if (bottom && hasMore && !isLoading) { + setPage((prevPage) => prevPage + 1); + } + }; + + const handleChange = (selectedOptions) => { + if (selectedOptions.length > maxSelections) { + if (customErrorMessage) { + pushNotification(customErrorMessage, NOTIFICATION.ERROR); + } + return; + } + + let uniqueSelectedOptions = []; + const optionMap = {}; + + selectedOptions.forEach((option) => { + if (optionMap[option.id]) { + uniqueSelectedOptions = uniqueSelectedOptions.filter( + (s) => s.id !== option.id + ); + delete optionMap[option.id]; + } else { + uniqueSelectedOptions.push(option); + optionMap[option.id] = true; + } + }); + + formik.setFieldValue(`${fieldValue}`, uniqueSelectedOptions); + const newSelectedOptionsMap = {}; + uniqueSelectedOptions.forEach((option) => { + newSelectedOptionsMap[option.id] = getOptionLabel(option); + }); + formik.setFieldValue(`${fieldObjectValue}`, newSelectedOptionsMap); + }; + + const handleOptionDelete = (optionId) => { + const newOptions = formik.values[fieldValue].filter( + (option) => option.id !== optionId + ); + formik.setFieldValue(`${fieldValue}`, newOptions); + const newSelectedOptionsMap = { ...formik.values[fieldObjectValue] }; + delete newSelectedOptionsMap[optionId]; + formik.setFieldValue(`${fieldObjectValue}`, newSelectedOptionsMap); + }; + + return ( + + + { + handleToggleDropdown(!dropdownOpen); + }} + onClose={() => { + handleToggleDropdown(false); + }} + value={formik?.values[fieldValue]} + open={dropdownOpen} + onInputChange={(event, value) => { + debouncedSearch(value); + setDropdownOpen(true); + }} + onChange={(event, newValue) => handleChange(newValue)} + renderOption={(props, option) => ( +
  • + selectedOption.id === option.id + )} + /> + +
  • + )} + renderInput={(params) => ( + { + event.preventDefault(); + debouncedSearch(event.target.value.trim()); + }} + onKeyDown={(event) => { + if (event.key === 'Backspace') { + event.stopPropagation(); + } + }} + InputProps={{ + ...params.InputProps, + type: 'search', + endAdornment: ( + <> + {isLoading ? ( + + ) : ( + setDropdownOpen(!dropdownOpen)} + className={`${classes.dropDownArrow} ${ + classes.hoverPointer + } ${ + dropdownOpen + ? classes.rotate180Deg + : classes.rotate0Deg + }`} + /> // Toggle dropdown on click + )} + {params.InputProps.endAdornment} + + ), + }} + inputRef={inputRef} + error={Boolean( + formik?.errors[fieldValue] && formik?.touched[fieldValue] + )} + helperText={ + formik?.errors[fieldValue] && formik?.touched[fieldValue] + ? formik?.errors[fieldValue] + : null + } + placeholder={placeholderText} + /> + )} + renderTags={() => ( +
    {/* {`${formik.values[fieldValue].length} selected`} */}
    + )} + ListboxProps={{ + onScroll: handleOptionScroll, + }} + /> + {/* {fieldName && ( + + {formik.values[fieldValue]?.length}{' '} + {formik.values[fieldValue]?.length > 1 + ? fieldName && `${fieldName + 's'}` + : fieldName}{' '} + Selected + + )} */} +
    + + + {formik?.values[fieldValue]?.map((option) => ( + handleOptionDelete(option.id)} + deleteIcon={} + /> + ))} + + +
    + ); +}; + +export default MultiSelect; diff --git a/src/components/PageHeader.jsx b/src/components/PageHeader.jsx new file mode 100644 index 0000000..2cf7de8 --- /dev/null +++ b/src/components/PageHeader.jsx @@ -0,0 +1,147 @@ +import { ArrowBack, Search } from '@mui/icons-material'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Grid, + InputAdornment, + TextField, + Typography, +} from '@mui/material'; +import { debounce } from 'lodash'; +import React, { useMemo } from 'react'; +import { useNavigate } from 'react-router'; +import { pxToRem } from '../theme/typography'; +import { useStyles } from './styles/pageHeaderStyles'; + +function PageHeader({ + showGoBack, + pageTitle, + hideAddButton = false, + addButtonTitle, + addButtonIcon, + buttonVariant = 'contained', + onAddButtonClick, + addButtonDisabled, + extraComponent, + showSearch, + onSearch, + searchPlaceholder, + isDetailPage, + addButtonLoading, + handleGoBack, + secondaryButton, + customChips, + titleAlignItems, +}) { + const classes = useStyles(); + const navigate = useNavigate(); + const debouncedSearch = + onSearch && useMemo(() => debounce(onSearch, 500), []); + + // Function to render button with icon if provided + const renderButton = (disabled) => { + if (addButtonIcon) { + return ( + + {addButtonTitle ?? 'Add'} + + ); + } else { + return ( + + {addButtonTitle ?? 'Add'} + + ); + } + }; + + return ( + + + + {showGoBack && ( + { + handleGoBack ? handleGoBack() : navigate(-1); + }} + /> + )} +
    + + {pageTitle} + + {customChips} +
    +
    + + {showSearch && ( + + + + ), + }} + /> + )} + {extraComponent} + {secondaryButton && ( + {secondaryButton} + )} + {!hideAddButton && renderButton(addButtonDisabled)} + +
    +
    + ); +} + +export default PageHeader; diff --git a/src/components/ProtectedComponent.jsx b/src/components/ProtectedComponent.jsx new file mode 100644 index 0000000..935c3d7 --- /dev/null +++ b/src/components/ProtectedComponent.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +// import { checkComponentPermission } from '../utils/share'; + +function ProtectedComponent({ permission, children }) { + // if (permission) { + // const hasPermission = checkComponentPermission(permission); + // return hasPermission ? <>{children} : null; + // } + return <>{children}; +} + +export default ProtectedComponent; diff --git a/src/components/Table.jsx b/src/components/Table.jsx new file mode 100644 index 0000000..491c593 --- /dev/null +++ b/src/components/Table.jsx @@ -0,0 +1,623 @@ +/* eslint-disable react/display-name */ +import { + Box, + Button, + InputAdornment, + MenuItem, + Select, + TextField, + Tooltip, +} from '@mui/material'; +import {MaterialReactTable} from 'material-react-table'; +import PreviousIcon from '@mui/icons-material/ArrowBack'; +import NextIcon from '@mui/icons-material/ArrowForward'; +import PropTypes from 'prop-types'; +import React, { + forwardRef, + memo, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState, +} from 'react'; +import SearchIcon from '@mui/icons-material/Search'; +import Typography from '@mui/material/Typography'; +import Pagination from '@mui/material/Pagination'; +import PaginationItem from '@mui/material/PaginationItem'; +import { debounce } from 'lodash'; +import { ExpandMore } from '@mui/icons-material'; +import { useNavigate } from 'react-router-dom'; + +import { useStyles } from './styles/tableStyles'; +import ProtectedComponent from '../components/ProtectedComponent'; + +const Table = memo( + forwardRef((props, ref) => { + const classes = useStyles(); + const { + getData, + columns, + options, + actions, + hideTopToolbar = false, + showAction, + handleBulkAction, + handleRowClick = () => {}, + showDownloadAction, + renderTopToolbar, + enableRowNumbers, + rowNumberMode, + hideShowPerPage = false, + getRowStyle = () => {}, + searchText, + navigateTo, + } = props; + const [data, setData] = useState([]); + const [formattedColumns, setFormattedColumns] = useState( + columns.map((column, i) => { + if (i === 0) { + return { + ...column, + muiTableBodyCellProps: { + sx: (theme) => ({ + opacity: 1, + fontWeight: 600, + fontSize: '16px', + fontFamily: theme.fontFamily.semiBold, + color: theme.palette.grey[10], + }), + }, + }; + } + return column; + }) + ); + const [isError, setIsError] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [isRefetching, setIsRefetching] = useState(false); + const [rowCount, setRowCount] = useState(0); + const [columnFilters, setColumnFilters] = useState([]); + const [globalFilter, setGlobalFilter] = useState(''); + const [sorting, setSorting] = useState([]); + const tableRef = useRef(); + const [pagination, setPagination] = useState({ + pageIndex: options?.pageIndex ?? 0, + pageSize: options?.pageSize ?? 10, + }); + const navigate = useNavigate(); + + useImperativeHandle(ref, () => ({ + reFetchData() { + setIsLoading(true); + setIsRefetching(true); + fetchData(); + // tableRef.current.resetRowSelection(); + }, + getSelectedRowData() { + const selectedRows = tableRef.current.getSelectedRowModel().flatRows; + const selectedData = selectedRows.map((row) => row.original); + return selectedData; + }, + resetPage(checkPage) { + setPagination((prev) => ({ + ...prev, + pageIndex: !checkPage + ? 0 + : data.length === 1 && prev.pageIndex > 0 + ? prev.pageIndex - 1 + : prev.pageIndex, + })); + }, + })); + useEffect(() => { + setFormattedColumns( + columns.map((column, i) => { + if (column.isBold) { + return { + ...column, + muiTableBodyCellProps: { + sx: (theme) => ({ + opacity: 1, + fontWeight: 600, + fontSize: '16px', + fontFamily: theme.fontFamily.semiBold, + color: theme.palette.grey[10], + }), + }, + }; + } + return column; + }) + ); + }, [columns]); + + const fetchData = async () => { + if (!data.length) { + setIsLoading(true); + } else { + setIsRefetching(true); + } + let filterString = ''; + columnFilters.forEach((filter) => { + filterString += `${filter.id}=${filter.value}&`; + }); + let sortingString = sorting + .map( + (sort) => `orderBy=${sort.id}&order=${sort.desc ? 'DESC' : 'ASC'}&` + ) + .join(''); + try { + const response = await getData({ + pagination, + filterString, + globalFilter, + sortingString, + }); + setData(response.data); + setRowCount(response.rowCount); + } catch (error) { + setIsError(true); + return; + } finally { + setIsLoading(false); + setIsRefetching(false); + } + setIsError(false); + }; + + useEffect(() => { + if (columnFilters && pagination && sorting) { + fetchData(); + } + }, [ + columnFilters, + globalFilter, + pagination.pageIndex, + pagination.pageSize, + sorting, + ]); + + const debouncedSearch = useMemo( + () => + debounce((value) => { + setGlobalFilter(value); + + /* --- set page index to 0 --- */ + setPagination((prevPagination) => ({ + ...prevPagination, + pageIndex: 0, + })); + }, 500), + [] + ); + + // Function to handle page size change + const handlePageSizeChange = (event) => { + const newPageSize = event.target.value; + + // Calculate the new page index to maintain the current set of records visible + const currentPage = pagination.pageIndex; + const currentRecordIndex = currentPage * pagination.pageSize; + const newPageIndex = Math.floor(currentRecordIndex / newPageSize); + + // Update pagination state with new page size and index + setPagination((prevPagination) => ({ + ...prevPagination, + pageSize: newPageSize, + pageIndex: newPageIndex, + })); + }; + + return ( + <> +
    + debouncedSearch(event.target.value.trim())} + InputProps={{ + startAdornment: ( + + {/* search icon */} + + + ), + }} + /> + {!hideShowPerPage && ( + + Show: + + + )} +
    + ( + + )} + muiTableBodyCellProps={{ + // className: classes?.muiTableBodyCell, + sx: (theme) => ({ + opacity: '1', + // lineHeight: '2.5rem', + margin: theme.spacing(1), + fontWeight: 500, + fontSize: '16px', + fontStretch: 'normal', + fontStyle: 'normal', + letterSpacing: 'normal', + fontFamily: theme.fontFamily.medium, + color: theme.palette.grey[22], + // textAlign: 'center', + textAlign: 'left', + }), + }} + muiTopToolbarProps={{ + sx: (theme) => ({ + borderRadius: theme.shape.borderRadius, + '& div': { + '& div': { + '& .MuiIconButton-sizeMedium': { + display: 'none', + }, + }, + }, + backgroundColor: theme.palette.common.white, + }), + }} + renderTopToolbar={false} + muiTableHeadCellProps={{ + className: classes?.muiTableHeadCell, + }} + enableRowNumbers={enableRowNumbers} + rowNumberMode={rowNumberMode} + muiTablePaperProps={{ + elevation: 8, + sx: (theme) => ({ + borderRadius: theme.shape.borderRadius, + backgroundColor: theme.palette.common.white, + boxShadow: 'none', + '& .MuiTableContainer-root': { + border: '0px solid', + borderRadius: '16px', + }, + }), + }} + muiTableHeadCellFilterTextFieldProps={{ + variant: 'outlined', + }} + muiTableBodyProps={{ + sx: (theme) => ({ + borderRadius: theme.shape.borderRadius, + }), + }} + columns={formattedColumns ?? []} + data={data} + enableTopToolbar={ + !hideTopToolbar && + (options?.showTopBar || + tableRef?.current?.getIsSomeRowsSelected() || + tableRef?.current?.getIsAllRowsSelected()) + } + manualPagination={true} + manualFiltering + manualSorting + enableFilterMatchHighlighting={false} + enableColumnActions={false} + enableRowSelection={options?.enableRowSelection ?? true} + muiToolbarAlertBannerProps={ + isError + ? { + color: 'error', + children: 'Error loading data', + } + : undefined + } + positionToolbarAlertBanner={'none'} + onColumnFiltersChange={setColumnFilters} + onGlobalFilterChange={setGlobalFilter} + onPaginationChange={setPagination} + onSortingChange={setSorting} + rowCount={rowCount} + enableFullScreenToggle={false} + enableDensityToggle={false} + enableHiding={false} + enableSorting={options?.enableSorting ?? true} + enableColumnFilters={options?.showFilters ?? false} + enableGlobalFilter={options?.showSearch ?? false} + enableRowActions={showAction ?? false} + positionActionsColumn="last" + muiTableHeadRowProps={{ + className: classes?.tableHeadRow, + }} + displayColumnDefOptions={{ + 'mrt-row-actions': { + header: '', + }, + }} + muiTableBodyRowProps={({ row }) => ({ + onClick: (event) => { + if ( + Object.values(event.target)?.[1] + ?.className?.split(' ') + .includes('MuiBackdrop-root') + ) { + return; + } + handleRowClick(row?.original); + if (navigateTo) { + navigate(`/${navigateTo}/${row?.original?.id}`); + } + }, + className: classes?.tableBodyRow, + sx: getRowStyle(row), + })} + muiSelectCheckboxProps={{ + className: classes?.tableCheckbox, + }} + muiSelectAllCheckboxProps={{ + className: classes?.tableCheckbox, + }} + renderRowActionMenuItems={({ row, closeMenu }) => + actions?.map((action, index) => + !(action?.renderAction?.(row) ?? true) ? null : ( + + {(action?.icon || + action?.text || + (action?.textFn && action?.textFn(row))) && ( + { + event.stopPropagation(); + action.onClick(row); + closeMenu(); + }} + disabled={ + action?.isDisabledValue + ? action?.isDisabledValue === + row?.original?.[action?.rowKey] + : false + } + > + {action?.icon} {action?.text}{' '} + {action.textFn && action.textFn(row)} + + )} + + ) + ) ?? [] + } + renderTopToolbarCustomActions={({ table }) => { + const handleActive = () => { + const data = table + .getSelectedRowModel() + .flatRows.map((row) => row?.original); + return handleBulkAction('ACTIVE', data); + }; + const handleDelete = () => { + const data = table + .getSelectedRowModel() + .flatRows.map((row) => row?.original); + return handleBulkAction('DELETE', data, table); + }; + const handleDownload = () => { + const data = table + .getSelectedRowModel() + .flatRows.map((row) => row?.original); + return handleBulkAction('DOWNLOAD', data); + }; + return table.getIsSomeRowsSelected() || + table.getIsAllRowsSelected() ? ( + + + + + + + + {showDownloadAction && ( + + + + )} + + ) : ( +
    + ); + }} + muiTablePaginationProps={{ + labelRowsPerPage: 'Records Per Page', + }} + tableInstanceRef={tableRef} + state={{ + showColumnFilters: true, + columnFilters, + globalFilter, + isLoading, + pagination, + showAlertBanner: isError, + showProgressBars: isRefetching, + sorting, + }} + /> + + ); + }) +); +Table.propTypes = { + getData: PropTypes.func.isRequired, + columns: PropTypes.any.isRequired, + options: PropTypes.shape({ + pageSize: PropTypes.number, + pageIndex: PropTypes.number, + tableTitle: PropTypes.string, + showSearch: PropTypes.bool, + showTopBar: PropTypes.bool, + showFilters: PropTypes.bool, + isBold: PropTypes.bool, + }), +}; + +const CustomPagination = ({ table }) => { + const classes = useStyles(); + + const totalPage = + Math.ceil( + table.options.rowCount / table.options.state.pagination.pageSize + ) || 1; + + const previousPage = () => { + table.setPageIndex( + table.options?.state.pagination.pageIndex > 0 + ? table.options?.state.pagination.pageIndex - 1 + : table.options?.state.pagination.pageIndex + ); + }; + + const nextPage = () => { + table.setPageIndex( + table.options?.state.pagination.pageIndex < totalPage + ? table.options?.state.pagination.pageIndex + 1 + : table.options?.state.pagination.pageIndex + ); + }; + + const setPageIndex = (newIndex) => { + table.setPageIndex(newIndex); + }; + + const currentPage = table.options.state.pagination.pageIndex + 1; + + return ( + + setPageIndex(page - 1)} + renderItem={(item) => { + if (item.type === 'previous') { + return ( + ( + + )} + /> + ); + } else if (item.type === 'next') { + return ( + ( + + )} + disabled={currentPage === totalPage} + /> + ); + } else { + return ; + } + }} + sx={{ + display: 'flex', + justifyContent: 'center', + flex: '1', + '& .MuiPagination-ul': { + width: '100%', + display: 'flex', + cursor: 'pointer', + }, + '& .MuiPagination-ul > :first-child': { + marginRight: 'auto', + }, + ' & .MuiPagination-ul > :last-child': { + marginLeft: 'auto', + }, + }} + /> + + ); +}; + +export default Table; diff --git a/src/components/TableSelect.jsx b/src/components/TableSelect.jsx new file mode 100644 index 0000000..741428e --- /dev/null +++ b/src/components/TableSelect.jsx @@ -0,0 +1,80 @@ +import { useEffect, useState } from 'react'; +import { Checkbox, ListItemText, MenuItem, Select } from '@mui/material'; +import { ExpandMore } from '@mui/icons-material'; +import { useStyles as tableStyles } from './styles/tableStyles'; +import { CLINIC_STATUS } from '../constants'; + +function TableSelect({ name, header, getOptions, options, MenuProps }) { + const tableClasses = tableStyles(); + const [optionsData, setOptionsData] = useState(options ?? []); + const [selectedValues, setSelectedValues] = useState([]); + + const handleChange = (event) => { + const value = event.target.value; + let newSelectedValues; + + if (Array.isArray(value)) { + newSelectedValues = value; + } else { + newSelectedValues = selectedValues.includes(value) + ? selectedValues.filter((item) => item !== value) + : [value]; + } + + setSelectedValues(newSelectedValues); + + header.column.setFilterValue( + newSelectedValues.length > 0 ? newSelectedValues : undefined + ); + }; + + const getOptionsData = async () => { + const resp = await getOptions(); + setOptionsData(resp?.data); + }; + + useEffect(() => { + if (!options) getOptionsData(); + }, []); + + return ( + <> + + + ); +} + +export default TableSelect; diff --git a/src/components/TermsAndCondition.jsx b/src/components/TermsAndCondition.jsx new file mode 100644 index 0000000..8b0cba4 --- /dev/null +++ b/src/components/TermsAndCondition.jsx @@ -0,0 +1,160 @@ +import CloseIcon from '@mui/icons-material/Close'; +import { Box, Grid, Typography } from '@mui/material'; +import React from 'react'; +import { useStyles } from './styles/termsAndConditionStyles'; + +const TermsAndCondition = ({ onClose }) => { + const closeTheDrawer = () => { + onClose(); + }; + + const classes = useStyles(); + return ( + <> + + + + + Terms and Conditions{' '} + + + + + + + + +
    +

    + Welcome to 24x7 AI Healthcare Receptionist, a mobile + SaaSlication owned and operated by Reddy Co Pty Ltd ("Company," + "we," "us," or "our"). By accessing or using the 24x7 AI + Healthcare Receptionist software as a service (the "SaaS"), you + agree to be bound by these Terms of Use ("Terms"). If you do not + agree to these Terms, please do not use the SaaS. +

    +
    +
    +

    + USE TO BE IN CONFORMITY WITH THE PURPOSE +

    {' '} +

    +

      +
    1. +

      Eligibility

      +
        +
      1. + You must be at least 18 years old to use this SaaS. By + using the SaaS, you represent and warrant that you have + the legal capacity to enter into these Terms. +
      2. +
      +
    2. +
    3. +

      Use of the SaaS

      +
        +
      1. + The SaaS is intended for personal, non-commercial use to + support mental wellness. +
      2. +
      3. + You agree not to misuse the SaaS, including engaging in + illegal activities, harassing other users, or attempting + to compromise the security of the SaaS. +
      4. +
      +
    4. +
    5. +

      + User Accounts and Privacy +

      +
        +
      1. + You may be required to create an account. You are + responsible for maintaining the confidentiality of your + account information. +
      2. +
      3. + Your use of the SaaS is also governed by our Privacy + Policy, which details how we collect, use, and protect + your data. +
      4. +
      +
    6. +
    7. +

      + Content and Community Guidelines +

      +
        +
      1. + You are responsible for any content you submit to the + SaaS. +
      2. +
      3. + We reserve the right to moderate, remove, or restrict + content that violates our guidelines or these Terms. +
      4. +
      +
    8. +
    9. +

      + Emergency and Medical Disclaimer +

      +
        +
      1. + The SaaS is not a substitute for professional medical or + emergency services. +
      2. +
      3. + In case of a crisis, we encourage you to contact + emergency services immediately. +
      4. +
      +
    10. +
    11. +

      Limitation of Liability

      +
        +
      1. + We do not guarantee uninterrupted access to the SaaS and + are not liable for any losses or damages resulting from + its use. +
      2. +
      3. + The SaaS is provided on an "as is" and "as available" + basis without warranties of any kind. +
      4. +
      +
    12. +
    13. +

      Modifications to Terms

      +

      + We reserve the right to update these Terms at any time. + Continued use of the SaaS constitutes acceptance of any + revisions. +

      +
    14. +
    15. +

      Limitation of Liability

      +

      + These Terms are governed by the laws of South Australia, + Australia. +

      +

      + For inquiries, contact us at: + + Reddy Co Pty Ltd, 606/147 Pirie St Adelaide SA 5000, + Australia. + +

      +
    16. +
    +

    +
    +
    +
    +
    + + ); +}; + +export default TermsAndCondition; diff --git a/src/components/styles/customBreadcrumbsStyles.js b/src/components/styles/customBreadcrumbsStyles.js new file mode 100644 index 0000000..ac7ad7f --- /dev/null +++ b/src/components/styles/customBreadcrumbsStyles.js @@ -0,0 +1,26 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + root: { + marginTop: theme.spacing(-1), + marginBottom: theme.spacing(1), + marginLeft: theme.spacing(0.6), + display: 'flex', + }, + sepreaterBreadCrumb: { + fontSize: pxToRem(18), + color: theme.palette.grey[29], + }, + activeBreadCrumb: { + fontSize: pxToRem(14), + color: theme.palette.grey[17], + textDecoration: 'none', + }, + inActiveBreadCrumb: { + color: theme.palette.primary.main, + cursor: 'pointer', + fontSize: pxToRem(14), + textDecoration: 'none', + }, +})); diff --git a/src/components/styles/customFileUploadStyles.js b/src/components/styles/customFileUploadStyles.js new file mode 100644 index 0000000..16f73f7 --- /dev/null +++ b/src/components/styles/customFileUploadStyles.js @@ -0,0 +1,249 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + mainBox: { + marginTop: theme.spacing(1.5), + }, + iconSize: { + fontSize: pxToRem(28), + }, + dropZoneOuterBox: { + width: '100%', + }, + reactCrop: { + /* Set a minimum height and width */ + minHeight: '200px', + minWidth: '200px', + }, + inputTitle: { + '&.MuiTypography-body1': { + fontSize: pxToRem(12), + }, + fontSize: pxToRem(12), + color: theme.palette.grey[10], + marginBottom: pxToRem(7), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + }, + imgButton: { + color: theme?.palette?.primary?.main, + fontSize: pxToRem(12), + fontWeight: 400, + fontFamily: theme?.fontFamily?.regular, + backgroundColor: 'transparent', + border: 'none', + cursor: 'pointer', + margin: `0 ${theme.spacing(0.8)}`, + padding: theme.spacing(0), + outline: 'none', + }, + + onImageButton: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + position: 'absolute', + top: '50%', + left: '50%', + marginLeft: 'auto', + transform: 'translate(-50%, -50%)', + backgroundColor: 'rgba(0,0,0,0.2)', + borderRadius: theme.spacing(1), + width: '100%', + height: '100%', + }, + temp: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + height: '100%', + }, + addButton: { + border: `1px dashed ${theme.palette.primary.main}`, + width: '100%', + height: '130px', + backgroundColor: theme.palette.common.white, + borderRadius: theme.spacing(1), + position: 'relative', + display: 'grid', + placeItems: 'center', + }, + addButtonContent: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + height: '100%', + cursor: 'pointer', + width: '100%', + fontSize: pxToRem(16), + }, + addText: { + opacity: 1, + fontFamily: theme?.fontFamily?.medium, + fontSize: pxToRem(14), + height: '16px', + fontWeight: 500, + lineHeight: 'normal', + letterSpacing: 'normal', + marginTop: theme?.spacing(1), + }, + addIcon: { + width: '26px', + height: '24px', + }, + addSubtext: { + fontSize: pxToRem(12), + color: theme.palette.grey[10], + opacity: '0.6', + textAlign: 'center', + padding: `${theme.spacing(0.5)} ${theme.spacing(7)}`, + [theme.breakpoints.down('md')]: { + padding: theme.spacing(1), + }, + [theme.breakpoints.down('lg')]: { + padding: theme.spacing(1), + }, + }, + previewBox: { + display: 'flex', + justifyContent: 'end', + alignItems: 'start', + width: '153px', + height: '111px', + borderRadius: theme.spacing(0.4), + backgroundSize: 'cover', + }, + previewFullScreenBox: { + margin: theme.spacing(4), + }, + previewHeading: { + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + }, + previewImageGrid: { + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + justifyContent: 'center', + marginTop: theme.spacing(2), + }, + previewImage: { + maxHeight: '450px', + [theme.breakpoints.down('sm')]: { + maxHeight: '350px', + }, + }, + previewFileTitle: { + marginTop: theme.spacing(-3), + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + [theme.breakpoints.down('sm')]: { + marginTop: theme.spacing(0), + }, + }, + pdfAndDocPreviewBox: { + minHeight: '450px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + }, + pdfAndDocPreviewDownloadButton: { + marginTop: theme.spacing(2), + fontFamily: theme?.fontFamily?.regular, + }, + transFormImageBox: { + minHeight: '65vh', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + controlButtons: { + marginTop: theme.spacing(1), + }, + customTransFormWrapper: { + overflow: 'visible', + }, + controlButton: { + color: theme.palette.common.white, + cursor: 'pointer', + border: 'none', + borderRadius: theme.spacing(0), + padding: theme.spacing(1.2), + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.9)', + color: theme.palette.common.white, + }, + }, + zoomOutButton: { + backgroundColor: 'rgba(0,0,0,0.7)', + borderTopLeftRadius: theme.spacing(0.8), + borderBottomLeftRadius: theme.spacing(0.8), + }, + zoomResetButton: { + backgroundColor: 'rgba(0,0,0,0.4)', + }, + zoomInButton: { + backgroundColor: 'rgba(0,0,0,0.7)', + borderTopRightRadius: theme.spacing(0.8), + borderBottomRightRadius: theme.spacing(0.8), + }, + eyeButton: { + padding: pxToRem(3), + borderRadius: pxToRem(4), + border: 'none', + cursor: 'pointer', + backgroundColor: 'rgba(0,0,0,0.6)', + color: theme.palette.common.white, + marginRight: pxToRem(7), + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + downloadButton: { + marginLeft: pxToRem(7), + padding: pxToRem(3), + borderRadius: pxToRem(4), + border: 'none', + cursor: 'pointer', + backgroundColor: 'rgba(0,0,0,0.6)', + color: theme.palette.common.white, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + previewCloseButton: { + color: theme.palette.common.white, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + previewDownloadButton: { + cursor: 'pointer', + color: theme.palette.common.white, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + previewDownloadIcon: { + height: pxToRem(26), + width: pxToRem(26), + cursor: 'pointer', + }, + errorMessage: { + color: theme.palette.error.main, + fontStyle: 'italic', + fontSize: pxToRem(12), + marginTop: theme.spacing(0.5), + fontFamily: theme.fontFamily.medium, + }, +})); diff --git a/src/components/styles/customStepperStyles.js b/src/components/styles/customStepperStyles.js new file mode 100644 index 0000000..7944834 --- /dev/null +++ b/src/components/styles/customStepperStyles.js @@ -0,0 +1,83 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + stepperMain: { + '& .MuiStepLabel-root': { + flexDirection: 'column', + alignItems: 'center', + }, + '& .MuiStep-root': { + paddingLeft: theme.spacing(0), + paddingRight: theme.spacing(0), + }, + '& .MuiStepLabel-iconContainer': { + paddingRight: theme.spacing(0), + paddingBottom: theme.spacing(0), + }, + '& .MuiStepLabel-label': { + fontFamily: theme.fontFamily.regular, + fontSize: pxToRem(12), + }, + marginLeft: theme.spacing(1), + marginTop: theme.spacing(1), + display: 'flex', + width: '70%', + justifyContent: 'center', + marginBottom: theme.spacing(1.4), + [theme.breakpoints.down('sm')]: { + width: '80%', + margin: 'auto', + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1.4), + }, + [theme.breakpoints.up('xs')]: { + zIndex: 1, + }, + }, + stepperBox: { + width: '35%', + marginLeft: theme.spacing(2), + marginTop: theme.spacing(0.2), + justifyContent: 'center', + [theme.breakpoints.down('md')]: { + width: '60%', + }, + [theme.breakpoints.down('sm')]: { + margin: 'auto', + width: '90%', + }, + }, + stepLabel: { + display: 'flex', + // justifyContent: 'center', + textAlign: 'center', + color: theme.palette.grey[29], + [theme.breakpoints.down('md')]: { + width: '100%', + }, + [theme.breakpoints.down('sm')]: { + width: '100%', + }, + }, + stepIconRoot: { + backgroundColor: theme.palette.grey[29], + color: theme.info, + width: 30, + height: 30, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + }, + stack: { + backgroundColor: theme.palette.grey[30], + borderRadius: `${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(0)} ${theme.spacing(0)}`, + }, + activeStepLabel: { + color: theme.palette.common.black, + }, + icons: { + fontSize: pxToRem(18), + }, +})); diff --git a/src/components/styles/formFooterMessageStyles.js b/src/components/styles/formFooterMessageStyles.js new file mode 100644 index 0000000..910f2e4 --- /dev/null +++ b/src/components/styles/formFooterMessageStyles.js @@ -0,0 +1,55 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + nextButton: { + '&.MuiButtonBase-root': { + padding: `${theme.spacing(2.1)} ${theme.spacing(1.6)}`, + backgroundColor: theme?.palette?.primary?.colors, + color: theme?.palette?.common?.white, + display: 'flex', + fontSize: pxToRem(14), + fontFamily: theme?.fontFamily?.semiBold, + width: 'auto', + alignItems: 'center', + justifyContent: 'center', + borderRadius: theme.spacing(0.8), + [theme.breakpoints.down('md')]: { + marginTop: theme.spacing(2), + width: '100%', + }, + }, + }, + checkBoxIconForNextButton: { + fontSize: pxToRem(20), + marginRight: theme.spacing(0.3), + }, + alert: { + backgroundColor: theme?.palette?.grey[51], + color: theme?.palette?.grey[52], + borderColor: theme?.palette?.grey[52], + borderRadius: theme.spacing(0.8), + padding: `${theme.spacing(0.2)} ${theme.spacing(1.6)}`, + }, + allElementOuterDiv: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + margin: theme.spacing(3), + [theme.breakpoints.down('md')]: { + flexDirection: 'column', + }, + }, + alertDivWithButton: { + flex: 0.98, + width: '100%', + }, + alertDivWithoutButton: { + flex: 1, + }, + nextButtonDiv: { + [theme.breakpoints.down('md')]: { + width: '100%', + }, + }, +})); diff --git a/src/components/styles/formPageHeaderStyles.js b/src/components/styles/formPageHeaderStyles.js new file mode 100644 index 0000000..2125d9d --- /dev/null +++ b/src/components/styles/formPageHeaderStyles.js @@ -0,0 +1,63 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + header: { + marginTop: theme.spacing(1.4), + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + [theme.breakpoints.up('md')]: { + marginTop: theme.spacing(0), + }, + }, + detailPageHeader: { + minHeight: theme.spacing(3.2), + // margin: theme.spacing(0.5), + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + // flexWrap: 'wrap', + }, + title: { + width: 'max-content', + marginLeft: theme.spacing(1), + }, + globalIcon: { + width: '24px', + height: '24px', + marginLeft: theme?.spacing(1), + marginTop: theme?.spacing(1.5), + }, + headerActions: { + width: '100%', + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + }, + detailHeaderActions: { + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + }, + gobackIcon: { + fontSize: pxToRem(24), + marginRight: theme.spacing(0.4), + cursor: 'pointer', + }, + headerTitle: { + display: 'flex', + justifyContent: 'center', + maxWidth: '100%', + alignItems: 'center', + }, + asyncDropdownContainer: { + height: '42px', + width: '251px', + marginRight: theme?.spacing(1.6), + }, + subHeading: { + marginLeft: theme.spacing(4), + fontFamily: 'Inter-Regular', + }, +})); diff --git a/src/components/styles/formSectionHeadingStyles.js b/src/components/styles/formSectionHeadingStyles.js new file mode 100644 index 0000000..161f96f --- /dev/null +++ b/src/components/styles/formSectionHeadingStyles.js @@ -0,0 +1,25 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + title: { + fontSize: pxToRem(14), + fontWeight: '600', + textTransform: 'uppercase', + }, + bracketText: { + fontSize: pxToRem(12), + textTransform: 'none', + color: theme.palette.grey[10], + opacity: 0.8, + fontFamily: theme?.fontFamily?.regular, + fontWeight: 500, + }, + root: { + paddingTop: theme.spacing(2), + paddingLeft: theme.spacing(2.5), + width: '100%', + display: 'flex', + alignItems: 'center', + }, +})); diff --git a/src/components/styles/loaderStyles.js b/src/components/styles/loaderStyles.js new file mode 100644 index 0000000..0b1b1af --- /dev/null +++ b/src/components/styles/loaderStyles.js @@ -0,0 +1,12 @@ +import makeStyles from '@mui/styles/makeStyles'; + +export const useStyles = makeStyles(() => ({ + root: { + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, +})); diff --git a/src/components/styles/multiSelectStyles.js b/src/components/styles/multiSelectStyles.js new file mode 100644 index 0000000..a02b4ac --- /dev/null +++ b/src/components/styles/multiSelectStyles.js @@ -0,0 +1,71 @@ +import { makeStyles } from '@mui/styles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + main: { + marginTop: '15px', + width: '42vw', + [theme.breakpoints.up('md')]: { + width: '100%', + }, + [theme.breakpoints.down('sm')]: { + width: '100%', + }, + }, + chipOuter: { + display: 'flex', + flexWrap: 'wrap', + flexDirection: 'row', + width: '100%', + }, + chip: { + minWidth: '170px', + height: '42px', + padding: theme.spacing(1.5), + margin: theme.spacing(1), + marginRight: theme.spacing(1.5), + justifyContent: 'space-between', + borderRadius: '24px', + fontSize: pxToRem(14), + }, + dropDownArrow: { + color: theme.palette.grey[3], + transition: 'transform 0.1s ease-in-out', + }, + rotate180Deg: { + transform: 'rotate(180deg)', + }, + rotate0Deg: { + transform: 'rotate(0deg)', + }, + hoverPointer: { + '&:hover': { + cursor: 'pointer', + }, + }, + selectAndSearchField: { + display: 'flex', + gap: '0.7rem', + flexDirection: 'column', + justifyContent: 'center', + [theme.breakpoints.up('md')]: { + flexDirection: 'row', + flexWrap: 'wrap', + alignItems: 'center', + }, + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + }, + }, + autoComplete: { + width: '90%', + [theme.breakpoints.up('md')]: { + flex: 1.1, + }, + }, + autoCompleteText: { + [theme.breakpoints.up('md')]: { + flex: 1, + }, + }, +})); diff --git a/src/components/styles/pageHeaderStyles.js b/src/components/styles/pageHeaderStyles.js new file mode 100644 index 0000000..390a129 --- /dev/null +++ b/src/components/styles/pageHeaderStyles.js @@ -0,0 +1,150 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + header: { + minHeight: theme.spacing(3.3), + margin: theme.spacing(0.5), + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + flexWrap: 'wrap', + [theme.breakpoints.down('sm')]: { + alignItems: 'flex-start', + }, + width: '99%', + }, + detailPageHeader: { + minHeight: theme.spacing(3.2), + margin: theme.spacing(0.5), + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + // flexWrap: 'wrap', + }, + title: { + width: 'max-content', + }, + globalIcon: { + width: '24px', + height: '24px', + marginLeft: theme?.spacing(1), + marginTop: theme?.spacing(1.5), + }, + headerResponsive: { + display: 'flex', + flexWrap: 'wrap', + [theme.breakpoints.down('sm')]: { + alignItems: 'flex-start', + }, + }, + extraText: { + display: 'flex', + justifyContent: 'flex-end', + marginRight: theme.spacing(2.0), + }, + secondaryButton: { + display: 'flex', + alignItems: 'center', + }, + headerActions: { + display: 'flex', + alignItems: 'center', + flexWrap: 'wrap', + maxWidth: '720px', + justifyContent: 'flex-end', + }, + detailHeaderActions: { + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + }, + searchBar: { + marginRight: theme.spacing(1.6), + width: '251px', + height: '43px', + '& .MuiAutocomplete-root': { + height: '43px', + borderWidth: 0, + }, + '&.MuiFormControl-root': { + margin: '16px', + }, + }, + selectBar: { + marginLeft: theme.spacing(10), + width: '251px', + height: '43px', + '& .MuiOutlinedInput-root': { + height: '43px', + borderWidth: 0, + }, + }, + addButton: { + '&.MuiButtonBase-root': { + padding: `${theme.spacing(0.6)} ${theme.spacing(1.6)}`, + backgroundColor: theme?.palette?.primary?.colors, + color: theme?.palette?.common?.white, + fontSize: pxToRem(15), + fontFamily: theme?.fontFamily?.light, + width: 'auto', + justifyContent: 'center', + }, + + whiteSpace: 'nowrap', + maxHeight: pxToRem(48), + minHeight: pxToRem(48), + marginTop: '5px', + minWidth: pxToRem(127), + }, + addButtonOutlined: { + '&.MuiButtonBase-root': { + padding: `${theme.spacing(0.6)} ${theme.spacing(1.6)}`, + backgroundColor: theme?.palette?.primary?.colors, + color: theme?.palette?.grey[10], + fontSize: pxToRem(15), + fontFamily: theme?.fontFamily?.light, + width: 'auto', + border: `1px solid ${theme.palette.grey[29]}`, + justifyContent: 'center', + '&:hover': { + backgroundColor: theme.palette.grey[50], + }, + minHeight: theme.spacing(4.4), + }, + + whiteSpace: 'nowrap', + maxHeight: pxToRem(48), + minHeight: pxToRem(48), + marginTop: '5px', + minWidth: pxToRem(127), + }, + gobackIcon: { + fontSize: '28px', + marginRight: theme.spacing(2.0), + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + }, + headerTitle: { + display: 'flex', + alignItems: 'center', + maxWidth: '100%', + }, + dropdownContainer: { + height: '42px', + width: '251px', + marginRight: theme?.spacing(1.6), + }, + dropdownAutocomplete: { + marginRight: theme.spacing(1.5), + width: '251px', + maxHeight: '43px', + minHeight: '43px', + + '& .MuiOutlinedInput-root': { + height: '48px', + backgroundColor: theme.palette.grey[2], + }, + }, +})); diff --git a/src/components/styles/tableStyles.js b/src/components/styles/tableStyles.js new file mode 100644 index 0000000..b092144 --- /dev/null +++ b/src/components/styles/tableStyles.js @@ -0,0 +1,184 @@ +import { makeStyles } from '@mui/styles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + textFilter: { + height: '32px', + '& .MuiInputBase-root': { + height: '32px', + fontSize: pxToRem(12), + paddingRight: 0, + }, + }, + selectFilter: { + boxShadow: 'none', + '& .MuiOutlinedInput-notchedOutline': { border: 0 }, + '&.MuiOutlinedInput-root:hover .MuiOutlinedInput-notchedOutline': { + border: 0, + }, + '&.MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline': { + border: 0, + }, + height: '32px', + fontSize: pxToRem(12), + fontFamily: theme.fontFamily.semiBold, + opacity: 0.56, + fontWeight: 600, + lineHeight: 'normal', + '& .MuiSelect-select.MuiInputBase-input': { + marginRight: theme.spacing(0), + paddingRight: theme.spacing(0), + }, + }, + ListItemTextClass: { + paddingLeft: theme.spacing(0.6), + }, + tableBodyRow: { + cursor: 'pointer', + backgroundColor: theme.palette.grey[0], + borderRadius: '8px', + '&.Mui-selected': { + backgroundColor: theme?.palette?.primary?.highlight, + }, + }, + tableHeadRow: { + cursor: 'pointer', + backgroundColor: theme.palette.grey[30], + borderRadius: '8px', + boxShadow: '0px 0px 0px #fff', + }, + tableCheckbox: { + '& .MuiSvgIcon-root': { + width: '21px', + }, + }, + muiTableBodyCell: { + '& .MuiTableCell-root': { + opacity: '0.7', + }, + // lineHeight: '2.5rem', + margin: theme.spacing(1), + fontWeight: '500', + fontSize: pxToRem(16), + fontStretch: 'normal', + fontStyle: 'normal', + letterSpacing: 'normal', + fontFamily: 'Inter-Medium', + color: theme.palette.grey[10], + textAlign: 'left', + }, + muiTableHeadCell: { + lineHeight: '2.0rem', + fontSize: pxToRem(12), + fontFamily: 'Inter-Bold', + fontWeight: 'bold', + fontStretch: 'normal', + fontStyle: 'normal', + letterSpacing: 'normal', + alignContent: 'center', + opacity: 0.56, + color: theme.palette.grey[10], + margin: theme.spacing(2.5), + '& .Mui-TableHeadCell-Content': { + '& .Mui-TableHeadCell-Content-Labels': { + '& span': { + '& .MuiIconButton-sizeSmall ': { + display: 'none', + }, + }, + }, + }, + '& .MuiTableRow-head': { + backgroundColor: theme.palette.grey[0], + }, + '& .MuiCollapse-root': { + marginTop: '-32px', + }, + }, + tableGrayRow: { + cursor: 'pointer', + backgroundColor: theme.palette.grey[26], + borderRadius: theme.spacing(0.8), + }, + tablehighlightRow: { + cursor: 'pointer', + backgroundColor: `${theme?.palette?.primary?.highlight} !important`, + borderRadius: theme.spacing(0.8), + '& .MuiTableCell-root': { + backgroundColor: `${theme?.palette?.primary?.highlight} !important`, + }, + }, + prevNextBtn: { + display: 'flex', + fontFamily: 'Inter-Regular', + fontSize: pxToRem(14), + justifyContent: 'center', + paddingLeft: '12px', + paddingRight: '12px', + boxShadow: '0px 1px 2px 0px #e9eaeb', + color: theme.palette.grey[10], + border: '1px solid #E9EAEB', + borderRadius: '8px', + }, + menuItem: { + paddingTop: theme.spacing(1.5), + paddingBottom: theme.spacing(1.5), + fontSize: pxToRem(14), + }, + searchContainer: { + display: 'flex', + justifyContent: 'space-between', + }, + searchBar: { + margin: theme.spacing(2.0), + // marginBottom: theme.spacing(2.8), + backgroundColor: theme.palette.grey[0], + '& .MuiOutlinedInput-root': { + borderRadius: theme.spacing(1.0), + // height: theme.spacing(4.0), + }, + '&.MuiTextField-root': { + width: theme.spacing(40.0), + // height: theme.spacing(4.0), + borderRadius: theme.spacing(1.0), + // maxHeight: theme.spacing(4.0), + }, + '& .MuiOutlinedInput-input::placeholder': { + fontStyle: 'normal', + fontSize: pxToRem(14), + color: theme.palette.grey[10], + opacity: 0.9, + }, + }, + searchIconImg: { + width: theme.spacing(2.0), + minWidth: theme.spacing(1.8), + }, + pageSizeDropdown: { + display: 'flex', + margin: theme.spacing(2.0), + marginBottom: theme.spacing(2.8), + }, + dropDownLabel: { + alignSelf: 'center', + marginRight: theme.spacing(0.6), + }, + perPageDropdown: { + backgroundColor: theme.palette.grey[0], + '& .MuiSelect-outlined': { + borderRadius: theme.spacing(1.0), + height: theme.spacing(1.0), + minHeight: theme.spacing(1.6), + display: 'flex', + alignItems: 'center', + }, + '&.MuiSelect-root': { + height: theme.spacing(1.0), + borderRadius: theme.spacing(1.0), + maxHeight: theme.spacing(1.0), + }, + '& .MuiSelect-icon': { + color: theme.palette.primary.main, + }, + }, +})); diff --git a/src/components/styles/termsAndConditionStyles.js b/src/components/styles/termsAndConditionStyles.js new file mode 100644 index 0000000..59a7452 --- /dev/null +++ b/src/components/styles/termsAndConditionStyles.js @@ -0,0 +1,85 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + outerBox: { + height: '100%', + display: 'flex', + flexDirection: 'column', + fontFamily: theme.fontFamily.regular, + }, + allContentBox: { + marginRight: pxToRem(5), + }, + heading: { + padding: theme.spacing(3), + justifyContent: 'space-between', + }, + headingTitle: { + fontSize: pxToRem(24), + fontFamily: theme.fontFamily.medium, + }, + + contentBox: { + fontSize: pxToRem(16), + overflowY: 'auto', + flexGrow: '1', + margin: theme.spacing(3), + marginTop: theme.spacing(1), + marginRight: theme.spacing(0), + }, + textBox: { + marginRight: theme.spacing(4), + }, + + subHeadingTitle: { + fontSize: pxToRem(18), + textTransform: 'uppercase', + paddingTop: theme.spacing(3), + fontFamily: theme.fontFamily.semiBold, + }, + subHeadingText: { + fontSize: pxToRem(16), + paddingTop: theme.spacing(1), + }, + TermsAndConditionCloseIcon: { + display: 'flex', + justifyContent: 'center', + }, + paragraphSpacing: { + marginTop: theme.spacing(2), + }, + listTitle: { + fontFamily: theme.fontFamily.bold, + marginLeft: theme.spacing(1), + fontSize: pxToRem(18), + }, + + listTitleText: { + marginLeft: theme.spacing(1), + }, + outerList: { + fontFamily: theme.fontFamily.bold, + marginLeft: theme.spacing(3), + display: 'flex', + justifyContent: 'flex-start', + flexDirection: 'column', + gap: pxToRem(10), + marginTop: pxToRem(20), + }, + subList: { + marginLeft: theme.spacing(3), + display: 'flex', + justifyContent: 'flex-start', + flexDirection: 'column', + gap: pxToRem(10), + marginTop: pxToRem(10), + fontSize: pxToRem(16), + fontFamily: theme.fontFamily.regular, + }, + listAdditionalText: { + fontSize: pxToRem(16), + paddingTop: theme.spacing(1), + fontFamily: theme.fontFamily.regular, + }, +})); diff --git a/src/config/api.js b/src/config/api.js new file mode 100644 index 0000000..bf7e744 --- /dev/null +++ b/src/config/api.js @@ -0,0 +1,195 @@ +import axios from 'axios'; +import { API_BASE_URL } from '../common/envVariables'; +import { ERRORS, NOTIFICATION } from '../constants'; +import { pushNotification } from '../utils/notification'; +import { commonLogoutFunc } from '../utils/share'; +import store from '../redux/store'; + +export const axiosInstance = axios.create({ + baseURL: API_BASE_URL, + crossDomain: true, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': '*', + }, + withCredentials: true, + timeout: 300000, +}); + +axiosInstance.interceptors.request.use( + async function (config) { + try { + const token = JSON.parse(localStorage.getItem('redux')); + if (token?.data?.data) { + config.headers.Authorization = `${token?.data?.data}`; + } + const state = store.getState(); + const companyId = state?.loginAsCompanyAdmin?.companyId; // Extract companyId + if (companyId) { + config.headers.companyId = companyId; // Attach companyId to headers + } + return config; + } catch (error) { + pushNotification(ERRORS.NETWORK_ERROR, NOTIFICATION.ERROR); + commonLogoutFunc(); + return null; + } + }, + function (error) { + if (error?.response?.data?.error && error?.response?.data?.message) { + pushNotification(error?.response?.data?.message, NOTIFICATION.ERROR); + } + // Do something with request error + return Promise.reject(error); + } +); + +axiosInstance.interceptors.response.use( + function (response) { + if (response?.data && response?.data?.error !== '') { + pushNotification(response?.data?.message, NOTIFICATION.ERROR); + return Promise.reject(response); + } + return response; + }, + function (error) { + // eslint-disable-next-line no-console + console.log(error); + if (!error?.response?.data) { + pushNotification(error.message, NOTIFICATION.ERROR); + if (error.message !== 'Network Error') commonLogoutFunc(); + } else if ( + error?.response?.data?.error && + error?.response?.data?.error[0]?.message + ) { + pushNotification( + error?.response?.data?.error[0]?.message, + NOTIFICATION.ERROR + ); + } else if (error?.response?.data?.message) { + pushNotification(error?.response?.data?.message, NOTIFICATION.ERROR); + } + // else if ( + // error?.response?.status === 440 || + // error?.response?.status === 401 || + // error?.response?.status === 403 + // ) { + // pushNotification(ERRORS.SESSION_EXPIRED_ERROR, NOTIFICATION.ERROR); + // // commonLogoutFunc(); + // } + else if (error?.response?.status === 440 || error?.error?.status === 401) { + pushNotification(ERRORS.SESSION_EXPIRED_ERROR, NOTIFICATION.ERROR); + // commonLogoutFunc(); + } + // else if (error?.response?.data?.error && error?.response?.data?.message) { + // pushNotification(error?.response?.data?.message, NOTIFICATION.ERROR); + // } + return Promise.reject(error); + } +); + +export const fileDownloadAxios = axios.create({ + baseURL: API_BASE_URL, + responseType: 'blob', + crossDomain: true, + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, + timeout: 300000, +}); + +fileDownloadAxios.interceptors.request.use( + async function (config) { + try { + const token = JSON.parse(localStorage.getItem('redux')); + if (token?.data?.data) { + config.headers.Authorization = `${token?.data?.data}`; + } + const state = store.getState(); + const companyId = state?.loginAsCompanyAdmin?.companyId; // Extract companyId + if (companyId) { + config.headers.companyId = companyId; // Attach companyId to headers + } + return config; + } catch (error) { + pushNotification(ERRORS.NETWORK_ERROR, NOTIFICATION.ERROR); + commonLogoutFunc(); + return null; + } + }, + function (error) { + if (error?.response?.data?.error && error?.response?.data?.message) { + pushNotification(error?.response?.data?.message, NOTIFICATION.ERROR); + } + return Promise.reject(error); + } +); + +fileDownloadAxios.interceptors.response.use( + async function (response) { + // Handle error response (when server responds with JSON instead of a file) + if ( + response?.headers['content-type']?.includes('application/json') && + response?.data + ) { + try { + const errorData = await response.data.text(); // Convert blob to text + const parsedError = JSON.parse(errorData); + + if (parsedError?.error) { + pushNotification( + parsedError?.message || 'Download failed', + NOTIFICATION.ERROR + ); + return Promise.reject(parsedError); + } + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error parsing error response', err); + } + } + + return response; + }, + function (error) { + // eslint-disable-next-line no-console + console.error(error); + + if (!error?.response?.data) { + pushNotification(error.message, NOTIFICATION.ERROR); + if (error.message !== 'Network Error') commonLogoutFunc(); + } else { + // Handle error message from API + const contentType = error?.response?.headers?.['content-type']; + + if (contentType?.includes('application/json')) { + error.response.data.text().then((text) => { + try { + const parsedError = JSON.parse(text); + pushNotification( + parsedError.message || 'Something went wrong', + NOTIFICATION.ERROR + ); + } catch (err) { + pushNotification('Unknown error occurred', NOTIFICATION.ERROR); + } + }); + } else { + pushNotification( + error?.response?.data?.message || 'Download failed', + NOTIFICATION.ERROR + ); + } + } + + // Handle session expiration + if (error?.response?.status === 440 || error?.response?.status === 401) { + pushNotification(ERRORS.SESSION_EXPIRED_ERROR, NOTIFICATION.ERROR); + commonLogoutFunc(); + } + + return Promise.reject(error); + } +); diff --git a/src/config/firebase.js b/src/config/firebase.js new file mode 100644 index 0000000..f5e3680 --- /dev/null +++ b/src/config/firebase.js @@ -0,0 +1,18 @@ +import { initializeApp } from 'firebase/app'; +import { getMessaging, onMessage } from 'firebase/messaging'; + +export const firebaseConfig = { + apiKey: "AIzaSyDBDwlnQsbIxKni_UzZxDjeIk0akK-vDPM", + authDomain: "health-apps-69256.firebaseapp.com", + projectId: "health-apps-69256", + storageBucket: "health-apps-69256.firebasestorage.app", + messagingSenderId: "257956159594", + appId: "1:257956159594:web:2e66b8d4d37e2382c76a13", +}; + +const firebaseApp = initializeApp(firebaseConfig); +export default firebaseApp; +export const messaging = getMessaging(firebaseApp); + +export const onForegroundMessage = () => + new Promise((resolve) => onMessage(messaging, (payload) => resolve(payload))); diff --git a/src/constants/index.js b/src/constants/index.js new file mode 100644 index 0000000..54d4116 --- /dev/null +++ b/src/constants/index.js @@ -0,0 +1,218 @@ +export const NOTIFICATION = { + INFO: 'info', + SUCCESS: 'success', + WARNING: 'warning', + ERROR: 'error', +}; + +export const CLINIC_TYPE = { + UNREGISTERED: 'UNREGISTERED', + REGISTERED: 'REGISTERED', + // SUBSCRIBED: 'SUBSCRIBED', +}; + +export const PERMISSIONS = { + CREATE_JOB_POSTING: 'CREATE_JOB_POSTING', + READ_JOB_POSTING: 'READ_JOB_POSTING', + DELETE_JOB_POSTING: 'DELETE_JOB_POSTING', + CREATE_SEARCH_CANDIDATE: 'CREATE_SEARCH_CANDIDATE', + READ_SEARCH_CANDIDATE: 'READ_SEARCH_CANDIDATE', + DELETE_SEARCH_CANDIDATE: 'DELETE_SEARCH_CANDIDATE', + CREATE_FOLDERS: 'CREATE_FOLDERS', + READ_FOLDERS: 'READ_FOLDERS', + DELETE_FOLDERS: 'DELETE_FOLDERS', + BILLING_AND_PAYMENTS: 'BILLING_AND_PAYMENTS', + CREATE_REC_USERS: 'CREATE_REC_USERS', + READ_REC_USERS: 'READ_REC_USERS', + DELETE_REC_USERS: 'DELETE_REC_USERS', +}; + +export const CLINIC_STATUS = { + ON_HOLD: 'ON_HOLD', + REJECTED: 'REJECTED', + NOT_REVIEWED: 'NOT_REVIEWED', + APPROVED: 'APPROVED', + APPROVAL_PENDING_DOCUMENT_RESUBMITTED: + 'APPROVAL_PENDING_DOCUMENT_RESUBMITTED', +}; + +export const CLINIC_DOCUMENT_STATUS = { + REJECTED: 'REJECTED', + NOT_REVIEWED: 'NOT_REVIEWED', + APPROVED: 'APPROVED', +}; + +export const STATUS = { + DRAFT: 'draft', + ACTIVE: 'active', + CLOSED: 'closed', +}; + +export const GENDER = { + FEMALE: 0, + MALE: 1, + OTHER: 2, +}; + +// Errors +export const ERRORS = { + NETWORK_ERROR: 'Network Error.', + SESSION_EXPIRED_ERROR: 'Your session has expired please login again.', +}; + +export const ADMIN_PORTALS = ['www', 'admin', 'portal ']; + +export const SUB_DOMAIN_REGEX = new RegExp( + /^([a-zA-Z0-9][a-zA-Z0-9-_]*\.)*[a-zA-Z0-9]*[a-zA-Z0-9-_]*[[a-zA-Z0-9]+$/gim +); + +export const ONLY_NUMBER_REGEX = /^[0-9]+$/; +export const WEBSITE_REGEX = + /^(https?:\/\/|ftp:\/\/|www\.)?([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}(:[0-9]+)?(\/[\w\-\.~:\/?#@!$&'()*+,;=%]*)?$/; + +export const NOT_VALID_SUB_DOMAIN_REGEX = + /subdomain\s+"[\s\S]+"(?:\s+doesn't\s+exists+\s+or+\s+it\s+is\s+in-active)/; + +export const GST_NUMBER_REGEX = + /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/; + +export const PAN_TAN_NUMBER_REGEX = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/; + +export const ONLY_ALPHA_NUMERIC_ACCEPT_REGEX = /^[0-9A-Za-z]+$/; + +export const NOT_ALLOWED_BLANK_SPACE_REGEX = /^(?! +$)[\s\S]+$/; + +export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6})$/; + +export const FILE_TYPE = [ + '.jpeg', + '.jpg', + '.png', + '.pdf', + '.svg', + '.doc', + '.docx', +]; + +export const IMAGE_TYPE = ['.jpeg', '.png', '.svg', '.jpg']; + +export const EXCEL_EXTENSION = ['.xls', '.xlsx']; + +export const ALLOWED_FILE_TYPE = [ + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/svg+xml', + 'application/pdf', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', +]; + +export const ALLOWED_IMAGE_TYPE = [ + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/svg+xml', +]; + +export const DOC_FILE_TYPE = [ + 'application/msword', + 'application/doc', + 'application/ms-doc', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', +]; + +export const EXCEL_FILE_TYPE = [ + 'application/excel', + 'application/vnd.ms-excel', + 'application/x-excel', + 'application/x-msexcel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', +]; + +export const MAX_TIP_TITLE_LENGTH = 500; +export const MAX_DESCRIPTION_LENGTH = 5000; +export const MAX_NOTIFICATION_TITLE_LENGTH = 100; +export const MAX_NOTIFICATION_CONTENT_LENGTH = 200; + +export const exceptThisSymbols = ['e', 'E', '+', '-', '.']; +export const numRegex = /^[0-9\b]+$/; + +import PdfIcon from '../assets/images/icon/pdf.png'; +import DocIcon from '../assets/images/icon/doc.png'; + +export const FILE_EXTENTIONS_ICONS = { + pdf: PdfIcon, + doc: DocIcon, + docx: DocIcon, + 'vnd.openxmlformats-officedocument.wordprocessingml.document': DocIcon, + msword: DocIcon, +}; + +export const DEBOUNCE_DELAY = 500; + +export const STUDENT_DETAILS_EXCEL_SHEET_COLUMN_ORDER = [ + { header: 'Name', key: 'name', width: 35 }, + { header: 'Program', key: 'program', width: 35 }, + { header: 'Year', key: 'year', width: 35 }, + { header: 'College', key: 'college', width: 35 }, + { header: 'Graduation', key: 'graduation', width: 35 }, + { header: 'Experience', key: 'experience', width: 35 }, + { header: 'Preferred Location', key: 'preferredLocations', width: 35 }, +]; + +export const STUDENT_DETAILS_EXCEL_SHEET = 'STUDENT_DETAILS_EXCEL_SHEET'; + +export const CANDIDATE_RESPONSE_RECORDS_SIZE = 10; + +export const CONTACT_EMAIL = 'aman.saxena@bsmail.in'; + +export const CONTACT_PHONE_NUMBER = '+917300062229'; + +export const MAX_FILES = 1; +export const MAX_FILE_SIZE_IN_MB = 5; +export const COMPANY_ABOUT_MAX_CHAR = 200; + +export const GST_NUMBER_LENGTH = 12; +export const ABN_NUMBER_LENGTH = 11; +export const MEDIICARE_NUMBER_LENGTH = 10; + +export const TO_LOCAL_STRING_LOCALS = 'hi-IN'; +export const MINIMAL_FRACTION_FOR_CURRENCY = 2; + +export const NOT_AVAILABLE_TEXT = 'NA'; + +export const PAYMENT_STATUS = { + PAYMENT_SUCCESS: 'PAYMENT_SUCCESS', + PAYMENT_FAILURE: 'PAYMENT_FAILURE', +}; + +export const FEATURES = { + SEARCH_CANDIDATE: 'SEARCH_CANDIDATE', +}; + +export const PLAN_STATUS_TYPE = { + ACTIVATED: 'ACTIVATED', + DEACTIVATED: 'DEACTIVATED', + PURCHASED: 'PURCHASED', +}; + +export const SUBSCRIPTIONS_STATUS = { + ACTIVE: 'ACTIVE', + EXPIRED: 'EXPIRED', + INACTIVE: 'INACTIVE', +}; + +export const SUBSCRIPTIONS_WARNING_LIMIT_IN_PERCENTAGE = 10; + +export const HIDE_MODULES = ['search']; + +export const FILTERS_CATEGORY = { + COLLEGE: 'college', + COURSES: 'courses', + EXPERIENCE: 'experience', + LANGUAGE: 'language', + LOCATION: 'location', + SKILLS: 'skills', +}; diff --git a/src/context/FirebaseProvider.jsx b/src/context/FirebaseProvider.jsx new file mode 100644 index 0000000..e3ee572 --- /dev/null +++ b/src/context/FirebaseProvider.jsx @@ -0,0 +1,80 @@ +import { onMessage } from 'firebase/messaging'; +import { createContext, useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { messaging } from '../config/firebase'; +import { redirectByNotificationType } from '../views/Notifications/notificationUtils'; + +// Create the context +const FirebaseContext = createContext(); + +const FirebaseProvider = (props) => { + const [notifications, setNotifications] = useState(null); + const navigate = useNavigate(); // Initialize navigation without page reload + + useEffect(() => { + checkNewMessage(messaging); + }, [messaging]); + + const showBrowserNotifications = (payload) => { + // Handle notification click + const notificationTitle = 'Clinical Notification'; // Customize the title as needed + const notificationOptions = { + body: payload.notification.body, + data: payload.data, + }; + const notification = new Notification( + notificationTitle, + notificationOptions + ); + + notification.onclick = (event) => { + event.preventDefault(); // Prevent focusing the notification's tab + + // const clickAction = + // payload?.data?.baseUrl || + // redirectByNotificationType( + // payload.data.type, + // payload?.data?.resourceId, + // true + // ); // Handle notification click + const clickAction = redirectByNotificationType( + payload.data.type, + payload?.data?.resourceId, + true + ); // Handle notification click + + // Use navigate to go to the destination without refreshing the page + if (clickAction) { + navigate(clickAction, { replace: true }); + } + }; + }; + + const checkNewMessage = (messagingInstance) => { + if (!messagingInstance) { + return; + } + + const handleForegroundNotification = (payload) => { + setNotifications(() => ({ data: payload?.data || '' })); + // Display a browser notification + if (payload.notification) { + showBrowserNotifications(payload); + } + }; + + // Listen for messages when the app is in the foreground + onMessage(messagingInstance, handleForegroundNotification); + }; + + return ( + + {props.children} + + ); +}; + +export default FirebaseProvider; + +export { FirebaseContext }; + diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..ed926f7 --- /dev/null +++ b/src/index.css @@ -0,0 +1,110 @@ +body { + margin: 0; + display: flex; +} +@font-face { + font-family: 'Inter-Black'; + src: local('Inter-Black'), + url('assets/fonts/Inter-Black.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-Bold'; + src: local('Inter-Bold'), + url('assets/fonts/Inter-Bold.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-ExtraBold'; + src: local('Inter-ExtraBold'), + url('assets/fonts/Inter-ExtraBold.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-ExtraLight'; + src: local('Inter-ExtraLight'), + url('assets/fonts/Inter-ExtraLight.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-Light'; + src: local('Inter-Light'), + url('assets/fonts/Inter-Light.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-Medium'; + src: local('Inter-Medium'), + url('assets/fonts/Inter-Medium.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-Regular'; + src: local('Inter-Regular'), + url('assets/fonts/Inter-Regular.ttf') format('truetype'); + font-stretch: normal; +} +@font-face { + font-family: 'Inter-SemiBold'; + src: local('Inter-SemiBold'), + url('assets/fonts/Inter-SemiBold.ttf') format('truetype'); +} +@font-face { + font-family: 'Inter-Thin'; + src: local('Inter-Thin'), + url('assets/fonts/Inter-Thin.ttf') format('truetype'); + font-stretch: normal; +} + +@font-face { + font-family: 'Gilroy-Bold'; + src: local('Gilroy-Bold'), + url('assets/fonts/Gilroy-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; + font-stretch: normal; +} +@font-face { + font-family: 'Gilroy-SemiBold'; + src: local('Gilroy-SemiBold'), + url('assets/fonts/Gilroy-SemiBold.ttf') format('truetype'); + font-weight: 600; + font-style: normal; + font-stretch: normal; +} +@font-face { + font-family: 'Gilroy-Medium'; + src: local('Gilroy-Medium'), + url('assets/fonts/Gilroy-Medium.ttf') format('truetype'); + font-style: normal; + font-stretch: normal; +} +@font-face { + font-family: 'Gilroy-Heavy'; + src: local('Gilroy-Heavy'), + url('assets/fonts/Gilroy-Heavy.ttf') format('truetype'); + font-style: normal; + font-stretch: normal; +} +@font-face { + font-family: 'GraphikMedium'; + src: local('GraphikMedium'), + url('assets/fonts/GraphikMedium.otf') format('truetype'); +} +div::-webkit-scrollbar { + width: 10px; + background-color: transparent; +} +div::-webkit-scrollbar-thumb { + background-color: #9c9c9c; + border-radius: 20px; +} +div::-webkit-scrollbar-thumb:hover { + background-color: #9c9c9c; +} +.socialMediaEmbed { + width: 40%; +} + +.hideScrollbar::-webkit-scrollbar { + display: none !important; + scroll-behavior: smooth !important; +} +.thumb4::-webkit-scrollbar { + width: 4px; + background-color: transparent; +} diff --git a/src/layouts/MinimalLayout/index.jsx b/src/layouts/MinimalLayout/index.jsx new file mode 100644 index 0000000..a985045 --- /dev/null +++ b/src/layouts/MinimalLayout/index.jsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { Outlet } from 'react-router-dom'; + +function MinimalLayout() { + return ; +} + +export default MinimalLayout; diff --git a/src/layouts/mainLayout/components/Header.jsx b/src/layouts/mainLayout/components/Header.jsx new file mode 100644 index 0000000..35d462e --- /dev/null +++ b/src/layouts/mainLayout/components/Header.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; +import ProfileSection from './ProfileSection'; +import { useStyles } from '../mainLayoutStyles'; +import NotificationSection from './NotificationSection '; +import { useSelector } from 'react-redux'; +import { CLINIC_STATUS } from '../../../constants'; + +function Header() { + const theme = useTheme(); + const user = useSelector((state) => state?.login?.user); + const classes = useStyles(); + + const isBelowMD = useMediaQuery((theme) => theme.breakpoints.down('md')); + + return ( + <> + + + {user?.isBsAdmin ? null : `Welcome to ${user?.company?.name}`} + + + + + + + + + + ); +} + +export default Header; diff --git a/src/layouts/mainLayout/components/MainCard.jsx b/src/layouts/mainLayout/components/MainCard.jsx new file mode 100644 index 0000000..bb6bbbf --- /dev/null +++ b/src/layouts/mainLayout/components/MainCard.jsx @@ -0,0 +1,105 @@ +import PropTypes from 'prop-types'; +import { forwardRef } from 'react'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import { + Card, + CardContent, + CardHeader, + Divider, + Typography, +} from '@mui/material'; + +// constant +const headerSX = { + '& .MuiCardHeader-action': { mr: 0 }, +}; + +// ==============================|| CUSTOM MAIN CARD ||============================== // + +// eslint-disable-next-line react/display-name +const MainCard = forwardRef( + ( + { + border = true, + boxShadow, + children, + content = true, + contentClass = '', + contentSX = {}, + darkTitle, + secondary, + shadow, + sx = {}, + title, + ...others + }, + ref + ) => { + const theme = useTheme(); + + return ( + + {/* card header and action */} + {title && ( + {title} : title + } + action={secondary} + /> + )} + + {/* content & header divider */} + {title && } + + {/* card content */} + {content && ( + + {children} + + )} + {!content && children} + + ); + } +); + +MainCard.propTypes = { + border: PropTypes.bool, + boxShadow: PropTypes.bool, + children: PropTypes.node, + content: PropTypes.bool, + contentClass: PropTypes.string, + contentSX: PropTypes.object, + darkTitle: PropTypes.bool, + secondary: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.string, + PropTypes.object, + ]), + shadow: PropTypes.string, + sx: PropTypes.object, + title: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.string, + PropTypes.object, + ]), +}; + +export default MainCard; diff --git a/src/layouts/mainLayout/components/NotificationSection .jsx b/src/layouts/mainLayout/components/NotificationSection .jsx new file mode 100644 index 0000000..f726502 --- /dev/null +++ b/src/layouts/mainLayout/components/NotificationSection .jsx @@ -0,0 +1,436 @@ +import React, { + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { + Box, + CircularProgress, + ClickAwayListener, + Divider, + Grid, + Paper, + Popper, + Typography, +} from "@mui/material"; +import { useTheme } from "@mui/material/styles"; +import notificationIcon from "../../../assets/images/icon/Actions.svg"; +import unreadBadgeIcon from "../../../assets/images/icon/UnreadBadge.svg"; +import bellIcon from "../../../assets/images/icon/bell.svg"; +import { useStyles } from "../mainLayoutStyles"; +import Transitions from "./Transitions"; +import MainCard from "./MainCard"; +import { useNavigate } from "react-router-dom"; +import InfiniteScroll from "react-infinite-scroll-component"; +import { + getNotifications, + markReadNotification, +} from "../../../services/notification.service"; +import { format, formatDistanceToNow } from "date-fns"; +import { FirebaseContext } from "../../../context/FirebaseProvider"; +import { redirectByNotificationType } from "../../../views/Notifications/notificationUtils"; + +const NotificationSection = () => { + const classes = useStyles(); + const theme = useTheme(); + const { notifications, setNotifications } = useContext(FirebaseContext); + const [isUnreadInfiniteLoading, setIsUnreadInfiniteLoading] = useState(false); + const [isRecentInfiniteLoading, setIsRecentInfiniteLoading] = useState(false); + const [open, setOpen] = useState(false); + const anchorRef = useRef(null); + const navigate = useNavigate(); + const [unreadNotifications, setUnreadNotifications] = useState([]); + const [unreadNotificationsPage, setUnreadNotificationsPage] = useState(0); + const [unreadNotificationsTotalCount, setUnreadNotificationsTotalCount] = + useState(0); + const [recentNotifications, setRecentNotifications] = useState([]); + const [recentNotificationsPage, setRecentNotificationsPage] = useState(0); + const [recentNotificationsTotalCount, setRecentNotificationsTotalCount] = + useState(0); + + const getUnreadNotifications = async (page = unreadNotificationsPage) => { + try { + setIsUnreadInfiniteLoading(true); + const response = await getNotifications(true, page); + if ( + response.status === 200 && + response?.data?.data?.notificationList?.length + ) { + setUnreadNotifications((notifications) => [ + ...notifications, + ...response?.data?.data?.notificationList, + ]); + setUnreadNotificationsTotalCount(response?.data?.data?.totalCount); + setUnreadNotificationsPage((page) => page + 1); + } + } catch (err) { + // eslint-disable-next-line no-console + console.log("Error::>", err); + } finally { + setIsUnreadInfiniteLoading(false); + } + }; + const getRecentNotifications = async (page = recentNotificationsPage) => { + try { + setIsRecentInfiniteLoading(true); + const response = await getNotifications(false, page); + if ( + response.status === 200 && + response?.data?.data?.notificationList?.length + ) { + setRecentNotifications((notifications) => [ + ...notifications, + ...response?.data?.data?.notificationList, + ]); + setRecentNotificationsTotalCount(response?.data?.data?.totalCount); + setRecentNotificationsPage((page) => page + 1); + } + } catch (err) { + // eslint-disable-next-line no-console + console.log("Error::>", err); + } finally { + setIsRecentInfiniteLoading(false); + } + }; + + const handleClose = useCallback((event) => { + if (anchorRef.current && anchorRef.current.contains(event.target)) { + return; + } + setOpen(false); + }, []); + + const setDefault = () => { + setRecentNotifications(() => []); + setUnreadNotifications(() => []); + setRecentNotificationsPage(0); + setUnreadNotificationsPage(0); + getUnreadNotifications(0); + getRecentNotifications(0); + setRecentNotificationsTotalCount(0); + setUnreadNotificationsTotalCount(0); + }; + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + useEffect(() => { + if (!open && !notifications) { + anchorRef.current?.focus(); + setDefault(); + } + setNotifications(() => null); + }, [open]); + + useEffect(() => { + if (notifications) { + setDefault(); + } + }, [notifications]); + + const markTheNotificationAsRead = async (ids) => { + try { + setIsUnreadInfiniteLoading(true); + await markReadNotification(ids); + setDefault(); + setNotifications(() => null); + } catch (err) { + // eslint-disable-next-line no-console + console.log("Error::>>", err); + } finally { + setIsUnreadInfiniteLoading(false); + } + }; + + const handleRouteClick = (type, resourceId, id, isRecent) => { + if (!isRecent) { + markTheNotificationAsRead([id]); + } + navigate(redirectByNotificationType(type, resourceId, true), { + replace: true, + }); + handleToggle(); + }; + + const renderNotification = ( + notification, + index, + lastNotification = false, + isRecent = false + ) => ( + + + handleRouteClick( + notification?.notificationType, + notification?.resourceId, + notification?.id, + isRecent + ) + } + className={isRecent ? classes.recentListOuterDiv : classes.listOuterDiv} + > + + + + + unreadBadgeIcon + + + + bell + + + + + + {notification?.message?.subText} + + + + + + + {isRecent + ? format(new Date(notification.createdAt), "MMM d, yyyy") + : formatDistanceToNow(new Date(notification.createdAt), { + addSuffix: true, + }).replace("about ", "")} + + + + + {!lastNotification && } + + ); + + return ( + <> + + + notificationIcon + {(unreadNotificationsTotalCount > 0 || + unreadNotifications?.length > 0) && ( +
    + )} +
    +
    + + {({ TransitionProps }) => ( + + + + + + + + + Notifications + + + + + + + Unread + + + 0 + ? markTheNotificationAsRead + : () => {} + } + className={classes.markAsReadDiv} + sx={ + unreadNotificationsTotalCount === 0 + ? { + opacity: 0.5, + cursor: "not-allowed !important", + } + : {} + } + > + + Mark all as read + + + + + {})} + hasMore={ + unreadNotificationsTotalCount > + unreadNotifications?.length || false + } + scrollThreshold={0.8} + loader={ + isUnreadInfiniteLoading && ( + + ) + } + scrollableTarget="scrollableDivOfUnreadNotification" + > + {unreadNotifications?.map((notification, index) => + renderNotification( + notification, + index, + index === unreadNotifications?.length - 1 + ) + )} + + + + + Recent + + + + {})} + hasMore={ + recentNotificationsTotalCount > + recentNotifications?.length || false + } + scrollThreshold={0.8} + loader={ + isRecentInfiniteLoading && ( + + ) + } + scrollableTarget="scrollableDivOfRecentNotification" + > + {recentNotifications?.map((notification, index) => + renderNotification( + notification, + index, + index === recentNotifications?.length - 1, + true + ) + )} + + + + + + + + + )} + + + ); +}; + +export default NotificationSection; diff --git a/src/layouts/mainLayout/components/ProfileSection.jsx b/src/layouts/mainLayout/components/ProfileSection.jsx new file mode 100644 index 0000000..29b8d19 --- /dev/null +++ b/src/layouts/mainLayout/components/ProfileSection.jsx @@ -0,0 +1,198 @@ +import React, { useEffect, useRef, useState } from 'react'; + +// material-ui +import { + Box, + ClickAwayListener, + Divider, + List, + ListItem, + ListItemAvatar, + ListItemText, + Paper, + Popper, +} from '@mui/material'; +import { useTheme } from '@mui/material/styles'; + +// assets +import signoutImg from '../../../assets/images/icon/signout.svg'; + +import { useStyles } from '../mainLayoutStyles'; +import MainCard from './MainCard'; +import Transitions from './Transitions'; +import { useNavigate } from 'react-router-dom'; +import { commonLogoutFunc } from '../../../utils/share'; +import defaultProfilePicture from '../../../assets/images/icon/defaultProfileIcon.svg'; +import { useSelector } from 'react-redux'; + +// ==============================|| PROFILE MENU ||============================== // + +const ProfileSection = () => { + const classes = useStyles(); + const theme = useTheme(); + const navigate = useNavigate(); + const [open, setOpen] = useState(false); + const [showPopup, setShowPopup] = useState(false); + // const [showTransactionHistoryPopup, setShowTransactionHistoryPopup] = + useState(false); + const anchorRef = useRef(null); + + const user = useSelector((state) => state?.login?.user); + const isBsAdmin = user?.isBsAdmin; + + const companyStatus = useSelector( + (state) => state?.login?.user?.company?.status + ); + + const handleClose = (event) => { + if (anchorRef.current && anchorRef.current.contains(event.target)) { + return; + } + setOpen(false); + }; + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + useEffect(() => { + const prevOpen = open; + return () => { + if (prevOpen && !open) { + anchorRef.current.focus(); + } + }; + }, [open]); + + const menuItems = [ + + { id: 5, img: signoutImg, text: 'Sign Out', alt: 'signoutImg' }, + ].filter(Boolean); + + const renderProfile = (item, index) => ( + + { + <> + handleMenuItemClick(item)}> + + {item.alt} + + + + {index === menuItems.length - 2 && ( + + )} + + } + + ); + + const handleMenuItemClick = (item) => { + switch (item.id) { + case 1: + navigate('/profile-settings'); + break; + case 2: + navigate(`/profile`); + break; + case 3: + // setShowTransactionHistoryPopup(true); + break; + case 4: + setShowPopup(true); + break; + case 5: + commonLogoutFunc(); + break; + default: + break; + } + setOpen(false); + }; + + // const handleTransactionHistoryPopupClose = () => { + // setShowTransactionHistoryPopup(false); + // }; + return ( + <> + + userAvatar +
    +
    + + {({ TransitionProps }) => ( + + + + + + + + userAvatar + + + + + {menuItems.map((item, index) => renderProfile(item, index))} + + + + + + )} + + {/* {showPopup && } */} + {/* {showTransactionHistoryPopup && ( + + )} */} + + ); +}; + +export default ProfileSection; diff --git a/src/layouts/mainLayout/components/Sidebar.jsx b/src/layouts/mainLayout/components/Sidebar.jsx new file mode 100644 index 0000000..56ef306 --- /dev/null +++ b/src/layouts/mainLayout/components/Sidebar.jsx @@ -0,0 +1,355 @@ +/* eslint-disable no-unused-vars */ +import CloseIcon from "@mui/icons-material/Close"; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + List, + ListItem, + ListItemText, +} from "@mui/material"; +import React, { useEffect, useState } from "react"; +import { NavLink, useLocation } from "react-router-dom"; + +/* ------------------- Custom Imports ------------------- */ +import { useDispatch, useSelector } from "react-redux"; +import arrowDownImg from "../../../assets/images/icon/arrow-down.svg"; // Make sure this path is correct +import companyLogo from "../../../assets/images/logo/app.svg"; // Make sure this path is correct +import { HIDE_FUNCTIONALITY } from "../../../common/envVariables"; +import { CLINIC_STATUS, HIDE_MODULES } from "../../../constants"; +import { isBSPortal } from "../../../utils/share"; +import { hideAndShowFunctionality } from "../../../views/Signup/signupAction"; +import { useStyles } from "../mainLayoutStyles"; + +// Import the configuration from the separate file +import { SIDEBAR_CONFIG } from "./sideBarConfig"; // Adjust path if necessary + +// NOTE: Removed the internal SIDEBAR_ITEMS definition. +// We will now use the imported SIDEBAR_CONFIG. + +// NOTE: Removed direct icon imports as they should come from SIDEBAR_CONFIG +// Ensure @mui/icons-material is installed and icons are correctly referenced in sideBarConfig.js + +const Sidebar = ({ onClose, showCloseIcon }) => { + const classes = useStyles(); + const location = useLocation(); + const { isBSAdmin } = isBSPortal(); + const [activeLink, setActiveLink] = useState("Dashboard"); + const [accordianActiveLink, setAccordianActiveLink] = useState("All Jobs"); + const [parentRoute, setParentRoute] = useState(""); + const [childRoute, setchildRoute] = useState(""); + const [combinedRoute, setcombinedRoute] = useState(""); + const dispatch = useDispatch(); + + // Assuming companyStatus is fetched or defined elsewhere correctly + const companyStatus = "APPROVED"; // Example status, replace with actual logic if needed + + const handleHideFeatures = (hideFeatures) => { + if (hideFeatures) { + dispatch(hideAndShowFunctionality({ hideFeatures })); + } + }; + + useEffect(() => { + const { pathname } = location; + // Ensure pathname splitting is safe + const pathParts = pathname?.split?.("/") ?? []; + const activeLinkFromPath = pathParts[1] ?? ""; // Default to empty string if no segment + const accordianActiveLinkFromPath = pathParts[2]; // Can be undefined + + if (accordianActiveLinkFromPath) { + setParentRoute(activeLinkFromPath); + setchildRoute(accordianActiveLinkFromPath); + setcombinedRoute(`${activeLinkFromPath}/${accordianActiveLinkFromPath}`); + } else { + setParentRoute(""); // Reset if no child route + setchildRoute(""); + setcombinedRoute(""); + } + + // Set active link based on the first path segment, default to '' which matches Dashboard path + setActiveLink(activeLinkFromPath || ""); // Use '' for Dashboard path + // Set accordion active link based on the full path segment if available + setAccordianActiveLink(activeLinkFromPath ? `${activeLinkFromPath}` : ""); + }, [location]); + + // Get companyId at the component level + const companyId = useSelector( + (state) => state?.loginAsCompanyAdmin?.companyId // Make sure this selector path is correct + ); + + // Get current user role from Redux store + const userRole = useSelector((state) => state.userRole.role); + + // Function to determine visibility and render a single sidebar item link + const checkVisibility = (item, i) => { + // Check if the user has the necessary role for this item + const hasRole = !item.roles || item.roles.includes(userRole); + + // Determine if the feature related to this item should be hidden + let hideFeature = + (companyStatus === CLINIC_STATUS.APPROVED && + HIDE_FUNCTIONALITY && + HIDE_MODULES.includes(item?.path)) || + (isBSAdmin && HIDE_FUNCTIONALITY && HIDE_MODULES.includes(item?.path)); + + // Only render if user has the required role + if (hasRole) { + // Determine if the link should be disabled + const isDisabled = + (!isBSAdmin && companyStatus !== CLINIC_STATUS.APPROVED) || hideFeature; + const targetPath = isDisabled ? "#" : `/${item.path}`; + const isActive = activeLink === item.path; // Check if this link is the active one + + return ( + handleHideFeatures(hideFeature)} // This seems odd, clicking the box hides features? Review this logic. + key={item.path || i} // Use a stable key like item.path + > + { + if (isDisabled) e.preventDefault(); + handleLinkClick(item.path); // Set active link on click + }} + > + + + {isActive + ? React.createElement(item.activeIcon, { + className: classes.sidebarLinkIcons, + }) + : React.createElement(item.icon, { + className: classes.sidebarLinkIcons, + style: { color: "black" }, + })} + + + + + + ); + } + return null; // Return null if item shouldn't be rendered + }; + + // Function to render a sidebar item that has children (accordion) + const rendersideMenu = (item, i) => { + // Check if the parent item has the necessary role + const hasParentRole = !item.roles || item.roles.includes(userRole); + + // Only proceed if the parent item has the necessary role + if (!hasParentRole) return null; + + // Filter visible children first + const visibleChildren = item.children + ?.filter((childItem) => { + // Check if child has the necessary role + const hasChildRole = !childItem.roles || childItem.roles.includes(userRole); + return hasChildRole; + }) + .filter(Boolean); // Filter out any null/undefined results + + // Only render the accordion if there are visible children + if (!visibleChildren || visibleChildren.length === 0) { + return null; + } + + // Check if the current parent route matches this accordion item's path + const isParentActive = + activeLink === parentRoute && item.path === parentRoute; + + return ( + + {" "} + {/* Use stable key */} + { + // // Handle accordion expansion logic if needed, e.g., update parentRoute + // // setParentRoute(isExpanded ? item.path : ""); + // }} + > + + Expand +
    + } + // Clicking the summary might navigate or just expand/collapse + // Consider if clicking the summary should set the activeLink or parentRoute + onClick={() => { + // Toggle parent route or handle navigation + // If clicking the parent should navigate, handle it here + // If it just expands/collapses, maybe update parentRoute state + setParentRoute(item.path === parentRoute ? "" : item.path); // Basic toggle example + }} + > + + {/* Use the helper function to render the icon */} + {activeLink === item.path + ? React.createElement(item.activeIcon, { + className: classes.sidebarLinkIcons, + }) + : React.createElement(item.icon, { + className: classes.sidebarLinkIcons, + style: { color: "black" }, + })} + + {/* Removed redundant Box wrapping ListItem */} + + + + + {/* Removed unnecessary
      */} + {visibleChildren.map((subItem, subIndex) => { + // Determine if the sub-item link should be disabled + const isSubDisabled = + !isBSAdmin && companyStatus !== CLINIC_STATUS.APPROVED; // Add hideFeature logic if needed for sub-items + const subTargetPath = isSubDisabled ? "#" : `/${subItem.path}`; + const isSubActive = combinedRoute === subItem.path; // Check if this child link is active + + return ( + { + if (isSubDisabled) e.preventDefault(); + // Set parent and child routes on click + setParentRoute(item.path); // Ensure parent is marked active + setchildRoute(subItem.path.split("/")[1]); // Extract child part + handleAccordianLinkClick(subItem.path); // Update combined route state + }} + > + {/* Use ListItem for better structure and accessibility */} + +
      + +
      +
      + ); + })} + {/* Removed unnecessary
    */} +
    +
    + + + ); + }; + + const closeSidebar = () => { + if (onClose) { + // Check if onClose is provided + onClose(); + } + }; + + // Sets the main active link (top-level or parent of accordion) + const handleLinkClick = (linkPath) => { + // If the clicked link is part of an accordion, don't reset parentRoute + const clickedItem = SIDEBAR_CONFIG.find((item) => item.path === linkPath); + if (!clickedItem?.children) { + setParentRoute(""); // Reset parent route if it's a top-level link + setchildRoute(""); // Reset child route + setcombinedRoute(""); // Reset combined route + } + setActiveLink(linkPath); + }; + + // Sets the active state for accordion children links + const handleAccordianLinkClick = (fullPath) => { + // Accordion link click implies a parent is active + // We already set parentRoute and childRoute in the NavLink onClick + setcombinedRoute(fullPath); // Update the combined route state + // Optionally update activeLink to the parent path if needed for styling + // setActiveLink(fullPath.split('/')[0]); + }; + + return ( + + {/* Only show close icon if prop is true */} + {showCloseIcon && ( + + {" "} + {/* Added some padding/alignment */} + + + )} + + {/* Ensure companyLogo is imported correctly and alt text is descriptive */} + Company Logo + + {/* Use the imported SIDEBAR_CONFIG */} + + {SIDEBAR_CONFIG?.map((item, i) => + // Render either an accordion menu or a direct link + item.children ? rendersideMenu(item, i) : checkVisibility(item, i) + )} + + + ); +}; + +export default Sidebar; diff --git a/src/layouts/mainLayout/components/TransactionHistoryPopup.jsx b/src/layouts/mainLayout/components/TransactionHistoryPopup.jsx new file mode 100644 index 0000000..786cd85 --- /dev/null +++ b/src/layouts/mainLayout/components/TransactionHistoryPopup.jsx @@ -0,0 +1,218 @@ +import { Box, Chip, CircularProgress } from '@mui/material'; +import React, { useEffect, useMemo, useState } from 'react'; +import CustomModal from '../../../views/Modal/Modal'; +import SimpleDataTable from '../../../components/SimpleDataTable'; +import { useStyles } from './transactionHistoryPopupStyles'; +import downloadIcon from '../../../assets/images/icon/downloadreciept.svg'; +import pdfIcon from '../../../assets/images/icon/pdfIcon.svg'; +import { NOTIFICATION, PAYMENT_STATUS } from '../../../constants'; +import { getTransactionHistory } from '../../../services/transaction.history.services'; +import { format } from 'date-fns'; +import { fileDownloadUsingUrl } from '../../../utils/share'; +import { pdf } from '@react-pdf/renderer'; +import ReceiptDownload from '../../../views/ReceiptDownload'; +import { pushNotification } from '../../../utils/notification'; + +const statusColorMap = { + [PAYMENT_STATUS.PAYMENT_SUCCESS]: { + color: '#12B76A', + backgroundColor: '#ECFDF3', + }, + [PAYMENT_STATUS.PAYMENT_FAILURE]: { + color: '#D6313A', + backgroundColor: '#FFF0F1', + }, +}; + +function TransactionHistoryPopup({ handleTransactionHistoryPopupClose }) { + const classes = useStyles(); + const [transactionHistoryData, setTransactionHistoryData] = useState(); + const [companyDetails, setCompanyDetails] = useState(); + const [downloadingReceiptId, setDownloadingReceiptId] = useState(null); + const [loading, setLoading] = useState(false); + + const fetchTransactionHistory = async () => { + try { + setLoading(true); + const response = await getTransactionHistory(); + setTransactionHistoryData(response?.data?.data?.records); + setCompanyDetails(response?.data?.data?.companyDetails); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchTransactionHistory(); + }, []); + + const handleReceiptDownload = async (row) => { + setDownloadingReceiptId(row.id); + try { + const MyDocument = ( + + ); + + const blob = await pdf(MyDocument).toBlob(); + const url = URL.createObjectURL(blob); + + fileDownloadUsingUrl(url, `receipt_${row.transactionId || ''}.pdf`); + + URL.revokeObjectURL(url); + } catch (error) { + pushNotification('Error while downloading receipt', NOTIFICATION.ERROR); + } finally { + setDownloadingReceiptId(null); + } + }; + + const columns = useMemo( + () => [ + { + accessorKey: 'transactionId', + header: 'Transaction ID', + minWidth: 140, + isBold: true, + }, + { + accessorKey: 'createdAt', + header: 'Trantaction Date', + minWidth: 160, + Cell: ({ row }) => ( +
    {format(new Date(row?.createdAt), 'dd MMM yyyy')}
    + ), + }, + { + accessorKey: 'amount', + header: 'Amount', + Cell: ({ row }) =>
    ₹{row?.amount}
    , + }, + { + accessorKey: 'paymentMode', + header: 'Payment Mode', + minWidth: 160, + Cell: ({ row }) => ( + <> +
    + {row?.paymentMode?.toUpperCase()} +
    + + ), + }, + { + accessorKey: 'status', + header: 'Status', + minWidth: 140, + align: 'center', + Cell: ({ row }) => ( + + {/* {capitalizeFirstLetter(row.original?.status)} */} + {row?.status === PAYMENT_STATUS.PAYMENT_SUCCESS + ? 'Payment Done' + : 'Payment Error'} + + } + style={{ + backgroundColor: + row?.status in statusColorMap + ? statusColorMap[row?.status].backgroundColor + : '#FFFFFF', + color: + row?.status in statusColorMap + ? statusColorMap[row?.status].color + : '#000000', + }} + /> + ), + }, + { + accessorKey: 'paymentReceipt', + header: 'Receipt', + align: 'center', + width: 140, + Cell: ({ row }) => ( +
    + {downloadingReceiptId === row.id ? ( +
    + +
    + ) : ( + row.paymentMode !== 'Offline' && ( +
    handleReceiptDownload(row)} + > + Download receipt +
    + ) + )} +
    + ), + }, + { + accessorKey: 'gstInvoice', + header: 'GST Invoice', + align: 'center', + minWidth: 160, + Cell: ({ row }) => ( +
    + {row?.gstInvoice ? ( +
    handleGstDownload(row)} + > + +
    + ) : ( + '' + )} +
    + ), + }, + ], + [classes, companyDetails, transactionHistoryData, downloadingReceiptId] + ); + + const handleGstDownload = (row) => { + fileDownloadUsingUrl(row.gstInvoice, `GST_Invoice_${row.transactionId}`); + }; + + return ( + ( + + + + )} + /> + ); +} + +export default TransactionHistoryPopup; diff --git a/src/layouts/mainLayout/components/Transitions.jsx b/src/layouts/mainLayout/components/Transitions.jsx new file mode 100644 index 0000000..c55516b --- /dev/null +++ b/src/layouts/mainLayout/components/Transitions.jsx @@ -0,0 +1,117 @@ +import PropTypes from 'prop-types'; +import { forwardRef } from 'react'; + +// material-ui +import { Collapse, Fade, Box, Grow, Slide, Zoom } from '@mui/material'; + +// ==============================|| TRANSITIONS ||============================== // + +// eslint-disable-next-line react/display-name +const Transitions = forwardRef( + ({ children, position, type, direction, ...others }, ref) => { + let positionSX = { + transformOrigin: '0 0 0', + }; + + switch (position) { + case 'top-right': + positionSX = { + transformOrigin: 'top right', + }; + break; + case 'top': + positionSX = { + transformOrigin: 'top', + }; + break; + case 'bottom-left': + positionSX = { + transformOrigin: 'bottom left', + }; + break; + case 'bottom-right': + positionSX = { + transformOrigin: 'bottom right', + }; + break; + case 'bottom': + positionSX = { + transformOrigin: 'bottom', + }; + break; + case 'top-left': + default: + positionSX = { + transformOrigin: '0 0 0', + }; + break; + } + + return ( + + {type === 'grow' && ( + + {children} + + )} + {type === 'collapse' && ( + + {children} + + )} + {type === 'fade' && ( + + {children} + + )} + {type === 'slide' && ( + + {children} + + )} + {type === 'zoom' && ( + + {children} + + )} + + ); + } +); + +Transitions.propTypes = { + children: PropTypes.node, + type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']), + position: PropTypes.oneOf([ + 'top-left', + 'top-right', + 'top', + 'bottom-left', + 'bottom-right', + 'bottom', + ]), + direction: PropTypes.oneOf(['up', 'down', 'left', 'right']), +}; + +Transitions.defaultProps = { + type: 'grow', + position: 'top-left', + direction: 'up', +}; + +export default Transitions; diff --git a/src/layouts/mainLayout/components/sideBarConfig.js b/src/layouts/mainLayout/components/sideBarConfig.js new file mode 100644 index 0000000..6c0593b --- /dev/null +++ b/src/layouts/mainLayout/components/sideBarConfig.js @@ -0,0 +1,70 @@ +// MUI Icons imports +import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; +import HomeIcon from '@mui/icons-material/Home'; +import LocalHospitalOutlinedIcon from '@mui/icons-material/LocalHospitalOutlined'; +import LocalHospitalIcon from '@mui/icons-material/LocalHospital'; +import PeopleOutlineIcon from '@mui/icons-material/PeopleOutline'; +import PeopleIcon from '@mui/icons-material/People'; +import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; +import SettingsIcon from '@mui/icons-material/Settings'; +import MedicationIcon from '@mui/icons-material/Medication'; +import MedicationOutlinedIcon from '@mui/icons-material/MedicationOutlined'; +import ArticleIcon from '@mui/icons-material/Article'; +import ArticleOutlinedIcon from '@mui/icons-material/ArticleOutlined'; + +import { USER_ROLES } from '../../../redux/userRoleSlice'; + +// Define the sidebar configuration with proper permission fields +export const SIDEBAR_CONFIG = [ + { + text: 'Dashboard', + path: '', + icon: HomeOutlinedIcon, + activeIcon: HomeIcon, + // Dashboard is accessible to all roles + roles: [USER_ROLES.SUPER_ADMIN, USER_ROLES.CLINIC_ADMIN] + }, + { + text: 'Clinics List', + path: 'clinics', + icon: LocalHospitalOutlinedIcon, + activeIcon: LocalHospitalIcon, + // Only super admin can access clinics list + roles: [USER_ROLES.SUPER_ADMIN] + }, + { + text: 'Admin Staff Management', + path: 'admin', + icon: PeopleOutlineIcon, + activeIcon: PeopleIcon, + // Only super admin can access admin staff management + roles: [USER_ROLES.SUPER_ADMIN] + }, + { + text: 'Doctor/Nurse Management', + path: 'doctor', + requireSaprateApp: false, + icon: PeopleOutlineIcon, + activeIcon: PeopleIcon, + // Clinic admin can access user management + roles: [USER_ROLES.CLINIC_ADMIN] + }, + { + text: 'Clinic Setup', + path: 'clinicSetup', + requireSaprateApp: false, + icon: SettingsOutlinedIcon, + activeIcon: SettingsIcon, + // Clinic admin can access clinic setup + roles: [USER_ROLES.CLINIC_ADMIN] + }, + { + text: 'Call Transcripts', + path: 'transcripts', + requireSaprateApp: false, + icon: ArticleOutlinedIcon, + activeIcon: ArticleIcon, + // Clinic admin can access call transcripts + roles: [USER_ROLES.CLINIC_ADMIN] + }, +]; diff --git a/src/layouts/mainLayout/components/transactionHistoryPopupStyles.js b/src/layouts/mainLayout/components/transactionHistoryPopupStyles.js new file mode 100644 index 0000000..87914c2 --- /dev/null +++ b/src/layouts/mainLayout/components/transactionHistoryPopupStyles.js @@ -0,0 +1,41 @@ +import { makeStyles } from '@mui/styles'; + +export const useStyles = makeStyles((theme) => ({ + transactionHistoryTableWrapper: { + marginTop: theme.spacing(2.0), + }, + receipt: { + width: '80%', + margin: 'auto', + cursor: 'pointer', + }, + receiptOuter: { + border: `1px solid ${theme.palette.grey[29]}`, + borderRadius: '6px', + padding: `${theme.spacing(0.7)} ${theme.spacing(0.5)}`, + display: 'flex', + justifyContent: 'center', + cursor: 'pointer', + }, + receiptIcon: { + width: '80%', + }, + gst: { + width: '30%', + margin: 'auto', + }, + gstOuter: { + border: `1px solid ${theme.palette.grey[29]}`, + borderRadius: '6px', + padding: `${theme.spacing(0.5)} ${theme.spacing(0.1)}`, + display: 'flex', + justifyContent: 'center', + cursor: 'pointer', + }, + gstIcon: { + width: '50%', + }, + paymentText: { + textAlign: 'center', + }, +})); diff --git a/src/layouts/mainLayout/index.jsx b/src/layouts/mainLayout/index.jsx new file mode 100644 index 0000000..c238f48 --- /dev/null +++ b/src/layouts/mainLayout/index.jsx @@ -0,0 +1,162 @@ +import MenuIcon from '@mui/icons-material/Menu'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Divider, + Grid, + IconButton, + Typography, + useMediaQuery, +} from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Outlet } from 'react-router-dom'; +import notAvailableForNow from '../../assets/images/icon/waiting.svg'; +import { calculateDaysLeft } from '../../utils/calculateDaysLeft'; +import UpgradeYourPlan from '../../views/GlobalPopups/UpgradeYourPlan'; +import CustomModal from '../../views/Modal/Modal'; +import { hideAndShowFunctionality } from '../../views/Signup/signupAction'; +import Header from './components/Header'; +import Sidebar from './components/Sidebar'; +import { useStyles } from './mainLayoutStyles'; + +function MainLayout() { + const classes = useStyles(); + const [sidebarOpen, setSidebarOpen] = useState(true); + const [alertInfo, setAlertInfo] = useState({ message: '', show: false }); + const dispatch = useDispatch(); + + const hideFeature = useSelector((state) => state?.signup?.hideFeatures); + + const planDetails = useSelector((state) => state?.login?.user?.account); + const availableJobPostings = useSelector( + (state) => state?.login?.user?.account?.maxJobPostings + ); + + const planDaysLeft = planDetails + ? calculateDaysLeft(planDetails.endTime) + : 'N/A'; + const totalPlanDays = useSelector( + (state) => state?.login?.user?.lastPurchasedPlan?.plan?.validityInDays + ); + + const isBelowMD = useMediaQuery((theme) => theme.breakpoints.down('md')); + useEffect(() => { + setSidebarOpen(!isBelowMD); + }, [isBelowMD]); + + useEffect(() => { + // Condition 1: Plan expiring in 10 days + if (planDaysLeft <= totalPlanDays * 0.1) { + setAlertInfo({ + message: `Your current plan is about to reach its expiration date. To continue + to enjoy BSmart update your plan.`, + show: true, + }); + } else { + setAlertInfo({ message: '', show: false }); + } + }, [ + planDaysLeft, + totalPlanDays, + availableJobPostings, + ]); + + const toggleSidebar = () => setSidebarOpen((prev) => !prev); + + const handleHideFeatures = () => { + dispatch(hideAndShowFunctionality({ hideFeatures: false })); + }; + + return ( + <> + {/* {} */} + + + {sidebarOpen && ( + + + + )} + + + + +
    + {isBelowMD && ( + + + + )} + + + + + + + + + + + + + {hideFeature && ( + ( + + + wait for functionality + + + Note! + + + + This section will launch soon. + + + Okay + + + )} + /> + )} + + ); +} + +export default MainLayout; diff --git a/src/layouts/mainLayout/mainLayoutStyles.js b/src/layouts/mainLayout/mainLayoutStyles.js new file mode 100644 index 0000000..f04aa83 --- /dev/null +++ b/src/layouts/mainLayout/mainLayoutStyles.js @@ -0,0 +1,510 @@ +import { makeStyles } from '@mui/styles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + root: { + fontFamily: 'Inter-Regular', + overflowX: 'visible', + [theme.breakpoints.down('md')]: { + overflowX: 'hidden', + }, + }, + contentBox: { + marginTop: 0, + overflow: 'auto', + height: '80vh', + paddingLeft: theme.spacing(4.0), + paddingRight: theme.spacing(4.0), + }, + contentBoxMobile: { + marginTop: 0, + paddingLeft: theme.spacing(1.0), + paddingRight: theme.spacing(1.0), + }, + mainContent: { + margin: theme.spacing(0), + width: '100%', + overflowX: 'scroll', + '&::-webkit-scrollbar': { + height: '6px', + }, + }, + sidebarMain: { + overflow: 'auto', + backgroundColor: theme.palette.grey[0], + // width: '220px', + paddingTop: theme.spacing(3.5), + color: theme.palette.grey[0], + height: '100vh', + // boxShadow: '10px 0 10px -3px rgba(230,230,230,0.75)', + }, + profileCardfirstTile: { + paddingTop: theme.spacing(0), + }, + sidebar: { + position: 'sticky', + width: '220px', + height: '100vh', + zIndex: 99, + flexShrink: 0, + backgroundColor: theme.palette.grey[19], + boxShadow: '10px 0 10px -3px rgba(230,230,230,0.75)', + [theme.breakpoints.down('md')]: { + position: 'fixed', + }, + }, + listMainDiv: { + padding: theme.spacing(1.8), + color: theme.palette.grey[0], + }, + sidebarLinkIcons: { + display: 'block', + maxWidth: '230px', + maxHeight: '95px', + width: 'auto', + // height: 'auto', + }, + redDot: { + width: '7px', + height: '7px', + backgroundColor: theme.palette.grey[0], + borderRadius: '50%', + display: 'inline-block', + }, + listItem: { + color: theme.palette.grey[0], + display: 'flex', + overflowWrap: 'break-word', + }, + sidebarListImg: { + marginLeft: theme.spacing(1.0), + marginRight: theme.spacing(2.0), + alignSelf: 'center', + }, + activeLink: { + backgroundColor: theme.palette.primary.main, + paddingTop: theme.spacing(0.2), + paddingBottom: theme.spacing(0.2), + color: theme.palette.grey[0], + borderRadius: '74px', + '&:hover': { + backgroundColor: theme.palette.primary.main, + }, + }, + activeLinkTextColor: { + color: theme.palette.grey[0], + }, + normalLinkTextColor: { + color: theme.palette.grey[10], + }, + activeLinkChildrenBackgroundColor: { + backgroundColor: theme.palette.primary.main, + }, + normalLinkChildrenBackgroundColor: { + backgroundColor: theme.palette.grey[10], + }, + activeLinkChildrenTextColor: { + color: theme.palette.primary.main, + }, + normalLinkChildrenTextColor: { + color: theme.palette.grey[10], + }, + header: { + paddingLeft: theme.spacing(4.0), + paddingRight: theme.spacing(4.0), + }, + headerMobile: { + paddingLeft: theme.spacing(1.0), + paddingRight: theme.spacing(1.0), + }, + companyImgDiv: { + display: 'flex', + justifyContent: 'center', + alignContent: 'center', + marginBottom: theme.spacing(3.0), + }, + companyImg: { + width: '180px', + minWidth: '130px', + }, + drawerPaper: { + width: '220px', + marginTop: theme.spacing(6.3), + background: theme.palette.background.sidebar, + color: theme.palette.common.white, + paddingBottom: theme.spacing(7), + overflowY: 'scroll', + '&::-webkit-scrollbar': { + width: 0, + }, + }, + routeList: { + paddingTop: theme.spacing(3.2), + }, + primaryTypographyProps: { + '&.MuiTypography-body1': { + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.medium, + color: theme.palette.common.white, + letterSpacing: pxToRem(0), + }, + color: theme.palette.common.white, + fontFamily: theme.fontFamily.medium, + fontSize: pxToRem(14), + }, + profileSection: { + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + cursor: 'pointer', + position: 'relative', + }, + sidebarRouter: { + textDecoration: 'none', + }, + activeSidebarRouter: { + textDecoration: 'none', + '& .MuiListItem-root': { + background: theme.palette.grey[16], + }, + }, + childActiveSidebarRouter: { + textDecoration: 'none', + '& .MuiListItem-root': { + background: theme.palette.grey[17], + }, + }, + accordionDiv: { + '&:before': { + display: 'none', + }, + }, + accordionSummary: { + paddingRight: '20px', + '&.MuiAccordionSummary-root': { + paddingTop: '2px', + paddingBottom: '2px', + paddingLeft: '15px', + minHeight: '35px !important', + overflow: 'hidden', + maxHeight: '30px', + }, + }, + blackColor: { + color: 'black', + }, + logout: { + backgroundColor: theme.palette.primary.highlight, + color: theme.palette.primary.main, + fontSize: pxToRem(14), + fontWeight: 500, + width: '100%', + '&:hover': { + backgroundColor: theme.palette.primary.highlight, + }, + }, + divider: { + width: '100%', + marginBottom: theme.spacing(1.6), + }, + zIndex: { + zIndex: 999, + }, + optionContainer: { + borderRadius: theme.shape.borderRadius, + cursor: 'pointer', + }, + profileList: { + padding: theme.spacing(2), + width: '100%', + maxWidth: theme.spacing(35), + minWidth: theme.spacing(18), + backgroundColor: theme.palette.background.paper, + borderRadius: '10px', + [theme.breakpoints.down('md')]: { + minWidth: '100%', + }, + '& .MuiListItemButton-root': { + mt: 0.5, + }, + }, + notificationHeader: { + padding: theme.spacing(1.6), + }, + notificationListHeader: { + width: '310px', + padding: theme.spacing(1.6), + display: 'flex', + justifyContent: 'space-between', + overflow: 'auto', + }, + unreadDiv: { + width: '60%', + }, + recentDiv: { + padding: theme.spacing(1.6), + paddingTop: theme.spacing(2.0), + paddingBottom: theme.spacing(2.0), + }, + markAsReadDiv: { + backgroundColor: theme.palette.grey[31], + width: '40%', + textAlign: 'center', + cursor: 'pointer', + }, + highlightBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.palette.primary.highlight, + borderRadius: '6px', + padding: theme.spacing(0.8), + }, + option: { + display: 'flex', + justifyContent: 'space-between', + marginLeft: theme.spacing(1.2), + marginRight: theme.spacing(1.2), + width: '100%', + alignItems: 'center', + }, + optionText: { + fontFamily: theme.fontFamily.medium, + }, + profileName: { + fontFamily: theme.fontFamily.bold, + marginRight: theme.spacing(1.2), + marginLeft: theme.spacing(1.2), + letterSpacing: 'normal', + fontSize: pxToRem(14), + fontWeight: 'bold', + }, + listClass: { + cursor: 'pointer', + display: 'flex', + justifyContent: 'center', + }, + avatar: { + width: '36px', + height: '36px', + }, + listIcon: { + width: '17%', + }, + iconImage: { + marginLeft: 'auto', + marginRight: 'auto', + }, + listText: { + paddingLeft: theme.spacing(0.6), + }, + accountCircleOutlined: { + stroke: theme.palette.primary.highlight, + strokeWidth: 1, + width: '18px', + height: '18px', + }, + sidebarAccordion: { + backgroundColor: 'transparent', + '&.Mui-expanded': { + backgroundColor: theme.palette.grey[16], + }, + '&::before': { + height: '0px', + }, + }, + headerMainDiv: { + display: 'flex', + justifyContent: 'flex-end', + marginLeft: theme.spacing(1), + fontSize: pxToRem(14), + paddingTop: theme.spacing(2.4), + paddingBottom: theme.spacing(2.4), + alignItems: 'center', + }, + headerMainDivMobile: { + display: 'flex', + justifyContent: 'space-between', + marginLeft: theme.spacing(0.5), + marginRight: theme.spacing(0.5), + fontSize: pxToRem(14), + paddingTop: theme.spacing(2.4), + paddingBottom: theme.spacing(2.4), + alignItems: 'center', + }, + dividerDiv: { + width: '85%', + margin: 'auto', + }, + profileIcon: { + width: '42px', + minWidth: '40px', + minHeight: '40px', + height: '42px', + borderRadius: '50%', + }, + onlineDot: { + position: 'absolute', + width: '14px', + height: '14px', + bottom: '-2px', + right: '-2px', + backgroundColor: theme.palette.grey[32], + border: '2px solid white', + borderRadius: '50%', + }, + notificationImg: { + width: '42px', + height: '42px', + minHeight: '40px', + minWidth: '40px', + }, + notificationDot: { + position: 'absolute', + width: '14px', + height: '14px', + top: '4px', + right: '20px', + backgroundColor: theme.palette.primary.main, + border: '2px solid white', + borderRadius: '50%', + }, + profilDiv: { + display: 'flex', + paddingLeft: theme.spacing(2.0), + }, + notificationSection: { + paddingLeft: theme.spacing(1.0), + cursor: 'pointer', + }, + notificationDiv: { + paddingRight: theme.spacing(1.2), + position: 'relative', + }, + notificationList: { + width: '320px', + maxHeight: '65vh', + overflow: 'auto', + }, + listOuterDiv: { + backgroundColor: theme.palette.grey[33], + paddingLeft: theme.spacing(0.8), + paddingRight: theme.spacing(0.8), + paddingBottom: theme.spacing(0.8), + }, + recentListOuterDiv: { + paddingLeft: theme.spacing(0.8), + paddingRight: theme.spacing(0.8), + paddingBottom: theme.spacing(0.8), + }, + notificationListImgDiv: { + display: 'flex', + }, + notificationHeadText: { + fontSize: pxToRem(14), + }, + notificationSubHeadText: { + fontSize: pxToRem(12), + backgroundColor: theme.palette.grey[31], + }, + centerAlignClass: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + paddingLeft: theme.spacing(0.4), + paddingRight: theme.spacing(0.6), + }, + imgHeightWidth: { + width: '55%', + }, + unreadListBody: { + height: '70px', + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + }, + contentDiv: { + fontSize: pxToRem(12), + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + width: '250px', + }, + timestamp: { + fontSize: pxToRem(11), + color: '#524A3ED1', + }, + headerAndMainContentBox: { + backgroundColor: theme.palette.grey[19], + }, + textDecorationNone: { + textDecoration: 'none', + }, + marginBottom: { + marginBottom: theme.spacing(1.5), + }, + dotOuterDiv: { + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + }, + dotInnerDiv: { + width: '7px', + height: '7px', + borderRadius: '50%', + marginRight: '8px', + }, + disabledLink: { + pointerEvents: 'none', + color: 'grey', + }, + functionalityNotAvailablePopupBox: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + width: '100%', + paddingLeft: theme.spacing(2.3), + paddingRight: theme.spacing(2), + }, + functionalityNotAvailableImage: { + width: '100%', + height: '50px', + [theme.breakpoints.up('md')]: { + width: '167px', + height: '95px', + }, + }, + functionalityNotAvailableTextBold: { + fontFamily: 'Inter', + fontSize: '30px', + fontWeight: 700, + lineHeight: '41px', + textAlign: 'center', + marginTop: theme.spacing(1.7), + }, + functionalityNotAvailableText: { + fontFamily: 'Inter', + fontSize: pxToRem(14), + fontWeight: 500, + lineHeight: pxToRem(17.6), + textAlign: 'center', + marginTop: theme.spacing(2.1), + }, + functionalityNotAvailableButton: { + width: '186px', + height: '48px', + backgroundColor: theme?.palette?.primary?.main, + color: theme?.palette?.common?.white, + fontSize: pxToRem(16), + marginTop: theme.spacing(2.2), + fontFamily: theme?.fontFamily?.semiBold, + border: 'none', + borderRadius: '12px', + cursor: 'pointer', + display: 'flex', + justifyContent: 'center', + }, +})); diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..d8ab9a1 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,11 @@ +import { createRoot } from "react-dom/client"; +import { Provider } from "react-redux"; +import App from "./App.jsx"; +import "./index.css"; +import store from "./redux/store.js"; + +createRoot(document.getElementById("root")).render( + + + +); diff --git a/src/mock/clinics/index.js b/src/mock/clinics/index.js new file mode 100644 index 0000000..6541a47 --- /dev/null +++ b/src/mock/clinics/index.js @@ -0,0 +1,291 @@ +export const clinicsData = { + data: { + records: [ + { + createdAt: '2025-04-16T06:46:01.402Z', + updatedAt: '2025-04-16T06:46:01.000Z', + deletedAt: null, + id: 41, + name: 'General Clinic', + website: 'www.general-clinic.com', + city: 'Victoria St.', + state: 'Victoria', + street: + 'flat no 12. sharda apartment. gujarath colony poud road, kothrud pune City near vanaj corner pune Ex Serviceman Colony Maharashtra', + pinCode: '411038', + logo: null, + about: 'rfre', + status: 'NOT_REVIEWED', + approvedAt: null, + requestRaisedOn: '2025-04-16T06:46:01.000Z', + lastEmailSent: null, + lastCustomPlanEmailSent: null, + requestedForCustomPlan: false, + industryId: 4, + accounts: { + createdAt: '2025-04-16T06:46:01.709Z', + updatedAt: '2025-04-16T06:46:01.709Z', + deletedAt: null, + id: 41, + startTime: null, + endTime: null, + maxJobPostings: 0, + maxResumeDownloads: 0, + features: null, + companyId: 41, + status: 'ACTIVE', + }, + }, + { + createdAt: '2025-03-05T05:37:51.162Z', + updatedAt: '2025-03-05T05:37:51.000Z', + deletedAt: null, + id: 16, + name: 'Star Clinic', + website: 'www.star-clinic.com', + city: 'Adelaide', + state: 'South Australia', + street: + 'flat no 12. sharda apartment. gujarath colony poud road, kothrud pune City near vanaj corner pune Ex Serviceman Colony Maharashtra', + pinCode: '411038', + logo: null, + about: '', + status: 'NOT_REVIEWED', + approvedAt: null, + requestRaisedOn: '2025-03-05T05:37:51.000Z', + lastEmailSent: null, + lastCustomPlanEmailSent: null, + requestedForCustomPlan: false, + industryId: 4, + accounts: { + createdAt: '2025-03-05T05:37:51.338Z', + updatedAt: '2025-03-05T05:37:51.338Z', + deletedAt: null, + id: 16, + startTime: null, + endTime: null, + maxJobPostings: 0, + maxResumeDownloads: 0, + features: null, + companyId: 16, + status: 'ACTIVE', + }, + }, + ], + totalCount: 5, + }, + message: 'Companies retrieved successfully', + error: '', + }; + + export const registeredClinicsData = { + data: { + records: [ + { + createdAt: '2025-04-16T06:56:35.060Z', + updatedAt: '2025-04-16T06:56:35.000Z', + deletedAt: null, + id: 44, + name: 'Para Hills', + website: 'www.param_hills.com', + city: 'Dharavi', + state: 'Maharashtra', + street: 'para hills', + pinCode: '400017', + logo: null, + about: + 'The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in thei', + status: 'APPROVED', + approvedAt: '2025-04-24T09:55:39.000Z', + requestRaisedOn: '2025-04-16T06:56:35.000Z', + lastEmailSent: null, + lastCustomPlanEmailSent: null, + requestedForCustomPlan: false, + industryId: 21, + accounts: { + createdAt: '2025-04-16T06:56:35.225Z', + updatedAt: '2025-04-16T06:56:35.225Z', + deletedAt: null, + id: 43, + startTime: null, + endTime: null, + maxJobPostings: 0, + maxResumeDownloads: 0, + features: null, + companyId: 44, + status: 'ACTIVE', + }, + plans: [], + }, + { + createdAt: '2025-01-21T11:23:44.529Z', + updatedAt: '2025-01-21T11:30:50.000Z', + deletedAt: null, + id: 10, + name: 'KPTCL Clinic', + website: 'www.kptcl.com', + city: 'Kolar Extension', + state: 'Karnataka', + street: 'Bengaluru', + pinCode: '563102', + logo: 'https://s3.ap-south-1.amazonaws.com/stcontent.bslearning.in/recruitment/companies/10/documents/1737458555506_logo.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQMCGMHZIC35CJ6KC%2F20250429%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20250429T064909Z&X-Amz-Expires=86400&X-Amz-Signature=c070a04a02e3129900818e7a2716255649dd7673095223c69d0bf1fe39a47aea&X-Amz-SignedHeaders=host&x-id=GetObject', + about: 'software', + status: 'APPROVED', + approvedAt: '2025-01-21T11:35:11.000Z', + requestRaisedOn: '2025-01-21T11:30:51.000Z', + lastEmailSent: '2025-01-21T11:25:18.000Z', + lastCustomPlanEmailSent: null, + requestedForCustomPlan: false, + industryId: 17, + accounts: { + createdAt: '2025-01-21T11:23:44.680Z', + updatedAt: '2025-01-21T11:23:44.680Z', + deletedAt: null, + id: 10, + startTime: null, + endTime: null, + maxJobPostings: 0, + maxResumeDownloads: 0, + features: null, + companyId: 10, + status: 'ACTIVE', + }, + plans: [], + hasActiveCustomPlan: null, + }, + ], + totalCount: 2, + }, + message: 'Companies retrieved successfully', + error: '', + }; + +// export const subscribersCompanyData = { +// data: { +// records: [ +// { +// createdAt: '2025-04-28T11:09:32.011Z', +// updatedAt: '2025-04-28T11:12:12.000Z', +// deletedAt: null, +// id: 45, +// name: 'Kidman Park', +// website: 'www.kidmanpark.com', +// city: 'Bhusari Colony', +// state: 'Maharashtra', +// street: +// 'flat no 12. sharda apartment. gujarath colony poud road, kothrud pune City near vanaj corner pune Ex Serviceman Colony Maharashtra', +// pinCode: '411038', +// logo: 'https://s3.ap-south-1.amazonaws.com/stcontent.bslearning.in/recruitment/companies/45/documents/undefined_1745838557374?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQMCGMHZIC35CJ6KC%2F20250429%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20250429T064947Z&X-Amz-Expires=86400&X-Amz-Signature=f08e527250674a0b9a5ce833b8bd907d4b130310db6cf8513978df4725aa0909&X-Amz-SignedHeaders=host&x-id=GetObject', +// about: 'test copmany', +// status: 'APPROVED', +// approvedAt: '2025-04-28T11:09:50.000Z', +// requestRaisedOn: '2025-04-28T11:09:32.000Z', +// lastEmailSent: null, +// lastCustomPlanEmailSent: '2025-04-28T11:12:13.000Z', +// requestedForCustomPlan: false, +// industryId: 5, +// accounts: { +// createdAt: '2025-04-28T11:09:32.396Z', +// updatedAt: '2025-04-28T13:34:01.000Z', +// deletedAt: null, +// id: 44, +// startTime: '2025-04-28T00:00:00.000Z', +// endTime: '2025-05-09T00:00:00.000Z', +// maxJobPostings: 109, +// maxResumeDownloads: 7, +// features: ',SEARCH_CANDIDATE', +// companyId: 45, +// status: 'ACTIVE', +// }, +// planPurchases: [ +// { +// offeredPricing: 11, +// }, +// ], +// hasActiveCustomPlan: { +// createdAt: '2025-04-28T11:12:12.883Z', +// updatedAt: '2025-04-28T11:12:26.000Z', +// deletedAt: null, +// id: 57, +// offerDate: '2025-04-27T18:30:00.000Z', +// planType: null, +// maxJobPostings: 111, +// maxResumeDownloads: 11, +// productPricing: 0, +// offeredPricing: 11, +// features: 'SEARCH_CANDIDATE', +// token: +// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW55SWQiOjQ1LCJvZmZlcmVkUHJpY2luZyI6MTEsIm9mZmVyRGF0ZSI6IjIwMjUtMDQtMjdUMTg6MzA6MDAuMDAwWiIsImZlYXR1cmVzIjoiU0VBUkNIX0NBTkRJREFURSIsInZhbGlkaXR5SW5EYXlzIjoxMSwiaWF0IjoxNzQ1ODM4NzMyLCJleHAiOjE3NDcxMzQ3MzJ9.mP01y7WXMKGQmhP9bAeVvuZVJWevSPKGyVU28zR9WLU', +// validityInDays: 11, +// isCustom: true, +// status: 'PURCHASED', +// companyId: 45, +// }, +// }, +// { +// createdAt: '2025-04-10T09:56:43.518Z', +// updatedAt: '2025-04-10T10:04:02.000Z', +// deletedAt: null, +// id: 40, +// name: 'Apollo Clinic', +// website: 'https://apolloclinic.com', +// city: 'Kudasan', +// state: 'Gujarat', +// street: +// 'BOSC Tech Labs Private Limited, UGATI CORPORATE PARK, 501, 502 Shree, near Nexa Showroom, Kudasan, Gandhinagar, Gujarat.', +// pinCode: '382421', +// logo: 'https://s3.ap-south-1.amazonaws.com/stcontent.bslearning.in/recruitment/companies/40/documents/newsletterresumeTip.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQMCGMHZIC35CJ6KC%2F20250429%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20250429T064947Z&X-Amz-Expires=86400&X-Amz-Signature=34424209b8bfe05bea2e89a74bbea787c0956dd1c5b21a4b4e0683eebb5eadeb&X-Amz-SignedHeaders=host&x-id=GetObject', +// about: +// 'In ancient times, it lived in warm seas. It disguised itself as seaweed to ambush its prey and devoured them whole when they got close.It vibrates its wings to generate ultrasonic waves, causing its p', +// status: 'APPROVED', +// approvedAt: '2025-04-10T09:58:40.000Z', +// requestRaisedOn: '2025-04-10T09:56:44.000Z', +// lastEmailSent: null, +// lastCustomPlanEmailSent: '2025-04-10T10:04:02.000Z', +// requestedForCustomPlan: false, +// industryId: 1, +// accounts: { +// createdAt: '2025-04-10T09:56:43.616Z', +// updatedAt: '2025-04-10T10:09:53.000Z', +// deletedAt: null, +// id: 40, +// startTime: '2025-04-10T00:00:00.000Z', +// endTime: '2033-06-27T00:00:00.000Z', +// maxJobPostings: 0, +// maxResumeDownloads: 0, +// features: '', +// companyId: 40, +// status: 'ACTIVE', +// }, +// planPurchases: [ +// { +// offeredPricing: 1, +// }, +// ], +// hasActiveCustomPlan: { +// createdAt: '2025-04-10T10:04:02.409Z', +// updatedAt: '2025-04-10T10:09:53.000Z', +// deletedAt: null, +// id: 37, +// offerDate: '2025-04-09T18:30:00.000Z', +// planType: null, +// maxJobPostings: 0, +// maxResumeDownloads: 0, +// productPricing: 0, +// offeredPricing: 1, +// features: null, +// token: +// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW55SWQiOjQwLCJvZmZlcmVkUHJpY2luZyI6MSwib2ZmZXJEYXRlIjoiMjAyNS0wNC0wOVQxODozMDowMC4wMDBaIiwidmFsaWRpdHlJbkRheXMiOjMwMDAsImlhdCI6MTc0NDI3OTQ0MiwiZXhwIjoxNzQ1NTc1NDQyfQ.J7yldjMlvhuZSeplCq5a6tIYTE-eBa8M4u9rwaHfO50', +// validityInDays: 3000, +// isCustom: true, +// status: 'PURCHASED', +// companyId: 40, +// }, +// }, +// ], +// totalCount: 2, +// }, +// message: 'Companies retrieved successfully', +// error: '', +// }; + \ No newline at end of file diff --git a/src/redux/combineReducers.js b/src/redux/combineReducers.js new file mode 100644 index 0000000..84fcd69 --- /dev/null +++ b/src/redux/combineReducers.js @@ -0,0 +1,31 @@ +import { combineReducers } from "redux"; +import { EMPTY_STORE_STATE, RESET_REDUX } from "../common/actionTypes"; +import logInAsCompanyAdminReducer from "../views/ClinicDetails/store/logInAsClinicAdminReducer"; +import loginReducer from "../views/Login/loginReducer"; +import signupReducer from "../views/Signup/signupReducer"; +import userRoleReducer from "./userRoleSlice"; + +const appReducer = combineReducers({ + signup: signupReducer, + login: loginReducer, + loginAsCompanyAdmin: logInAsCompanyAdminReducer, + userRole: userRoleReducer, +}); + +const rootReducer = (state, action) => { + if (action.type === EMPTY_STORE_STATE) { + state = {}; + } + if (action.type === RESET_REDUX) { + if (action.payload.reducerKey && action.payload.key) { + const { reducerKey, key, value } = action.payload; + state = { + ...state, + [reducerKey]: { ...state[reducerKey], [key]: value }, + }; + } + } + return appReducer(state, action); +}; + +export default rootReducer; diff --git a/src/redux/mockUserData.js b/src/redux/mockUserData.js new file mode 100644 index 0000000..e06750f --- /dev/null +++ b/src/redux/mockUserData.js @@ -0,0 +1,44 @@ +// Mock user data for testing dashboard rendering +export const mockSuperAdmin = { + id: 1, + name: "Super Admin User", + email: "superadmin@example.com", + isBsAdmin: true, + isAdmin: true, + permissions: ["SUPER_ADMIN_PERMISSION"], + roles: [ + { + id: 1, + name: "Super Admin", + rolePermissionMapping: [ + { + permission: { + name: "SUPER_ADMIN_PERMISSION" + } + } + ] + } + ] +}; + +export const mockClinicAdmin = { + id: 2, + name: "Clinic Admin User", + email: "clinicadmin@example.com", + isBsAdmin: false, + isAdmin: true, + permissions: ["CLINIC_ADMIN_PERMISSION"], + roles: [ + { + id: 2, + name: "Clinic Admin", + rolePermissionMapping: [ + { + permission: { + name: "CLINIC_ADMIN_PERMISSION" + } + } + ] + } + ] +}; diff --git a/src/redux/setMockUser.js b/src/redux/setMockUser.js new file mode 100644 index 0000000..9854541 --- /dev/null +++ b/src/redux/setMockUser.js @@ -0,0 +1,31 @@ +import store from './store'; +import { mockSuperAdmin, mockClinicAdmin } from './mockUserData'; +import { PROFILE_FULFILLED } from '../views/Login/loginActionTypes'; + +/** + * Sets mock user data in the Redux store for testing purposes + * @param {string} userType - 'superAdmin' or 'clinicAdmin' + */ +export const setMockUser = (userType) => { + const userData = userType === 'superAdmin' ? mockSuperAdmin : mockClinicAdmin; + + // Dispatch action to set user in the login reducer + store.dispatch({ + type: PROFILE_FULFILLED, + payload: { + payload: { + data: { + data: userData + } + } + } + }); + + console.log(`Mock ${userType} user set in Redux store`); +}; + +// Export mock user types for convenience +export const MOCK_USER_TYPES = { + SUPER_ADMIN: 'superAdmin', + CLINIC_ADMIN: 'clinicAdmin' +}; diff --git a/src/redux/store.js b/src/redux/store.js new file mode 100644 index 0000000..00d4753 --- /dev/null +++ b/src/redux/store.js @@ -0,0 +1,23 @@ +import { applyMiddleware, compose, createStore } from "redux"; +import persistState from "redux-localstorage"; +import promiseMiddleware from "redux-promise-middleware"; +import { thunk } from "redux-thunk"; +import rootReducer from "./combineReducers"; + +const composeEnhancers = + (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && + window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ + trace: true, + traceLimit: 100, + })) || + compose; + +const createStoreWithMiddleware = composeEnhancers( + applyMiddleware(thunk, promiseMiddleware), + persistState([]), + window.REDUX_DEVTOOLS_EXTENSION ? window.devToolsExtension() : (f) => f +)(createStore); + +const store = createStoreWithMiddleware(rootReducer); + +export default store; diff --git a/src/redux/userRoleSlice.js b/src/redux/userRoleSlice.js new file mode 100644 index 0000000..a9c47de --- /dev/null +++ b/src/redux/userRoleSlice.js @@ -0,0 +1,35 @@ +// User role constants +export const USER_ROLES = { + SUPER_ADMIN: 'SUPER_ADMIN', + CLINIC_ADMIN: 'CLINIC_ADMIN', +}; + +// Initial state +const initialState = { + role: null, +}; + +// Action types +const SET_USER_ROLE = 'userRole/SET_USER_ROLE'; + +// Action creators +export const setUserRole = (role) => ({ + type: SET_USER_ROLE, + payload: role, +}); + +// Selector to get user role +export const selectUserRole = (state) => state.userRole.role; + +// Reducer +export default function userRoleReducer(state = initialState, action) { + switch (action.type) { + case SET_USER_ROLE: + return { + ...state, + role: action.payload, + }; + default: + return state; + } +} diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..b2ee5b5 --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,41 @@ +import MainLayout from "../layouts/mainLayout"; +import MinimalLayout from "../layouts/MinimalLayout"; +import ClinicsList from "../views/ClinicsList"; +import Dashboard from "../views/Dashboard"; +import StaffManagement from "../views/StaffManagement"; +import ClinicDetails from "../views/ClinicDetails"; +import Login from '../views/Login'; +import YourDetailsForm from "../views/Signup/YourDetailsForm"; +import MockPayment from "../views/MockPayment"; +import Users from "../views/User"; +import ClinicSetup from "../views/ClinicSetup"; +import ClinicTranscripts from "../views/ClinicTranscripts"; + +export const routesData = [ + { + path: "/", + layout: MainLayout, + routes: [ + { path: "/", component: Dashboard }, + { path: "/clinics", component: ClinicsList }, + { path: "/clinics/:id", component: ClinicDetails }, + { path: "/admin", component: StaffManagement }, + { path: "/doctor", component: Users }, + { path: "/clinicSetup", component: ClinicSetup }, + { path: "/transcripts", component: ClinicTranscripts }, + ], + isProtected: true, + }, + { + path: "/auth", + layout: MinimalLayout, + routes: [ + { path: "/auth/login", component: Login }, + { path: "/auth/signup/payment", component: MockPayment }, + { + path: 'signup/your-details', + component: YourDetailsForm, + }, + ], + }, +]; diff --git a/src/routes/withAuth.jsx b/src/routes/withAuth.jsx new file mode 100644 index 0000000..afbfa76 --- /dev/null +++ b/src/routes/withAuth.jsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Navigate } from "react-router-dom"; +import { + // checkModulePermission, + // checkModulePermission, + // getPortalType, + // isCollageId, + isLoggedIn, +} from "../utils/share"; +import NotFound from "../views/NotFound"; +// import NotFound from '../views/NotFound'; + +export const withAuth = (Component, permission, isProtected) => (props) => { + const isAuthenticated = isLoggedIn(); + if (isProtected && !isAuthenticated) { + return ; + } + + // if (!checkModulePermission(permission)) { + // return ; + // } + + return ; +}; diff --git a/src/routes/withPermission.jsx b/src/routes/withPermission.jsx new file mode 100644 index 0000000..029a684 --- /dev/null +++ b/src/routes/withPermission.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import { Navigate } from "react-router-dom"; +import { CLINIC_STATUS } from "../constants"; +// import { isBSPortal } from "../utils/share"; + +// eslint-disable-next-line +const withPermission = (Component) => (props) => { + const companyStatus = useSelector( + (state) => state?.login?.user?.company?.status + ); + const { isBSAdmin } = false; + // const { isBSAdmin } = isBSPortal(); + + // If the user is a BS Admin, render the component without any checks + if (isBSAdmin === true) { + return ; + } + + if (companyStatus !== CLINIC_STATUS.APPROVED) { + return ; + } + + return ; +}; + +export default withPermission; diff --git a/src/services/clinics.service.js b/src/services/clinics.service.js new file mode 100644 index 0000000..243aecd --- /dev/null +++ b/src/services/clinics.service.js @@ -0,0 +1,45 @@ +import { CLINIC_TYPE } from "../constants"; +import { clinicsData, registeredClinicsData } from "../mock/clinics"; + +export const getClinics = (params) => { + switch (params.type) { + case CLINIC_TYPE.UNREGISTERED: + return { data: clinicsData }; + case CLINIC_TYPE.REGISTERED: + return { data: registeredClinicsData }; + case CLINIC_TYPE.SUBSCRIBED: + return { data: registeredClinicsData }; + default: + return { data: clinicsData }; + } + }; + + export const getClinicsById = (id) => { + const url = `/companies/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); + }; + + export const updateClinicStatus = (id, data) => { + const url = `/companies/${id}/company-review`; + return new Promise((resolve, reject) => { + axiosInstance + .post(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); + }; + + export const getClinicsDashboardStatsById = () => { + const url = `/companies/dashboard-stats`; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); + }; \ No newline at end of file diff --git a/src/services/file.upload.services.js b/src/services/file.upload.services.js new file mode 100644 index 0000000..175d634 --- /dev/null +++ b/src/services/file.upload.services.js @@ -0,0 +1,25 @@ +import { axiosInstance } from '../config/api'; + +export const fileUpload = (data, type = null, companyId = null) => { + let searchParams = new URLSearchParams(); + + if (type) { + searchParams.append('type', type); + } + if (companyId) { + searchParams.append('companyId', companyId); + } + + const url = `/upload?${searchParams.toString()}`; + + return new Promise((resolve, reject) => { + axiosInstance + .post(url, data, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; diff --git a/src/services/notification.service.js b/src/services/notification.service.js new file mode 100644 index 0000000..43bb79e --- /dev/null +++ b/src/services/notification.service.js @@ -0,0 +1,52 @@ +import { axiosInstance } from "../config/api"; +import { + RECENT_NOTIFICATIONS_SIZE, + UNREAD_NOTIFICATIONS_SIZE, +} from "../views/Notifications/notificationConstant"; +export const getNotifications = (unread = false, page = 0) => { + // let url; + + // if (unread) { + // url = `/notifications?status=1&page=${page}&size=${UNREAD_NOTIFICATIONS_SIZE}`; // unread notifications + // } else { + // url = `/notifications?status=0&page=${page}&size=${RECENT_NOTIFICATIONS_SIZE}`; // recent notifications + // } + + // return new Promise((resolve, reject) => { + // axiosInstance + // .get(url) + // .then((response) => resolve(response)) + // .catch((err) => reject(err)); + // }); + return { + data: { + data: [], + message: "Success", + error: "", + }, + }; +}; + +export const markReadNotification = (notificationsIds = []) => { + const url = notificationsIds?.length + ? `/notifications?ids=${notificationsIds?.join(",")}` + : `/notifications`; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const saveFcmToken = (data) => { + const url = `/fcm`; + + return new Promise((resolve, reject) => { + axiosInstance + .post(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; diff --git a/src/services/users.service.js b/src/services/users.service.js new file mode 100644 index 0000000..ff2c3c4 --- /dev/null +++ b/src/services/users.service.js @@ -0,0 +1,247 @@ +import { axiosInstance } from '../config/api'; + +export const getUsers = (params) => { + let searchParams = new URLSearchParams(); + searchParams.append('size', params?.pagination?.pageSize ?? 10); + searchParams.append('page', params?.pagination.pageIndex ?? 0); + if (params?.globalFilter) searchParams.append('search', params?.globalFilter); + if (params?.sortingString) { + const sortingParams = new URLSearchParams(params.sortingString); + sortingParams.forEach((value, key) => { + searchParams.append(key, value); + }); + } + + let url = `/users?${searchParams.toString()}`; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response?.data)) + .catch((err) => reject(err)); + }); +}; + +export const getUserById = (id) => { + const url = `/users/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const getRoles = ({ page }) => + page == 0 + ? { + // data: { + data: [ + { + id: 1, + name: 'Adult Immunisation', + }, + { + id: 2, + name: 'Allied health', + }, + { + id: 3, + name: 'Assist', + }, + { + id: 4, + name: 'Care plan', + }, + { + id: 5, + name: 'Care plan reviews', + }, + { + id: 6, + name: 'Cervical Screening', + }, + { + id: 7, + name: 'Child Immunisation', + }, + { + id: 8, + name: 'Health assessments', + }, + { + id: 9, + name: 'Mental Health Care Plans (MHCP)', + }, + { + id: 10, + name: 'Travel Immunisation', + }, + ], + // }, + } + : { data: [] }; +// const url = '/roles'; +// return new Promise((resolve, reject) => { +// axiosInstance +// .get(url) +// .then((response) => resolve(response)) +// .catch((err) => reject(err)); +// }); + +export const addUser = (data) => { + const url = '/users'; + return new Promise((resolve, reject) => { + axiosInstance + .post(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const updateUserById = (id, data) => { + const url = `/users/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const revokeAdminTransferAccess = () => { + const url = `/users/admin-access-transfer-revoke`; + return new Promise((resolve, reject) => { + axiosInstance + .put(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const approveAdminTransferAccess = (id, token) => { + const url = `/users/admin-access-transfer-approve`; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, { id, token }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const deleteUserById = (id) => { + const url = `/users/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .delete(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const logout = () => { + // const url = '/auth/companies/logout'; + // return new Promise((resolve, reject) => { + // axiosInstance + // .get(url) + // .then((response) => resolve(response)) + // .catch((err) => reject(err)); + // }); + return; +}; + +export const changePassword = (data) => { + const url = '/users/me/password'; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const updateMailersSettings = (data) => { + const url = '/users/me/mail-notification'; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const updateUser = (imageUrl, isProfileDelete = false) => { + let url = '/users/me'; + const data = { profilePhoto: imageUrl, isProfileDelete }; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const companyMe = () => { + let url = '/companies/me'; + + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const updateCompanyDetails = (data) => { + let url = '/companies/me'; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const bsAdminLogin = (token) => { + const url = '/auth/admin/login'; + return new Promise((resolve, reject) => { + axiosInstance + .post(url, { + token, + }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const resendPasswordMailer = (data) => { + const url = '/users/remind-password-setup'; + return new Promise((resolve, reject) => { + axiosInstance + .patch(url, { + email: data, + }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const updateUserEmailAndContact = (data) => { + let url = '/users/me'; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const transferMasterAdminAccess = (id) => { + if (!id) throw new Error('User id is required'); + const url = `/users/${id}/transfer-master-admin-access`; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; diff --git a/src/services/users.services.js b/src/services/users.services.js new file mode 100644 index 0000000..9c67b27 --- /dev/null +++ b/src/services/users.services.js @@ -0,0 +1,246 @@ +import { axiosInstance } from '../config/api'; + +export const getUsers = (params) => { + let searchParams = new URLSearchParams(); + searchParams.append('size', params?.pagination?.pageSize ?? 10); + searchParams.append('page', params?.pagination.pageIndex ?? 0); + if (params?.globalFilter) searchParams.append('search', params?.globalFilter); + if (params?.sortingString) { + const sortingParams = new URLSearchParams(params.sortingString); + sortingParams.forEach((value, key) => { + searchParams.append(key, value); + }); + } + + let url = `/users?${searchParams.toString()}`; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response?.data)) + .catch((err) => reject(err)); + }); +}; + +export const getUserById = (id) => { + const url = `/users/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const getRoles = ({ page }) => + page == 0 + ? { + // data: { + data: [ + { + id: 1, + name: 'Adult Immunisation', + }, + { + id: 2, + name: 'Allied health', + }, + { + id: 3, + name: 'Assist', + }, + { + id: 4, + name: 'Care plan', + }, + { + id: 5, + name: 'Care plan reviews', + }, + { + id: 6, + name: 'Cervical Screening', + }, + { + id: 7, + name: 'Child Immunisation', + }, + { + id: 8, + name: 'Health assessments', + }, + { + id: 9, + name: 'Mental Health Care Plans (MHCP)', + }, + { + id: 10, + name: 'Travel Immunisation', + }, + ], + // }, + } + : { data: [] }; +// const url = '/roles'; +// return new Promise((resolve, reject) => { +// axiosInstance +// .get(url) +// .then((response) => resolve(response)) +// .catch((err) => reject(err)); +// }); + +export const addUser = (data) => { + const url = '/users'; + return new Promise((resolve, reject) => { + axiosInstance + .post(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const updateUserById = (id, data) => { + const url = `/users/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const revokeAdminTransferAccess = () => { + const url = `/users/admin-access-transfer-revoke`; + return new Promise((resolve, reject) => { + axiosInstance + .put(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const approveAdminTransferAccess = (id, token) => { + const url = `/users/admin-access-transfer-approve`; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, { id, token }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const deleteUserById = (id) => { + const url = `/users/${id}`; + return new Promise((resolve, reject) => { + axiosInstance + .delete(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const logout = () => { + const url = '/auth/companies/logout'; + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const changePassword = (data) => { + const url = '/users/me/password'; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const updateMailersSettings = (data) => { + const url = '/users/me/mail-notification'; + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const updateUser = (imageUrl, isProfileDelete = false) => { + let url = '/users/me'; + const data = { profilePhoto: imageUrl, isProfileDelete }; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const companyMe = () => { + let url = '/companies/me'; + + return new Promise((resolve, reject) => { + axiosInstance + .get(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; +export const updateCompanyDetails = (data) => { + let url = '/companies/me'; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const bsAdminLogin = (token) => { + const url = '/auth/admin/login'; + return new Promise((resolve, reject) => { + axiosInstance + .post(url, { + token, + }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const resendPasswordMailer = (data) => { + const url = '/users/remind-password-setup'; + return new Promise((resolve, reject) => { + axiosInstance + .patch(url, { + email: data, + }) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const updateUserEmailAndContact = (data) => { + let url = '/users/me'; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url, data) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; + +export const transferMasterAdminAccess = (id) => { + if (!id) throw new Error('User id is required'); + const url = `/users/${id}/transfer-master-admin-access`; + + return new Promise((resolve, reject) => { + axiosInstance + .put(url) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); +}; diff --git a/src/theme/ThemeOptions.jsx b/src/theme/ThemeOptions.jsx new file mode 100644 index 0000000..094d4a5 --- /dev/null +++ b/src/theme/ThemeOptions.jsx @@ -0,0 +1,24 @@ +import { deepmerge } from '@mui/utils'; +import palette from './palette'; + +const themeOptions = (settings) => { + // ** Vars + const { themeColor } = settings; + + const themeConfig = { + palette: palette(), + typography: { + fontFamily: 'Inter-Medium', + }, + }; + + return deepmerge(themeConfig, { + palette: { + primary: { + ...themeConfig.palette[themeColor], + }, + }, + }); +}; + +export default themeOptions; diff --git a/src/theme/index.jsx b/src/theme/index.jsx new file mode 100644 index 0000000..038db67 --- /dev/null +++ b/src/theme/index.jsx @@ -0,0 +1,71 @@ +import { CssBaseline } from "@mui/material"; +import { + createTheme, + ThemeProvider as MUIThemeProvider, + StyledEngineProvider, +} from "@mui/material/styles"; +import PropTypes from "prop-types"; +import { useMemo } from "react"; +import logo from "../assets/images/logo/app.svg"; +import componentsOverrides from "./overrides"; + +import palette from "./palette"; +import shadows, { customShadows } from "./shadows"; +import typography from "./typography"; +import merge from "lodash.merge"; + +export default function ThemeProvider({ children }) { + const themeOptions = useMemo( + () => ({ + palette, + shape: { + borderRadius: 4, + borderRadiusComponent: 8, + roundButtonRadius: 24, + outlinedBorderWidth: 1.5, + outlinedInputPadding: 12.5, + outlinedTabHeight: 34, + }, + spacing: 10, + typography, + shadows, + customShadows, + fontFamily: { + regular: "Inter-Regular", + medium: "Inter-Medium", + semiBold: "Inter-SemiBold", + bold: "Inter-Bold", + heavy: "Inter-ExtraBold", + light: "Inter-Light", + GraphikMedium: "GraphikMedium", + }, + logo, + }), + [] + ); + + const theme = useMemo(() => { + const baseTheme = createTheme(themeOptions); + const componentsOverride = componentsOverrides(baseTheme); + + return createTheme(baseTheme, { + components: merge({}, componentsOverride, themeOptions.components), + }); + }, [themeOptions]); + + // const theme = createTheme(themeOptions); + // theme.components = componentsOverride(theme); + + return ( + + + + {children} + + + ); +} + +ThemeProvider.propTypes = { + children: PropTypes.node, +}; diff --git a/src/theme/overrides/Accordion.jsx b/src/theme/overrides/Accordion.jsx new file mode 100644 index 0000000..d532791 --- /dev/null +++ b/src/theme/overrides/Accordion.jsx @@ -0,0 +1,32 @@ +export default function Accordion(theme) { + return { + MuiAccordion: { + styleOverrides: { + root: { + bgcolor: 'transparent', + margin: theme.spacing(0), + boxShadow: 'none', + }, + }, + }, + MuiAccordionSummary: { + styleOverrides: { + root: { + margin: theme.spacing(0), + minHeight: '48px !important', + padding: theme.spacing(0), + '& .MuiAccordionSummary-content': { + margin: '0 !important', + }, + }, + }, + }, + MuiAccordionDetails: { + styleOverrides: { + root: { + padding: theme.spacing(0), + }, + }, + }, + }; +} diff --git a/src/theme/overrides/Button.jsx b/src/theme/overrides/Button.jsx new file mode 100644 index 0000000..778a14d --- /dev/null +++ b/src/theme/overrides/Button.jsx @@ -0,0 +1,34 @@ +export default function Button(theme) { + return { + MuiButton: { + styleOverrides: { + root: { + borderRadius: theme.shape.borderRadiusComponent, + boxShadow: 'none', + '&:hover': { boxShadow: 'none' }, + }, + }, + variants: [ + { + props: { variant: 'contained', color: 'secondary' }, + style: { + bgcolor: theme.palette.grey[2], + color: theme.palette.grey[3], + }, + }, + { + props: { variant: 'outlined' }, + style: { + border: 'solid', + backgroundColor: theme.palette.common.white, + borderWidth: theme.shape.outlinedBorderWidth, + '&:hover': { + border: 'solid', + borderWidth: theme.shape.outlinedBorderWidth, + }, + }, + }, + ], + }, + }; +} diff --git a/src/theme/overrides/Checkbox.jsx b/src/theme/overrides/Checkbox.jsx new file mode 100644 index 0000000..1b3edf8 --- /dev/null +++ b/src/theme/overrides/Checkbox.jsx @@ -0,0 +1,14 @@ +const Checkbox = (theme) => ({ + MuiCheckbox: { + styleOverrides: { + root: { + padding: theme.spacing(0), + '& .MuiSvgIcon-root': { + width: '15px', + }, + }, + }, + }, +}); + +export default Checkbox; diff --git a/src/theme/overrides/Chip.jsx b/src/theme/overrides/Chip.jsx new file mode 100644 index 0000000..12cfd11 --- /dev/null +++ b/src/theme/overrides/Chip.jsx @@ -0,0 +1,17 @@ +import { pxToRem } from '../typography'; + +const Chip = (theme) => ({ + MuiChip: { + styleOverrides: { + root: { + fontSize: pxToRem(12), + backgroundColor: theme.palette.primary.highlight, + color: theme.palette.primary.main, + '&:hover': { + backgroundColor: theme.palette.primary.highlight, + }, + }, + }, + }, +}); +export default Chip; diff --git a/src/theme/overrides/CssBaseline.jsx b/src/theme/overrides/CssBaseline.jsx new file mode 100644 index 0000000..dc203b7 --- /dev/null +++ b/src/theme/overrides/CssBaseline.jsx @@ -0,0 +1,43 @@ +export default function CssBaseline() { + return { + MuiCssBaseline: { + styleOverrides: { + '*': { + margin: 0, + padding: 0, + boxSizing: 'border-box', + }, + html: { + width: '100%', + height: '100%', + WebkitOverflowScrolling: 'touch', + }, + body: { + width: '100%', + height: '100%', + }, + '#root': { + width: '100%', + height: '100%', + }, + input: { + '&[type=number]': { + MozAppearance: 'textfield', + '&::-webkit-outer-spin-button': { + margin: 0, + WebkitAppearance: 'none', + }, + '&::-webkit-inner-spin-button': { + margin: 0, + WebkitAppearance: 'none', + }, + }, + }, + img: { + display: 'block', + maxWidth: '100%', + }, + }, + }, + }; +} diff --git a/src/theme/overrides/Icon.jsx b/src/theme/overrides/Icon.jsx new file mode 100644 index 0000000..1718097 --- /dev/null +++ b/src/theme/overrides/Icon.jsx @@ -0,0 +1,11 @@ +const Icon = (theme) => ({ + MuiIcon: { + styleOverrides: { + root: { + color: theme.palette.grey[7], + }, + }, + }, +}); + +export default Icon; diff --git a/src/theme/overrides/Input.jsx b/src/theme/overrides/Input.jsx new file mode 100644 index 0000000..87dc6b7 --- /dev/null +++ b/src/theme/overrides/Input.jsx @@ -0,0 +1,95 @@ +import { pxToRem } from '../typography'; + +export default function Input(theme) { + return { + MuiFormLabel: { + styleOverrides: { + root: { + marginLeft: 0, + fontFamily: theme.fontFamily.medium, + opacity: 1, + }, + }, + }, + MuiFormHelperText: { + styleOverrides: { + root: { + marginLeft: 0, + fontStyle: 'italic', + }, + }, + }, + MuiInput: { + styleOverrides: { + root: { + fontFamily: theme.fontFamily.medium, + fontSize: pxToRem(10), + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(10), + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(13), + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(21), + }, + input: { textAlign: 'center' }, + '& input::placeholder': { + opacity: 0.37, + fontFamily: theme.fontFamily.medium, + fontSize: pxToRem(10), + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(10), + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(13), + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(21), + }, + }, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + border: 'solid', + borderWidth: 0, + borderColor: theme.palette.grey[5], + '& .MuiOutlinedInput-input': { + padding: theme.shape.outlinedInputPadding, + }, + '&:hover': { + border: 'solid', + borderWidth: 0, + borderColor: theme.palette.grey[5], + }, + '& .Mui-disabled': { + backgroundColor: theme.palette.grey[23], + borderWidth: 0, + }, + }, + multiline: { + padding: 0, + }, + }, + }, + MuiInputLabel: { + styleOverrides: { + root: { + color: theme.palette.grey[4], + fontFamily: theme.fontFamily.regular, + fontSize: pxToRem(14), + fontWeight: 400, + marginBottom: theme.spacing(1.3), + opacity: 0.7, + fontStretch: 'normal', + fontStyle: 'normal', + lineHeight: 'normal', + letterSpacing: 'normal', + }, + }, + }, + }; +} diff --git a/src/theme/overrides/InputAdornment.jsx b/src/theme/overrides/InputAdornment.jsx new file mode 100644 index 0000000..f7154be --- /dev/null +++ b/src/theme/overrides/InputAdornment.jsx @@ -0,0 +1,11 @@ +const InputAdornment = (theme) => ({ + MuiInputAdornment: { + styleOverrides: { + root: { + color: theme.palette.grey[7], + }, + }, + }, +}); + +export default InputAdornment; diff --git a/src/theme/overrides/Link.jsx b/src/theme/overrides/Link.jsx new file mode 100644 index 0000000..2a372c0 --- /dev/null +++ b/src/theme/overrides/Link.jsx @@ -0,0 +1,49 @@ +import { pxToRem } from '../typography'; + +export default function Link(theme) { + return { + MuiLink: { + styleOverrides: { + root: { + cursor: 'pointer', + textDecorationColor: theme.palette.primary.main, + '&:hover': { + color: theme.palette.common.black, + }, + }, + }, + variants: [ + { + props: { size: 'small' }, + style: { + fontSize: pxToRem(7.5), + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(7.5), + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(9), + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(15), + }, + }, + }, + { + props: { size: 'medium' }, + style: { + fontSize: pxToRem(13), + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(13), + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(16), + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(26), + }, + }, + }, + ], + }, + }; +} diff --git a/src/theme/overrides/LoadingButton.jsx b/src/theme/overrides/LoadingButton.jsx new file mode 100644 index 0000000..59ce605 --- /dev/null +++ b/src/theme/overrides/LoadingButton.jsx @@ -0,0 +1,30 @@ +import { pxToRem } from '../typography'; + +export default function LoadingButton(theme) { + return { + MuiLoadingButton: { + styleOverrides: { + root: { + justifyContent: 'space-between', + borderRadius: 8, + width: '100%', + height: '25px', + fontSize: pxToRem(8), + fontFamily: theme.fontFamily.semiBold, + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(14), + height: '40px', + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(14), + height: '40px', + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(16), + height: '50px', + }, + }, + }, + }, + }; +} diff --git a/src/theme/overrides/Select.jsx b/src/theme/overrides/Select.jsx new file mode 100644 index 0000000..d1b2768 --- /dev/null +++ b/src/theme/overrides/Select.jsx @@ -0,0 +1,11 @@ +const Select = (theme) => ({ + MuiSelect: { + styleOverrides: { + root: { + '& .MuiSvgIcon-root': { color: theme.palette.grey[27] }, + borderRadius: 8, + }, + }, + }, +}); +export default Select; diff --git a/src/theme/overrides/Switch.jsx b/src/theme/overrides/Switch.jsx new file mode 100644 index 0000000..4ee6135 --- /dev/null +++ b/src/theme/overrides/Switch.jsx @@ -0,0 +1,42 @@ +const Switch = (theme) => ({ + MuiSwitch: { + styleOverrides: { + root: { + width: 35, + height: 16, + padding: 0, + marginLeft: theme.spacing(1), + '& .MuiSwitch-thumb': { + boxSizing: 'border-box', + width: 12, + height: 12, + }, + '& .MuiSwitch-track': { + borderRadius: 26 / 2, + backgroundColor: theme.palette.grey[1], + opacity: 1, + transition: theme.transitions.create(['background-color'], { + duration: 500, + }), + }, + '& .MuiSwitch-switchBase': { + padding: theme.spacing(0), + margin: theme.spacing(0.2), + transitionDuration: '300ms', + '&.Mui-checked': { + transform: 'translateX(19px)', + color: theme.palette.grey[0], + '& + .MuiSwitch-track': { + opacity: 1, + border: 0, + }, + '&.Mui-disabled + .MuiSwitch-track': { + opacity: 0.5, + }, + }, + }, + }, + }, + }, +}); +export default Switch; diff --git a/src/theme/overrides/Tabs.jsx b/src/theme/overrides/Tabs.jsx new file mode 100644 index 0000000..691013f --- /dev/null +++ b/src/theme/overrides/Tabs.jsx @@ -0,0 +1,70 @@ +import { pxToRem } from '../typography'; + +const Tabs = (theme) => ({ + MuiTabs: { + styleOverrides: { + root: { + // '& .MuiTabs-flexContainer': { + // backgroundColor: theme?.palette?.background?.default, + // borderRadius: theme.shape.borderRadiusComponent, + // border: '1px solid', + // borderColor: theme.palette.grey[5], + // }, + // '& .MuiTabs-indicator': { + // display: 'none', + // }, + // '& .Mui-selected': { + // fontSize: pxToRem(14), + // backgroundColor: theme.palette.primary.highlight, + // borderRadius: theme.shape.borderRadiusComponent, + // }, + // '& .MuiTouchRipple-root': { + // borderRadius: theme.shape.borderRadiusComponent, + // }, + // '& .MuiTab-textColorPrimary': { + // fontSize: pxToRem(14), + // fontFamily: theme.fontFamily.medium, + // fontWeight: 500, + // margin: theme.spacing(0.3), + // minHeight: theme.shape.outlinedTabHeight, + // height: theme.shape.outlinedTabHeight, + // }, + // '& .MuiButtonBase-root-MuiTab-root': { + // fontSize: pxToRem(14), + // fontFamily: theme.fontFamily.medium, + // fontWeight: 500, + // padding: 0, + // backgroundColor: theme.palette.primary.highlight, + // }, + '& .MuiTabs-root': { + borderBottom: `1px solid ${theme.palette.divider}`, + }, + '& .MuiTab-root': { + minWidth: 'unset', + textTransform: 'none', + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.regular, + padding: theme.spacing(2), + '&:hover': { + backgroundColor: 'transparent', + }, + }, + '& .MuiTab-textColorInherit.Mui-selected': { + color: theme.palette.primary.main, + }, + '& .MuiTab-textColorInherit': { + color: theme.palette.text.primary, + }, + '& .MuiTab-wrapper': { + flexDirection: 'row', + alignItems: 'center', + }, + '& .MuiTab-icon': { + marginLeft: theme.spacing(1), + }, + }, + }, + }, +}); + +export default Tabs; diff --git a/src/theme/overrides/TextField.jsx b/src/theme/overrides/TextField.jsx new file mode 100644 index 0000000..06feeeb --- /dev/null +++ b/src/theme/overrides/TextField.jsx @@ -0,0 +1,23 @@ +import { pxToRem } from '../typography'; + +export default function TextField(theme) { + return { + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: theme.spacing(1), + fontFamily: 'Inter-Regular', + }, + '& .MuiOutlinedInput-input::placeholder': { + fontStyle: 'italic', + fontSize: pxToRem(14), + }, + '& .Mui-disabled': { + borderRadius: theme.spacing(1), + }, + }, + }, + }, + }; +} diff --git a/src/theme/overrides/Timeline.jsx b/src/theme/overrides/Timeline.jsx new file mode 100644 index 0000000..36bf81d --- /dev/null +++ b/src/theme/overrides/Timeline.jsx @@ -0,0 +1,11 @@ +const Timeline = (theme) => ({ + MuiTimeline: { + styleOverrides: { + root: { + padding: `${theme.spacing(0.6)} 0`, + }, + }, + }, +}); + +export default Timeline; diff --git a/src/theme/overrides/TimelineConnector.jsx b/src/theme/overrides/TimelineConnector.jsx new file mode 100644 index 0000000..696a5e3 --- /dev/null +++ b/src/theme/overrides/TimelineConnector.jsx @@ -0,0 +1,15 @@ +const TimelineConnector = (theme) => ({ + MuiTimelineConnector: { + styleOverrides: { + root: { + width: '1px', + borderStyle: 'dashed', + borderColor: theme.palette.grey[18], + borderWidth: '1px', + backgroundColor: 'transparent', + }, + }, + }, +}); + +export default TimelineConnector; diff --git a/src/theme/overrides/TimelineDot.jsx b/src/theme/overrides/TimelineDot.jsx new file mode 100644 index 0000000..998e9ef --- /dev/null +++ b/src/theme/overrides/TimelineDot.jsx @@ -0,0 +1,13 @@ +const TimelineDot = (theme) => ({ + MuiTimelineDot: { + styleOverrides: { + root: { + margin: 0, + padding: theme.spacing(0.6), + borderWidth: '4px', + }, + }, + }, +}); + +export default TimelineDot; diff --git a/src/theme/overrides/TimlineContent.jsx b/src/theme/overrides/TimlineContent.jsx new file mode 100644 index 0000000..1df45c4 --- /dev/null +++ b/src/theme/overrides/TimlineContent.jsx @@ -0,0 +1,11 @@ +const TimelineContent = (theme) => ({ + MuiTimelineContent: { + styleOverrides: { + root: { + padding: `${theme.spacing(0.1)} ${theme.spacing(1.6)}`, + }, + }, + }, +}); + +export default TimelineContent; diff --git a/src/theme/overrides/Tooltip.jsx b/src/theme/overrides/Tooltip.jsx new file mode 100644 index 0000000..31b8a01 --- /dev/null +++ b/src/theme/overrides/Tooltip.jsx @@ -0,0 +1,16 @@ +const Tooltip = () => ({ + MuiTooltip: { + defaultProps: { + TransitionProps: { + sx: { + display: '-webkit-box', + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + textOverflow: 'ellipsis', + WebkitLineClamp: 5, + }, + }, + }, + }, +}); +export default Tooltip; diff --git a/src/theme/overrides/Typography.jsx b/src/theme/overrides/Typography.jsx new file mode 100644 index 0000000..ed8af20 --- /dev/null +++ b/src/theme/overrides/Typography.jsx @@ -0,0 +1,37 @@ +import { pxToRem } from '../typography'; + +export default function Typography(theme) { + return { + MuiTypography: { + styleOverrides: { + root: { + color: theme.palette.grey[10], + }, + }, + variants: [ + { + props: { variant: 'Inter-Medium' }, + style: { + fontFamily: theme.fontFamily.medium, + }, + }, + { + props: { variant: 'Inter-SemiBold' }, + style: { + fontFamily: theme.fontFamily.semiBold, + fontSize: pxToRem(24), + fontWeight: 600, + }, + }, + { + props: { variant: 'Inter-Bold' }, + style: { + fontFamily: theme.fontFamily.bold, + fontSize: pxToRem(24), + fontWeight: 900, + }, + }, + ], + }, + }; +} diff --git a/src/theme/overrides/index.jsx b/src/theme/overrides/index.jsx new file mode 100644 index 0000000..d2e0e17 --- /dev/null +++ b/src/theme/overrides/index.jsx @@ -0,0 +1,45 @@ +import Icon from './Icon'; +import Chip from './Chip'; +import Link from './Link'; +import Tabs from './Tabs'; +import Input from './Input'; +import Select from './Select'; +import Switch from './Switch'; +import Button from './Button'; +import Tooltip from './Tooltip'; +import Checkbox from './Checkbox'; +import Timeline from './Timeline'; +import Accordion from './Accordion'; +import Typography from './Typography'; +import TextField from './TextField'; +import TimelineDot from './TimelineDot'; +import CssBaseline from './CssBaseline'; +import LoadingButton from './LoadingButton'; +import InputAdornment from './InputAdornment'; +import TimelineContent from './TimlineContent'; +import TimelineConnector from './TimelineConnector'; + +export default function ComponentsOverrides(theme) { + return Object.assign( + Icon(theme), + Chip(theme), + Link(theme), + Tabs(theme), + Input(theme), + Select(theme), + Switch(theme), + Button(theme), + Tooltip(theme), + Checkbox(theme), + Timeline(theme), + Accordion(theme), + Typography(theme), + TextField(theme), + TimelineDot(theme), + CssBaseline(theme), + LoadingButton(theme), + InputAdornment(theme), + TimelineContent(theme), + TimelineConnector(theme) + ); +} diff --git a/src/theme/palette.jsx b/src/theme/palette.jsx new file mode 100644 index 0000000..ee1cf04 --- /dev/null +++ b/src/theme/palette.jsx @@ -0,0 +1,158 @@ +import { alpha } from '@mui/material/styles'; + +const GREY = { + 0: '#FFFFFF', + 1: '#a7a7a7', + 2: '#ececec', + 3: '#757575', + 4: '#7e7e7e', + 5: '#d6d6d6', + 6: '#575555', + 7: '#b4b4b4', + 8: '#b5b5b5', + 9: '#564f4f', + 10: '#0E2354', + 11: '#f5f8fa', + 12: '#ebf1f5', + 13: 'rgba(206,217,224,.5)', + 14: '#373737', + 15: '#efefef', + 16: '#353d4e', + 17: '#5d6471', + 18: '#d7dcef', + 19: '#f6f6f6', + 20: '#dbdbdb', + 21: '#f1f3f4', + 22: '#636363', + 23: '#f4f4f4', + 24: '#edeceb', + 25: '#70707033', + 26: '#f7f7f7', + 27: '#2121218a', + 28: '#f8f8f8', + 29: '#b6b6b6', + 30: '#E9EAEB', + 31: '#FFF5EA', + 32: '#12B76A', + 33: '#FFF0F1', + 34: '#212121', + 35: '#F5F5F5', + 40: '#808080', + 36: '#E7F4EE', + 37: '#FDEBD7', + 38: '#000000', + 39: '#E9EAEB6B', + 41: '#878787', + 42: '#FFF5F4', + 43: '#AD6FFF', + 51: '#FFF5EA', + 52: '#FF8A00', + 53: '#0075FF', + 54: '#12B76A', + 55: '#232323', + 56: '#949494', + 57: '#D6313A', + 58: '#686868', + 59: '#FAFAFA', + 60: '#E3D8CB', + 61: '#C1DED1', + 62: '#EEF6FF', + 63: '#DCE4ED', + 64: '#00C76A', + 65: '#D8D8D8', + 66: '#5E616A', + 67: '#FF385C', + 100: '#F9FAFB', + 200: '#F4F6F8', + 300: '#DFE3E8', + 400: '#C4CDD5', + 500: '#919EAB', + 600: '#637381', + 700: '#454F5B', + 800: '#0E2354', + 900: '#161C24', + 500_8: alpha('#919EAB', 0.08), + 500_12: alpha('#919EAB', 0.12), + 500_16: alpha('#919EAB', 0.16), + 500_24: alpha('#919EAB', 0.24), + 500_32: alpha('#919EAB', 0.32), + 500_48: alpha('#919EAB', 0.48), + 500_56: alpha('#919EAB', 0.56), + 500_80: alpha('#919EAB', 0.8), +}; + +const primary = { + main: '#FF385C', + // main: '#d6313a', + highlight: '#ffe4e2', +}; + +const secondary = { + main: '#ececec', +}; +const info = { + main: '#1890FF', + contrastText: '#fff', +}; +const success = { + main: '#05DC68', + lightGreen: '#1FEE7E', + + contrastText: GREY[800], +}; + +const warning = { + main: '#FFC107', + contrastText: GREY[800], +}; + +const error = { + main: '#FF4842', + contrastText: '#fff', +}; + +const palette = { + common: { black: '#101010', white: '#fff' }, + primary: { ...primary }, + secondary: { ...secondary }, + info: { ...info }, + success: { ...success }, + warning: { ...warning }, + error: { ...error }, + grey: GREY, + divider: GREY[500_24], + text: { primary: GREY[800], secondary: GREY[600], disabled: GREY[500] }, + background: { + paper: '#fff', + default: GREY[100], + neutral: GREY[200], + sidebar: '#192235', + }, + action: { + active: GREY[600], + hover: GREY[500_8], + selected: GREY[500_16], + disabled: GREY[500_80], + disabledBackground: GREY[500_24], + focus: GREY[500_24], + hoverOpacity: 0.08, + disabledOpacity: 0.48, + }, + green: { + main: '#14d1ad', + light: '#eafffb', + }, + orange: { + main: '#ff6200', + light: '#FFF5EA', + }, + blue: { + main: '#3d90c6', + light: '#e0f2ff', + }, + pink: { + main: '#FF6F78', + }, +}; + +export default palette; diff --git a/src/theme/shadows.jsx b/src/theme/shadows.jsx new file mode 100644 index 0000000..e38f5c5 --- /dev/null +++ b/src/theme/shadows.jsx @@ -0,0 +1,62 @@ +import { alpha } from '@mui/material/styles'; +import palette from './palette'; + +const LIGHT_MODE = palette.grey[500]; + +const createShadow = (color) => { + const transparent1 = alpha(color, 0.2); + const transparent2 = alpha(color, 0.14); + const transparent3 = alpha(color, 0.12); + return [ + 'none', + `0px 2px 1px -1px ${transparent1},0px 1px 1px 0px ${transparent2},0px 1px 3px 0px ${transparent3}`, + `0px 3px 1px -2px ${transparent1},0px 2px 2px 0px ${transparent2},0px 1px 5px 0px ${transparent3}`, + `0px 3px 3px -2px ${transparent1},0px 3px 4px 0px ${transparent2},0px 1px 8px 0px ${transparent3}`, + `0px 2px 4px -1px ${transparent1},0px 4px 5px 0px ${transparent2},0px 1px 10px 0px ${transparent3}`, + `0px 3px 5px -1px ${transparent1},0px 5px 8px 0px ${transparent2},0px 1px 14px 0px ${transparent3}`, + `0px 3px 5px -1px ${transparent1},0px 6px 10px 0px ${transparent2},0px 1px 18px 0px ${transparent3}`, + `0px 4px 5px -2px ${transparent1},0px 7px 10px 1px ${transparent2},0px 2px 16px 1px ${transparent3}`, + `0px 5px 5px -3px ${transparent1},0px 8px 10px 1px ${transparent2},0px 3px 14px 2px ${transparent3}`, + `0px 5px 6px -3px ${transparent1},0px 9px 12px 1px ${transparent2},0px 3px 16px 2px ${transparent3}`, + `0px 6px 6px -3px ${transparent1},0px 10px 14px 1px ${transparent2},0px 4px 18px 3px ${transparent3}`, + `0px 6px 7px -4px ${transparent1},0px 11px 15px 1px ${transparent2},0px 4px 20px 3px ${transparent3}`, + `0px 7px 8px -4px ${transparent1},0px 12px 17px 2px ${transparent2},0px 5px 22px 4px ${transparent3}`, + `0px 7px 8px -4px ${transparent1},0px 13px 19px 2px ${transparent2},0px 5px 24px 4px ${transparent3}`, + `0px 7px 9px -4px ${transparent1},0px 14px 21px 2px ${transparent2},0px 5px 26px 4px ${transparent3}`, + `0px 8px 9px -5px ${transparent1},0px 15px 22px 2px ${transparent2},0px 6px 28px 5px ${transparent3}`, + `0px 8px 10px -5px ${transparent1},0px 16px 24px 2px ${transparent2},0px 6px 30px 5px ${transparent3}`, + `0px 8px 11px -5px ${transparent1},0px 17px 26px 2px ${transparent2},0px 6px 32px 5px ${transparent3}`, + `0px 9px 11px -5px ${transparent1},0px 18px 28px 2px ${transparent2},0px 7px 34px 6px ${transparent3}`, + `0px 9px 12px -6px ${transparent1},0px 19px 29px 2px ${transparent2},0px 7px 36px 6px ${transparent3}`, + `0px 10px 13px -6px ${transparent1},0px 20px 31px 3px ${transparent2},0px 8px 38px 7px ${transparent3}`, + `0px 10px 13px -6px ${transparent1},0px 21px 33px 3px ${transparent2},0px 8px 40px 7px ${transparent3}`, + `0px 10px 14px -6px ${transparent1},0px 22px 35px 3px ${transparent2},0px 8px 42px 7px ${transparent3}`, + `0px 11px 14px -7px ${transparent1},0px 23px 36px 3px ${transparent2},0px 9px 44px 8px ${transparent3}`, + `0px 11px 15px -7px ${transparent1},0px 24px 38px 3px ${transparent2},0px 9px 46px 8px ${transparent3}`, + ]; +}; + +const createCustomShadow = (color) => { + const transparent = alpha(color, 0.24); + + return { + z1: `0 1px 2px 0 ${transparent}`, + z8: `0 8px 16px 0 ${transparent}`, + z12: `0 0 2px 0 ${transparent}, 0 12px 24px 0 ${transparent}`, + z16: `0 0 2px 0 ${transparent}, 0 16px 32px -4px ${transparent}`, + z20: `0 0 2px 0 ${transparent}, 0 20px 40px -4px ${transparent}`, + z24: `0 0 4px 0 ${transparent}, 0 24px 48px 0 ${transparent}`, + primary: `0 8px 16px 0 ${alpha(palette.primary.main, 0.24)}`, + secondary: `0 8px 16px 0 ${alpha(palette.secondary.main, 0.24)}`, + info: `0 8px 16px 0 ${alpha(palette.info.main, 0.24)}`, + success: `0 8px 16px 0 ${alpha(palette.success.main, 0.24)}`, + warning: `0 8px 16px 0 ${alpha(palette.warning.main, 0.24)}`, + error: `0 8px 16px 0 ${alpha(palette.error.main, 0.24)}`, + }; +}; + +export const customShadows = createCustomShadow(LIGHT_MODE); + +const shadows = createShadow(LIGHT_MODE); + +export default shadows; diff --git a/src/theme/typography.jsx b/src/theme/typography.jsx new file mode 100644 index 0000000..6a58f7d --- /dev/null +++ b/src/theme/typography.jsx @@ -0,0 +1,99 @@ +export function pxToRem(value) { + return `${value / 16}rem`; +} + +export function responsiveFontSizes({ sm, md, lg }) { + return { + '@media (min-width:600px)': { + fontSize: pxToRem(sm), + }, + '@media (min-width:900px)': { + fontSize: pxToRem(md), + }, + '@media (min-width:1200px)': { + fontSize: pxToRem(lg), + }, + }; +} + +const FONT_PRIMARY = 'Inter-Medium'; + +const typography = { + fontFamily: FONT_PRIMARY, + fontWeightRegular: 400, + fontWeightMedium: 600, + fontWeightBold: 700, + h1: { + fontWeight: 700, + lineHeight: 80 / 64, + fontSize: pxToRem(40), + ...responsiveFontSizes({ sm: 52, md: 58, lg: 64 }), + }, + h2: { + fontWeight: 700, + lineHeight: 64 / 48, + fontSize: pxToRem(32), + ...responsiveFontSizes({ sm: 40, md: 44, lg: 48 }), + }, + h3: { + fontWeight: 700, + lineHeight: 1.5, + fontSize: pxToRem(24), + ...responsiveFontSizes({ sm: 26, md: 30, lg: 32 }), + }, + h4: { + fontWeight: 700, + lineHeight: 1.5, + fontSize: pxToRem(20), + ...responsiveFontSizes({ sm: 20, md: 24, lg: 24 }), + }, + h5: { + fontWeight: 700, + lineHeight: 1.5, + fontSize: pxToRem(18), + ...responsiveFontSizes({ sm: 19, md: 20, lg: 20 }), + }, + h6: { + fontWeight: 700, + lineHeight: 28 / 18, + fontSize: pxToRem(17), + ...responsiveFontSizes({ sm: 18, md: 18, lg: 18 }), + }, + subtitle1: { + fontWeight: 600, + lineHeight: 1.5, + fontSize: pxToRem(16), + }, + subtitle2: { + fontWeight: 600, + lineHeight: 22 / 14, + fontSize: pxToRem(14), + }, + body1: { + lineHeight: 1.5, + fontSize: pxToRem(16), + }, + body2: { + lineHeight: 22 / 14, + fontSize: pxToRem(14), + }, + caption: { + lineHeight: 1.5, + fontSize: pxToRem(12), + }, + overline: { + fontWeight: 700, + lineHeight: 1.5, + fontSize: pxToRem(12), + letterSpacing: 1.1, + textTransform: 'uppercase', + }, + button: { + fontWeight: 700, + lineHeight: 24 / 14, + fontSize: pxToRem(14), + textTransform: 'capitalize', + }, +}; + +export default typography; diff --git a/src/utils/calculateDaysLeft.js b/src/utils/calculateDaysLeft.js new file mode 100644 index 0000000..f879b17 --- /dev/null +++ b/src/utils/calculateDaysLeft.js @@ -0,0 +1,7 @@ +import { differenceInDays, parseISO } from 'date-fns'; + +export const calculateDaysLeft = (endTime) => { + const endDate = parseISO(endTime); + const currentDate = new Date(); + return differenceInDays(endDate, currentDate); +}; diff --git a/src/utils/notification.js b/src/utils/notification.js new file mode 100644 index 0000000..2f2e6b5 --- /dev/null +++ b/src/utils/notification.js @@ -0,0 +1,73 @@ +import { toast } from 'react-toastify'; +import { NOTIFICATION } from '../constants'; + +let lastNotification; +let lastNotificationTime; + +export function pushNotification(msg, type) { + if (msg === lastNotification && Date.now() - lastNotificationTime < 1000) { + // if the same message was shown within last 1 seconds, do not show it again + return; + } + switch (type) { + case NOTIFICATION.INFO: + toast.info(msg, { + position: toast.POSITION.TOP_RIGHT, + autoClose: 3000, + pauseOnFocusLoss: true, + pauseOnHover: true, + newestOnTop: true, + theme: 'colored', + }); + break; + case NOTIFICATION.SUCCESS: + toast.success(msg, { + position: toast.POSITION.TOP_RIGHT, + autoClose: 3000, + pauseOnFocusLoss: true, + pauseOnHover: true, + newestOnTop: true, + theme: 'colored', + }); + break; + + case NOTIFICATION.WARNING: + toast.warn(msg, { + position: toast.POSITION.TOP_RIGHT, + autoClose: 3000, + pauseOnFocusLoss: true, + pauseOnHover: true, + newestOnTop: true, + theme: 'colored', + }); + break; + case NOTIFICATION.ERROR: + toast.error(msg, { + position: toast.POSITION.TOP_RIGHT, + autoClose: 3000, + pauseOnFocusLoss: true, + pauseOnHover: true, + newestOnTop: true, + theme: 'colored', + }); + break; + default: + toast.info(msg, { + position: toast.POSITION.TOP_RIGHT, + autoClose: 3000, + pauseOnFocusLoss: true, + pauseOnHover: true, + newestOnTop: true, + theme: 'colored', + }); + break; + } + + // // update the last notification and time in local storage + // localStorage.setItem(LAST_NOTIFICATION_KEY, msg); + // localStorage.setItem(LAST_NOTIFICATION_TIME_KEY, Date.now()); + + // update the last notification and time in memory + lastNotification = msg; + lastNotificationTime = Date.now(); +} diff --git a/src/utils/regex.js b/src/utils/regex.js new file mode 100644 index 0000000..e17f2c0 --- /dev/null +++ b/src/utils/regex.js @@ -0,0 +1,11 @@ +// passwordLengthRegex.js +export const passwordLengthRegex = /^.{8,}$/; + +// passwordLetterRegex.js +export const passwordLetterRegex = /[a-zA-Z]/; + +// passwordNumberRegex.js +export const passwordNumberRegex = /\d/; + +// passwordSpacesRegex.js +export const passwordSpacesRegex = /^\S.*\S$/; diff --git a/src/utils/share.js b/src/utils/share.js new file mode 100644 index 0000000..32d65d5 --- /dev/null +++ b/src/utils/share.js @@ -0,0 +1,71 @@ +import { logout } from "../services/users.service"; +import { deleteToken } from "firebase/messaging"; +import { messaging } from "../config/firebase"; + +export const isLoggedIn = () => { + let redux = JSON.parse(localStorage.getItem("redux")); + if (redux?.login?.user) { + return true; + } + return false; +}; + +export function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +export function capitalizeAllLetters(string) { + return string.toUpperCase(); +} + + +export const resetLocalStorage = () => { + Object.keys(localStorage).forEach((key) => { + localStorage.removeItem(key); + }); +}; + +export const isBSPortal = () => { + let redux = JSON.parse(localStorage.getItem("redux")); + const isBSAdmin = redux?.login?.user?.isBsAdmin; + return { isBSAdmin }; +}; + +export const commonLogoutFunc = async (redirectPath = "/auth/login") => { + try { + await logout(); + resetLocalStorage(); + // Try to delete the Firebase messaging token, but don't let it block logout if it fails + try { + await deleteToken(messaging); + } catch (error) { + console.warn('Failed to delete Firebase messaging token:', error); + // Continue with logout even if token deletion fails + } + window.location.replace(redirectPath); + } catch (error) { + console.error('Error during logout:', error); + // Ensure user is still logged out even if there's an error + resetLocalStorage(); + window.location.replace(redirectPath); + } +}; + +export const checkModulePermission = (moduleName) => { + let redux = JSON.parse(localStorage.getItem("redux")); + const userPermissions = redux?.login?.user?.permissions ?? []; + let hasAccess = false; + if (moduleName && userPermissions) { + for (let permission of userPermissions) { + let trimmedPermissions = permission.replace("CREATE_", ""); + trimmedPermissions = permission.replace("READ_", ""); + trimmedPermissions = permission.replace("DELETE_", ""); + if (trimmedPermissions.includes(moduleName)) { + hasAccess = true; + break; + } + } + return hasAccess; + } + return true; +}; diff --git a/src/views/ClinicDetails/clinicDetailsStyles.js b/src/views/ClinicDetails/clinicDetailsStyles.js new file mode 100644 index 0000000..a2ef136 --- /dev/null +++ b/src/views/ClinicDetails/clinicDetailsStyles.js @@ -0,0 +1,151 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + inputLabel: { + marginBottom: theme.spacing(0.5), + fontSize: pxToRem(12), + color: theme.palette.grey[10], + }, + generalInfoPaper: { + border: '1px solid', + borderColor: theme.palette.grey[20], + marginTop: theme.spacing(1), + }, + cancelRejectionBox: { + // paddingTop: theme.spacing(0.5), + width: '100%', + justifyContent: 'space-between', + marginTop: '10px', + }, + cancelRejectionLink: { + color: theme.palette.primary.main, + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.regular, + fontWeight: 600, + }, + enableRejectButton: { + marginLeft: theme.spacing(1.5), + color: theme.palette.common.white, + backgroundColor: theme.palette.primary.main, + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.primary.main, + }, + }, + disableRejectButton: { + opacity: 0.5, + marginLeft: theme.spacing(1.5), + cursor: 'default', + border: '1px solid', + borderColor: theme.palette.grey[13], + backgroundColor: theme.palette.common.white, + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.common.white, + }, + }, + statusButtonIcon: { + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + enableOnHoldButton: { + maxHeight: '35px', + marginLeft: theme.spacing(1.5), + color: theme.palette.common.white, + backgroundColor: theme.palette.grey[52], + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.grey[52], + }, + }, + disableOnHoldButton: { + maxHeight: '35px', + opacity: 0.5, + marginLeft: theme.spacing(1.5), + cursor: 'default', + border: '1px solid', + color: theme.palette.grey[52], + borderColor: theme.palette.grey[13], + backgroundColor: theme.palette.common.white, + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.common.white, + }, + }, + onHoldButtonIcon: { + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + enableAcceptButton: { + marginLeft: theme.spacing(1.5), + color: theme.palette.common.white, + backgroundColor: theme.palette.grey[32], + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.grey[32], + }, + }, + disableAcceptButton: { + maxHeight: '38px', + opacity: 0.5, + marginLeft: theme.spacing(1.5), + color: theme.palette.grey[32], + backgroundColor: theme.palette.common.white, + fontFamily: theme.fontFamily.regular, + border: '1px solid', + borderColor: theme.palette.grey[13], + borderRadius: theme.shape.borderRadiusComponent, + cursor: 'default', + '&:hover': { + backgroundColor: theme.palette.common.white, + }, + }, + acceptButtonIcon: { + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + reasonModelBox: { + marginTop: theme.spacing(2), + marginLeft: theme.spacing(0), + marginRight: theme.spacing(0), + paddingLeft: theme.spacing(0), + paddingRight: theme.spacing(0), + }, + inputTextGrid: { + marginTop: theme.spacing(3), + paddingTop: theme.spacing(1), + }, + submitButton: { + justifyContent: 'center', + marginTop: theme.spacing(2), + }, + uploadGSTInvoiceIcon: { + width: '20px', + height: '20px', + }, + approvedCompanyMoreDetailsHeading: { + paddingLeft: theme.spacing(0), + marginBottom: theme.spacing(1), + }, + tabsPanel: { + borderTop: '1px solid', + borderTopColor: theme.palette.grey[7], + padding: theme.spacing(0), + margin: theme.spacing(0), + }, + warning: { + color: theme.palette.primary.main, + }, + success: { + color: theme.palette.success.main, + }, + editCompanyInfoButton: { + height: '46px', + }, +})); diff --git a/src/views/ClinicDetails/component/FileEvaluate.jsx b/src/views/ClinicDetails/component/FileEvaluate.jsx new file mode 100644 index 0000000..c6a1e27 --- /dev/null +++ b/src/views/ClinicDetails/component/FileEvaluate.jsx @@ -0,0 +1,659 @@ +import CloseIcon from '@mui/icons-material/Close'; +import DoneIcon from '@mui/icons-material/Done'; +import RemoveRedEyeOutlinedIcon from '@mui/icons-material/RemoveRedEyeOutlined'; +import SaveAltIcon from '@mui/icons-material/SaveAlt'; +import VerifiedIcon from '@mui/icons-material/Verified'; +import { Box, Button, Grid, Modal, Typography } from '@mui/material'; +import { format } from 'date-fns'; +import React, { useEffect, useState } from 'react'; +import downloadIcon from '../../../assets/images/icon/download.svg'; +import ImagePreviewComponent from '../../../components/ImagePreviewComponent'; +import { + ABN_NUMBER_LENGTH, + CLINIC_DOCUMENT_STATUS, + CLINIC_STATUS, + FILE_EXTENTIONS_ICONS, + GST_NUMBER_LENGTH, +} from '../../../constants'; +import { useStyles } from './styles/fileEvaluateStyles'; + +const FileEvaluate = ({ + companyStatus, + companyName, + files, + onFileButtonClick, +}) => { + const classes = useStyles(); + const [isPreview, setIsPreview] = useState(false); + const [previewFile, setPreviewFile] = useState(); + const [reviewedLogoFiles, setReviewedLogoFiles] = useState([]); + const [notReviewedLogoFiles, setNotReviewedLogoFiles] = useState([]); + const [reviewedOtherFiles, setReviewedOtherFiles] = useState([]); + const [notReviewedOtherFiles, setNotReviewedOtherFile] = useState([]); + + const getAscendingArray = (array) => { + const gstFiles = array.filter((file) => file.documentType === 'GST'); + const panFiles = array.filter((file) => file.documentType === 'PAN'); + const tanFiles = array.filter((file) => file.documentType === 'TAN'); + const filteredArray = []; + const maxLength = Math.max( + gstFiles.length, + panFiles.length, + tanFiles.length + ); + + for (let i = 0; i < maxLength; i++) { + if (gstFiles[i]) { + filteredArray.push(gstFiles[i]); + } + if (panFiles[i]) { + filteredArray.push(panFiles[i]); + } + if (tanFiles[i]) { + filteredArray.push(tanFiles[i]); + } + } + + return filteredArray; + }; + + useEffect(() => { + // ...........reviewed logo file set............... + if (Array.isArray(files)) { + const filteredFiles = files.filter( + (file) => + file.documentType === 'LOGO' && + (file.status === CLINIC_DOCUMENT_STATUS.APPROVED || + file.status === CLINIC_DOCUMENT_STATUS.REJECTED) + ); + setReviewedLogoFiles(filteredFiles); + } + // .............no review logo files set............ + if (Array.isArray(files)) { + const filteredFiles = files.filter( + (file) => + file.documentType === 'LOGO' && + file.status === CLINIC_DOCUMENT_STATUS.NOT_REVIEWED + ); + setNotReviewedLogoFiles(filteredFiles); + } + // ..............reviewed other file set............ + if (Array.isArray(files)) { + const filteredFiles = files.filter( + (file) => + (file.documentType === 'PAN' || + file.documentType === 'TAN' || + file.documentType === 'GST') && + (file.status === CLINIC_DOCUMENT_STATUS.APPROVED || + file.status === CLINIC_DOCUMENT_STATUS.REJECTED) + ); + setReviewedOtherFiles(getAscendingArray(filteredFiles)); + } + // .............. no reviewed other file set........... + if (Array.isArray(files)) { + const filteredFiles = files.filter( + (file) => + (file.documentType === 'PAN' || + file.documentType === 'TAN' || + file.documentType === 'GST') && + file.status === CLINIC_DOCUMENT_STATUS.NOT_REVIEWED + ); + setNotReviewedOtherFile(getAscendingArray(filteredFiles)); + } + }, [files]); + // .........................get file name and extention function....................... + const getFileNameUsingFile = (file) => { + if (file) { + const url = new URL(file.fileURL); + return url.pathname.split('/').pop(); + } + return; + }; + + const getFileExtentionUsingFile = (file) => { + if (file) { + const url = new URL(file.fileURL); + const fileName = url.pathname.split('/').pop(); + return fileName.split('.').pop(); + } + return; + }; + + // ..............handle reject and accept button click on not review files.................... + const handleActionClick = (index, file, actionType) => { + const newFile = { + ...file, + status: + actionType === 'ACCEPT' + ? CLINIC_DOCUMENT_STATUS.APPROVED + : CLINIC_DOCUMENT_STATUS.REJECTED, + }; + + if (file.documentType === 'LOGO') { + const updatedFiles = [...notReviewedLogoFiles]; + updatedFiles.splice(index, 1, newFile); + setNotReviewedLogoFiles(updatedFiles); + } else { + const updatedFiles = [...notReviewedOtherFiles]; + updatedFiles.splice(index, 1, newFile); + setNotReviewedOtherFile(updatedFiles); + } + + onFileButtonClick(newFile); + }; + + // ..............buttons................. + const ActionButton = ({ index, file, actionType, onClick }) => { + const isAccept = actionType === 'ACCEPT'; + const buttonClass = isAccept ? classes.acceptButton : classes.rejectButton; + const icon = isAccept ? ( + + ) : ( + + ); + const buttonText = isAccept ? 'Accept' : 'Reject'; + + return ( + + ); + }; + + const DisableRejectedButton = () => ( + <> + + + ); + + const DisableAcceptedButton = () => ( + <> + + + ); + // ............handle dowanload.......... + + const handleDownload = (file) => { + if (file) { + const anchor = document.createElement('a'); + anchor.href = file.fileURL; + anchor.download = getFileNameUsingFile(file); + anchor.click(); + } + }; + + const handlePreview = (file) => { + setIsPreview(true); + setPreviewFile(file); + }; + + const handleClose = () => { + setIsPreview(false); + setPreviewFile(); + }; + + return ( + <> + {/* ............CLINIC is Approved that time show only card............. */} + {companyStatus === CLINIC_STATUS.APPROVED ? ( + <> + + {reviewedLogoFiles.map((file, index) => ( + + + + + CLINIC LOGO INFO + + + Uploaded:{' '} + {format(new Date(file?.updatedAt), 'dd MMM yyyy')} + + + {companyName} LOGO + + + + Uploaded File + + + + ))} + + + {reviewedOtherFiles.map((file, index) => ( + + + + + CLINIC {file.documentType} INFO + + + Uploaded:{' '} + {format(new Date(file?.updatedAt), 'dd MMM yyyy')} + + + {file.documentNumber} + + + + Uploaded file + + + + ))} + + + ) : ( + <> + {/* .................logo reviewed grid................ */} + + {reviewedLogoFiles.map((file, index) => ( + + + + + CLINIC LOGO INFO + + + Uploaded:{' '} + {format(new Date(file?.updatedAt), 'dd MMM yyyy')} + + + {companyName} LOGO + + + + Uploaded File + + + + + + + {file.status === CLINIC_DOCUMENT_STATUS.REJECTED && ( + + )} + {file.status === CLINIC_DOCUMENT_STATUS.APPROVED && ( + + )} + {file.status === CLINIC_DOCUMENT_STATUS.NOT_REVIEWED && ( + + )} + + + + ))} + {notReviewedLogoFiles.map((file, index) => ( + + + + + CLINIC {file.documentType} INFO + + + Uploaded:{' '} + {format(new Date(file?.updatedAt), 'dd MMM yyyy')} + + + {companyName} LOGO + + + + {file.documentType} + + + + + + + {file.status === CLINIC_STATUS.NOT_REVIEWED ? ( + <> + + handleActionClick(index, file, 'REJECT') + } + /> + + handleActionClick(index, file, 'ACCEPT') + } + /> + + ) : file.status === CLINIC_DOCUMENT_STATUS.REJECTED ? ( + <> + + + handleActionClick(index, file, 'ACCEPT') + } + /> + + ) : ( + <> + + handleActionClick(index, file, 'REJECT') + } + /> + + + )} + + + + ))} + + + {/* .................another image grid............. */} + + {reviewedOtherFiles.map((file, index) => ( + + + + + CLINIC {file.documentType} INFO + + + Uploaded:{' '} + {format(new Date(file?.updatedAt), 'dd MMM yyyy')} + + + {file.documentNumber} + + + + Uploaded file + + + + + + + {file.status === CLINIC_DOCUMENT_STATUS.REJECTED && ( + + )} + {file.status === CLINIC_DOCUMENT_STATUS.APPROVED && ( + + )} + {file.status === CLINIC_DOCUMENT_STATUS.NOT_REVIEWED && ( + + )} + + + + ))} + + {notReviewedOtherFiles.map((file, index) => ( + + + + + CLINIC {file.documentType} INFO + + + Uploaded:{' '} + {format(new Date(file?.updatedAt), 'dd MMM yyyy')} + + + + {file.documentNumber} + + {file.documentNumber.length === GST_NUMBER_LENGTH && + file.documentType !== 'TAN' && + file.documentType !== 'PAN' && ( + + + + )} + {file.documentNumber.length === ABN_NUMBER_LENGTH && + (file.documentType === 'TAN' || + file.documentType === 'PAN') && ( + + + + )} + + + + + Uploaded file + + + + + + + {file.status === CLINIC_STATUS.NOT_REVIEWED ? ( + <> + + handleActionClick(index, file, 'REJECT') + } + /> + + handleActionClick(index, file, 'ACCEPT') + } + /> + + ) : file.status === CLINIC_DOCUMENT_STATUS.REJECTED ? ( + <> + + + handleActionClick(index, file, 'ACCEPT') + } + /> + + ) : ( + <> + + handleActionClick(index, file, 'REJECT') + } + /> + + + )} + + + + ))} + + + {previewFile && ( + + + + + + + + + + + + + {getFileNameUsingFile(previewFile)} + + + + + + handleDownload(previewFile)} + classes={classes} + /> + + + + )} + + )} + + ); +}; + +export default FileEvaluate; diff --git a/src/views/ClinicDetails/component/GeneralInformation.jsx b/src/views/ClinicDetails/component/GeneralInformation.jsx new file mode 100644 index 0000000..d8b8e4a --- /dev/null +++ b/src/views/ClinicDetails/component/GeneralInformation.jsx @@ -0,0 +1,147 @@ +import { LoadingButton } from '@mui/lab'; +import { Box, Divider, Grid, Typography } from '@mui/material'; +import { format } from 'date-fns'; +import { useDispatch } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import showIcon from '../../../assets/images/icon/Show.svg'; +import { CLINIC_STATUS, NOT_AVAILABLE_TEXT } from '../../../constants'; +import { profile } from '../../Login/loginAction'; +import { setClinicId } from '../store/logInAsClinicAdminAction'; +import { useStyles } from './styles/generalInformationStyles'; + +const GeneralInformation = ({ companyData, companyAdminData }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const classes = useStyles(); + + const handleViewCompanyDashboard = async () => { + dispatch(setClinicId({ companyId: companyData?.id })); + await dispatch(profile()); + navigate('/'); + }; + + return ( + + {/* ..................general info label............... */} + + + + GENERAL INFORMATION + + + + + Uploaded:{' '} + {companyData?.updatedAt + ? format(new Date(companyData?.createdAt), 'dd MMM yyyy') + : ''} + + + + {companyData?.status === CLINIC_STATUS.APPROVED && ( + } + onClick={handleViewCompanyDashboard} + > + View Clinics Dashboard + + )} + + + {/* .................company Name grid................. */} + + + + Para Hills + + + +
    + + Raised On:{' '} + {companyData?.requestRaisedOn + ? format(new Date(companyData?.requestRaisedOn), 'dd MMM yyyy') + : NOT_AVAILABLE_TEXT} + +
    + +
    + + Website: {companyData?.website} + +
    + +
    + + User ID: admin@gmail.com + +
    +
    +
    + {/* ..................comapny user details grid.............................. */} + + + + {companyAdminData?.name} + + + User Name + + + + + {companyAdminData?.designation + ? companyAdminData?.designation + : NOT_AVAILABLE_TEXT} + + + User Designation + + + + + {companyAdminData?.mobile + ? `+91-${companyAdminData.mobile}` + : NOT_AVAILABLE_TEXT} + + + Contact Number + + + + + {companyData?.pinCode ? companyData?.pinCode : NOT_AVAILABLE_TEXT} + + + Pincode + + + + + {/* ......................company address grid................ */} + + + + {companyData?.street ? companyData?.street : NOT_AVAILABLE_TEXT} + + + Full Address + + + +
    + ); +}; + +export default GeneralInformation; diff --git a/src/views/ClinicDetails/component/PlanDetails.jsx b/src/views/ClinicDetails/component/PlanDetails.jsx new file mode 100644 index 0000000..e95a880 --- /dev/null +++ b/src/views/ClinicDetails/component/PlanDetails.jsx @@ -0,0 +1,390 @@ +import { useStyles } from './styles/planDetailsStyles'; +import { useEffect, useMemo, useState } from 'react'; +import { + FEATURES, + NOTIFICATION, + PLAN_STATUS_TYPE, + SUBSCRIPTIONS_WARNING_LIMIT_IN_PERCENTAGE, +} from '../../../constants'; +import SimpleDataTable from '../../../components/SimpleDataTable'; +import { Box, Chip, Typography } from '@mui/material'; +import CircleIcon from '@mui/icons-material/Circle'; +import FormSectionHeading from '../../../components/FormSectionHeading'; +import { LoadingButton } from '@mui/lab'; +import { differenceInDays, format } from 'date-fns'; +import { updateCompanyCustomPlanStatus } from '../../../services/plan.services'; +import { pushNotification } from '../../../utils/notification'; +import CustomModal from '../../Modal/Modal'; +import OfflinePaymentForm from '../../../components/OfflinePaymentForm'; + +const PlanDetails = ({ companyData, reFetchData }) => { + const classes = useStyles(); + const [isStatusUpdateLoading, setIsStatusUpdateLoading] = useState(false); + const [lastPlanRows, setLastPlanRows] = useState([]); + const [planHistoryRows, setPlanHistoryRows] = useState([]); + const [topUpPlanRows, setTopUpPlanRows] = useState([]); + const [ + showTopUpPlanOfflinePaymentModel, + setShowTopUpPlanOfflinePaymentModel, + ] = useState(false); + + useEffect(() => { + setTopUpPlanRows( + companyData?.hasActivePlan ? [companyData?.hasActivePlan] : [] + ); + setLastPlanRows(companyData?.accounts ? [companyData?.accounts] : []); + setPlanHistoryRows( + companyData?.planHistory ? companyData?.planHistory : [] + ); + }, [companyData]); + + const checkValueIsInWarning = ( + firstValues, + secondValues, + extraAppendString + ) => { + const difference = (firstValues / secondValues) * 100; + + if (firstValues === null || secondValues === null) { + return ( + + {0} {'/'} {0} remaining + + ); + } + + if (firstValues === 0 && secondValues === 0) { + return ( + + {firstValues} {'/'} {secondValues} remaining + + ); + } + if (firstValues < 0 || secondValues < 0) { + return ( + + {0} {'/'} {0} {extraAppendString} remaining + + ); + } + if (firstValues === 0 && secondValues > 0) { + return ( + + {firstValues} {'/'} {secondValues} {extraAppendString} remaining + + ); + } + + return ( + <> + + {firstValues} {'/'} {secondValues} {extraAppendString} remaining + + + ); + }; + + const lastPlanColumns = useMemo( + () => [ + { + header: 'Plan Validity(days)', + isBold: true, + Cell: ({ row }) => ( +
    + {checkValueIsInWarning( + differenceInDays(new Date(row?.endTime), new Date()), + differenceInDays( + new Date(new Date(row?.endTime).setHours(0, 0, 0, 0)), + new Date(new Date(row?.startTime).setHours(0, 0, 0, 0)) + ), + 'days' + )} +
    + ), + }, + { + accessorKey: 'maxResumeDownloads', + header: 'View Resume', + isBold: true, + Cell: ({ row }) => ( +
    + {checkValueIsInWarning( + row?.maxResumeDownloads, + companyData?.totalMaxResumeDownloads + )} +
    + ), + }, + { + accessorKey: 'maxJobPostings', + header: 'Job Posting', + isBold: true, + Cell: ({ row }) => ( +
    + {checkValueIsInWarning( + row?.maxJobPostings, + companyData?.totalMaxJobPostings + )} +
    + ), + }, + { + accessorKey: 'features', + header: 'Candidate Search', + isBold: true, + Cell: ({ row }) => ( +
    + {row?.features?.includes(FEATURES.SEARCH_CANDIDATE) ? 'Yes' : 'No'} +
    + ), + }, + ], + [classes] + ); + + const planHistoryColumn = useMemo( + () => [ + { + header: 'Plan Start And End Date', + isBold: true, + Cell: ({ row }) => ( +
    + {format(new Date(row?.startTime), 'dd MMM yyyy')} - + {format(new Date(row?.endTime), 'dd MMM yyyy')} +
    + ), + }, + { + header: 'Plan Validity(days)', + isBold: true, + Cell: ({ row }) => ( +
    + {differenceInDays( + new Date(new Date(row?.endTime).setHours(0, 0, 0, 0)), + new Date(new Date(row?.startTime).setHours(0, 0, 0, 0)) + )}{' '} + days +
    + ), + }, + { + accessorKey: 'maxResumeDownloads', + isBold: true, + header: 'View Resume', + Cell: ({ row }) => ( +
    {row?.maxResumeDownloads}
    + ), + }, + { + accessorKey: 'maxJobPostings', + header: 'Job Posting', + isBold: true, + Cell: ({ row }) => ( +
    {row?.maxJobPostings}
    + ), + }, + { + accessorKey: 'features', + header: 'Candidate Search', + isBold: true, + Cell: ({ row }) => ( +
    + {row?.features?.includes(FEATURES.SEARCH_CANDIDATE) ? 'Yes' : 'No'} +
    + ), + }, + ], + [classes] + ); + + const topUpPlanColumns = useMemo( + () => [ + { + header: 'Plan Validity(days)', + isBold: true, + Cell: ({ row }) => ( +
    {row?.validityInDays} days
    + ), + }, + { + accessorKey: 'maxResumeDownloads', + isBold: true, + header: 'View Resume', + Cell: ({ row }) => ( +
    {row?.maxResumeDownloads}
    + ), + }, + { + accessorKey: 'maxJobPostings', + header: 'Job Posting', + isBold: true, + Cell: ({ row }) => ( +
    {row?.maxJobPostings}
    + ), + }, + { + accessorKey: 'features', + header: 'Candidate Search', + isBold: true, + Cell: ({ row }) => ( +
    + {row?.features?.includes(FEATURES.SEARCH_CANDIDATE) ? 'Yes' : 'No'} +
    + ), + }, + ], + [classes] + ); + + const handleTopUpPlanStatusUpdate = async () => { + try { + setIsStatusUpdateLoading(true); + const updateStatus = + companyData?.hasActivePlan?.status === PLAN_STATUS_TYPE.ACTIVATED + ? PLAN_STATUS_TYPE.DEACTIVATED + : PLAN_STATUS_TYPE.ACTIVATED; + + const body = { status: updateStatus }; + const resp = await updateCompanyCustomPlanStatus( + companyData?.hasActivePlan?.id, + body + ); + + if (resp?.data?.error) { + pushNotification(resp?.data?.error, NOTIFICATION.ERROR); + } else { + pushNotification(resp?.data?.message, NOTIFICATION.SUCCESS); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error will status update:', error); + } finally { + setIsStatusUpdateLoading(false); + reFetchData(); + } + }; + + const handleOfflinePayment = () => { + setShowTopUpPlanOfflinePaymentModel(true); + }; + + const handleModelClose = () => { + reFetchData(); + setShowTopUpPlanOfflinePaymentModel(false); + }; + + return ( + + {companyData?.hasActivePlan?.status !== PLAN_STATUS_TYPE.PURCHASED && ( + + + + {companyData?.planHistory?.length === 0 + ? 'Added Custom Plan' + : 'Top Up Plan'} + + {companyData?.hasActivePlan?.status === + PLAN_STATUS_TYPE.ACTIVATED && ( + + + Payment Awaited + + } + /> + )} + + + + {companyData?.hasActivePlan?.status === + PLAN_STATUS_TYPE.ACTIVATED && ( + + Deactivate Plan + + )} + {companyData?.hasActivePlan?.status === + PLAN_STATUS_TYPE.DEACTIVATED && ( + + Reactivate Plan + + )} + {companyData?.hasActivePlan?.status === + PLAN_STATUS_TYPE.ACTIVATED && ( + + Offline Payment + + )} + + + )} + {/* ................last plan data table......... */} + + + + + + + + + {showTopUpPlanOfflinePaymentModel && ( + <> + ( + + )} + /> + + )} + + ); +}; + +export default PlanDetails; diff --git a/src/views/ClinicDetails/component/RecruitmentAnalytics.jsx b/src/views/ClinicDetails/component/RecruitmentAnalytics.jsx new file mode 100644 index 0000000..3bf4ce0 --- /dev/null +++ b/src/views/ClinicDetails/component/RecruitmentAnalytics.jsx @@ -0,0 +1,95 @@ +import { Box, Card, Divider, Grid, Typography } from '@mui/material'; +import { useStyles } from './styles/recruitmentAnalyticsComponentStyles'; + +const RecruitmentAnalytics = ({ + totalJobPublishedCount, + activeJobsCount, + expiredJobsCount, + jobsInDraftCount, +}) => { + const classes = useStyles(); + + const formatCount = (count) => (count < 10 ? `0${count}` : count); + + return ( + + + + {/* ............Total Job Published.............. */} + + + {formatCount(totalJobPublishedCount)} + + + Total Job Published + + + + + {/* ..............Active Jobs.................... */} + + + {formatCount(activeJobsCount)} + + + Active Jobs + + + + {/* ................Expired Jobs.................. */} + + + {formatCount(expiredJobsCount)} + + + Expired Jobs + + + + {/* ................Jobs in Draft................. */} + + + {formatCount(jobsInDraftCount)} + + + Jobs in Draft + + + + + + + ); +}; + +export default RecruitmentAnalytics; diff --git a/src/views/ClinicDetails/component/styles/fileEvaluateStyles.js b/src/views/ClinicDetails/component/styles/fileEvaluateStyles.js new file mode 100644 index 0000000..f882846 --- /dev/null +++ b/src/views/ClinicDetails/component/styles/fileEvaluateStyles.js @@ -0,0 +1,242 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + mainGrid: { + marginTop: theme.spacing(2), + display: 'flex', + }, + outerGrid: { + width: '31%', + backgroundColor: 'white', + border: '1px solid', + borderColor: theme.palette.grey[7], + borderRadius: theme.shape.borderRadiusComponent, + }, + disableOuterGrid: { + opacity: 0.5, + width: '31%', + backgroundColor: 'white', + border: '1px solid', + borderColor: theme.palette.grey[7], + borderRadius: theme.shape.borderRadiusComponent, + }, + enableApprovedDocument: { + width: '31%', + backgroundColor: 'white', + border: '1px solid', + borderColor: theme.palette.grey[7], + borderRadius: theme.shape.borderRadiusComponent, + }, + outerBox: { + padding: theme.spacing(2), + }, + titleOfBox: { + color: theme.palette.grey[40], + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.semiBold, + }, + logoPreviewImageBox: { + position: 'relative', + height: 176, + width: 176, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + overflow: 'hidden', + margin: 'auto', + marginTop: theme.spacing(2), + }, + imageBox: { + position: 'relative', + height: 160, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + overflow: 'hidden', + marginTop: theme.spacing(2), + }, + image: { + maxWidth: '100%', + maxHeight: '100%', + width: 'auto', + height: 'auto', + margin: theme.spacing(0), + }, + documentNumberAndNameLabelBox: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + documentNumberAndNameLabel: { + fontSize: pxToRem(20), + fontWeight: 600, + color: theme.palette.common.black, + overflow: 'hidden', + whiteSpace: 'pre-wrap', + }, + updatedDate: { + marginTop: theme.spacing(0.5), + textAlign: 'left', + fontSize: pxToRem(12), + }, + acceptButton: { + color: theme.palette.grey[32], + fontFamily: theme.fontFamily.regular, + border: '1px solid', + borderColor: theme.palette.grey[7], + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.green.light, + }, + }, + disableAcceptedButton: { + color: theme.palette.common.white, + backgroundColor: theme.palette.grey[32], + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + cursor: 'default', + '&:hover': { + backgroundColor: theme.palette.grey[32], + }, + }, + rejectButton: { + border: '1px solid', + borderColor: theme.palette.grey[7], + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + }, + disableRejectedButton: { + cursor: 'default', + backgroundColor: theme.palette.primary.main, + color: theme.palette.common.white, + fontFamily: theme.fontFamily.regular, + borderRadius: theme.shape.borderRadiusComponent, + '&:hover': { + backgroundColor: theme.palette.primary.main, + }, + }, + rejectButtonIcon: { + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + rejectedButtonIcon: { + color: theme.palette.common.white, + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + acceptButtonIcon: { + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + acceptedButtonIcon: { + color: theme.palette.common.white, + fontSize: pxToRem(22), + marginRight: theme.spacing(0.6), + }, + buttonBox: { + margin: 'auto', + marginTop: theme.spacing(2), + display: 'flex', + justifyContent: 'space-evenly', + alignItems: 'center', + width: '80%', + textAlign: 'center', + }, + verifyIcon: { + fontSize: pxToRem(20), + color: theme.palette.grey[54], + }, + onImageButton: { + border: '1px solid', + borderColor: theme.palette.grey[7], + borderRadius: theme.shape.borderRadiusComponent, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + position: 'absolute', + top: '50%', + left: '50%', + marginLeft: 'auto', + transform: 'translate(-50%, -50%)', + backgroundColor: 'rgba(0,0,0,0.3)', + + width: '100.2%', + height: '100.2%', + }, + eyeButton: { + padding: pxToRem(3), + borderRadius: theme.shape.borderRadius, + border: 'none', + cursor: 'pointer', + backgroundColor: 'rgba(0,0,0,0.6)', + color: theme.palette.common.white, + marginRight: pxToRem(7), + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + iconSize: { + fontSize: pxToRem(28), + }, + downloadButton: { + marginLeft: pxToRem(7), + padding: pxToRem(3), + borderRadius: theme.shape.borderRadius, + border: 'none', + cursor: 'pointer', + backgroundColor: 'rgba(0,0,0,0.6)', + color: theme.palette.common.white, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + + // ............preview componnt style..... + previewFullScreenBox: { + margin: theme.spacing(4), + }, + previewHeading: { + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + }, + previewDownloadButton: { + cursor: 'pointer', + color: theme.palette.common.white, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + previewDownloadIcon: { + height: pxToRem(26), + width: pxToRem(26), + cursor: 'pointer', + }, + previewCloseButton: { + color: theme.palette.common.white, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.7)', + color: theme.palette.common.white, + }, + }, + previewFileTitle: { + marginTop: theme.spacing(-3), + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + [theme.breakpoints.down('sm')]: { + marginTop: theme.spacing(0), + }, + }, + previewImageGrid: { + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + justifyContent: 'center', + marginTop: theme.spacing(2), + }, +})); diff --git a/src/views/ClinicDetails/component/styles/generalInformationStyles.js b/src/views/ClinicDetails/component/styles/generalInformationStyles.js new file mode 100644 index 0000000..b3051f8 --- /dev/null +++ b/src/views/ClinicDetails/component/styles/generalInformationStyles.js @@ -0,0 +1,66 @@ +import { makeStyles } from '@mui/styles'; +import { pxToRem } from '../../../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + generalInfoBox: { + padding: theme.spacing(2), + }, + generalInfoLabel: { + color: theme.palette.grey[40], + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.semiBold, + }, + generalInfoUploaded: { + fontSize: pxToRem(12), + color: theme.palette.grey[17], + fontFamily: theme.fontFamily.regular, + }, + viewCompanyDashboardButton: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + showIcon: { + height: '24px', + width: '24px', + }, + companyNameGrid: { + paddingTop: theme.spacing(3), + }, + companyNameLabel: { + fontSize: pxToRem(24), + }, + companyNameSubTitleGrid: { + display: 'flex', + justifyContent: 'flex-start', + }, + companyNameSubTitleLabel: { + fontSize: pxToRem(14), + color: theme.palette.grey[17], + opacity: 0.6, + fontFamily: theme.fontFamily.regular, + }, + dividerClass: { + fontSize: pxToRem(14), + marginLeft: theme.spacing(1.8), + marginRight: theme.spacing(1.2), + }, + companyUserDetails: { + display: 'flex', + justifyContent: 'space-between', + paddingTop: theme.spacing(3), + marginRight: theme.spacing(5), + }, + companyUserDetailsTitle: { + fontSize: pxToRem(18), + }, + companyUserDetailsSubTitle: { + fontSize: pxToRem(12), + color: theme.palette.grey[17], + opacity: 0.6, + fontFamily: theme.fontFamily.regular, + }, + companyDetailsAddressGrid: { + paddingTop: theme.spacing(3), + }, +})); diff --git a/src/views/ClinicDetails/component/styles/planDetailsStyles.js b/src/views/ClinicDetails/component/styles/planDetailsStyles.js new file mode 100644 index 0000000..3b41b0b --- /dev/null +++ b/src/views/ClinicDetails/component/styles/planDetailsStyles.js @@ -0,0 +1,67 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + topUpPlanBox: { + width: '70%', + marginTop: theme.spacing(3), + }, + lastPlanBox: { + width: '70%', + marginTop: theme.spacing(1), + }, + planHistoryBox: { + marginTop: theme.spacing(1), + }, + topUpPlanHeadingBox: { + display: 'flex', + alignItems: 'center', + alignSelf: 'center', + flexDirection: 'row', + marginBottom: theme.spacing(1), + }, + topUpPlanHeading: { + paddingLeft: theme.spacing(0), + textTransform: 'uppercase', + fontSize: pxToRem(14), + fontWeight: '600', + }, + lastPlanHeading: { + paddingLeft: theme.spacing(0), + marginTop: theme.spacing(2), + marginBottom: theme.spacing(1.5), + }, + topUpPlanPaymentStatusChip: { + marginLeft: theme.spacing(1), + color: theme.palette.orange.main, + }, + dotIcon: { + fontSize: pxToRem(8), + color: 'inherit', + marginRight: theme.spacing(0.8), + }, + topUpButtonBox: { + marginTop: theme.spacing(1), + display: 'flex', + alignItem: 'center', + justifyContent: 'left', + width: '50%', + }, + topUpButtonClass: { + display: 'flex', + alignItem: 'center', + justifyContent: 'center', + }, + reactivateButtonClass: { + width: '50%', + display: 'flex', + alignItem: 'center', + justifyContent: 'center', + }, + warning: { + color: theme.palette.primary.main, + }, + success: { + color: theme.palette.success.main, + }, +})); diff --git a/src/views/ClinicDetails/component/styles/recruitmentAnalyticsComponentStyles.js b/src/views/ClinicDetails/component/styles/recruitmentAnalyticsComponentStyles.js new file mode 100644 index 0000000..8727820 --- /dev/null +++ b/src/views/ClinicDetails/component/styles/recruitmentAnalyticsComponentStyles.js @@ -0,0 +1,75 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + recruitmentAnalyticsCard: { + marginTop: theme.spacing(2), + width: '100%', + backgroundColor: theme.palette.common.white, + padding: theme.spacing(3), + border: '1px solid', + borderColor: theme.palette.grey[20], + borderRadius: theme.shape.borderRadiusComponent, + }, + recruitmentAnalyticsBox: { + borderRadius: theme.shape.borderRadiusComponent, + backgroundColor: theme.palette.primary.highlight, + }, + recruitmentAnalyticsGridContainer: { + padding: theme.spacing(2), + width: '100%', + display: 'flex', + justifyContent: 'center', + flexDirection: 'column', + alignItems: 'center', + }, + recruitmentAnalyticsJobTitleGrid: { + width: '100%', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + marginBottom: theme.spacing(1), + }, + recruitmentAnalyticsJobDetailsGrid: { + display: 'flex', + justifyContent: 'space-around', + paddingTop: theme.spacing(2), + paddingLeft: theme.spacing(3), + paddingRight: theme.spacing(3), + width: '100%', + }, + recruitmentAnalyticsJobDetailsGridItem: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + }, + recruitmentAnalyticsTotalJobTitle: { + fontSize: pxToRem(18), + fontFamily: theme.fontFamily.bold, + }, + recruitmentAnalyticsTotalJobCount: { + fontSize: pxToRem(24), + fontFamily: theme.fontFamily.bold, + }, + recruitmentAnalyticsJobDetailsItemTitle: { + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.regular, + }, + totalJobPublishCountColor: { + color: theme.palette.orange.main, + }, + totalCompletedJobsCountColor: { + color: theme.palette.blue.main, + }, + activeJobsCountColor: { + color: theme.palette.success.main, + }, + expiredJobsCountColor: { + color: theme.palette.primary.main, + }, + jobsInDraftCountColor: { + color: theme.palette.grey[43], + }, +})); diff --git a/src/views/ClinicDetails/index.jsx b/src/views/ClinicDetails/index.jsx new file mode 100644 index 0000000..e9810fa --- /dev/null +++ b/src/views/ClinicDetails/index.jsx @@ -0,0 +1,517 @@ +import CloseIcon from '@mui/icons-material/Close'; +import DoneIcon from '@mui/icons-material/Done'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Button, + Grid, + InputLabel, + Paper, + Tab, + TextField, + Typography, +} from '@mui/material'; +import { useFormik } from 'formik'; +import React, { useEffect, useRef, useState } from 'react'; +import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; +import * as Yup from 'yup'; +import onHoldDisableIcon from '../../assets/images/icon/onHoldDisable.svg'; +import onHoldEnableIcon from '../../assets/images/icon/onHoldEnable.svg'; +import CustomBreadcrumbs from '../../components/CustomBreadcrumbs'; +import Loader from '../../components/Loader'; +import PageHeader from '../../components/PageHeader'; +import { + CLINIC_DOCUMENT_STATUS, + CLINIC_STATUS, + NOTIFICATION, +} from '../../constants'; +import { + getClinicsById, + updateClinicStatus, +} from '../../services/clinics.service'; +import { pushNotification } from '../../utils/notification'; +import CustomModal from '../Modal/Modal'; +import { useStyles } from './clinicDetailsStyles'; + +import GeneralInformation from './component/GeneralInformation'; + +function ClinicDetails() { + const classes = useStyles(); + const { id } = useParams(); + const location = useLocation(); + const navigate = useNavigate(); + const queryParams = new URLSearchParams(location.search); + const [isLoading, setIsLoading] = useState(false); + const [companyData, setCompanyData] = useState(''); + const [companyAdminData, setCompanyAdminData] = useState(''); + const [isShowReasonModel, setIsShowReasonModel] = useState(false); + const [updateFiles, setUpdateFiles] = useState([]); + const [buttonClickStatus, setButtonClickStatus] = useState(''); + const [isRejectButtonShow, setIsRejectedButtonShow] = useState(false); + const [isOnHoldButtonShow, setIsOnHoldButtonShow] = useState(false); + const [isAcceptButtonShow, setIsAcceptedButtonShow] = useState(false); + const [tabValue, setTabValue] = useState(1); + + // ........................company details fetch................ + const fetchData = async () => { + try { + setIsLoading(true); + const response = await getClinicsById(id); + setCompanyData(response?.data?.data); + setCompanyAdminData( + response?.data?.data?.companyUsers?.find((user) => user.isAdmin) || null + ); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error fetching data:', error); + } finally { + setIsLoading(false); + } + }; + useEffect(() => { + fetchData(); + }, [id]); + + // ...................breadcrumbs array........................ + const breadcrumbs = [ + { + label: 'Dashboard', + path: '/', + }, + { + label: 'Clinic List', + path: '/clinics', + query: { tab: queryParams.get('tab') || 'UNREGISTERED' }, + }, + { + label: 'Clinics Details', + path: '', + }, + ]; + + // .......................formik schema and functions............................ + + const updateCompany = async (body) => { + try { + const response = await updateClinicStatus(companyData?.id, body); + if (response?.data?.error) { + pushNotification(response?.data?.message, NOTIFICATION.ERROR); + setIsShowReasonModel(false); + fetchData(); + } else { + pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + setIsShowReasonModel(false); + fetchData(); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error', error); + } + }; + + const handleSubmit = async (values) => { + const body = formatedData(values); + updateCompany(body); + setIsRejectedButtonShow(false); + setIsOnHoldButtonShow(false); + }; + const defaultFormData = useRef({ + reason: '', + }); + + const fieldRefs = { + reason: useRef(null), + }; + + const validationSchema = Yup.object({ + reason: Yup.string().required('Reason is required.'), + }); + const formik = useFormik({ + initialValues: defaultFormData.current, + validationSchema, + onSubmit: (values) => handleSubmit(values, formik), + }); + + // ................functions................. + const handleFileButtonClick = (file) => { + const oldIndex = updateFiles.findIndex((f) => f.fileURL === file.fileURL); + + if (oldIndex === -1) { + setUpdateFiles((prevFiles) => [...prevFiles, file]); + } else { + setUpdateFiles((prevFiles) => { + const updatedFiles = [...prevFiles]; + updatedFiles[oldIndex] = file; + return updatedFiles; + }); + } + }; + + const getNotReviewedFileCount = () => { + if (companyData?.companyDocuments) { + const notReviewedDocuments = companyData.companyDocuments.filter( + (document) => document.status === 'NOT_REVIEWED' + ); + return notReviewedDocuments.length; + } + return; + }; + + const handleCancelRejection = async () => { + try { + setIsLoading(true); + // const resp = await companyCancelRejection(companyData?.id); + // if (resp?.data?.error) { + // pushNotification(resp?.data?.message, NOTIFICATION.ERROR); + // fetchData(); + // } else { + // pushNotification(resp?.data?.message, NOTIFICATION.SUCCESS); + // fetchData(); + // setIsOnHoldButtonShow(false); + // setIsRejectedButtonShow(false); + // setUpdateFiles(() => []); + // setButtonClickStatus(''); + // } + console.log("cancel rejection") + } catch (error) { + // Handle error if needed + } finally { + setIsLoading(false); + } + }; + + const handleReasonModelClose = () => { + setIsShowReasonModel(false); + formik.resetForm(); + }; + const formatedData = (values) => { + const documentStatusMap = {}; + + updateFiles.forEach((doc) => { + documentStatusMap[doc.documentType] = doc.status; + }); + + const data = { + userId: companyAdminData?.id, + reason: values ? values.reason : '', + status: + buttonClickStatus === 'Rejected' + ? CLINIC_STATUS.REJECTED + : buttonClickStatus === 'On Hold' + ? CLINIC_STATUS.ON_HOLD + : CLINIC_STATUS.APPROVED, + documentStatus: { + ...documentStatusMap, + }, + }; + return data; + }; + + // .................handle accept reject and on hold event............. + const handleRejectClick = () => { + setIsShowReasonModel(true); + setButtonClickStatus('Rejected'); + }; + + const handleOnHoldClick = () => { + setIsShowReasonModel(true); + setButtonClickStatus('On Hold'); + }; + + const handleAcceptClick = () => { + // setIsShowReasonModel(true); + setButtonClickStatus('Accepted'); + const body = formatedData(); + + updateCompany(body); + }; + + // ..................update file use effects................ + useEffect(() => { + setIsRejectedButtonShow( + updateFiles.some( + (file) => file.status === CLINIC_DOCUMENT_STATUS.REJECTED + ) + ); + setIsOnHoldButtonShow( + updateFiles.some( + (file) => file.status === CLINIC_DOCUMENT_STATUS.REJECTED + ) + ); + const notReviewedFileCount = getNotReviewedFileCount(); + + const areAllFilesApproved = updateFiles.every( + (file) => file.status === CLINIC_DOCUMENT_STATUS.APPROVED + ); + if ( + notReviewedFileCount === updateFiles.length && + areAllFilesApproved && + companyData?.status !== CLINIC_STATUS.ON_HOLD && + companyData?.status !== CLINIC_STATUS.REJECTED + ) { + setIsAcceptedButtonShow(true); + } else { + setIsAcceptedButtonShow(false); + } + }, [updateFiles]); + + // ..............handle table change.......... + const handleTabChange = (event, newValue) => { + setTabValue(newValue); + }; + + const handleEditCompanyInfo = () => { + navigate(`/clinics/${companyData?.id}/edit`); + }; + + const handleSubmitClick = async () => { + const formikErrors = await formik.validateForm(); + const errors = Object.keys(formikErrors); + + if (errors.length) { + // Find the first invalid field and focus it + const firstErrorField = errors[0]; + const firstErrorRef = fieldRefs[firstErrorField]?.current; + + if (firstErrorRef) { + // Scroll to the first invalid field smoothly + if (typeof firstErrorRef?.scrollIntoView === 'function') { + firstErrorRef?.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + + // Focus the field after a slight delay (to ensure scrolling completes first) + setTimeout(() => firstErrorRef.focus(), 300); + } + + // Show error notification + if (formik?.touched[firstErrorField]) + pushNotification(formikErrors[firstErrorField], NOTIFICATION.ERROR); + } + + formik.handleSubmit(); + }; + + return ( + <> + + + + + + ) : ( + + {/* ...rejected button.... */} + + + + {companyData?.status === CLINIC_STATUS.REJECTED && ( + + Cancel Rejection + + )} + + + {/* ..........on hold Button..... */} + + + {/* ......accept button...... */} + + + ) + } + /> + + + {isLoading ? ( + + ) : ( + <> + + + + + + {/* {companyData.status === CLINIC_STATUS.APPROVED ? ( + <> + + + + + + + {/* */} + {/* */} + {/* + + + + + + + + + + + + + + + */} + {/* ) : ( + + + + )} */} + + )} + {isShowReasonModel && ( + ( + + + {buttonClickStatus === 'Rejected' + ? 'Please define a reason for rejecting this company.' + : 'Please define a reason for putting this company on hold.'} + + + + {buttonClickStatus === 'Rejected' + ? 'Reason for Rejection' + : 'Reason for On Hold'} + + + + + Submit + + + )} + > + )} + + + ); +} +export default ClinicDetails; diff --git a/src/views/ClinicDetails/store/logInAsClinicAdminAction.js b/src/views/ClinicDetails/store/logInAsClinicAdminAction.js new file mode 100644 index 0000000..760c516 --- /dev/null +++ b/src/views/ClinicDetails/store/logInAsClinicAdminAction.js @@ -0,0 +1,13 @@ +import { + RESET_CLINIC_ID, + SET_CLINIC_ID, +} from './logInAsClinicAdminActionType'; + +export const setClinicId = (data) => ({ + type: SET_CLINIC_ID, + payload: data, +}); + +export const resetClinicId = () => ({ + type: RESET_CLINIC_ID, +}); diff --git a/src/views/ClinicDetails/store/logInAsClinicAdminActionType.js b/src/views/ClinicDetails/store/logInAsClinicAdminActionType.js new file mode 100644 index 0000000..0df91bf --- /dev/null +++ b/src/views/ClinicDetails/store/logInAsClinicAdminActionType.js @@ -0,0 +1,2 @@ +export const SET_CLINIC_ID = 'SET_CLINIC_ID'; +export const RESET_CLINIC_ID = 'RESET_CLINIC_ID'; diff --git a/src/views/ClinicDetails/store/logInAsClinicAdminReducer.js b/src/views/ClinicDetails/store/logInAsClinicAdminReducer.js new file mode 100644 index 0000000..b49d014 --- /dev/null +++ b/src/views/ClinicDetails/store/logInAsClinicAdminReducer.js @@ -0,0 +1,27 @@ +import { + RESET_CLINIC_ID, + SET_CLINIC_ID, +} from './logInAsClinicAdminActionType'; + +const initialState = { + companyId: '', +}; +const updateCompanyId = (state, payload) => ({ + ...state, + ...payload, +}); +const resetClinicId = (state) => ({ + ...state, + ...initialState, +}); + +export default function (state = initialState, action) { + switch (action.type) { + case SET_CLINIC_ID: + return updateCompanyId(state, action.payload); + case RESET_CLINIC_ID: + return resetClinicId(state); + default: + return state; + } +} diff --git a/src/views/ClinicSetup/accordion.jsx b/src/views/ClinicSetup/accordion.jsx new file mode 100644 index 0000000..3334658 --- /dev/null +++ b/src/views/ClinicSetup/accordion.jsx @@ -0,0 +1,444 @@ +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import Accordion from '@mui/material/Accordion'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import FormControl from '@mui/material/FormControl'; +import Grid from '@mui/material/Grid'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import Paper from '@mui/material/Paper'; +import Select from '@mui/material/Select'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; +import * as React from 'react'; + +// Days of week for clinic timings +const daysOfWeek = [ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', +]; + +// Integration software options +const integrationOptions = ['BP Software', 'Medical Director', 'Other']; + +export default function ClinicSetupAccordion() { + // State for expanded accordion panel + const [expanded, setExpanded] = React.useState('generalInfo'); + + const handleAccordionChange = (panel) => (event, isExpanded) => { + setExpanded(isExpanded ? panel : false); + }; + + // Form state + const [formData, setFormData] = React.useState({ + // General Information + clinicPhone: '', + clinicAddress: '', + otherInfo: '', + + // Clinic Timings + regularTimings: daysOfWeek.reduce( + (acc, day) => ({ + ...acc, + [day]: { open: '', close: '' }, + }), + {} + ), + emergencyTimings: daysOfWeek.reduce( + (acc, day) => ({ + ...acc, + [day]: { open: '', close: '' }, + }), + {} + ), + + // Greetings Setup + clinicGreetings: '', + + // Integration Settings + integrationSoftware: '', + practiceId: '', + practiceName: '', + }); + + // Handle simple input changes + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + // Handle timing changes + const handleTimingChange = (timingType, day, timeType, value) => { + setFormData((prev) => ({ + ...prev, + [timingType]: { + ...prev[timingType], + [day]: { + ...prev[timingType][day], + [timeType]: value, + }, + }, + })); + }; + + // Handle form submission + const handleSubmit = (e) => { + e.preventDefault(); + console.log('Form submitted:', formData); + // Here you would typically send the data to your backend + alert('Clinic setup completed successfully!'); + }; + + return ( + + + + Clinic Setup + + + {/* General Information Accordion */} + + } + aria-controls="generalInfo-content" + id="generalInfo-header" + sx={{ backgroundColor: '#f5f5f5' }} + > + + Scenario + + + + + + + + + + + + + + + + + + {/* Clinic Timings Accordion */} + + } + aria-controls="clinicTimings-content" + id="clinicTimings-header" + sx={{ backgroundColor: '#f5f5f5' }} + > + + Clinic Timings + + + + + + Regular Timings (Daywise) + + + {daysOfWeek.map((day) => ( + + + {day} + + + + handleTimingChange( + 'regularTimings', + day, + 'open', + e.target.value + ) + } + variant="outlined" + size="small" + /> + + + + handleTimingChange( + 'regularTimings', + day, + 'close', + e.target.value + ) + } + variant="outlined" + size="small" + /> + + + ))} + + + + Emergency Timings (Daywise) + + + {daysOfWeek.map((day) => ( + + + {day} + + + + handleTimingChange( + 'emergencyTimings', + day, + 'open', + e.target.value + ) + } + variant="outlined" + size="small" + /> + + + + handleTimingChange( + 'emergencyTimings', + day, + 'close', + e.target.value + ) + } + variant="outlined" + size="small" + /> + + + ))} + + + + + + {/* Greetings Setup Accordion */} + + } + aria-controls="greetingsSetup-content" + id="greetingsSetup-header" + sx={{ backgroundColor: '#f5f5f5' }} + > + + Greetings Setup + + + + + + + + {/* Integration Settings Accordion */} + + } + aria-controls="integrationSettings-content" + id="integrationSettings-header" + sx={{ backgroundColor: '#f5f5f5' }} + > + + Integration Settings + + + + + + + + Integrate with + + + + + + + + + + + + + + + {/* Form Actions */} + + + + + + + ); +} diff --git a/src/views/ClinicSetup/index.jsx b/src/views/ClinicSetup/index.jsx new file mode 100644 index 0000000..d6c537c --- /dev/null +++ b/src/views/ClinicSetup/index.jsx @@ -0,0 +1,913 @@ +import SendIcon from '@mui/icons-material/Send'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import FormControl from '@mui/material/FormControl'; +import Grid from '@mui/material/Grid'; +import InputLabel from '@mui/material/InputLabel'; +import * as React from 'react'; +import { pushNotification } from '../../utils/notification'; +import { NOTIFICATION_TYPE } from '../Notifications/notificationConstant'; +import { + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + MenuItem, + Paper, + Select, + Step, + StepLabel, + Stepper, + TextField, + Typography, +} from '@mui/material'; +import WarningAmberIcon from '@mui/icons-material/WarningAmber'; + +// Integration software options +const integrationOptions = ['BP Software', 'Medical Director']; + +// Steps for the stepper +const steps = [ + 'General Information', + 'AI Receptionist Setup', + 'Scenarios Setup', + 'General Info Setup', + 'Integration Settings', +]; + +const voiceModels = [ + { id: 'stream', name: 'Stream', gender: 'female' }, + { id: 'sandy', name: 'Sandy', gender: 'female' }, + { id: 'breeze', name: 'Breeze', gender: 'female' }, + { id: 'wolf', name: 'Wolf', gender: 'male' }, + { id: 'stan', name: 'Sten', gender: 'male' }, + { id: 'blaze', name: 'Blaze', gender: 'male' }, +]; + +const voiceModelGender = [ + { id: 'male', name: 'Male' }, + { id: 'female', name: 'Female' }, +]; + +export default function ClinicSetup() { + // Active step state + const [activeStep, setActiveStep] = React.useState(0); + const [openDialog, setOpenDialog] = React.useState(false); + const [audioContext, setAudioContext] = React.useState(null); + const [testConnDone, setTestConnDone] = React.useState(false); + + // Completed steps state + const [completed, setCompleted] = React.useState({}); + + // Form state + const [formData, setFormData] = React.useState({ + // General Information + clinicPhone: '159875654', + clinicAddress: '1 Wilkinson Road, Para Hills SA 5096', + otherInfo: '', + + // Greetings Setup + clinicGreetings: + 'Welcome to 365 days medical center group, para hills clinic. We are here to help you.', + clinicScenarios: `Booking for care plan /Care plan Review +Existing patients- Book with regular GP and Nurse on same day ( if not possible book different day, F2F with nurse for care plan and another days F2F consultation with Dr to sign of care plan ) +New patients- At the moment we are not taking New patients on long term care but tell them they can see GP and can discuss with GP before booking for care plan . +Need to tell thee is not gap when booking for care plan and care plan review + +Booking for MHCP/MHCP review +Do you have a Medicare card? +If No -Pt who do not have a Medicare card / Private patients with or without Insurance can have MHCP/MHCP review but have to pay if full and need to check with their insurance if they get back any amount. Need to tell Dressing fee ............but need toTell them to Visit the Patient Info tab at 365daysmedicalcentre.com.au to view our billing policy +`, + clinicOthers: `# Wellness Way Medical Centre + +## Contact Information +**Address**: 83 Kesters Road, Para Hills, SA 5096, Australia +**Website**: www.wellnesswaymedical.com.au + +## Accessibility Information +**Parking Facility**: On-site parking available with 20 general spaces and 4 dedicated accessible parking spots. First 2 hours free with validation, $3/hour thereafter. + +**Wheelchair Accessibility**: Fully wheelchair accessible facility with ramps at all entrances, automatic doors, accessible toilets on each floor, and elevators to all levels. Wheelchairs available for patient use upon request at reception. + +**Baby Care Facilities**: Dedicated parents' room located on Level 1 next to the pediatric wing, equipped with: +- Private feeding area with comfortable chairs +- Changing tables +- Bottle warming facilities +- Microwave +- Small play area for siblings + +## Transportation +**Nearby Public Transport**: +- Bus: Routes 208, 209, and 580 stop directly outside (Para Hills Shopping Centre stop) +- Train: Parafield Station (3.2km away) with connecting buses +- O-Bahn Busway: Klemzig Interchange (7km) with connecting services + +**Taxi/Rideshare Information**: +- Dedicated pickup/dropoff zone at main entrance +- Rideshare pickup point clearly marked with signage +- Reception can call taxis for patients upon request + +## Hours of Operation +**Regular Hours**: +- Monday to Friday: 7:00 AM - 7:00 PM +- Saturday: 8:00 AM - 4:00 PM +- Sunday: 9:00 AM - 2:00 PM + +**Holiday Schedule**: +- New Year's Day: 10:00 AM - 2:00 PM +- Australia Day: 10:00 AM - 2:00 PM +- Good Friday: Closed +- Easter Monday: 10:00 AM - 2:00 PM +- Anzac Day: 1:00 PM - 5:00 PM +- Queen's Birthday: 10:00 AM - 2:00 PM +- Labour Day: 10:00 AM - 2:00 PM +- Christmas Eve: 8:00 AM - 1:00 PM +- Christmas Day: Closed +- Boxing Day: 10:00 AM - 2:00 PM +- New Year's Eve: 8:00 AM - 1:00 PM + +## Amenities +**Wi-Fi Availability**: +- Free Wi-Fi available throughout the facility +- Ask front desk for Wifi details +- Usage limit: 2GB per device per day + +## Policies +**Pet Policy**: +- Service animals welcome throughout the facility +- Therapy animals permitted with prior arrangement +- Pet-friendly outdoor waiting area in courtyard garden +- Other pets must remain outside the building except for veterinary consultations (available Tuesdays and Thursdays)`, + + // Integration Settings + integrationSoftware: '', + practiceId: '', + practiceName: '', + + // Voice Configuration + voice: '', + voiceGender: voiceModelGender[1].id, + }); + + // Handle form reset + const handleReset = () => { + setActiveStep(0); + setCompleted({}); + // reset form data to initial state + setFormData( + Object.keys(formData).reduce((acc, key) => { + acc[key] = ''; + return acc; + }, {}) + ); + }; + + React.useEffect(() => { + // Initialize audio context when component mounts + function initAudioContext() { + if (!window.AudioContext && window.webkitAudioContext) { + window.AudioContext = window.webkitAudioContext; + } + + if (!window.AudioContext) { + pushNotification( + 'Your browser does not support AudioContext and cannot play back audio.', + 'error' + ); + return null; + } + + return new AudioContext(); + } + + const context = initAudioContext(); + setAudioContext(context); + + // initialize voice model + formData.voiceGender = voiceModelGender[0].id; + + // Cleanup function + return () => { + if (context && context.state !== 'closed') { + context.close(); + } + }; + }, []); + + // Calculate total steps + const totalSteps = () => steps.length; + + // Calculate completed steps + const completedSteps = () => Object.keys(completed).length; + + // Check if all steps completed + const allStepsCompleted = () => completedSteps() === totalSteps(); + + // Check if current step is the last step + const isLastStep = () => activeStep === totalSteps() - 1; + + // Handle next button click + const handleNext = () => { + const newActiveStep = + isLastStep() && !allStepsCompleted() + ? // It's the last step, but not all steps have been completed, + // find the first step that has not been completed + steps.findIndex((step, i) => !(i in completed)) + : activeStep + 1; + setActiveStep(newActiveStep); + }; + + // Listen to voice using rime.ai API + const listenVoice = () => { + if (!audioContext) { + pushNotification('Audio context not initialized', 'error'); + return; + } + + if (!formData.clinicGreetings || formData.clinicGreetings.length === 0) { + pushNotification( + 'Please enter clinic greetings to listen preview', + 'error' + ); + return; + } + + const options = { + method: 'POST', + headers: { + Accept: 'audio/mp3', + Authorization: 'Bearer ElD2r2Dn9Lsl5qB5TupaGcKlWSEf2llo9CkHi2OrOOU', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + speaker: formData.voice || 'wolf', + text: formData.clinicGreetings, + modelId: 'mist', + lang: 'eng', + samplingRate: 22050, + speedAlpha: 1.0, + reduceLatency: false, + }), + }; + + // Show loading state + pushNotification('Generating audio...', 'info'); + + fetch('https://users.rime.ai/v1/rime-tts', options) + .then((response) => { + // Check if the response is successful + if (!response.ok) { + throw new Error(`API responded with status ${response.status}`); + } + // Get the response as an ArrayBuffer instead of text + return response.arrayBuffer(); + }) + .then((arrayBuffer) => { + // Decode the audio data + audioContext.decodeAudioData( + arrayBuffer, + (buffer) => playAudioBuffer(buffer, audioContext), + (error) => { + pushNotification('Failed to decode audio data', 'error'); + } + ); + }) + .catch((err) => { + pushNotification(`Error: ${err.message}`, 'error'); + }); + }; + + // Play decoded audio buffer + function playAudioBuffer(audioBuffer, context) { + // Create a source node + const source = context.createBufferSource(); + source.buffer = audioBuffer; + + // Connect to the audio context destination (speakers) + source.connect(context.destination); + + // Play the audio + source.start(0); + + // Notify user + pushNotification('Playing audio...', 'success'); + } + + // Handle back button click + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + // Handle step click + const handleStep = (step) => () => { + // If trying to skip ahead with errors in current step, validate first + if (step > activeStep) { + if (validateStep()) { + setActiveStep(step); + } else { + // Alert user about validation errors + pushNotification( + 'Please complete the current step before proceeding', + NOTIFICATION_TYPE.ERROR + ); + } + } else { + // Allow going backward anytime + setActiveStep(step); + } + }; + + // test connection + const handleTestConnection = () => { + // logic here + setTestConnDone(true); + pushNotification('Test connection successful', 'success'); + }; + + // Form validation state + const [errors, setErrors] = React.useState({ + clinicPhone: false, + clinicAddress: false, + }); + + // Validate the current step + const validateStep = () => { + switch (activeStep) { + case 0: // General Information + // eslint-disable-next-line no-case-declarations + // const stepErrors = { + // clinicPhone: formData.clinicPhone.trim() === '', + // clinicAddress: formData.clinicAddress.trim() === '', + // }; + + // setErrors(stepErrors); + // return !Object.values(stepErrors).some((isError) => isError); + return true; + + case 1: // Clinic Timings + // No required fields in this step + return true; + + case 2: // Greetings Setup + // No required fields in this step + return true; + + case 3: // Integration Settings + // Validate only if software is selected + if (formData.integrationSoftware) { + const integrationErrors = { + practiceId: formData.practiceId.trim() === '', + practiceName: formData.practiceName.trim() === '', + }; + setErrors(integrationErrors); + return !Object.values(integrationErrors).some((isError) => isError); + } + return true; + + default: + return true; + } + }; + + // Handle complete step + const handleComplete = () => { + if (true) { + const newCompleted = { ...completed }; + newCompleted[activeStep] = true; + setCompleted(newCompleted); + handleNext(); + } else { + // Scroll to the top to make error messages visible + window.scrollTo(0, 0); + } + }; + + // Handle simple input changes + const handleChange = (e) => { + const { name, value } = e.target; + + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + + // Clear error for this field if it exists + if (errors[name]) { + setErrors((prev) => ({ + ...prev, + [name]: false, + })); + } + }; + + // Handle timing changes + const handleTimingChange = (timingType, day, timeType, value) => { + setFormData((prev) => ({ + ...prev, + [timingType]: { + ...prev[timingType], + [day]: { + ...prev[timingType][day], + [timeType]: value, + }, + }, + })); + }; + + // Validate the entire form + const validateForm = () => { + // Check all steps + const generalInfoValid = + formData.clinicPhone.trim() !== '' && + formData.clinicAddress.trim() !== ''; + + let integrationValid = true; + if (formData.integrationSoftware) { + integrationValid = + formData.practiceId.trim() !== '' && + formData.practiceName.trim() !== ''; + } + + return generalInfoValid && integrationValid; + }; + + // Handle form submission + const handleSubmit = (e) => { + e.preventDefault(); + + if (validateForm()) { + console.log('Form submitted:', formData); + // Here you would typically send the data to your backend + pushNotification('Clinic setup updated successfully', 'success'); + // Reset the form after submission + handleReset(); + } else { + // Find the first step with errors and go to it + if ( + formData.clinicPhone.trim() === '' || + formData.clinicAddress.trim() === '' + ) { + setActiveStep(0); + setErrors({ + ...errors, + clinicPhone: formData.clinicPhone.trim() === '', + clinicAddress: formData.clinicAddress.trim() === '', + }); + } else if ( + formData.integrationSoftware && + (formData.practiceId.trim() === '' || + formData.practiceName.trim() === '') + ) { + setActiveStep(3); + setErrors({ + ...errors, + practiceId: formData.practiceId.trim() === '', + practiceName: formData.practiceName.trim() === '', + }); + } + + // Show an alert + pushNotification( + 'Please fill in all required fields before submitting', + NOTIFICATION_TYPE.ERROR + ); + } + }; + + // Render the current step content + const renderStepContent = (step) => { + switch (step) { + case 0: // General Information + return ( + + + + + <> + + setOpenDialog(false)} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + {'Confirm Disable AI Receptionist'} + + + + Warning: This action will disable the 24x7 AI Receptionist + service for your clinic. This may affect your clinic's + ability to handle after-hours calls and appointments. You + have to manually disable the call forwarding from this + number. Are you sure you want to proceed? + + + + + + + + + + + + + + + + + ); + + case 1: // Greetings Setup + return ( + <> + +
    + + + + + {formData.voiceGender && ( + + + + )} +
    + + ); + + case 2: + return ( + <> + + + ); + + case 3: + return ( + <> + + + ); + + case 4: // Integration Settings + return ( + + {/* Error summary - show only if there are errors */} + {/* {Object.values(errors).some((error) => error) && ( + + + + Please correct the following errors: + +
      + {errors.practiceId && ( +
    • + Practice ID is required when integration software is + selected +
    • + )} + {errors.practiceName && ( +
    • + Practice Name is required when integration software is + selected +
    • + )} +
    +
    +
    + )} */} + + + + + Integrate with + + + + + + + + + + +
    + ); + + default: + return 'Unknown step'; + } + }; + + return ( + + + + Clinic Setup + + + {/* Stepper */} + + {steps.map((label, index) => { + const stepProps = {}; + const labelProps = {}; + return ( + + + {label} + + + ); + })} + + + {/* Step Content */} + + {allStepsCompleted() ? ( + + + All steps completed - Setup is ready to be saved. + + + + {/* */} + + + + ) : ( + + {renderStepContent(activeStep)} + + {/* Navigation Buttons */} + + + + {activeStep == 4 && ( + + )} + {activeStep != 4 && ( + + )} + {activeStep !== steps.length && ( + + )} + + + )} + + + + ); +} diff --git a/src/views/ClinicTranscripts/index.jsx b/src/views/ClinicTranscripts/index.jsx new file mode 100644 index 0000000..7d4f1ab --- /dev/null +++ b/src/views/ClinicTranscripts/index.jsx @@ -0,0 +1,402 @@ +import DownloadIcon from '@mui/icons-material/Download'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import { + Box, + Button, + IconButton, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TablePagination, + TableRow, + TableSortLabel, + Typography, + TextField, +} from '@mui/material'; +import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { format } from 'date-fns'; +import React, { useEffect, useState } from 'react'; + +// Sample data - replace with your actual data source +const sampleCalls = [ + { + id: 1, + date: '2025-04-20', + time: '09:30', + callerNumber: '555-123-4567', + patientName: 'John Smith', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 2, + date: '2025-04-21', + time: '10:15', + callerNumber: '555-234-5678', + patientName: 'Jane Doe', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 3, + date: '2025-04-19', + time: '14:45', + callerNumber: '555-345-6789', + patientName: 'Robert Johnson', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 4, + date: '2025-04-22', + time: '11:00', + callerNumber: '555-456-7890', + patientName: 'Emily Davis', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 5, + date: '2025-04-20', + time: '16:30', + callerNumber: '555-567-8901', + patientName: 'Michael Brown', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 6, + date: '2025-04-19', + time: '14:45', + callerNumber: '555-345-6789', + patientName: 'Robert Johnson', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 7, + date: '2025-04-22', + time: '11:00', + callerNumber: '555-456-7890', + patientName: 'Emily Davis', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 8, + date: '2025-04-20', + time: '16:30', + callerNumber: '555-567-8901', + patientName: 'Michael Brown', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 9, + date: '2025-04-19', + time: '14:45', + callerNumber: '555-345-6789', + patientName: 'Robert Johnson', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 10, + date: '2025-04-22', + time: '11:00', + callerNumber: '555-456-7890', + patientName: 'Emily Davis', + callDuration: Math.floor(Math.random() * 60), + }, + { + id: 11, + date: '2025-04-20', + time: '16:30', + callerNumber: '555-567-8901', + patientName: 'Michael Brown', + callDuration: Math.floor(Math.random() * 60), + }, +]; + +const CallListTable = () => { + const [calls, setCalls] = useState([]); + const [orderBy, setOrderBy] = useState('date'); + const [order, setOrder] = useState('desc'); + const [showFilters, setShowFilters] = useState(false); + const [startDate, setStartDate] = useState(null); + const [endDate, setEndDate] = useState(null); + + // Pagination state + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + + // Initialize with sample data + useEffect(() => { + setCalls(sampleCalls); + }, []); + + // Handle sort request + const handleRequestSort = (property) => { + const isAsc = orderBy === property && order === 'asc'; + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(property); + }; + + const formatDate = (dateString) => { + const date = new Date(dateString); + const day = date.getDate().toString().padStart(2, '0'); + const month = date.toLocaleString('en-US', { month: 'short' }); + const year = date.getFullYear(); + return `${day}-${month}-${year}`; + }; + + // Handle download transcript + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const handleDownloadTranscript = (id) => { + // TODO: Implement actual download functionality here + }; + + // Apply filters + const applyFilters = () => { + let filteredCalls = [...sampleCalls]; + + if (startDate) { + const startDateStr = format(startDate, 'yyyy-MM-dd'); + filteredCalls = filteredCalls.filter((call) => call.date >= startDateStr); + } + + if (endDate) { + const endDateStr = format(endDate, 'yyyy-MM-dd'); + filteredCalls = filteredCalls.filter((call) => call.date <= endDateStr); + } + + setCalls(filteredCalls); + setPage(0); // Reset to first page after filtering + }; + + // Reset filters + const resetFilters = () => { + setStartDate(null); + setEndDate(null); + setCalls(sampleCalls); + setPage(0); // Reset to first page after clearing filters + }; + + // Handle page change + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; + + // Handle rows per page change + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + // Sort function + const sortedCalls = calls.sort((a, b) => { + if (orderBy === 'date') { + const dateComparison = a.date.localeCompare(b.date); + if (dateComparison !== 0) + return order === 'asc' ? dateComparison : -dateComparison; + + // If dates are equal, sort by time + return order === 'asc' + ? a.time.localeCompare(b.time) + : b.time.localeCompare(a.time); + } + + if (orderBy === 'time') { + return order === 'asc' + ? a.time.localeCompare(b.time) + : b.time.localeCompare(a.time); + } + + return 0; + }); + + // Get current page data + const currentPageCalls = sortedCalls.slice( + page * rowsPerPage, + page * rowsPerPage + rowsPerPage + ); + + // Custom render function for DatePicker to avoid renderInput prop issues + const CustomDateField = ({ value, onChange, label }) => ( + { + try { + const dateStr = e.target.value; + const date = new Date(dateStr); + if (!isNaN(date.getTime())) { + onChange(date); + } + } catch (error) { + // Invalid date + } + }} + placeholder="YYYY-MM-DD" + size="small" + sx={{ minWidth: 200 }} + /> + ); + + return ( + + + + + Call Transcripts + + + + + + + + {showFilters && ( + + + + + + + + + + + + + + + )} + + + + + + + handleRequestSort('date')} + > + Date + + + + handleRequestSort('time')} + > + Received Time + + + Call Duration + Caller Number + Patient Name + Action + + + + {currentPageCalls.map((call) => ( + + + {formatDate(call.date)} + + {call.time} + {call.callDuration} Sec + {call.callerNumber} + {call.patientName} + + handleDownloadTranscript(call.id)} + aria-label="download transcript" + sx={{ color: '#f5365c' }} + > + + + + + ))} + {currentPageCalls.length === 0 && ( + + + No calls found + + + )} + +
    +
    + + +
    +
    + ); +}; + +export default CallListTable; diff --git a/src/views/ClinicsList/clinicListStyles.js b/src/views/ClinicsList/clinicListStyles.js new file mode 100644 index 0000000..a2a130e --- /dev/null +++ b/src/views/ClinicsList/clinicListStyles.js @@ -0,0 +1,119 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + chipClass: { + height: 'fit-content', + minHeight: '30px', + padding: '2px', + alignItems: 'center', + }, + statusColor: { + color: theme.palette.primary.main, + fontSize: pxToRem(10), + }, + tabsBox: { + display: 'flex', + justifyContent: ' space-around', + // width: '55%', + marginTop: theme.spacing(0.5), + marginRight: theme.spacing(5.0), + alignItems: 'center', + }, + secondaryButton: { + width: '200px', + height: '46px', + borderRadius: '8px', + justifyContent: 'space-evenly', + fontSize: pxToRem(16), + }, + tableActionIcons: { + marginRight: theme.spacing(1.4), + width: '15px', + }, + companyNameTableColumn: { + display: 'flex', + alignItems: 'center', + }, + companyName: { + marginLeft: theme.spacing(1), + fontSize: pxToRem(14), + objectFit: 'contain', + width: '260px', + }, + companyNameLink: { + textDecoration: 'none', + color: theme.palette.grey[10], + '&:hover': { + color: theme.palette.info.main, + textDecoration: 'underline', + }, + }, + companyWebsiteLabel: { + fontSize: pxToRem(12), + }, + companyNameLogo: { + height: '40px', + width: '40px', + borderRadius: theme.shape.borderRadiusComponent, + objectFit: 'contain', + }, + sendEmailStatus: { + fontSize: pxToRem(14), + color: theme.palette.primary.main, + }, + sendEmailLastSentMailDate: { + fontSize: pxToRem(12), + }, + addDiscountCodeLink: { + fontSize: pxToRem(12), + color: theme.palette.primary.main, + }, + addDiscountCodeLabel: { + fontSize: pxToRem(14), + backgroundColor: theme.palette.common.white, + color: theme.palette.common.black, + }, + customModel: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + height: '100%', + }, + customModelBox: { + paddingLeft: theme.spacing(5), + paddingRight: theme.spacing(5), + textAlign: 'center', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + // height: '100%', + border: 'none', + }, + newPlanTitleText: { + fontSize: pxToRem(28), + fontFamily: theme.fontFamily.bold, + }, + newPlanSubTitleText: { + fontSize: pxToRem(18), + padding: theme.spacing(1), + }, + addPlanSuccessIcon: { + padding: theme.spacing(2), + paddingTop: theme.spacing(1), + }, + planAddedText: { + fontSize: pxToRem(12), + color: theme.palette.grey[54], + display: 'flex', + alignItems: 'center', + alignSelf: 'center', + }, + verifyIcon: { + marginLeft: theme.spacing(0.3), + fontSize: pxToRem(12), + color: theme.palette.grey[54], + }, +})); diff --git a/src/views/ClinicsList/index.jsx b/src/views/ClinicsList/index.jsx new file mode 100644 index 0000000..6548c84 --- /dev/null +++ b/src/views/ClinicsList/index.jsx @@ -0,0 +1,542 @@ +import React, { useState, useMemo, useEffect, useRef } from "react"; +import { useTheme } from "@emotion/react"; +import { useLocation, useNavigate,Link } from "react-router-dom"; +import StoreIcon from '@mui/icons-material/Store'; +import DescriptionIcon from '@mui/icons-material/Description'; +import { Box, Chip,Tab, Tabs } from '@mui/material'; +import { differenceInDays, format } from 'date-fns'; +import { LoadingButton } from '@mui/lab'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; + + + +import { useStyles } from "./clinicListStyles"; +import { CLINIC_STATUS, CLINIC_TYPE, PLAN_STATUS_TYPE } from "../../constants"; +import { getClinics } from "../../services/clinics.service"; +import CustomBreadcrumbs from "../../components/CustomBreadcrumbs"; +import Table from "../../components/Table"; +import TableSelect from "../../components/TableSelect"; +import PageHeader from '../../components/PageHeader'; + + +const ClinicsList = () => { + const theme = useTheme(); + const ref = useRef(null); + const classes = useStyles(); + const params = useLocation(); + const navigate = useNavigate(); + const queryParams = new URLSearchParams(params.search); + const [type, setType] = useState( + queryParams.get("tab") ?? CLINIC_TYPE.UNREGISTERED + ); + const showNewPlanAddedModel = + Boolean(queryParams.get("isPlanCreated")) ?? false; + const [isDownloading, setIsDownloading] = useState(false); + const [showModel, setShowModel] = useState(false); + const [modelType, setModelType] = useState(""); + const [rowActionData, setRowActionData] = useState(""); + const [isEmailSentPopupOpen, setIsEmailSentPopupOpen] = useState(false); + const emailList = useRef(); + + + const UNREGISTERED_CLINIC_STATUS_OPTIONS = [ + { id: 1, name: CLINIC_STATUS.NOT_REVIEWED, showName: 'Approval Pending' }, + { id: 2, name: CLINIC_STATUS.APPROVAL_PENDING_DOCUMENT_RESUBMITTED }, + { id: 3, name: CLINIC_STATUS.ON_HOLD, showName: 'On Hold' }, + { id: 4, name: CLINIC_STATUS.REJECTED, showName: 'Rejected' }, + ]; + + const columnsUnregistered = useMemo( + () => + [ + // ..............sro number column...................... + { + size: 50, + isBold: true, + header: "Sr. No.", + Cell: (props) => { + const tableState = props?.table?.getState(); + const serialNumber = ( + props?.row?.index + + 1 + + tableState?.pagination?.pageIndex * + tableState?.pagination?.pageSize + ) + ?.toString() + ?.padStart(2, "0"); + return {serialNumber}; + }, + enableSorting: false, + }, + // .................company name column.................. + { + enableColumnFilter: false, + enableSorting: true, + size: "260px", + accessorKey: "name", + header: "Clinic Name", + Cell: ({ row }) => ( + <> +
    + {row?.original?.logo ? ( + logo + ) : ( + + )} + {/* */} +
    + + <>{row?.original.name} + + +
    + {row.original.website} +
    +
    +
    + + ), + }, + // .................req. raised on column.................. + { + enableColumnFilter: false, + enableSorting: true, + size: 100, + accessorKey: "updatedAt", + header: "Req.Raised On", + Cell: ({ row }) => ( + <> +
    + {row?.original?.requestRaisedOn + ? format( + new Date(row.original.requestRaisedOn), + "dd MMM yyyy" + ) + : NOT_AVAILABLE_TEXT} +
    + + ), + }, + // .................send email column.................. + { + enableColumnFilter: false, + enableSorting: true, + accessorKey: "lastEmailSent", + size: 190, + header: "Send Mail", + Cell: ({ row }) => ( + <> + {(row.original.status === CLINIC_STATUS.REJECTED || + row.original.status === CLINIC_STATUS.ON_HOLD) && ( +
    + handleEmailSent(row)} + className={classes.sendEmailStatus} + > + + {row.original.status === CLINIC_STATUS.ON_HOLD && + "Resend On Hold Mail"} + {row.original.status === CLINIC_STATUS.REJECTED && + "Resend Rejection Mail"} + + +
    + Last Sent On: + {format( + new Date(row?.original?.lastEmailSent), + "dd MMM yyyy" + )} +
    +
    + )} + + ), + }, + // .................location column.................. + { + size: 100, + enableColumnFilter: false, + enableSorting: true, + accessorKey: "city", + header: "Location", + Cell: ({ row }) => ( + <> +
    {row.original.city}
    + + ), + }, + // .................Company status column.................. + { + enableSorting: true, + accessorKey: "status", + header: "Status", + size: 100, + Filter: ({ header }) => ( + + ), + accessorFn: ({ status }) => ( + <> + + Approval Pending +
    + Document Resubmitted +
    + + ) : status === CLINIC_STATUS.REJECTED ? ( + "Rejected" + ) : ( + "" + ) + } + style={{ + backgroundColor: + status === CLINIC_STATUS.ON_HOLD + ? theme.palette.orange.light + : status === CLINIC_STATUS.NOT_REVIEWED || + status === CLINIC_STATUS.REJECTED + ? theme.palette.primary.highlight + : status === + CLINIC_STATUS.APPROVAL_PENDING_DOCUMENT_RESUBMITTED + ? theme.palette.primary.highlight + : theme.palette.blue.light, + color: + status === CLINIC_STATUS.ON_HOLD + ? theme.palette.orange.main + : status === CLINIC_STATUS.NOT_REVIEWED || + status === CLINIC_STATUS.REJECTED + ? theme.palette.primary.main + : status === + CLINIC_STATUS.APPROVAL_PENDING_DOCUMENT_RESUBMITTED + ? theme.palette.pink.main // Set to yellow for resubmitted + : theme.palette.blue.main, + }} + /> + + ), + }, + ].filter(Boolean), + [type] + ); + + const columnsRegistered = useMemo( + () => + [ + // ..............sro number column...................... + { + size: 60, + header: "Sr. No.", + Cell: (props) => { + const tableState = props?.table?.getState(); + const serialNumber = ( + props?.row?.index + + 1 + + tableState?.pagination?.pageIndex * + tableState?.pagination?.pageSize + ) + ?.toString() + ?.padStart(2, "0"); + return {serialNumber}; + }, + enableSorting: false, + }, + // .................company name column.................. + { + enableSorting: true, + size: 260, + accessorKey: "name", + header: "Clinic Name", + Cell: ({ row }) => ( + <> +
    + {row?.original?.logo ? ( + logo + ) : ( + + )} + {/* */} + +
    + + <>{row?.original.name} + +
    + {row.original.website} +
    +
    +
    + + ), + }, + // ......................approve at column............... + { + enableSorting: true, + size: 100, + accessorKey: "approvedAt", + header: "Approved Since", + Cell: ({ row }) => { + const approvedAtDate = new Date(row?.original?.approvedAt); + const today = new Date(); + const daysDiff = differenceInDays(today, approvedAtDate); + + return ( + <> +
    + {daysDiff === 0 + ? "Today" + : daysDiff === 1 + ? "Yesterday" + : `${daysDiff} days`} +
    + + ); + }, + }, + // ........................... sent email column............................. + // { + // enableSorting: true, + // accessorKey: 'hasActiveCustomPlan', + // minSize: 250, + // header: 'Sent Mail', + // Cell: ({ row }) => ( + // <> + // {row?.original?.plans?.[0]?.status === + // PLAN_STATUS_TYPE.ACTIVATED && ( + //
    + // handleRegisterEmailSent(row)} + // className={classes.sendEmailStatus} + // > + // Resend Subscription Mail + // + //
    + // Last Sent On:{' '} + // {format( + // new Date(row?.original?.lastCustomPlanEmailSent), + // 'dd MMM yyyy' + // )} + //
    + //
    + // )} + // + // ), + // }, + + // ................add plan column.............................. + // { + // enableSorting: true, + // isBold: true, + // size: 120, + // accessorKey: 'hasActiveCustomPlan', + // header: 'Custom Plan', + // Cell: ({ row }) => ( + // <> + // {row?.original?.plans?.[0]?.status === + // PLAN_STATUS_TYPE.ACTIVATED ? ( + // + //
    + // Plan Added + //
    + //
    + // ) : ( + // + // + // ADD PLAN + // + // + // )} + // + // ), + // }, + ].filter(Boolean), + [type] + ); + + const handleTabsChange = (event, newValue) => { + setType(newValue); + }; + + const getData = async (filters) => { + let params = { + ...filters, + type, + }; + const resp = await getClinics(params); + return { + data: resp?.data?.data?.records, + rowCount: resp?.data?.data?.totalCount, + }; + }; + + const handleExport = async () => { + // const selectedRowData = ref.current.getSelectedRowData(); + // const idArray = selectedRowData.map((item) => item.id); + // setIsDownloading(true); + // try { + // const response = await getCompaniesExport(type, idArray); + // const columnOrder = EXCEL_COLUMN_ORDERS[type.toUpperCase()]; + // exportToExcel( + // response?.data?.data?.records, + // columnOrder, + // type, + // COMPANY_TYPE[type.toUpperCase()].toLowerCase(), + // type + // ); + // } catch (e) { + // pushNotification(e.message, NOTIFICATION.ERROR); + // } finally { + // setIsDownloading(false); + // } + }; + + const handleRequestSentClose = () => { + // setIsEmailSentPopupOpen(false); + }; + + const breadcrumbs = [ + { + label: "Dashboard", + path: "/", + }, + { + label: "Clinic List", + path: "", + }, + ]; + + useEffect(() => { + ref.current.resetPage(); + ref.current.reFetchData(); + }, [type]); + + return ( + <> + + + } + extraComponent={ + + + + + {/* */} + + + } + secondaryButton={ + + } + // onClick={handleExport} + > + Download Excel + + } + /> + + + + + {type === CLINIC_TYPE.UNREGISTERED && ( + + )} + {type === CLINIC_TYPE.REGISTERED && ( +
    + handleModelOpen(row, "transactionHistory"), + text: "Freeze Clinic", + icon: ( + + // transaction history + ), + }, + ]} + /> + )} + + + + + ); +}; + +export default ClinicsList; diff --git a/src/views/Dashboard/Tiles/SuperAdminTotals.jsx b/src/views/Dashboard/Tiles/SuperAdminTotals.jsx new file mode 100644 index 0000000..a5709eb --- /dev/null +++ b/src/views/Dashboard/Tiles/SuperAdminTotals.jsx @@ -0,0 +1,65 @@ +import React from "react"; +import { useStyles } from "../dashboardStyles"; +import { Grid, useTheme } from "@mui/material"; +import ProtectedComponent from "../../../components/ProtectedComponent"; +import TotalNumber from "../components/TotalNumber"; + +const SuperAdminTotals = ({ isLoading, data }) => { + const classes = useStyles(); + const theme = useTheme(); + + const viewAllClick = (tab) => { + if (false) return; + // if (!user?.permission?.includes("STATISTIC_DASHBOARD")) return; + let path = "/"; + // navigate(path, { state: { tab } }); + }; + + const { totalAccounts, registrationRequest, rejected, registered } = data; + + return ( + + + + + + + viewAllClick(true)} + value={registrationRequest} + helperText={"Clinics"} + /> + + + viewAllClick(false)} + value={rejected} + helperText={"Clinics"} + color={theme.palette.grey[52]} + /> + + + viewAllClick(false)} + value={registered} + helperText={"Clinics"} + color={theme.palette.grey[57]} + /> + + + + ); +}; + +export default SuperAdminTotals; diff --git a/src/views/Dashboard/Tiles/Totals.jsx b/src/views/Dashboard/Tiles/Totals.jsx new file mode 100644 index 0000000..14d27dd --- /dev/null +++ b/src/views/Dashboard/Tiles/Totals.jsx @@ -0,0 +1,74 @@ +import { Grid } from '@mui/material'; +import { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import ProtectedComponent from '../../../components/ProtectedComponent'; +import { getClinicsDashboardStatsById } from '../../../services/clinics.service'; +import TotalNumber from '../components/TotalNumber'; +import { useStyles } from '../dashboardStyles'; + +const Totals = ({ data, setData }) => { + const classes = useStyles(); + + const user = useSelector((state) => state?.login?.user); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + fetchData(); + }, []); + + const fetchData = async () => { + try { + setIsLoading(true); + const response = {}; + // const response = await getClinicsDashboardStatsById(); + const apiData = response?.data?.data || {}; + setData({ + activeJobs: apiData.recruitmentAnalytics?.activeJobs, + totalMaxJobPostings: apiData.totalMaxJobPostings, + }); + } catch (error) { + console.error('Error fetching data:', error); + } finally { + setIsLoading(false); + } + }; + + const { + activeJobs, + totalMaxJobPostings, + } = data; + const hasCandidateSearchFeature = + data?.accounts?.features?.includes('SEARCH_CANDIDATE') || false; + const viewAllClick = (tab) => { + if (!user?.permission?.includes('STATISTIC_DASHBOARD')) return; + let path = '/'; + // navigate(path, { state: { tab } }); + }; + + return ( + + + + + + + viewAllClick(true)} + value={activeJobs} + // infoPopover={'Total no. of jobs which are in active state.'} + // color="#14d1ad" + /> + + + + ); +}; + +export default Totals; diff --git a/src/views/Dashboard/components/SuperAdmin.jsx b/src/views/Dashboard/components/SuperAdmin.jsx new file mode 100644 index 0000000..904a7e4 --- /dev/null +++ b/src/views/Dashboard/components/SuperAdmin.jsx @@ -0,0 +1,60 @@ +import { Typography } from "@mui/material"; +import { Box } from "@mui/system"; +import React from "react"; +import { useStyles } from "../dashboardStyles"; +import SuperAdminTotals from "../Tiles/SuperAdminTotals"; + +const SuperAdmin = () => { + const classes = useStyles(); + const [isLoading, setIsLoading] = React.useState(true); + const [data, setData] = React.useState({ + totalAccounts: 0, + registrationRequest: 0, + rejected: 0, + registered: 0, + }); + + const fetchDashboardStats = async () => { + try { + setIsLoading(true); + // const response = await getAdminDashboardStats(); + // const apiData = response?.data?.data || {}; + // setData({ + // totalAccounts: apiData?.dashboardStatistics?.totalAccounts, + // registrationRequest: apiData?.dashboardStatistics?.registrationRequest, + // rejected: apiData?.dashboardStatistics?.rejected, + // registered: apiData?.dashboardStatistics?.registered, + // }); + setTimeout(() => { + setData({ + totalAccounts: 10, + registrationRequest: 5, + rejected: 2, + registered: 8, + }); + }, 1000); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setIsLoading(false); + } + }; + + React.useEffect(() => { + fetchDashboardStats(); + }, []); + + return ( + + {/* */} + + Clinics Info + + + + + + ); +}; + +export default SuperAdmin; diff --git a/src/views/Dashboard/components/TotalNumber.jsx b/src/views/Dashboard/components/TotalNumber.jsx new file mode 100644 index 0000000..2cce8c3 --- /dev/null +++ b/src/views/Dashboard/components/TotalNumber.jsx @@ -0,0 +1,95 @@ +import { + Card, + Typography, + CircularProgress, + Popover, + useTheme, +} from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; +import { useStyles } from '../dashboardStyles'; +import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; + +function TotalNumber({ + value, + heading, + color, + isLoading, + viewAllClick, + showAsterisk, + infoPopover, + helperText, +}) { + const classes = useStyles(); + const theme = useTheme(); + const [anchorEl, setAnchorEl] = useState(null); + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + const id = open ? 'simple-popover' : undefined; + + const textColor = + color || (value == 0 || value == 0o0 ? `${theme.palette.grey[57]}` : ''); + + return ( + + + {heading} + {infoPopover && ( + <> + + + + {infoPopover} + + + + )} + + {!isLoading ? ( + + {value || 0} + {showAsterisk && *} + + ) : ( + + )} + {helperText && ( + + {helperText} + + )} + + ); +} + +export default TotalNumber; diff --git a/src/views/Dashboard/dashboardStyles.js b/src/views/Dashboard/dashboardStyles.js new file mode 100644 index 0000000..1840c77 --- /dev/null +++ b/src/views/Dashboard/dashboardStyles.js @@ -0,0 +1,349 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + textArea: { + '&.MuiFormControl-marginNormal': { + marginTop: theme.spacing(0.8), + }, + marginTop: theme.spacing(0.8), + marginBottom: theme.spacing(1.6), + }, + textAreaCap: { + '&.MuiFormControl-marginNormal': { + marginTop: theme.spacing(0.8), + }, + marginTop: theme.spacing(0.8), + marginBottom: theme.spacing(1.6), + }, + inputTitle: { + '&.MuiTypography-body1': { + fontSize: '0.875rem', + }, + fontSize: '14px', + opacity: '0.54', + color: '#212121', + }, + selectTitle: { + fontSize: '1.2rem', + marginTop: '-10px', + marginLeft: '-12px', + }, + optionValue: { + cursor: 'pointer', + width: '100%', + height: '30px', + }, + deleteModalText: { + textAlign: 'center', + marginLeft: theme.spacing(1), + fontSize: '17px', + }, + labelStyle: { + marginBottom: '0px', + }, + helperText: { + color: '#FF4842', + }, + textAreaLast: { + marginTop: '8px', + marginBottom: '0px', + }, + numberCard: { + width: '260px', + padding: theme.spacing(1.8), + boxShadow: '0px 2px 10px 0 rgba(0, 0, 0, 0.08)', + minHeight: '127px', + }, + numberheader: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: theme.spacing(0.5), + }, + value: { + lineHeight: 1, + fontWeight: 600, + fontSize: pxToRem(28), + paddingTop: theme.spacing(1.0), + }, + numberHeading: { + fontSize: pxToRem(16), + fontFamily: theme.fontFamily.medium, + fontWeight: 600, + opacity: 0.96, + }, + numbers: { + width: '100%', + display: 'flex', + justifyContent: 'flex-start', + [theme.breakpoints.down('sm')]: { + justifyContent: 'center', + }, + }, + popoverContent: { + padding: theme.spacing(1.5), + paddingTop: theme.spacing(1.5), + paddingBottom: theme.spacing(1.5), + width: '160px', + fontSize: pxToRem(12), + backgroundColor: theme.palette.grey[37], + }, + dashboardTitleBox: { + display: 'flex', + alignItems: 'center', + alignSelf: 'center', + marginTop: theme.spacing(1.0), + marginBottom: theme.spacing(4.0), + }, + goToMainDashboardButton: { + marginLeft: theme.spacing(2), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '20%', + }, + card: { + // overflowY: 'scroll', + paddingBottom: theme?.spacing(1.5), + height: '560px', + maxWidth: '1200px', + padding: theme.spacing(2), + '&:hover': { + '&::-webkit-scrollbar-thumb': { + backgroundColor: '#999', + }, + }, + borderRadius: '8px', + boxShadow: '0 6px 24px 0 rgba(0, 0, 0, 0.08)', + // [theme.breakpoints.down('xl')]: { + // width: '490px', + // }, + // [theme.breakpoints.down('md')]: { + // width: '370px', + // }, + }, + header: { + display: 'flex', + justifyContent: 'space-between', + }, + heading: { + fontFamily: 'Inter-Bold', + fontWeight: 600, + fontSize: pxToRem(20), + color: theme.palette.grey[10], + }, + jobApplicationsAndManageSubscription: { + marginTop: theme.spacing(0.5), + display: 'flex', + flexWrap: 'wrap', + justifyContent: 'center', + }, + jobApplicationsTileHeader: { + display: 'flex', + justifyContent: 'space-between', + fontSize: pxToRem(12), + color: theme.palette.grey[38], + padding: theme.spacing(1.8), + fontFamily: 'Inter-Bold', + }, + jobApplicationsTile: { + overflow: 'auto', + height: '435px', + }, + jobApplicationsTileOuterDiv: { + display: 'flex', + justifyContent: 'space-between', + padding: theme.spacing(2.0), + }, + jobApplicationsTileOuterDivWithBackgroundColor: { + backgroundColor: theme.palette.grey[39], + }, + applicationRecievedDiv: { + display: 'flex', + alignItems: 'center', + fontFamily: 'Inter-Bold', + }, + jobTitleDiv: { + fontSize: pxToRem(14), + fontFamily: 'Inter-Bold', + }, + jobBodyDiv: { + fontSize: pxToRem(12), + color: theme.palette.grey[40], + }, + planDaysLeft: { + backgroundColor: theme.palette.grey[30], + padding: `${theme.spacing(1.5)} ${theme.spacing(2.4)}`, + marginTop: theme.spacing(2.5), + display: 'flex', + border: '1px solid #B6B6B6', + borderRadius: '6px', + justifyContent: 'space-between', + }, + planDetailOuterDiv: { + backgroundColor: theme.palette.grey[30], + padding: `${theme.spacing(1.5)} ${theme.spacing(2.4)}`, + marginTop: theme.spacing(2.5), + border: '1px solid #B6B6B6', + borderRadius: '6px', + height: '285px', + overflow: 'auto', + }, + planNameAndPrice: { + display: 'flex', + }, + plan: { + fontFamily: 'Inter-Bold', + color: theme.palette.grey[10], + }, + planName: { + fontSize: pxToRem(14), + }, + planPrice: { + fontSize: pxToRem(12), + }, + daysLeft: { + fontFamily: 'Inter-SemiBold', + fontSize: pxToRem(10), + color: theme.palette.grey[41], + }, + subscriptionIconOuterDiv: { + display: 'flex', + alignItems: 'center', + }, + subscriptionIconInnerDiv: { + backgroundColor: theme.palette.grey[0], + padding: theme.spacing(0.8), + borderRadius: '6px', + }, + planOuterDiv: { + marginLeft: theme.spacing(1.5), + }, + daysLeftDiv: { + display: 'flex', + alignItems: 'center', + color: theme.palette.primary.main, + fontFamily: 'Inter-SemiBold', + textAlign: 'right', + }, + planDetail: { + fontFamily: 'Inter-Bold', + color: theme.palette.grey[10], + }, + packageIncludes: { + fontSize: pxToRem(12), + color: theme.palette.grey[10], + opacity: 0.7, + fontFamily: 'Inter-Bold', + marginTop: theme.spacing(0.6), + marginBottom: theme.spacing(0.6), + }, + detailTile: { + display: 'flex', + justifyContent: 'space-between', + fontSize: pxToRem(12), + }, + detailName: { + fontSize: pxToRem(12), + color: theme.palette.grey[10], + opacity: 0.7, + fontFamily: 'Inter-Bold', + }, + detailTileValue: { + fontFamily: 'Inter-Bold', + }, + upgradePlanButtonOuterDiv: { + display: 'flex', + justifyContent: 'center', + marginTop: theme.spacing(3.0), + }, + upgradePlanButton: { + fontFamily: 'Inter-Light', + fontSize: pxToRem(14), + borderRadius: '6px', + width: '238px', + height: '42px', + }, + asPartOfPlan: { + marginTop: theme.spacing(-10), + justifyContent: 'end', + alignItems: 'end', + [theme.breakpoints.down('xl')]: { + justifyContent: 'center', + alignItems: 'center', + marginTop: theme.spacing(2.0), + }, + }, + asPartOfPlanContent: { + padding: theme.spacing(1.5), + fontSize: pxToRem(16), + fontFamily: 'Inter-Bold', + borderRadius: '8px', + }, + asterisk: { + color: theme.palette.primary.main, + }, + noJobPost: { + height: '485px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + noJobPostText: { + fontSize: pxToRem(18), + fontFamily: 'Inter-SemiBold', + marginTop: theme.spacing(2.0), + color: theme.palette.grey[10], + }, + postJobButton: { + fontFamily: 'Inter-Light', + fontSize: pxToRem(14), + marginTop: theme.spacing(4.0), + width: '131px', + height: '44px', + padding: theme.spacing(1.2), + borderRadius: '8px', + }, + addIcon: { + marginRight: theme.spacing(0.6), + }, + adminCompanies: { + margin: theme.spacing(0.5), + }, + adminCompaniesInfoText: { + fontSize: pxToRem(18), + fontFamily: 'Inter-Bold', + textTransform: 'uppercase', + color: theme.palette.grey[55], + marginTop: theme.spacing(4.0), + }, + helperTextAdmin: { + fontSize: pxToRem(14), + color: theme.palette.grey[56], + marginTop: theme.spacing(1.0), + }, + companies: { + marginTop: theme.spacing(1.6), + }, + transactionHistoryText: { + fontSize: pxToRem(14), + fontStyle: 'italic', + color: theme.palette.grey[52], + cursor: 'pointer', + textDecoration: 'underline', + fontFamily: theme.fontFamily.medium, + }, + requestTopupQueryText: { + backgroundColor: theme.palette.grey[31], + borderRadius: '6px', + padding: `${theme.spacing(1.0)} ${theme.spacing(1.5)}`, + marginBottom: theme.spacing(2.0), + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.medium, + }, + link: { + color: theme.palette.grey[53], + }, +})); diff --git a/src/views/Dashboard/index.jsx b/src/views/Dashboard/index.jsx new file mode 100644 index 0000000..eec9a36 --- /dev/null +++ b/src/views/Dashboard/index.jsx @@ -0,0 +1,115 @@ +import React, { useEffect, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import Loader from "../components/Loader"; +import ThankYou from "../ThankYou/"; +import { Box, Typography } from '@mui/material'; +import SuperAdmin from "./components/SuperAdmin"; +import { + selectUserRole, + setUserRole, + USER_ROLES, +} from "../../redux/userRoleSlice"; +import Totals from "./Tiles/Totals"; +import { useStyles } from './dashboardStyles'; + +function Dashboard() { + const classes = useStyles(); + + const [isLoading, setIsLoading] = React.useState(true); + const [data, setData] = useState({ + draftedJobs: 0, + activeJobs: 0, + closedJobs: 0, + totalJobs: 0, + totalMaxJobPostings: 0, + totalMaxResumeDownloads: 0, + resumeDownloadedCount: 0, + remainingJobPosts: 0, + remainingResumeDownload: 0, + accounts: null, + lastPlanPurchase: null, + }); + const dispatch = useDispatch(); + const userRole = useSelector(selectUserRole); + const user = useSelector((state) => state.login.user); + + useEffect(() => { + // Determine user role based on user data from login reducer + if (user) { + + // Check if user is a super admin + // This logic can be adjusted based on your specific role criteria + const isSuperAdmin = user.isBsAdmin || + (user.isAdmin && user.permissions?.includes("SUPER_ADMIN_PERMISSION")); + // Set the appropriate role in Redux + if (isSuperAdmin) { + dispatch(setUserRole(USER_ROLES.SUPER_ADMIN)); + } else { + dispatch(setUserRole(USER_ROLES.CLINIC_ADMIN)); + } + } + setIsLoading(false); + }, [user, dispatch]); + + const superAdmin = ( + + + + ); + + const clinicAdmin = ( + + {/* */} + + + + + Dashboard + + + + + {/* + + setPostAJobModal(true)} + isJobPostAvailable={data?.remainingJobPosts > 0} + /> + + {hasSubscriptionManagementPermission && ( + + + + )} + {postAJobModal && ( + setPostAJobModal(false)} + > + )} + */} + + + ); + + return ( + <> + {isLoading ? ( + + ) : userRole === USER_ROLES.SUPER_ADMIN ? ( + superAdmin + ) : ( + clinicAdmin + )} + + ); +} + +export default Dashboard; diff --git a/src/views/GlobalPopups/UpgradeYourPlan/index.jsx b/src/views/GlobalPopups/UpgradeYourPlan/index.jsx new file mode 100644 index 0000000..cbbd41b --- /dev/null +++ b/src/views/GlobalPopups/UpgradeYourPlan/index.jsx @@ -0,0 +1,56 @@ +import { Box, Grid, Typography } from '@mui/material'; +import React, { useState, useEffect } from 'react'; +import { useStyles } from './upgradeYourPlanStyles'; +import { LoadingButton } from '@mui/lab'; +import { ExpandMore } from '@mui/icons-material'; +import { useNavigate } from 'react-router-dom'; + +export default function UpgradeYourPlan({ message, show }) { + const classes = useStyles(); + const navigate = useNavigate(); + const [isOpen, setIsOpen] = useState(show); + + useEffect(() => { + setIsOpen(show); + }, [show]); + + const handleExpandClick = () => { + setIsOpen(!isOpen); + }; + + if (!isOpen || !message) return null; + + return ( + + + + + Update Your Plan + + + {message} + + + + navigate('/choose-plan')} + > + Update Now + + + + + + + + ); +} diff --git a/src/views/GlobalPopups/UpgradeYourPlan/upgradeYourPlanStyles.js b/src/views/GlobalPopups/UpgradeYourPlan/upgradeYourPlanStyles.js new file mode 100644 index 0000000..e63a463 --- /dev/null +++ b/src/views/GlobalPopups/UpgradeYourPlan/upgradeYourPlanStyles.js @@ -0,0 +1,67 @@ +import { makeStyles } from '@mui/styles'; +import { pxToRem } from '../../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + upgradePlanBox: { + width: '100%', + backgroundColor: theme.palette.primary.highlight, + bottom: 0, + zIndex: 1000, + padding: theme.spacing(1.5), + position: 'fixed', + transition: 'transform 1.0s ease-in-out', + }, + upgradePlanBoxHidden: { + transform: 'translateY(100%)', + }, + upgradePlanGrid: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + paddingLeft: theme.spacing(10.0), + paddingRight: theme.spacing(10.0), + }, + logoGrid: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '70px', + width: '70px', + }, + textGrid: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + padding: theme.spacing(1), + }, + updatePlanText: { + fontSize: pxToRem(24), + fontFamily: theme.fontFamily.bold, + }, + updatePlanSubText: { + fontSize: pxToRem(14), + fontFamily: theme.fontFamily.semibold, + }, + buttonGrid: { + alignSelf: 'center', + width: '150px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + button: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + upgradePlanDownArrowGrid: { + position: 'absolute', + top: 10, + right: 10, + cursor: 'pointer', + }, + expandArrow: { + fontSize: pxToRem(32), + color: theme.palette.grey[17], + }, +})); diff --git a/src/views/Login/LoginForm.jsx b/src/views/Login/LoginForm.jsx new file mode 100644 index 0000000..762f45f --- /dev/null +++ b/src/views/Login/LoginForm.jsx @@ -0,0 +1,288 @@ +import { VisibilityOffOutlined, VisibilityOutlined } from '@mui/icons-material'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Grid, + IconButton, + InputLabel, + Link, + TextField, + Typography, +} from '@mui/material'; +import { useFormik } from 'formik'; +import React, { useRef, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import * as Yup from 'yup'; +import { messaging } from '../../config/firebase'; +import { NOTIFICATION } from '../../constants'; +import { pushNotification } from '../../utils/notification'; +import { + passwordLengthRegex, + passwordLetterRegex, + passwordNumberRegex, + passwordSpacesRegex, +} from '../../utils/regex'; +import { requestNotificationPermission } from '../Notifications/notificationUtils'; +import { resetFormData } from '../Signup/signupAction'; +import PasswordValidation from './component/PasswordValidation'; +import { login, profile } from './loginAction'; +import { useStyles } from './loginStyles'; + +const validationSchema = Yup.object().shape({ + email: Yup.string() + .email('Invalid Email Address') + .required('Email is required'), +}); + +function LoginForm() { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const defaultFormData = useRef({ email: '', password: '' }); + const classes = useStyles(); + const [showPassword, setShowPassword] = useState(false); + + const fieldRefs = { + email: useRef(null), + password: useRef(null), + }; + + const handleTogglePasswordVisibility = () => { + setShowPassword((prevShowPassword) => !prevShowPassword); + }; + + const handleSubmit = async (values, formik) => { + try { + // Dispatch login action and wait for it to complete + const loginResp = await dispatch(login(values)); + + // Dispatch profile action separately and don't wait for it + await dispatch(profile()); + + // Reset form data + await dispatch(resetFormData()); + + // Show success notification + pushNotification('Login successful', NOTIFICATION.SUCCESS); + + formik.setSubmitting(false); + + // Navigate to dashboard immediately without waiting for profile + navigate('/'); + } catch (error) { + console.error('Login error:', error); + + // Handle errors in a more robust way + let errorMessage = 'Login failed. Please try again.'; + + if (error?.response?.data?.message) { + errorMessage = error.response.data.message; + + if (errorMessage.includes('Password')) { + fieldRefs.password.current.focus(); + formik.setErrors({ + password: errorMessage, + email: '', + }); + } else { + fieldRefs.email.current.focus(); + formik.setErrors({ + email: errorMessage, + password: '', + }); + } + } else { + // Generic error handling + pushNotification(errorMessage, NOTIFICATION.ERROR); + } + } finally { + formik.setSubmitting(false); + } + }; + + const handleForgotClick = () => { + navigate('/auth/forgotpassword'); + }; + + const formik = useFormik({ + initialValues: defaultFormData.current, + validationSchema, + onSubmit: (values) => handleSubmit(values, formik), + }); + + const handleSubmitClick = async (e) => { + e.preventDefault(); + // const formikErrors = await formik.validateForm(); + // const errors = Object.keys(formikErrors); + + // if (errors.length) { + // // Find the first invalid field and focus it + // const firstErrorField = errors[0]; + // const firstErrorRef = fieldRefs[firstErrorField]?.current; + + // if (firstErrorRef) { + // // Scroll to the first invalid field smoothly + // if (typeof firstErrorRef?.scrollIntoView === 'function') { + // firstErrorRef?.scrollIntoView({ + // behavior: 'smooth', + // block: 'center', + // }); + // } + + // // Focus the field after a slight delay (to ensure scrolling completes first) + // setTimeout(() => firstErrorRef.focus(), 300); + // } + + // // Show error notification + // if (formik?.touched[firstErrorField]) + // pushNotification(formikErrors[firstErrorField], NOTIFICATION.ERROR); + // } + + formik.handleSubmit(); + }; + + return ( + + + Hello! + + + Welcome to 24x7 AI HealthCare Receptionist + + + + Login + + navigate('/auth/signup/your-details')} + > + Sign Up + + +
    + + + Email + + + + Password + + + { + formik.handleBlur(e); + }} + inputRef={fieldRefs.password} + error={Boolean(formik.errors.password && formik.touched.password)} + helperText={ + formik.errors.password && formik.touched.password + ? formik.errors.password + : '' + } + InputProps={{ + endAdornment: ( + + {!showPassword ? ( + + ) : ( + + )} + + ), + }} + /> + + {/* Password validation display */} + {!formik.isValid && ( + <> + + Note: Password must be + + + + + + + + + )} + + + + + Login + + + + Forgot Password + + + + + +
    + ); +} + +export default LoginForm; diff --git a/src/views/Login/component/PasswordValidation.jsx b/src/views/Login/component/PasswordValidation.jsx new file mode 100644 index 0000000..0a41a44 --- /dev/null +++ b/src/views/Login/component/PasswordValidation.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Typography, Box } from '@mui/material'; +import { Check, Clear } from '@mui/icons-material'; +import { useStyles } from '../loginStyles'; +// import vectorIcon from '../../../assets/images/icon/Vector.png'; + +function PasswordValidation({ isValid, text }) { + const classes = useStyles(); + return ( + + {isValid ? ( + + ) : ( + + )} + + {text} + + + ); +} + +export default PasswordValidation; diff --git a/src/views/Login/index.jsx b/src/views/Login/index.jsx new file mode 100644 index 0000000..7f44a09 --- /dev/null +++ b/src/views/Login/index.jsx @@ -0,0 +1,28 @@ +import { Box, Card, Grid } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import LoginForm from './LoginForm'; +import { useStyles } from './loginStyles'; + +function Login() { + const theme = useTheme(); + const classes = useStyles(); + + return ( + + + + + College Logo + + + + + + ); +} + +export default Login; diff --git a/src/views/Login/loginAction.js b/src/views/Login/loginAction.js new file mode 100644 index 0000000..8eda59c --- /dev/null +++ b/src/views/Login/loginAction.js @@ -0,0 +1,106 @@ +import { axiosInstance } from '../../config/api'; +import { LOGIN, PROFILE } from './loginActionTypes'; + +// export const login = (data) => ({ +// type: LOGIN, +// payload: axiosInstance.post('/auth/companies/login', data), +// }); + +// export const profile = () => ({ +// type: PROFILE, +// payload: axiosInstance.get('/me'), +// }); + +export const login = (data) => { + // Determine user type based on email + const isSuperAdmin = data.email === 'admin@gmail.com'; + const userRole = isSuperAdmin ? 'SUPER_ADMIN' : 'CLINIC_ADMIN'; + const userId = isSuperAdmin ? 'super-admin-123' : 'clinic-admin-123'; + + // Store token and user type in localStorage to simulate a real login + localStorage.setItem('token', `mock-jwt-token-for-${isSuperAdmin ? 'super' : 'clinic'}-admin`); + localStorage.setItem('userType', userRole); + + // Mock successful login response + const mockLoginResponse = { + data: { + message: 'Login successful', + token: `mock-jwt-token-for-${isSuperAdmin ? 'super' : 'clinic'}-admin`, + user: { + id: userId, + email: data.email, + role: userRole + } + } + }; + + return { + type: LOGIN, + // Use a real Promise with a slight delay to simulate network request + payload: new Promise(resolve => setTimeout(() => resolve(mockLoginResponse), 300)) + }; +}; + +export const profile = () => { + // Get user type from localStorage to determine which profile to return + const userType = localStorage.getItem('userType') || 'CLINIC_ADMIN'; + const isSuperAdmin = userType === 'SUPER_ADMIN'; + + // Create appropriate mock profile based on user type + let profileData; + + if (isSuperAdmin) { + // Super Admin profile + profileData = { + id: 'super-admin-123', + name: 'Super Administrator', + email: 'admin@gmail.com', + role: 'SUPER_ADMIN', + isAdmin: true, + isBsAdmin: true, // This flag identifies super admin + permissions: [ + 'CREATE_REC_USERS', + 'READ_REC_USERS', + 'DELETE_REC_USERS', + 'SUPER_ADMIN_PERMISSION' + ] + }; + } else { + // Clinic Admin profile + profileData = { + id: 'clinic-admin-123', + name: 'Clinic Administrator', + email: 'admin@clinic.com', + role: 'CLINIC_ADMIN', + isAdmin: true, + isBsAdmin: false, + permissions: [ + 'CREATE_REC_USERS', + 'READ_REC_USERS', + 'DELETE_REC_USERS' + ], + clinic: { + id: 'clinic-123', + name: 'Health Plus Clinic', + address: '123 Medical Center Blvd', + city: 'Healthcare City', + state: 'HC', + zipCode: '12345', + phone: '555-123-4567' + } + }; + } + + // Create the response with the nested data structure the reducer expects + const mockProfileResponse = { + data: { + data: profileData + } + }; + + return { + type: PROFILE, + // Use a real Promise with a slight delay to simulate network request + payload: new Promise(resolve => setTimeout(() => resolve(mockProfileResponse), 300)) + }; +}; diff --git a/src/views/Login/loginActionTypes.js b/src/views/Login/loginActionTypes.js new file mode 100644 index 0000000..e65c3b2 --- /dev/null +++ b/src/views/Login/loginActionTypes.js @@ -0,0 +1,9 @@ +export const LOGIN_PENDING = 'LOGIN_PENDING'; +export const LOGIN_FULFILLED = 'LOGIN_FULFILLED'; +export const LOGIN_REJECTED = 'LOGIN_REJECTED'; +export const LOGIN = 'LOGIN'; + +export const PROFILE_PENDING = 'PROFILE_PENDING'; +export const PROFILE_FULFILLED = 'PROFILE_FULFILLED'; +export const PROFILE_REJECTED = 'PROFILE_REJECTED'; +export const PROFILE = 'PROFILE'; diff --git a/src/views/Login/loginReducer.js b/src/views/Login/loginReducer.js new file mode 100644 index 0000000..50043bf --- /dev/null +++ b/src/views/Login/loginReducer.js @@ -0,0 +1,115 @@ +import { PERMISSIONS } from '../../constants'; +import { + LOGIN_FULFILLED, + LOGIN_PENDING, + LOGIN_REJECTED, + PROFILE_FULFILLED, + PROFILE_PENDING, + PROFILE_REJECTED, +} from './loginActionTypes'; + +const initialState = {}; +const loginPending = (state) => ({ + ...state, +}); +const loginFulfilled = (state) => ({ + ...state, +}); +const loginRejected = (state) => ({ + ...state, +}); + +const profilePending = (state) => ({ + ...state, + profile: 'loading', +}); +const profileFulfilled = (state, payload) => { + // console.log('state', state); + let { payload: payloadData } = payload; + + let user = payloadData?.data?.data; + let userPermissions = []; + + if (Array.isArray(user?.roles)) { + user.roles.forEach((role) => { + userPermissions.push( + ...(role?.rolePermissionMapping?.map( + (permission) => permission?.permission.name + ) || []) + ); + }); + // Check if user is admin, add additional permissions + if (user.isAdmin && !user?.isBsAdmin) { + userPermissions.push( + 'CREATE_REC_USERS', + 'READ_REC_USERS', + 'DELETE_REC_USERS' + ); + } + } else { + userPermissions = + user?.role?.rolePermissionMapping?.map( + (permission) => permission?.permission.name + ) || []; + } + const storeData = JSON.parse(localStorage.getItem('redux')); + if (storeData?.loginAsCompanyAdmin?.companyId) { + userPermissions.push( + PERMISSIONS.CREATE_JOB_POSTING, + PERMISSIONS.READ_JOB_POSTING, + PERMISSIONS.DELETE_JOB_POSTING, + PERMISSIONS.CREATE_SEARCH_CANDIDATE, + PERMISSIONS.READ_SEARCH_CANDIDATE, + PERMISSIONS.DELETE_SEARCH_CANDIDATE, + PERMISSIONS.CREATE_FOLDERS, + PERMISSIONS.READ_FOLDERS, + PERMISSIONS.DELETE_FOLDERS, + PERMISSIONS.BILLING_AND_PAYMENTS, + PERMISSIONS.CREATE_REC_USERS, + PERMISSIONS.READ_REC_USERS, + PERMISSIONS.DELETE_REC_USERS + ); + } + + user.permissions = []; + + delete user?.role; + + return { + ...state, + user: user, + profile: 'success', + lastUpdated: new Date().getTime(), + }; +}; + +const profileRejected = (state) => ({ + ...state, + user: null, + profile: 'error', +}); + +export default function (state = initialState, action) { + switch (action.type) { + case LOGIN_PENDING: + return loginPending(state, action); + + case LOGIN_FULFILLED: + return loginFulfilled(state, action); + + case LOGIN_REJECTED: + return loginRejected(state, action); + + case PROFILE_PENDING: + return profilePending(state, action); + + case PROFILE_FULFILLED: + return profileFulfilled(state, action); + + case PROFILE_REJECTED: + return profileRejected(state, action); + + default: + return state; + } +} diff --git a/src/views/Login/loginStyles.js b/src/views/Login/loginStyles.js new file mode 100644 index 0000000..23c5b5c --- /dev/null +++ b/src/views/Login/loginStyles.js @@ -0,0 +1,248 @@ +import makeStyles from '@mui/styles/makeStyles'; +import backgroundImage from '../../assets/images/background/background.jpg'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + root: { + fontFamily: 'Inter-Regular', + height: '100vh', + backgroundImage: `url(${backgroundImage})`, + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + overflow: 'scroll', + overflowX: 'hidden', + padding: theme.spacing(2.0), + // paddingTop: theme.spacing(4), + }, + card: { + width: '370px', + // height: '650px', + maxHeight: '835px', + marginTop: 'auto', + marginBottom: 'auto', + // overflow: 'auto', + [theme.breakpoints.up('sm')]: { + width: '500px', + // height: '750px', + }, + [theme.breakpoints.up('md')]: { + width: '500px', + // height: '750px', + }, + [theme.breakpoints.up('lg')]: { + width: '500px', + // height: '700px', + }, + [theme.breakpoints.up('xl')]: { + width: '500px', + // height: '650px', + }, + boxShadow: 'none', + borderRadius: theme.spacing(3.8), + opacity: '0.95', + }, + errorMessage: { + fontStyle: 'italic', + }, + emailField: { + marginBottom: '20px', + }, + labelStyle: { + marginBottom: '0px', + fontSize: pxToRem(3.2), + color: theme.palette.grey[10], + // paddingBottom: '20px', + }, + inputTitle: { + '&.MuiTypography-body1': { + fontSize: pxToRem(12), + color: theme.palette.grey[10], + }, + fontSize: pxToRem(12), + // opacity: '0.54', + color: theme.palette.grey[10], + marginBottom: theme.spacing(0.6), + marginLeft: theme.spacing(0.6), + }, + loginButton: { + fontFamily: 'Inter-Regular', + fontWeight: 500, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + padding: '15px', + height: '40px', + fontSize: pxToRem(13.8), + }, + defaultLogo: { + margin: 'auto', + height: '80px', + marginTop: theme.spacing(4.1), + marginBottom: theme.spacing(2.3), + [theme.breakpoints.up('md')]: { + height: '80px', + marginTop: theme.spacing(3.0), + marginBottom: theme.spacing(0.8), + }, + [theme.breakpoints.up('lg')]: { + height: '80px', + marginTop: theme.spacing(3.0), + marginBottom: theme.spacing(0.8), + }, + [theme.breakpoints.up('xl')]: { + height: '80px', + marginTop: theme.spacing(3.0), + marginBottom: theme.spacing(0.8), + }, + }, + right: { + textAlign: 'left', + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + paddingLeft: theme.spacing(0), + paddingRight: theme.spacing(0), + }, + form: { + width: '70%', + marginTop: '30px', + }, + formBody: { + justifyContent: 'center', + display: 'flex', + flexDirection: 'column', + // minHeight: '360px', + overflow: 'auto', + marginBottom: '40px', + [theme.breakpoints.up('md')]: { + // height: '400px', + }, + [theme.breakpoints.up('lg')]: { + // height: '580x', + }, + [theme.breakpoints.up('xl')]: { + // height: '250px', + }, + + '& .MuiTextField-root': { + marginTop: theme.spacing(0), + // marginTop: theme.spacing(0.8), + [theme.breakpoints.up('md')]: { + marginTop: theme.spacing(0), + }, + [theme.breakpoints.up('lg')]: { + marginTop: theme.spacing(0), + }, + [theme.breakpoints.up('xl')]: { + marginTop: theme.spacing(0), + }, + }, + }, + gridContainer: { + marginTop: '30px', + width: '70%', + textAlign: 'center', + }, + loginGridItem: { + fontWeight: 200, + color: 'white', + fontSize: pxToRem(14.4), + padding: '12px !important', + backgroundColor: 'black', + borderTopLeftRadius: '10px', + borderBottomLeftRadius: '10px', + }, + signupGridItem: { + fontWeight: 200, + color: 'white', + fontSize: pxToRem(14.4), + padding: '12px !important', + backgroundColor: 'grey', + borderTopRightRadius: '10px', + borderBottomRightRadius: '10px', + cursor: 'pointer', + }, + heading: { + fontFamily: 'Inter-ExtraBold', + lineHeight: 1.2, + // fontWeight: 700, + fontSize: pxToRem(28), + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(28), + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(26), + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(24), + }, + }, + collageName: { + fontWeight: 500, + fontFamily: 'Inter-Medium', + lineHeight: 1.2, + fontSize: pxToRem(15), + overflow: 'hidden', + textOverflow: 'ellipsis', + padding: '8px', + display: '-webkit-box', + '-webkit-line-clamp': 2, + '-webkit-box-orient': 'vertical', + [theme.breakpoints.up('md')]: { + fontSize: pxToRem(15), + }, + [theme.breakpoints.up('lg')]: { + fontSize: pxToRem(18), + }, + [theme.breakpoints.up('xl')]: { + fontSize: pxToRem(18), + }, + }, + passwordCheckListTitle: { + color: 'grey', + fontSize: '0.9rem', + fontStyle: 'italic', + }, + passwordCheckList: { + color: 'grey', + // fontSize: '0.9rem', + paddingTop: '2px', + fontStyle: 'italic', + }, + actions: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + marginBottom: theme.spacing(3.6), + marginLeft: theme.spacing(0.2), + marginTop: theme.spacing(1.9), + }, + btnMainDiv: { + margin: theme.spacing(0.8), + }, + dialogScrollingWrapper: { + overflow: 'scroll', + overflowX: 'hidden', + }, + passwordValidationText: { + fontSize: '0.8rem', + padding: '1px', + }, + forgotPasswordButton: { + color: 'gray', + textDecoration: 'none', + textDecorationColor: 'gray', + fontSize: '0.8rem', + }, + linkWithLine: { + content: '', + display: 'block', + borderBottom: '1.6px solid #0E2354', + opacity: 0.4, + marginTop: '-2px', + }, +})); diff --git a/src/views/MockPayment/index.jsx b/src/views/MockPayment/index.jsx new file mode 100644 index 0000000..b65f100 --- /dev/null +++ b/src/views/MockPayment/index.jsx @@ -0,0 +1,326 @@ +import { + Box, + Button, + Card, + CardContent, + Chip, + CircularProgress, + Container, + Divider, + FormControlLabel, + Grid, + Paper, + Radio, + RadioGroup, + Stack, + TextField, + Typography, +} from '@mui/material'; +import React, { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import creditcard from '../../assets/images/logo/credit_card.png'; +import { NOTIFICATION } from '../../constants'; +import { pushNotification } from '../../utils/notification'; +import { useNavigate } from 'react-router-dom'; +import { setClinicId } from '../../views/ClinicDetails/store/logInAsClinicAdminAction'; + +const PaymentPage = () => { + const [paymentMethod, setPaymentMethod] = useState('card'); + const [cardNumber, setCardNumber] = useState(''); + const [cardExpiry, setCardExpiry] = useState(''); + const [cardCVC, setCardCVC] = useState(''); + const [name, setName] = useState(''); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + const navigate = useNavigate(); + const dispatch = useDispatch(); + + const handleSubmit = (e) => { + e.preventDefault(); + setLoading(true); + + // Simulate payment processing + setTimeout(() => { + setLoading(false); + setSuccess(true); + }, 2000); + + pushNotification( + 'Your Account is under review. You will be notified once it is approved.', + NOTIFICATION.SUCCESS + ); + + // Set clinic admin credentials in Redux store and navigate to login page after 4 seconds + setTimeout(() => { + // Dispatch action to set clinic admin credentials + dispatch(setClinicId({ companyId: 'clinic-admin-123' })); + navigate('/auth/login'); + }, 4000); + }; + + const formatCardNumber = (value) => { + const v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, ''); + const matches = v.match(/\d{4,16}/g); + const match = (matches && matches[0]) || ''; + const parts = []; + + for (let i = 0; i < match.length; i += 4) { + parts.push(match.substring(i, i + 4)); + } + + if (parts.length) { + return parts.join(' '); + } else { + return value; + } + }; + + const formatExpiry = (value) => { + const v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, ''); + + if (v.length >= 2) { + return `${v.substring(0, 2)}/${v.substring(2, 4)}`; + } + + return value; + }; + + return ( + + + + Premium Subscription + + + + + + {success ? ( + + + Payment Successful! + + + Thank you for your purchase. You will receive a confirmation + email shortly. + + + ) : ( +
    + + *Using Stripe Payment Gateway + + + + Payment Method + + + setPaymentMethod(e.target.value)} + > + + } + label={ + + + Credit / Debit Card + + } + /> + + {paymentMethod === 'card' && ( + + + setCardNumber(formatCardNumber(e.target.value)) + } + placeholder="1234 5678 9012 3456" + inputProps={{ maxLength: 19 }} + /> + + + + + setCardExpiry(formatExpiry(e.target.value)) + } + placeholder="MM/YY" + inputProps={{ maxLength: 5 }} + /> + + + setCardCVC(e.target.value)} + placeholder="123" + inputProps={{ maxLength: 3 }} + /> + + + + setName(e.target.value)} + placeholder="John Smith" + /> + + )} + + + + + + )} +
    +
    + + + + + + Order Summary + + + + + + + Premium Plan + + + Annual Subscription + + + + $10,000.00 + + + + + + + + Subtotal + $10,000.00 + + + + Tax + + + $0.00 + + + + + + Total + + + $10,000.00 + + + + + + Your subscription will automatically renew after 12 months + + + + + + + + Secure Payment: All transactions are encrypted + and secure. + + + Customer Support: If you have any questions, + please contact our support team at support@healthapps.com + + + +
    +
    +
    + ); +}; + +export default PaymentPage; diff --git a/src/views/Modal/ConfirmationModal.jsx b/src/views/Modal/ConfirmationModal.jsx new file mode 100644 index 0000000..b4e09d1 --- /dev/null +++ b/src/views/Modal/ConfirmationModal.jsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { Box, Grid, Typography } from '@mui/material'; +import { LoadingButton } from '@mui/lab'; +import { useStyles } from './confirmationModalStyles'; +import CustomModal from './Modal'; + +const ConfirmationModal = ({ + heading, + bodyContent, + bodyFunction, + confirmationLine, + leftButtonText, + rightButtonText, + onCancel, + onConfirm, + buttonLoading, +}) => { + const classes = useStyles(); + return ( + ( + + + + {heading} + + + + + + {bodyContent} + + {bodyFunction ? bodyFunction() : null} + + + +

    {confirmationLine}

    +
    + + + + + {rightButtonText} + + +
    + )} + /> + ); +}; + +export default ConfirmationModal; diff --git a/src/views/Modal/Dialog.jsx b/src/views/Modal/Dialog.jsx new file mode 100644 index 0000000..8bcd376 --- /dev/null +++ b/src/views/Modal/Dialog.jsx @@ -0,0 +1,16 @@ +import { Dialog as MuiDialog } from '@mui/material'; +import React from 'react'; +import { useStyles } from './modalStyle.js'; + +const Dialog = ({ ...rest }) => { + const { dialogModalWrapper } = useStyles({ ...rest }); + return ( + + ); +}; +export default Dialog; diff --git a/src/views/Modal/DialogContent.jsx b/src/views/Modal/DialogContent.jsx new file mode 100644 index 0000000..aea17c8 --- /dev/null +++ b/src/views/Modal/DialogContent.jsx @@ -0,0 +1,9 @@ +import { DialogContent as MuiDialogContent } from '@mui/material'; +import React from 'react'; +import { useStyles } from './modalStyle.js'; + +const DialogContent = ({ sx, ...rest }) => { + const { dialogContentSXProps } = useStyles({ ...rest }); + return ; +}; +export default DialogContent; diff --git a/src/views/Modal/Modal.jsx b/src/views/Modal/Modal.jsx new file mode 100644 index 0000000..e31490d --- /dev/null +++ b/src/views/Modal/Modal.jsx @@ -0,0 +1,224 @@ +import { CloseRounded } from '@mui/icons-material'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + DialogActions, + DialogContentText, + DialogTitle, + Grid, + Typography, +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; + +import Dialog from './Dialog.jsx'; +import DialogContent from './DialogContent.jsx'; +import { useStyles } from './modalStyle.js'; +const CustomModal = ({ + src, + title, + subTitle, + info, + modalBodyFunction, + leftButtonTitle, + rightButtonTitle, + leftButtonFunctionCall, + rightButtonFunctionCall, + rightButtonLoading, + isScrolling, + isTitleLeft, + isTitleLeftWithBackAndCloseButton, + contentWrapperStyles, + detailModal, + contentScroll, + hideTitle, + header, + disableRightButton, + showCloseButton = true, + showBack, + backFunctionCall, + onClose, + imageStyle, + noPadding, + showFooter, + footerLeftButtonTitle, + footerRightButtonTitle, + footerRightButtonLoading, + onFooterLeftButtonClick, + onFooterRightButtonClick, + customClassForTitle, +}) => { + const classes = useStyles(); + + const dialogContentStyle = noPadding ? { padding: '0px' } : {}; + + return ( +
    + + {header ? header() : null} + {!hideTitle && ( + + {!detailModal && (backFunctionCall || onClose) && showBack && ( + + )} + {!detailModal && + (onClose || leftButtonFunctionCall) && + showCloseButton && ( + + )} + +
    + {src && ( + + )} + + {title} + + {subTitle && ( + + {subTitle} + + )} +
    +
    + )} + + {info} + {modalBodyFunction ? modalBodyFunction() : null} + + {!showFooter && !detailModal && ( +
    + + {leftButtonTitle && ( + + )} + {rightButtonTitle && ( + + + {rightButtonTitle} + + + )} + +
    + )} + + {showFooter && ( + + + + + {footerLeftButtonTitle} + + + + + {footerRightButtonTitle} + + + + + )} +
    +
    + ); +}; + +export default CustomModal; diff --git a/src/views/Modal/confirmationModalStyles.js b/src/views/Modal/confirmationModalStyles.js new file mode 100644 index 0000000..c2f115c --- /dev/null +++ b/src/views/Modal/confirmationModalStyles.js @@ -0,0 +1,57 @@ +import makeStyles from '@mui/styles/makeStyles'; + +export const useStyles = makeStyles((theme) => ({ + title: { + textAlign: 'center', + }, + body: { + textAlign: 'center', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + marginTop: theme.spacing(1), + }, + bodyContent: { + width: '80%', + display: 'inline-block', + verticalAlign: 'top', + whiteSpace: 'normal', + textAlign: 'center', + }, + confirmationLine: { + textAlign: 'center', + marginTop: theme.spacing(2), + fontFamily: theme.fontFamily.bold, + }, + leftButton: { + width: '200px', + height: '48px', + marginRight: theme.spacing(1), + border: 'none', + borderRadius: '12px', + cursor: 'pointer', + color: theme.palette.grey[600], + fontWeight: 600, + fontSize: theme.typography.pxToRem(16), + textTransform: 'none', + }, + rightButton: { + width: '200px', + height: '48px', + backgroundColor: theme.palette.primary.main, + color: theme.palette.common.white, + fontSize: theme.typography.pxToRem(16), + fontWeight: 600, + border: 'none', + borderRadius: '12px', + cursor: 'pointer', + justifyContent: 'center', + }, + actionWrapper: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + paddingTop: theme.spacing(4), + paddingBottom: theme.spacing(1), + }, +})); diff --git a/src/views/Modal/modalStyle.js b/src/views/Modal/modalStyle.js new file mode 100644 index 0000000..beea6f7 --- /dev/null +++ b/src/views/Modal/modalStyle.js @@ -0,0 +1,185 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + dialogWrapper: { + // overflow: 'scroll', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + dialogScrollingWrapper: { + overflow: 'scroll', + overflowX: 'hidden', + }, + dialogContainerWrapper: { + marginTop: theme.spacing(3.2), + marginBottom: theme.spacing(3.2), + maxWidth: '1110px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '20px', + }, + detailContainerWrapper: { + marginTop: theme.spacing(3.2), + marginBottom: theme.spacing(3.2), + width: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + maxWidth: '1000px', + }, + dialogTitleWrapper: { + minWidth: '200px', + maxWidth: '1110px', + width: '100%', + display: 'flex', + alignItems: 'center', + paddingTop: theme.spacing(2.4), + justifyContent: 'space-between', + flexDirection: 'column', + }, + detailTitleWrapper: { + width: '100%', + display: 'flex', + padding: `${theme.spacing(1.8)} ${theme.spacing(3.1)}`, + marginBottom: theme.spacing(1), + position: 'relative', + }, + closeIcon: { + cursor: 'pointer', + position: 'absolute', + right: 20, + // top: 20, + }, + backIcon: { + cursor: 'pointer', + position: 'absolute', + left: 20, + // top: 20, + }, + actionWrapper: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + // padding: '20px 12px', + // paddingBottom: theme.spacing(2.4), + }, + actions: { + '&.MuiDialogActions-root': { + minWidth: '100px', + maxWidth: '300px', + }, + }, + imageWrapper: { + width: '164px', + height: '110px', + marginTop: theme.spacing(1.6), + marginBottom: theme.spacing(1), + }, + imageContainer: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + textAlign: 'start', + width: '100%', + }, + imageContainerTitleLeftAligned: { + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + textAlign: 'start', + width: '80%', + }, + contentWrapper: { + maxWidth: '530px', + minWidth: '200px', + // width: '100%', + // overflow: 'hidden', + }, + contentScrollWrapper: { + width: '100%', + height: '100%', + overflowY: 'auto', + }, + leftButton: { + width: '186px', + height: '48px', + marginRight: theme.spacing(1), + marginBottom: theme.spacing(2.0), + border: 'none', + borderRadius: '12px', + cursor: 'pointer', + }, + leftButtonText: { + color: theme.palette.grey[34], + opacity: '0.54', + fontWeight: 600, + fontFamily: theme?.fontFamily?.semiBold, + fontSize: pxToRem(16), + textTransform: 'none', + }, + rightButton: { + width: '186px', + height: '48px', + backgroundColor: theme?.palette?.primary?.main, + color: theme?.palette?.common?.white, + fontSize: pxToRem(16), + marginBottom: theme.spacing(2.0), + fontFamily: theme?.fontFamily?.semiBold, + border: 'none', + borderRadius: '12px', + cursor: 'pointer', + justifyContent: 'center', + }, + rightButtonText: { + textTransform: 'none', + color: theme.palette.grey[0], + }, + dialogModalWrapper: { + '& .MuiDialog-paper': { + background: theme.palette.grey[0], + }, + }, + modalTitle: { + fontFamily: theme.fontFamily.bold, + fontSize: pxToRem(24), + fontWeight: 600, + lineHeight: 'normal', + letterSpacing: 'normal', + }, + modalTitleLeft: { + fontFamily: theme.fontFamily.bold, + fontSize: pxToRem(24), + fontWeight: 600, + lineHeight: 'normal', + letterSpacing: 'normal', + // marginLeft: '-240px', + alignSelf: 'flex-start', + marginBottom: '-20px', + }, + modalSubTitleLeft: { + width: '100%', + marginTop: theme.spacing(2.5), + }, + footerMainDiv: { + width: '100%', + marginTop: theme.spacing(0.6), + padding: theme.spacing(1.8), + backgroundColor: '#E9EAEB', + }, + modalFooter: { + display: 'flex', + justifyContent: 'flex-end', + paddingRight: theme.spacing(0.6), + '& .MuiButton-root ': { + display: 'flex', + justifyContent: 'center', + }, + }, + cancelButton: { + marginRight: theme.spacing(0.5), + }, +})); diff --git a/src/views/NotFound/index.jsx b/src/views/NotFound/index.jsx new file mode 100644 index 0000000..e11fd48 --- /dev/null +++ b/src/views/NotFound/index.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Button, Container, Typography } from '@mui/material'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import { useStyles } from './notFoundStyles'; +import { useNavigate } from 'react-router-dom'; + +function NotFound() { + const classes = useStyles(); + const navigate = useNavigate(); + return ( + + + + Page not found + + + + ); +} + +export default NotFound; diff --git a/src/views/NotFound/notFoundStyles.js b/src/views/NotFound/notFoundStyles.js new file mode 100644 index 0000000..41695c7 --- /dev/null +++ b/src/views/NotFound/notFoundStyles.js @@ -0,0 +1,19 @@ +import makeStyles from '@mui/styles/makeStyles'; +export const useStyles = makeStyles((theme) => ({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingTop: theme.spacing(10), + }, + icon: { + fontSize: '10rem', + color: theme.palette.error.main, + }, + message: { + paddingTop: theme.spacing(2), + }, + home: { + marginTop: theme.spacing(2), + }, +})); diff --git a/src/views/Notifications/notificationConstant.js b/src/views/Notifications/notificationConstant.js new file mode 100644 index 0000000..350b19c --- /dev/null +++ b/src/views/Notifications/notificationConstant.js @@ -0,0 +1,15 @@ +export const NOTIFICATION_TYPE = { + NEW_COMPANY_REQUEST: 'new_company_request', + DOCUMENT_REUPLOADED: 'document_reuploaded', + ONLINE_PAYMENT_NOTIFICATION: 'online_payment_notification', + JC_PLAN_IS_GOING_TO_GET_EXPIRED: 'jc_plan_is_going_to_get_expired', + JC_PARTICULAR_JOB_HAS_BEEN_IN_DRAFT: 'jc_particular_job_has_been_in_draft', + PLAN_IS_GOING_TO_GET_EXPIRED: 'plan_is_going_to_get_expired', + PARTICULAR_JOB_HAS_BEEN_IN_DRAFT: 'particular_job_has_been_in_draft', + JOB_CREATED: 'job_created', + JOB_EXPIRY_DATE_CHANGED: 'job_expiry_date_changed', + JOB_CLOSED: 'job_closed', +}; + +export const UNREAD_NOTIFICATIONS_SIZE = 2; +export const RECENT_NOTIFICATIONS_SIZE = 4; diff --git a/src/views/Notifications/notificationUtils.js b/src/views/Notifications/notificationUtils.js new file mode 100644 index 0000000..c5d4fd8 --- /dev/null +++ b/src/views/Notifications/notificationUtils.js @@ -0,0 +1,63 @@ +import { getToken } from 'firebase/messaging'; +import { RECRUITMENT_URL, FB_VAPID_KEY } from '../../common/envVariables'; +import { messaging } from '../../config/firebase'; +import { saveFcmToken } from '../../services/notification.service'; +import { NOTIFICATION_TYPE } from './notificationConstant'; + +export const requestNotificationPermission = async () => { + const permission = await Notification.requestPermission(); + if (permission !== 'granted') { + return; + } + + if (!messaging) return; + + try { + const currentToken = await getToken(messaging, { + vapidKey: FB_VAPID_KEY, + }); + if (currentToken) { + await saveFcmToken({ + token: currentToken, + }); + } + } catch (err) { + // eslint-disable-next-line no-console + console.error('An error occurred while retrieving token. ', err); + } +}; + +export const redirectByNotificationType = ( + notificationType, + id, + inAppNotification = false +) => { + if (!notificationType || !id) { + // eslint-disable-next-line no-console + console.error('Please provide both notification-type and id'); + return `${RECRUITMENT_URL}`; + } + + const companyDetailRoute = `companies/${id}`; + const planPageRoute = `choose-plan`; + const jobDetailRoute = `jobs/${id}`; + + const redirectMap = { + [NOTIFICATION_TYPE.NEW_COMPANY_REQUEST]: `${companyDetailRoute}`, + [NOTIFICATION_TYPE.DOCUMENT_REUPLOADED]: `${companyDetailRoute}`, + [NOTIFICATION_TYPE.ONLINE_PAYMENT_NOTIFICATION]: `${companyDetailRoute}`, + [NOTIFICATION_TYPE.JC_PLAN_IS_GOING_TO_GET_EXPIRED]: `${planPageRoute}`, + [NOTIFICATION_TYPE.JC_PARTICULAR_JOB_HAS_BEEN_IN_DRAFT]: `${jobDetailRoute}`, + [NOTIFICATION_TYPE.PLAN_IS_GOING_TO_GET_EXPIRED]: `${planPageRoute}`, + [NOTIFICATION_TYPE.PARTICULAR_JOB_HAS_BEEN_IN_DRAFT]: `${jobDetailRoute}`, + [NOTIFICATION_TYPE.JOB_CREATED]: `${jobDetailRoute}`, + [NOTIFICATION_TYPE.JOB_EXPIRY_DATE_CHANGED]: `${jobDetailRoute}`, + [NOTIFICATION_TYPE.JOB_CLOSED]: `${jobDetailRoute}`, + }; + + let route = `${redirectMap[notificationType]}`; + if (!inAppNotification) { + route = RECRUITMENT_URL + route; + } + return route; +}; diff --git a/src/views/Signup/YourDetailsForm.jsx b/src/views/Signup/YourDetailsForm.jsx new file mode 100644 index 0000000..f727dfc --- /dev/null +++ b/src/views/Signup/YourDetailsForm.jsx @@ -0,0 +1,1515 @@ +import { ExpandMore } from "@mui/icons-material"; +import { VisibilityOffOutlined, VisibilityOutlined } from "@mui/icons-material"; +import VerifiedIcon from "@mui/icons-material/Verified"; +import { + AppBar, + Box, + Button, + ButtonBase, + Drawer, + FormHelperText, + Grid, + IconButton, + InputAdornment, + InputLabel, + MenuItem, + Paper, + Select, + TextField, + Toolbar, + Typography, +} from "@mui/material"; +import { useTheme } from "@mui/material/styles"; +import axios from "axios"; +import { useFormik } from "formik"; +import { debounce } from "lodash"; +import React, { useEffect, useRef, useState, useCallback } from "react"; +import Dropzone from "react-dropzone"; +import ReactCrop from "react-image-crop"; +import "react-image-crop/dist/ReactCrop.css"; +import "react-phone-input-2/lib/style.css"; +import { useDispatch, useSelector } from "react-redux"; +import { Link, useNavigate } from "react-router-dom"; +import * as Yup from "yup"; +import logo from "../../../src/assets/images/logo/BSmart_Logo_1b 2.svg"; +import uploadIcon from "../../assets/images/icon/upload.svg"; +import { IMAGE_LOCATION_BASE_URL } from "../../common/envVariables"; +import CustomizedCheckbox from "../../components/Checkbox"; +import CustomFileUpload from "../../components/CustomFileUpload"; +import CustomStepper from "../../components/CustomStepper"; +import FormFooterMessage from "../../components/FormFooterMessage"; +import FormPageHeader from "../../components/FormPageHeader"; +import FormSectionHeading from "../../components/FormSectionHeading"; +import Loader from "../../components/Loader"; +import TermsAndCondition from "../../components/TermsAndCondition"; +import { + ABN_NUMBER_LENGTH, + GST_NUMBER_LENGTH, + IMAGE_TYPE, + MAX_FILES, + MAX_FILE_SIZE_IN_MB, + MEDIICARE_NUMBER_LENGTH, + NOTIFICATION, + ONLY_ALPHA_NUMERIC_ACCEPT_REGEX, + WEBSITE_REGEX, +} from "../../constants"; +import { fileUpload } from "../../services/file.upload.services"; +import { pushNotification } from "../../utils/notification"; +import { + passwordLengthRegex, + passwordLetterRegex, + passwordNumberRegex, + passwordSpacesRegex, +} from "../../utils/regex"; +import { capitalizeAllLetters, capitalizeFirstLetter } from "../../utils/share"; +import PasswordValidation from "../Login/component/PasswordValidation"; +import { updateFormDetails } from "./signupAction"; +import { useStyles } from "./styles/signupStyles"; + +function YourDetailsForm() { + const classes = useStyles(); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const theme = useTheme(); + const [otpField, setOtpField] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [billingUsers, setBillingUsers] = useState([]); + const [drawerOpen, setDrawerOpen] = useState(false); + const [localityOption, setLocalityOption] = useState([]); + const [countryOption, setCountryOption] = useState(["Australia", "India"]); + const [pincodeData, setPincodeData] = useState(); + const selectedLocalityRef = useRef(); + + // Form data from Redux + const yourDetailsFormData = useSelector((state) => state.signup); + + // Logo state variables + const logoRef = useRef(null); + const fileInputRef = useRef(null); + const [aspect, setAspect] = useState(1); + const [logoname, setLogoname] = useState(); + const [logoImage, setLogoImage] = useState(); + const [crop, setCrop] = useState({ + unit: "%", + width: 100, + height: 100, + aspect: 1 / 1, + }); + + const userRoles = [ + { id: "practice_manager", name: "Practice Manager" }, + { id: "director", name: "Director" }, + ]; + + const integrationOptions = ["BP Software", "Medical Director"]; + + // Default form data + const defaultFormData = useRef({ + // Personal details + name: "", + email: "", + password: "", + mobileNumber: "", + confirmPassword: "", + currentEmail: "", + otp: "", + + // Clinic details + companyName: "", + designation: "", + companyWebsite: "", + companyLogo: "", + companyIndustry: "", + pincode: "", + state: "", + locality: "", + country: "", + fullAddress: "", + companyPANImage: "", + companyPANImage: "", + termsAccepted: "", + }); + + // Field references for focus and validation + const fieldRefs = { + // Personal details fields + name: useRef(null), + email: useRef(null), + mobileNumber: useRef(null), + password: useRef(null), + confirmPassword: useRef(null), + otp: useRef(null), + + // Clinic details fields + companyName: useRef(null), + businessPhone: useRef(null), + businessFax: useRef(null), + practiceManagementSystem: useRef(null), + designation: useRef(null), + companyWebsite: useRef(null), + companyIndustry: useRef(null), + pincode: useRef(null), + state: useRef(null), + locality: useRef(null), + country: useRef(null), + fullAddress: useRef(null), + companyPANImage: useRef(null), + companyTANNumber: useRef(null), + }; + + if (yourDetailsFormData) { + defaultFormData.current = { ...yourDetailsFormData }; + } + + useEffect(() => { + if (yourDetailsFormData) { + setBillingUsers(yourDetailsFormData?.billingUsers); + } + }, [yourDetailsFormData]); + + const validationSchema = Yup.object().shape({ + // Personal details validation + name: Yup.string().required("Name is required"), + email: Yup.string() + .email("Please enter valid Email Address") + .required("Email Address is required") + .typeError("Email Address must be string"), + mobileNumber: Yup.string() + .required("Mobile Number is required") + .matches(/^[0-9]+$/, "Mobile Number must be a valid number") + .min(10, "Mobile number should have 10 digits only.") + .max(10, "Mobile number should have 10 digits only."), + password: Yup.string() + .matches(passwordLengthRegex, "Password must be at least 8 characters") + .matches(passwordLetterRegex, "Password must contain at least one letter") + .matches(passwordNumberRegex, "Password must contain at least one number") + .matches( + passwordSpacesRegex, + "Password must not start or end with a space" + ) + .required("Password is required"), + confirmPassword: Yup.string() + .oneOf([Yup.ref("password"), null], "Passwords must match") + .required("The password must match"), + currentEmail: Yup.string().email("Invalid email").notRequired(), + otp: Yup.string().required("OTP is required"), + + // Clinic details validation + companyName: Yup.string().required("Clinic Name is required"), + businessPhone: Yup.string() + .required("Business Phone is required") + .matches(/^[0-9]+$/, "Business Phone must be a valid number") + .min(10, "Business Phone number should have 10 digits only.") + .max(10, "Business Phone number should have 10 digits only."), + businessFax: Yup.string() + .required("Business Fax is required") + .matches(/^[0-9]+$/, "Business Fax must be a valid number") + .min(10, "Business Fax number should have 10 digits only.") + .max(10, "Business Fax number should have 10 digits only."), + practiceManagementSystem: Yup.string() + .required("Practice Management System is required") + .typeError("Practice Management System must be string"), + designation: Yup.string() + .required("Designation is required") + .typeError("Designation must be string"), + companyWebsite: Yup.string() + .required("Clinic website is required") + .matches(WEBSITE_REGEX, "Please enter a valid URL"), + companyIndustry: Yup.string().required("Clinic Industry is required"), + state: Yup.string().required(), + locality: Yup.string().required("City is required"), + country: Yup.string().required("Country is required"), + fullAddress: Yup.string() + .required("Full Address is required") + .typeError("Address must be string"), + companyPANImage: Yup.string() + .required("ABN number is required") + .matches( + ONLY_ALPHA_NUMERIC_ACCEPT_REGEX, + "ABN Number must only contain numbers and letters" + ) + .length(ABN_NUMBER_LENGTH, "Enter valid ABN Number"), + companyTANNumber: Yup.string() + .required("Medicare number is required") + .matches( + ONLY_ALPHA_NUMERIC_ACCEPT_REGEX, + "Medicare Number must only contain numbers and letters" + ) + .length(MEDIICARE_NUMBER_LENGTH, "Enter valid Medicare Number"), + companyGSTImage: Yup.string().required("Clinic GST document is required"), + companyPANImage: Yup.string().required("Clinic ABN document is required"), + companyTANImage: Yup.string().required( + "Clinic MEDICARE document is required" + ), + termsAccepted: Yup.boolean() + .oneOf([true], "You must accept the terms and conditions") + .required("You must accept the terms and conditions"), + }); + + // Debounced function for pincode lookup + const debouncedFetchPincodeData = useCallback( + debounce( + async (pincode, formik, setLocalityOption, selectedLocalityRef) => { + if (!pincode) return; // Don't make API call if pincode is empty + try { + const countryShort = + formik.values.country === "Australia" ? "au" : "in"; + const resp = await axios.get( + `https://api.zippopotam.us/${countryShort}/${pincode}` + ); + formik.setFieldValue("state", resp?.data?.places[0]?.state); + setLocalityOption( + resp?.data?.places?.map((object) => object["place name"]) + ); + formik.setFieldValue("locality", selectedLocalityRef.current); + } catch (error) { + // Handle error + setLocalityOption([]); + formik.setFieldValue("state", ""); + formik.setFieldValue("locality", ""); + pushNotification("Invalid pincode", NOTIFICATION.ERROR); + } + }, + 1000 + ), + [] + ); + + const handleTogglePasswordVisibility = (field) => { + if (field === "password") { + setShowPassword((prevShowPassword) => !prevShowPassword); + } else if (field === "confirmPassword") { + setShowConfirmPassword( + (prevShowConfirmPassword) => !prevShowConfirmPassword + ); + } + }; + + const handleOTPButton = () => { + console.log("btn"); + setOtpField(true); + }; + + const verifyOTP = () => {}; + + const handleFormSubmit = async () => { + dispatch(updateFormDetails(formik.values)); + + // const body = formatedData(formik.values); + try { + // const response = await signup(body); + // if (response?.data?.error) { + // pushNotification(response?.data?.message, NOTIFICATION.ERROR); + // } else { + // pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + // dispatch(resetFormData()); + // navigate('/'); + // } + // pushNotification('Your request is submitted', NOTIFICATION.SUCCESS); + // dispatch(resetFormData()); + // navigate('/'); + navigate("/auth/signup/payment"); + } catch (error) { + // console.error('Error signing up:', error); + } finally { + formik.isSubmitting(false); + } + }; + + // Initialize formik with a submission handler defined inline to avoid circular references + const formik = useFormik({ + initialValues: defaultFormData.current, + // validationSchema, + validateOnBlur: true, + validateOnChange: false, // Only validate on blur, not on every keystroke + onSubmit: handleFormSubmit, + }); + + // Handle country and pincode changes + useEffect(() => { + if (formik?.values?.country !== defaultFormData.current.country) { + formik.setFieldValue("pincode", ""); + formik.setFieldValue("state", ""); + formik.setFieldValue("locality", ""); + } + }, [formik?.values?.country]); + + const handlePincodeChange = (event) => { + formik.handleChange(event); + debouncedFetchPincodeData( + event.target.value, + formik, + setLocalityOption, + selectedLocalityRef + ); + }; + + useEffect(() => { + if (Array.isArray(pincodeData) && pincodeData.length > 0) { + formik.setFieldValue("state", pincodeData[0]?.state); + formik.setFieldValue("locality", selectedLocalityRef.current); + } else { + setCountryOption(["Australia", "India"]); + formik.setFieldValue("state", ""); + formik.setFieldValue("locality", ""); + formik.setFieldError("pincode", "Invalid pincode"); + } + }, [pincodeData]); + + // Logo image handling functions + const handleClearLogoImage = () => { + setLogoImage(); + setCrop({ + unit: "%", + width: 100, + height: 100, + aspect: 1 / 1, + }); + setLogoname(); + formik.setFieldValue("companyLogo", ""); + }; + + const handleFileUpload = async (value) => { + let formData = new FormData(); + formData.append("file", value); + formData.append("fileName", value?.name); + try { + const data = await fileUpload(formData); + const imageUrl = data?.data?.data?.Key; + formik.setFieldValue("companyLogo", imageUrl); + return imageUrl; + } catch (error) { + pushNotification("Error while uploading file", NOTIFICATION.ERROR); + } + }; + + const handleDrop = (acceptedFiles) => { + if (acceptedFiles.length > 0) { + handleFileUpload(acceptedFiles?.[0]); + if (acceptedFiles?.[0]) { + const reader = new FileReader(); + reader.onload = (event) => { + const img = new Image(); + img.onload = () => { + const aspectRatio = img?.width / img?.height; + setAspect(aspectRatio); + }; + img.src = event.target.result; + setLogoImage(img.src); + }; + reader?.readAsDataURL(acceptedFiles?.[0]); + } + } + }; + + const handelRejection = (rejectedFiles) => { + rejectedFiles.forEach((rejection) => { + pushNotification( + `${rejection?.file?.name} : ${rejection?.errors?.[0]?.message}`, + NOTIFICATION.ERROR + ); + }); + }; + + const onLoad = (img) => { + logoRef.current = img; + }; + + const onCropChange = (crop) => { + setCrop(crop); + }; + + const onCropComplete = (crop) => { + createCroppedImage(crop); + }; + + const createCroppedImage = async (crop) => { + try { + const { file } = await getCroppedImg(logoRef.current, crop, logoname); + handleFileUpload(file); + } catch (error) { + // Error handling + } + }; + + const getCroppedImg = (logoImage, crop, fileName) => { + const canvas = document.createElement("canvas"); + const scaleX = logoImage?.naturalWidth / logoImage?.width; + const scaleY = logoImage?.naturalHeight / logoImage?.height; + canvas.width = crop.width; + canvas.height = crop.height; + const ctx = canvas.getContext("2d"); + + ctx.drawImage( + logoImage, + crop.x * scaleX, + crop.y * scaleY, + crop.width * scaleX, + crop.height * scaleY, + 0, + 0, + crop.width, + crop.height + ); + + return new Promise((resolve, reject) => { + canvas.toBlob((blob) => { + if (!blob) { + reject("Canvas is empty"); + return; + } + const file = new File([blob], fileName, { type: "image/*" }); + const blobUrl = URL.createObjectURL(blob); + resolve({ file, blobUrl }); + }, "image/*"); + }); + }; + + // Terms and conditions drawer + const toggleDrawer = () => { + setDrawerOpen(!drawerOpen); + }; + + // Set uploaded file URL + const setUploadedFileUrl = (documentName, fileUrl) => { + formik.setFieldValue(documentName, fileUrl); + }; + + // handleSaveAndNext will use formik's handleSubmit + + // Helper function for formatted data + function formatedData(inputData) { + const data = { + name: inputData.companyName || "", + website: inputData.companyWebsite || "", + city: inputData.locality || "", + state: inputData.state || "", + logo: inputData.companyLogo || null, + street: inputData.fullAddress || "", + pinCode: inputData.pincode || "", + country: inputData.country || "", + locality: inputData.locality || "", + industryId: inputData.companyIndustry || 0, + companyAdmin: { + name: inputData.name || "", + email: inputData.email || "", + mobile: inputData.mobileNumber || "", + password: inputData.password || "", + designation: inputData.designation || "", + }, + companyUsers: inputData.billingUsers || "", + companyDocuments: [ + ...(inputData.companyLogo + ? [ + { + documentNumber: "LOGO", + fileURL: inputData.companyLogo, + documentType: "LOGO", + }, + ] + : []), + { + documentNumber: inputData.companyPANImage || "", + fileURL: inputData.companyPANImage || "", + documentType: "PAN", + }, + ], + }; + return data; + } + + const handleAddUser = () => { + if ( + billingUsers?.includes(formik?.values?.currentEmail) || + formik?.values?.email === formik.values?.currentEmail + ) { + pushNotification("Email Address must be unique", NOTIFICATION.ERROR); + return; + } + if (formik.values.currentEmail && billingUsers?.length < 5) { + setBillingUsers([...billingUsers, formik.values.currentEmail]); + formik.setFieldValue("currentEmail", ""); + } + }; + + const handleUserDelete = (index) => { + const updatedUsers = billingUsers.filter((_, i) => i !== index); + setBillingUsers(updatedUsers); + }; + + const handleSaveAndNext = async () => { + const formikError = await formik.validateForm(formik.values); + + const errors = Object.keys(formikError); + + if (errors.length) { + // Find the first invalid field and focus it + const firstErrorField = errors[0]; + const firstErrorRef = fieldRefs[firstErrorField]?.current; + + if (firstErrorRef) { + // Scroll to the first invalid field smoothly + if (typeof firstErrorRef?.scrollIntoView === "function") { + firstErrorRef?.scrollIntoView({ + behavior: "smooth", + block: "center", + }); + } + + // Focus the field after a slight delay (to ensure scrolling completes first) + setTimeout(() => firstErrorRef.focus(), 300); + } + + // Show error notification + if (formik?.touched[firstErrorField]) + pushNotification(formikError[firstErrorField], NOTIFICATION.ERROR); + return; + } + + // If no errors, trigger the formik submission + formik.handleSubmit(); + }; + + return ( + + + + + + + + + + + + + + + + + {isLoading ? ( + + ) : ( + + + +
    + {/* Personal Details Section */} + + + {/* your name grid */} + + + Your Full Name* + + { + e.target.value = capitalizeFirstLetter( + e.target.value + ); + formik.handleChange(e); + }} + onBlur={(e) => { + formik.setFieldValue( + "name", + e.target.value.trim() + ); + formik.handleBlur(e); + }} + inputRef={fieldRefs.name} + error={Boolean( + formik.errors.name && formik.touched.name + )} + helperText={ + formik.errors.name && formik.touched.name + ? formik.errors.name + : "" + } + /> + + + {/* email id grid */} + + + Your Official Email ID* + + + + + ), + }} + /> + + Note: This will be your login ID. + + + + {/* mobile numer grid */} + + + Enter Mobile Number* + + { + if (e.target.value.length <= 10) { + const value = + e.target.value?.match(/\d+/g) || ""; + formik.setFieldValue( + "mobileNumber", + value?.toString() + ); + } + }} + onBlur={formik.handleBlur} + InputProps={{ + type: "text", + pattern: "[0-9]*", + startAdornment: ( + + + + +61 + + + + ), + }} + inputRef={fieldRefs.mobileNumber} + error={Boolean( + formik.errors.mobileNumber && + formik.touched.mobileNumber + )} + helperText={ + formik.errors.mobileNumber && + formik.touched.mobileNumber + ? formik.errors.mobileNumber + : "" + } + /> + + + {/* otp verification grid */} + {otpField && ( + + + Enter OTP* + + + + )} + + {/* password grid heading */} + + {/* enter password grid */} + + + Enter Password* + + + handleTogglePasswordVisibility("password") + } + edge="end" + > + {showPassword ? ( + + ) : ( + + )} + + ), + }} + /> + {/* Password validation display */} + {!formik.isValid && ( + <> + + Note: Password must be + + + + + + + + + )} + + + {/* confirmPassword Grid */} + + + Confirm Password* + + { + formik.handleChange(e); + }} + onBlur={formik.handleBlur} + inputRef={fieldRefs.confirmPassword} + error={Boolean( + formik.errors.confirmPassword && + formik.touched.confirmPassword + )} + helperText={ + formik.errors.confirmPassword && + formik.touched.confirmPassword + ? formik.errors.confirmPassword + : "" + } + InputProps={{ + endAdornment: ( + + handleTogglePasswordVisibility( + "confirmPassword" + ) + } + edge="end" + > + {showConfirmPassword ? ( + + ) : ( + + )} + + ), + }} + /> + + + + {/* Business Details Section */} + + + {/* Clinic name grid */} + + + Business Name* + + + { + e.target.value = capitalizeFirstLetter( + e.target.value + ); + formik.handleChange(e); + }} + onBlur={(e) => { + formik.setFieldValue( + "companyName", + e.target.value.trim() + ); + formik.handleBlur(e); + }} + inputRef={fieldRefs.companyName} + error={Boolean( + formik.errors.companyName && + formik.touched.companyName + )} + helperText={ + formik.errors.companyName && + formik.touched.companyName + ? formik.errors.companyName + : "" + } + /> + + + {/* Your designation grid */} + + + Your designation* + + + + + {/* Clinic Website grid */} + + + Clinic Website* + + + + + {/* Clinic logo grid */} + + + Add Business Logo + {(logoImage || formik.values.companyLogo) && ( +
    + +
    + )} +
    + + + + + +
    +
    + + {/* ADD Business ADDRESS */} + + + + {/* Country grid */} + + + Country* + + + + + {formik.errors.country && formik.touched.country ? ( + + {formik.errors.country} + + ) : null} + + + {/* pincode grid */} + + + Pin/Post Code* + + { + if (e.target.value.length <= 6) { + handlePincodeChange(e); + } + }} + onBlur={formik.handleBlur} + inputRef={fieldRefs.pincode} + error={Boolean( + formik.errors.pincode && formik.touched.pincode + )} + helperText={ + formik.errors.pincode && formik.touched.pincode + ? formik.errors.pincode + : "" + } + /> + + + {/* state grid disable */} + + + State + + { + formik.setFieldValue("state", e.target.value); + }} + /> + + + {/* locality grid dropdown */} + + + City* + + + + + {formik.errors.locality && + formik.touched.locality ? ( + + {formik.errors.locality} + + ) : null} + + + {/* full address grid */} + + + Full Address* + + { + formik.setFieldValue( + "fullAddress", + e.target.value.trim() + ); + formik.handleBlur(e); + }} + inputRef={fieldRefs.fullAddress} + error={Boolean( + formik.errors.fullAddress && + formik.touched.fullAddress + )} + helperText={ + formik.errors.fullAddress && + formik.touched.fullAddress + ? formik.errors.fullAddress + : "" + } + /> + + + + {/* ADD DOCUMENTS */} + + + + {/* PAN NUMBER GRID */} + + + Clinic ABN Number* + + + { + if ( + e.target.value.length <= ABN_NUMBER_LENGTH + ) { + e.target.value = capitalizeAllLetters( + e.target.value + ); + formik.handleChange(e); + } + }} + onBlur={(e) => { + formik.setFieldValue( + "companyPANImage", + e.target.value.trim() + ); + formik.handleBlur(e); + }} + inputRef={fieldRefs.companyPANImage} + error={Boolean( + formik.errors.companyPANImage && + formik.touched.companyPANImage + )} + helperText={ + formik.errors.companyPANImage && + formik.touched.companyPANImage + ? formik.errors.companyPANImage + : "" + } + InputProps={{ + endAdornment: ( + + {!formik.errors.companyPANImage ? ( + + ) : null} + + ), + }} + /> + + + {/* PAN image upload grid */} + + + + + {/* terms and condition grid */} + + + + + formik.setFieldValue( + "termsAccepted", + formik.values.termsAccepted === "true" + ? "false" + : "true" + ) + } + name="termsAccepted" + /> + + + I agree to{" "} + + {" "} + + {" "} + Terms and Conditions + + {" "} + of 24/7 AI Healthcare Receptionist. + + + {formik.errors.termsAccepted && + formik.touched.termsAccepted && ( +
    + {formik.errors.termsAccepted} +
    + )} +
    + + + +
    + +
    +
    + +
    + )} +
    +
    +
    +
    +
    + ); +} + +export default YourDetailsForm; diff --git a/src/views/Signup/signupAction.js b/src/views/Signup/signupAction.js new file mode 100644 index 0000000..0a31eb3 --- /dev/null +++ b/src/views/Signup/signupAction.js @@ -0,0 +1,19 @@ +import { + HIDE_FUNCTIONALITIES, + RESET_FORM_DATA, + UPDATE_FORM_DETAILS, +} from './signupActionType'; + +export const updateFormDetails = (data) => ({ + type: UPDATE_FORM_DETAILS, + payload: data, +}); + +export const resetFormData = () => ({ + type: RESET_FORM_DATA, +}); + +export const hideAndShowFunctionality = (data) => ({ + type: HIDE_FUNCTIONALITIES, + payload: data, +}); diff --git a/src/views/Signup/signupActionType.js b/src/views/Signup/signupActionType.js new file mode 100644 index 0000000..1b961b4 --- /dev/null +++ b/src/views/Signup/signupActionType.js @@ -0,0 +1,4 @@ +export const UPDATE_FORM_DETAILS = 'UPDATE_FORM_DETAILS'; +export const RESET_FORM_DATA = 'RESET_FORM_DATA'; + +export const HIDE_FUNCTIONALITIES = 'HIDE_FUNCTIONALITIES'; diff --git a/src/views/Signup/signupReducer.js b/src/views/Signup/signupReducer.js new file mode 100644 index 0000000..df8b818 --- /dev/null +++ b/src/views/Signup/signupReducer.js @@ -0,0 +1,60 @@ +import { + HIDE_FUNCTIONALITIES, + RESET_FORM_DATA, + UPDATE_FORM_DETAILS, +} from './signupActionType'; + +const initialState = { + name: '', + email: '', + mobileNumber: '', + password: '', + confirmPassword: '', + billingUsers: [], + currentEmail: '', + companyName: '', + designation: '', + companyWebsite: '', + companyAbout: '', + companyLogo: '', + companyIndustry: '', + pincode: '', + state: '', + locality: '', + fullAddress: '', + companyGSTNumber: '', + companyPANNumber: '', + companyTANNumber: '', + companyGSTImage: '', + companyPANImage: '', + companyTANImage: '', + termsAccepted: '', + hideAndShowFunctionality: false, +}; +const updateFormDetails = (state, payload) => ({ + ...state, + ...payload, +}); +const resetFormData = (state) => ({ + ...state, + ...initialState, +}); + +const hideAndShowFunctionality = (state, payload) => ({ + ...state, + ...payload, +}); + +export default function (state = initialState, action) { + switch (action.type) { + case UPDATE_FORM_DETAILS: + return updateFormDetails(state, action.payload); + case RESET_FORM_DATA: + return resetFormData(state); + case HIDE_FUNCTIONALITIES: + return hideAndShowFunctionality(state, action.payload); + + default: + return state; + } +} diff --git a/src/views/Signup/styles/signupStyles.js b/src/views/Signup/styles/signupStyles.js new file mode 100644 index 0000000..01d820c --- /dev/null +++ b/src/views/Signup/styles/signupStyles.js @@ -0,0 +1,325 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + appBarStyle: { + backgroundColor: theme.palette.grey[28], + }, + contentBox: { + marginTop: '3%', + }, + mainContent: { + marginTop: theme.spacing(4), + marginBottom: theme.spacing(4), + marginLeft: theme.spacing(4), + marginRight: theme.spacing(4), + width: '100%', + overflowX: 'scroll', + '&::-webkit-scrollbar': { + height: '6px', + }, + }, + logoBox: { + display: 'flex', + width: '100%', + marginLeft: '1%', + [theme.breakpoints.down('md')]: { + width: 'auto', + }, + }, + logo: { + maxHeight: '61px', + objectFit: 'contain', + justifyContent: 'center', + }, + whitePaper: { + width: '100%', + marginBottom: theme.spacing(0.3), + paddingBottom: theme.spacing(0.7), + borderRadius: `${theme.spacing(2)} ${theme.spacing(2)} ${theme.spacing( + 1 + )} ${theme.spacing(1)}`, + }, + inputLabel: { + marginBottom: theme.spacing(0.5), + fontSize: pxToRem(12), + color: theme.palette.grey[10], + }, + textAreaLast: { + width: '-webkit-fill-available', + '&.MuiButtonBase-root': { + padding: theme.spacing(0.8), + }, + }, + helperText: { + color: theme.palette.error.main, + fontStyle: 'italic', + }, + formRoot: { + paddingTop: theme.spacing(3.2), + }, + formPassswordHeading: { + fontSize: pxToRem(14), + fontWeight: '600', + }, + passwordTextFiled: { + marginTop: theme.spacing(0), + }, + icon: { + cursor: 'pointer', + paddingLeft: theme.spacing(3), + display: 'flex', + alignItems: 'center', + width: '50px', + [theme.breakpoints.up('md')]: { + width: '50px', + }, + [theme.breakpoints.up('lg')]: { + width: '50px', + }, + [theme.breakpoints.up('xl')]: { + width: '50px', + }, + }, + formPasswordHeadingRoot: { + paddingTop: theme.spacing(2), + paddingLeft: theme.spacing(2), + width: '100%', + }, + subjectText: { + fontSize: pxToRem(12), + position: 'absolute', + right: pxToRem(8), + top: '90%', + transform: 'translateY(-90%)', + }, + // --- AddForm Style --- + screenViewGridItem: { + marginTop: theme.spacing(1), + }, + paperContainerBox: { + width: '100%', + }, + countryCodeBox: { + padding: pxToRem(12), + paddingRight: pxToRem(20), + paddingLeft: pxToRem(16), + borderRadius: pxToRem(10), + backgroundColor: theme.palette.grey[20], + }, + countryCodeLabel: { + marginBottom: theme.spacing(0), + opacity: 1, + fontFamily: 'Inter-Regular', + }, + mobileNumberInput: { + fontFamily: 'Inter-Regular', + '& .MuiInputBase-root': { + paddingLeft: theme.spacing(0), + border: 0, + }, + '& .MuiOutlinedInput-input': { + paddingLeft: theme.spacing(0), + }, + }, + passwordCheckListTitle: { + color: theme.palette.grey[20], + fontSize: pxToRem(12), + fontStyle: 'italic', + textAlign: 'left', + marginBottom: theme.spacing(0.4), + }, + emailNote: { + color: theme.palette.grey[20], + fontSize: pxToRem(14), + fontStyle: 'italic', + textAlign: 'left', + marginBottom: theme.spacing(0.4), + }, + passwordCheckList: { + fontSize: pxToRem(14), + paddingTop: theme.spacing(0.2), + fontStyle: 'italic', + color: theme.palette.grey[600], + }, + stateTextField: { + '& .Mui-disabled': { + WebkitTextFillColor: theme.palette.grey[10], // Change to the appropriate color value + opacity: 1, + }, + }, + termsAndConditionErrorMessage: { + color: theme.palette.error.main, + fontStyle: 'italic', + fontSize: pxToRem(12), + marginTop: theme.spacing(0.5), + }, + checkBox: { + marginTop: theme.spacing(3), + paddingRight: theme.spacing(1), + }, + checkBoxLabel: { + marginTop: theme.spacing(3.2), + marginLeft: theme.spacing(1.2), + fontSize: pxToRem(16), + color: theme.palette.common.black, + }, + linkText: { + color: theme.palette.grey[53], + }, + placeholderText: { + fontStyle: 'italic', + fontSize: pxToRem(14), + opacity: 0.5, + color: theme.palette.grey[10], + }, + // logo style + reactCrop: { + /* Set a minimum height and width */ + minHeight: '200px', + minWidth: '200px', + }, + dropZoneOuterBox: { + width: '100%', + }, + inputTitle: { + '&.MuiTypography-body1': { + fontSize: pxToRem(12), + color: theme.palette.grey[10], + }, + marginBottom: theme.spacing(0.6), + marginLeft: theme.spacing(0.6), + color: theme.palette.grey[10], + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + }, + imgButton: { + color: theme?.palette?.primary?.main, + fontSize: pxToRem(12), + fontWeight: 600, + fontFamily: theme?.fontFamily?.Regular, + backgroundColor: 'transparent', + border: 'none', + cursor: 'pointer', + margin: `${theme.spacing(0)} ${theme.spacing(0.8)}`, + padding: theme.spacing(0), + outline: 'none', + }, + logoUploadErrorBox: { + display: 'flex', + justifyContent: 'space-between', + }, + errorText: { + color: theme.palette.error.main, + fontSize: pxToRem(12), + fontStyle: 'italic', + }, + addButton: { + border: `1px dashed ${theme.palette.primary.main}`, + width: '100%', + height: '130px', + backgroundColor: theme.palette.common.white, + borderRadius: theme.spacing(1), + marginBottom: theme.spacing(1.48), + }, + addButtonContent: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + height: '100%', + cursor: 'pointer', + width: '100%', + }, + addText: { + opacity: 1, + fontFamily: theme?.fontFamily?.medium, + fontSize: pxToRem(14), + height: '16px', + fontWeight: 500, + lineHeight: 'normal', + letterSpacing: 'normal', + marginTop: theme?.spacing(1), + }, + addIcon: { + width: '26px', + height: '24px', + }, + addSubtext: { + fontSize: pxToRem(12), + color: theme.palette.grey[10], + opacity: '0.6', + textAlign: 'center', + padding: `${theme.spacing(0.5)} ${theme.spacing(7)}`, + [theme.breakpoints.down('md')]: { + padding: theme.spacing(1), + }, + [theme.breakpoints.down('lg')]: { + padding: theme.spacing(1), + }, + }, + previewBox: { + display: 'flex', + justifyContent: 'end', + alignItems: 'start', + width: '153px', + height: '111px', + borderRadius: theme.spacing(0.4), + backgroundSize: 'cover', + }, + formSectionHeadingMargin: { + marginTop: theme.spacing(1.5), + }, + + termsAndConditionDrawer: { + '& .MuiDrawer-paper': { + maxWidth: pxToRem(550), + boxShadow: '-10px 0px 10px -2px rgba(0,0,0,0.1)', + }, + [theme.breakpoints.down('md')]: { + width: '100%', + }, + }, + verifyIcon: { + fontSize: pxToRem(20), + color: theme.palette.grey[54], + }, + chipOuter: { + display: 'flex', + flexWrap: 'wrap', + paddingLeft: theme.spacing(3.2), + }, + chip: { + minWidth: '170px', + height: '42px', + padding: theme.spacing(1.5), + margin: theme.spacing(1), + marginRight: theme.spacing(1.5), + justifyContent: 'space-between', + borderRadius: '24px', + fontSize: pxToRem(14), + }, + billingUserOuterSection: { + marginTop: theme.spacing(3.0), + }, + billingUserEmailSection: { + padding: `${theme.spacing(2.2)} ${theme.spacing(3.2)}`, + }, + saveUserButton: { + marginLeft: theme.spacing(1.0), + width: '150px', + display: 'flex', + justifyContent: 'space-evenly', + borderRadius: '8px', + height: '47px', + // opacity: 0.5, + }, + billingUserEmailWrapper: { + display: 'flex', + [theme.breakpoints.down('md')]: { + flexDirection: 'column', + gap: '15px', + }, + }, +})); diff --git a/src/views/StaffManagement/index.jsx b/src/views/StaffManagement/index.jsx new file mode 100644 index 0000000..b2b699e --- /dev/null +++ b/src/views/StaffManagement/index.jsx @@ -0,0 +1,428 @@ +import AddIcon from '@mui/icons-material/Add'; +import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew'; +import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; +import CloseIcon from '@mui/icons-material/Close'; +import PersonAddIcon from '@mui/icons-material/PersonAdd'; +import SearchIcon from '@mui/icons-material/Search'; +import { + Alert, + Box, + Button, + Container, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Divider, + IconButton, + InputAdornment, + Paper, + Snackbar, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, +} from '@mui/material'; +import React, { useState } from 'react'; +import CustomBreadcrumbs from '../../components/CustomBreadcrumbs'; +import PageHeader from '../../components/PageHeader'; + +const StaffManagement = () => { + // State for form fields + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + const [email, setEmail] = useState(''); + const [emailError, setEmailError] = useState(''); + const queryParams = new URLSearchParams(location.search); + + // State for staff list + const [staffList, setStaffList] = useState([]); + + // State for dialog + const [openDialog, setOpenDialog] = useState(false); + + // State for search + const [searchQuery, setSearchQuery] = useState(''); + + // State for pagination + const [page, setPage] = useState(1); + const rowsPerPage = 10; + + // State for notification + const [notification, setNotification] = useState({ + open: false, + message: '', + severity: 'success', + }); + + // Email validation function + const validateEmail = (email) => { + const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return regex.test(email); + }; + + // Handle dialog open/close + const handleOpenDialog = () => { + setOpenDialog(true); + }; + + const handleCloseDialog = () => { + setOpenDialog(false); + // Clear form + setFirstName(''); + setLastName(''); + setEmail(''); + setEmailError(''); + }; + + // Handle form submission + const handleSubmit = (e) => { + e.preventDefault(); + + // Validate email + if (!validateEmail(email)) { + setEmailError('Please enter a valid email address'); + return; + } + + // Add new staff member + const newStaff = { + id: staffList.length + 1, + firstName, + lastName, + email, + }; + + setStaffList([...staffList, newStaff]); + + // Close dialog + handleCloseDialog(); + + // Show success notification + setNotification({ + open: true, + message: 'Staff member added successfully!', + severity: 'success', + }); + }; + + // Handle notification close + const handleCloseNotification = () => { + setNotification({ + ...notification, + open: false, + }); + }; + + // ...................breadcrumbs array........................ + const breadcrumbs = [ + { + label: 'Dashboard', + path: '/', + }, + { + label: 'Staff Management', + path: '/staff', + }, + ]; + + return ( + + {/* + Staff Management + */} + + } + // addButtonDisabled={false} + // permissionName={'CREATE_USERS'} + // infiniteDropdown + /> + + + + + {/* Staff List Header with Add Button */} + + + Staff List + + + + + + {/* Search Box */} + + setSearchQuery(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ + backgroundColor: '#fff', + '& .MuiOutlinedInput-root': { + borderRadius: 2, + }, + }} + /> + + + {/* Staff List Table */} + +
    + + + Sr. No. + User Name + + User Email Address + + + + + {staffList.length > 0 ? ( + staffList + .filter( + (staff) => + `${staff.firstName} ${staff.lastName}` + .toLowerCase() + .includes(searchQuery.toLowerCase()) || + staff.email + .toLowerCase() + .includes(searchQuery.toLowerCase()) + ) + .slice((page - 1) * rowsPerPage, page * rowsPerPage) + .map((staff, index) => ( + + + {(page - 1) * rowsPerPage + index + 1} + + {`${staff.firstName} ${staff.lastName}`} + {staff.email} + + )) + ) : ( + + + No staff members added yet + + + )} + +
    + + + {/* Pagination */} + {staffList.length > 0 && ( + + + + + + + + )} + + + {/* Add Staff Dialog */} + + + + + + Add New Staff + + + + + + + + + + + + setFirstName(e.target.value)} + placeholder="First Name" + required + InputLabelProps={{ + shrink: true, + }} + /> + + setLastName(e.target.value)} + placeholder="Last Name" + required + InputLabelProps={{ + shrink: true, + }} + /> + + { + setEmail(e.target.value); + setEmailError(''); + }} + placeholder="Email Address" + error={!!emailError} + helperText={emailError} + required + InputLabelProps={{ + shrink: true, + }} + /> + + + + + + + + + {/* Notification */} + + + {notification.message} + + + + ); +}; + +export default StaffManagement; diff --git a/src/views/ThankYou/index.jsx b/src/views/ThankYou/index.jsx new file mode 100644 index 0000000..4900975 --- /dev/null +++ b/src/views/ThankYou/index.jsx @@ -0,0 +1,171 @@ +import React, { useState } from 'react'; +import { + Box, + Typography, + AccordionDetails, + AccordionSummary, + Accordion, + Grid, +} from '@mui/material'; +import { useStyles } from './thankYouStyles'; +import thankYouImg from '../../assets/images/card/thankYou.png'; +import { LoadingButton } from '@mui/lab'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { ArrowBackOutlined } from '@mui/icons-material'; +import CustomModal from '../Modal/Modal'; + +const ThankYou = ({ canClose = true, setShowPopup }) => { + const classes = useStyles(); + const [showFaqSection, setShowFqaSection] = useState(false); + const [showModal, setShowModal] = useState(true); + + const handleCloseModal = () => { + if (canClose === false) { + // Don't close the modal if onClose prop is false + return; + } + // Close the modal + setShowModal(false); + setShowPopup(false); + }; + + const faqData = [ + { + question: 'How do I post a job on the recruitment portal?', + answer: + 'To post a job, log in to your account, navigate to the dashboard, and click on the "Post a Job" button. Follow the prompts to input job details, requirements, and preferences.', + }, + { + question: 'Can I customise the recruitment process for each job posting?', + answer: + 'Yes, our platform allows you to set a tailored recruitment process for each job. Define specific stages to streamline your hiring workflow.', + }, + { + question: 'How can I screen candidates effectively using the portal?', + answer: + 'Utilise our advanced screening and search tools to filter candidates based on key criteria such as skills, and education. Easily review and compare candidate profiles within the platform.', + }, + { + question: + 'Is it possible to search for a specific candidate in the database?', + answer: + "Yes, the portal features a powerful search functionality. Simply enter the candidate's name or relevant keywords to quickly locate and review their profile.", + }, + { + question: + 'Can I collaborate with other team members during the hiring process?', + answer: + 'Absolutely. Our platform supports collaboration among team members. You can share', + }, + { + question: + 'How do I save or reject individual candidate profiles for future reference?', + answer: + 'Easily manage candidate profiles by using the intuitive interface. You can shortlist promising candidates, and save profiles in a specific folder for future reference, or reject applicants with just a few clicks.', + }, + { + question: 'Can I download resumes and view them on my mobile device?', + answer: + 'Yes, our platform offers the convenience of downloading resumes directly to your mobile device. This allows you to review candidate information anytime, anywhere, and share it effortlessly.', + }, + { + question: + 'What security measures are in place to protect sensitive candidate information?', + answer: + 'Our platform prioritises data security. We employ encryption protocols and follow industry best practices to ensure the confidentiality and integrity of candidate information.', + }, + { + question: 'Is there a support team available for assistance if needed?', + answer: + 'Absolutely. Our dedicated support team is available to assist you with any queries or concerns. Feel free to reach out through —-----------@bsmail.in', + }, + ]; + return ( + <> + {showModal && ( + ( + + {showFaqSection ? ( + + + + setShowFqaSection(false)} + /> + + + + Frequently Asked Questions + + + + {faqData.map((faq, index) => ( + + } + aria-controls={`panel${index + 1}-content`} + id={`panel${index + 1}-header`} + > + + Q{index + 1}. {faq.question} + + + + + {faq.answer} + + + + ))} + + ) : ( + + + Thank you for showing your interest + + + Thank You + + + + Please wait while BSmart team is verifying your details. + + + + + After verification we will send you a link on your + registered Email Address. + + + setShowFqaSection(true)} + > + FAQs + + + )} + + )} + /> + )} + + ); +}; + +export default ThankYou; diff --git a/src/views/ThankYou/thankYouStyles.js b/src/views/ThankYou/thankYouStyles.js new file mode 100644 index 0000000..37069f7 --- /dev/null +++ b/src/views/ThankYou/thankYouStyles.js @@ -0,0 +1,102 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + root: { + height: '100vh', + // backgroundImage: `url(${backgroundImage})`, + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + card: { + width: '370px', + // height: '650px', + maxHeight: '635px', + overflowY: 'scroll', + [theme.breakpoints.up('sm')]: { + width: '500px', + // height: '750px', + }, + [theme.breakpoints.up('md')]: { + width: '500px', + // height: '750px', + }, + [theme.breakpoints.up('lg')]: { + width: '500px', + // height: '700px', + }, + [theme.breakpoints.up('xl')]: { + width: '500px', + // height: '650px', + maxHeight: '635px', + }, + boxShadow: 'none', + borderRadius: theme.spacing(1.8), + opacity: '0.95', + }, + thankYouTitle: { + width: '100%', + textAlign: 'center', + // padding: theme.spacing(2.2), + }, + thankYouImg: { + width: '90%', + margin: 'auto', + }, + descriptionDiv: { + marginTop: theme.spacing(2.0), + }, + description: { + fontSize: pxToRem(12.8), + marginBottom: theme.spacing(0.6), + }, + faqButton: { + display: 'flex', + justifyContent: 'center', + marginTop: theme.spacing(2.0), + margin: 'auto', + height: '38px', + fontSize: pxToRem(12.8), + borderRadius: '8px', + width: '30%', + }, + faqHeaderDiv: { + marginBottom: theme.spacing(2.0), + }, + faqTitle: { + display: 'flex', + justifyContent: 'flex-start', + }, + goBackicon: { + paddingTop: theme.spacing(0.2), + paddingBottom: theme.spacing(0.2), + cursor: 'pointer', + color: 'grey', + // width: '30px', + }, + goBackDiv: { + display: 'flex', + justifyContent: 'flex-start', + // paddingTop: theme.spacing(0.6), + alignItems: 'center', + }, + question: { + fontSize: pxToRem(15.2), + paddingTop: theme.spacing(2.0), + paddingBottom: theme.spacing(2.0), + }, + answer: { + color: 'grey', + fontSize: pxToRem(14.4), + lineHeight: 1.1, + }, + faqMainDiv: { + maxHeight: '550px', + fontFamily: 'Gilroy-Bold', + padding: theme.spacing(1.0), + }, +})); diff --git a/src/views/User/AdminTransferRequest/AdminTransferRequestApprove.jsx b/src/views/User/AdminTransferRequest/AdminTransferRequestApprove.jsx new file mode 100644 index 0000000..a792462 --- /dev/null +++ b/src/views/User/AdminTransferRequest/AdminTransferRequestApprove.jsx @@ -0,0 +1,101 @@ +import { Box, Button, Divider, Typography } from '@mui/material'; +import logo from '../../../assets/images/logo/BSmart_Logo.svg'; +import approveIcon from '../../../assets/images/icon/Approve.svg'; +import expiredIcon from '../../../assets/images/icon/Expired.svg'; +import { useStyles } from '../userStyles'; +import { useEffect, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { + approveAdminTransferAccess, + logout, +} from '../../../services/users.services'; +import Loader from '../../../components/Loader'; + +function AdminTransferRequestApprove() { + const classes = useStyles(); + const location = useLocation(); + const navigate = useNavigate(); + const [approveUI, setApproveUI] = useState(false); + const [loading, setLoading] = useState(false); + + const iconSrc = approveUI ? approveIcon : expiredIcon; + const message = approveUI + ? 'Your confirmation for Master Admin account transfer has been successfully accepted. The transfer ownership will be completed once, its approved by the other party as well.' + : 'Link has expired. Please reinitiate the Master Admin account transfer process.'; + const statusText = approveUI ? 'Verification successful' : 'Link Expired'; + + useEffect(() => { + const fetchData = async () => { + setLoading(true); + const urlParams = new URLSearchParams(location.search); + const id = parseInt(urlParams.get('id')); + const token = urlParams.get('token'); + if (id && token) { + try { + const response = await approveAdminTransferAccess(id, token); + if (response.status === 200) { + setApproveUI(true); + await logout(); + } else { + setApproveUI(false); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error:', error); + } finally { + setLoading(false); + } + } + }; + + fetchData(); + }, []); + + return ( + + + BSmart Logo + + + {loading ? ( + + ) : ( + + + {statusText} + + + {statusText} + + + + + {message} + + + + + + )} + + ); +} + +export default AdminTransferRequestApprove; diff --git a/src/views/User/UserInfoPopup.jsx b/src/views/User/UserInfoPopup.jsx new file mode 100644 index 0000000..58b53a0 --- /dev/null +++ b/src/views/User/UserInfoPopup.jsx @@ -0,0 +1,166 @@ +import React, { useRef } from 'react'; +import { useFormik } from 'formik'; +import * as Yup from 'yup'; +import CustomModal from '../Modal/Modal'; +import { Box, TextField } from '@mui/material'; +import { useStyles } from './userStyles'; +import { updateUserEmailAndContact } from '../../services/users.services'; +import { pushNotification } from '../../utils/notification'; +import { NOTIFICATION } from '../../constants'; + +function UserInfoPopup({ handleCancel, handleSubmit }) { + const classes = useStyles(); + + const fieldRefs = { + name: useRef(null), + mobile: useRef(null), + }; + + // Define validation schema using Yup + const validationSchema = Yup.object({ + name: Yup.string().required('User Name is required'), + mobile: Yup.string() + .min(10, 'Enter a valid 10-digit mobile number') + .required('Mobile number is required'), + }); + + // Initialize Formik + const formik = useFormik({ + initialValues: { + name: '', + mobile: '', + }, + validationSchema: validationSchema, + onSubmit: (values) => { + // Handle form submission here + updateDetails(values); + }, + }); + + const updateDetails = async (values) => { + try { + const response = await updateUserEmailAndContact(values); + if (response.status === 200) { + handleSubmit(); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } + }; + + const handleSubmitClick = async () => { + const formikErrors = await formik.validateForm(); + const errors = Object.keys(formikErrors); + + if (errors.length) { + // Find the first invalid field and focus it + const firstErrorField = errors[0]; + const firstErrorRef = fieldRefs[firstErrorField]?.current; + + if (firstErrorRef) { + // Scroll to the first invalid field smoothly + if (typeof firstErrorRef?.scrollIntoView === 'function') { + firstErrorRef?.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + + // Focus the field after a slight delay (to ensure scrolling completes first) + setTimeout(() => firstErrorRef.focus(), 300); + } + + // Show error notification + if (formik?.touched[firstErrorField]) + pushNotification(formikErrors[firstErrorField], NOTIFICATION.ERROR); + } + + formik.handleSubmit(); + }; + + return ( + ( + +
    + { + formik.setFieldValue('name', e.target.value.trim()); + formik.handleBlur(e); + }} + inputRef={fieldRefs.name} + error={Boolean(formik.errors.name && formik.touched.name)} + helperText={ + formik.errors.name && formik.touched.name ? ( + + {formik.errors.name} + + ) : ( + '' + ) + } + /> + { + if (e.target.value?.length <= 10) { + const value = e.target.value?.match(/\d+/g) || ''; + formik.setFieldValue('mobile', value?.toString()); + } + }} + onBlur={formik.handleBlur} + inputRef={fieldRefs.mobile} + error={Boolean(formik.errors.mobile && formik.touched.mobile)} + helperText={ + formik.errors.mobile && formik.touched.mobile ? ( + + {formik.errors.mobile} + + ) : ( + '' + ) + } + inputProps={{ + type: 'text', + pattern: '[0-9]*', + autocomplete: 'off', + }} + /> + +
    + )} + /> + ); +} + +export default UserInfoPopup; diff --git a/src/views/User/index.jsx b/src/views/User/index.jsx new file mode 100644 index 0000000..a68965e --- /dev/null +++ b/src/views/User/index.jsx @@ -0,0 +1,761 @@ +import { LoadingButton } from '@mui/lab'; +import { Box, Grid, MenuItem, Select, TextField } from '@mui/material'; +import { useFormik } from 'formik'; +import React, { useMemo, useRef, useState } from 'react'; +import { useSelector } from 'react-redux'; +import * as Yup from 'yup'; + +/* ----------------- Custom Imports ----------------- */ +import PageHeader from '../../components/PageHeader'; +import Table from '../../components/Table'; +import ConfirmationModal from '../Modal/ConfirmationModal'; +import CustomModal from '../Modal/Modal'; +import { useStyles } from './userStyles'; + +/* ----------------- Assets ----------------- */ +import SendIcon from '@mui/icons-material/Send'; +import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import RemoveIcon from '@mui/icons-material/Remove'; +import { + deleteUserById, + getRoles, + getUserById, + resendPasswordMailer, + revokeAdminTransferAccess, + transferMasterAdminAccess, +} from '../../services/users.services'; + +/* ----------------- Utils ----------------- */ +import { format } from 'date-fns'; +import MultiSelect from '../../components/MultiSelect'; +import { NOTIFICATION, NOT_AVAILABLE_TEXT } from '../../constants'; +import { pushNotification } from '../../utils/notification'; + +/* ----------------- Validation Schema ----------------- */ +const validationSchema = Yup.object().shape({ + userName: Yup.string().required('User Name is required'), + email: Yup.string() + .email('Invalid Email Address') + .required('Email is required'), + mobile: Yup.string() + .matches(/^\d{10}$/, 'Mobile Number must be exactly 10 digits') + .required('Mobile Number is required'), +}); + +function Users() { + const ref = useRef(null); + const defaultFormData = useRef({ + userName: '', + email: 'q@gmail.com', + mobile: '1234567890', + isEditUser: false, + lastName: '', + userType: '', + appointmentType: [], + appointmentTypeObject: '', + }); + + const fieldRefs = { + userName: useRef(null), + email: useRef(null), + mobile: useRef(null), + lastName: useRef(null), + userType: useRef(null), + appointmentType: useRef(null), + }; + + const classes = useStyles(); + + /* ----------------- State Variables ----------------- */ + const [showModal, setShowModal] = useState(false); + const [showUserAccessModal, setShowUserAccessModal] = useState(false); + const [isEditUser, setIsEditUser] = useState(false); + const [deleteModal, setDeleteModal] = useState(false); + const [masterAdminModal, setMasterAdminModal] = useState(false); + const [revokeAdminAccessRequestModal, setRevokeAdminAccessRequestModal] = + useState(false); + const [seletedUserData, setSelectedUserData] = useState(); + const [buttonLoading, setButtonLoading] = useState(false); + const [userId, setUserId] = useState(); + const [userTotalCount, setUserTotalCount] = useState(0); + const [selectedCheckboxes, setSelectedCheckboxes] = useState({}); + const [checkboxError, setCheckboxError] = useState(false); + const [roles, setRoles] = useState(); + const [isAdmin, setIsAdmin] = useState(); + const [hasProfileCompleted, setHasProfileCompleted] = useState(false); + const [ + requestRaisedForTransferMasterAdminAccess, + setRequestRaisedForTransferMasterAdminAccess, + ] = useState(false); + + const isBsAdmin = useSelector((state) => state?.login?.user?.isBsAdmin); + + /* ----------------- Get Users ----------------- */ + const getData = async (filters) => { + const resp = await getUsers(filters); + setUserTotalCount(resp?.data?.totalCount); + setRequestRaisedForTransferMasterAdminAccess( + resp.data?.requestRaisedForTransferMasterAdminAccess + ); + + const role = await getRoles(); + setRoles(role?.data?.data); + + return { data: resp?.data?.records, rowCount: resp?.data?.totalCount }; + }; + + /* ----------------- Handle Submit ----------------- */ + const handleSubmit = async (values, formik) => { + try { + // if (!showUserAccessModal) { + // setShowUserAccessModal(true); + // return; + // } + + // const hasSelectedCheckbox = + // Object.values(selectedCheckboxes).some(Boolean); + // if (!hasSelectedCheckbox) { + // setCheckboxError(true); + // return; + // } + // setCheckboxError(false); + + // const combinedValues = { + // ...values, + // name: values.userName, + // roles: Object.entries(selectedCheckboxes) + // .filter( + // ([id, isChecked]) => + // isChecked && id !== 'transferMasterAdminAccess' && id !== null + // ) + // .map(([id]) => parseInt(id)), + // transferMasterAdminAccess: + // selectedCheckboxes.transferMasterAdminAccess || false, + // }; + // delete combinedValues.userName; + // delete combinedValues.confirmPassword; + + // let response = isEditUser + // ? await updateUserById(userId, combinedValues) + // : await addUser(combinedValues); + + // if (response.status === 200) { + // pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + pushNotification('Doctor added successfully', NOTIFICATION.SUCCESS); + // ref.current.reFetchData(); + toggle(); + formik.resetForm(); + setSelectedCheckboxes({}); + // } + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + }; + + /* ----------------- Toggle Modal ----------------- */ + const toggle = () => { + setIsEditUser(false); + setShowUserAccessModal(false); + setShowModal(!showModal); + }; + + /* ----------------- Edit User ----------------- */ + const editUser = async (row) => { + try { + const { data: { data: userData } = {} } = await getUserById( + row?.original?.id + ); + const formData = { + userName: userData?.name, + email: userData?.email, + mobile: userData?.mobile, + password: userData?.password, + transferMasterAdminAccess: false, + isEditUser: true, + }; + setIsAdmin(userData?.isAdmin); + setHasProfileCompleted(userData?.hasProfileCompleted); + const updatedCheckboxes = roles.reduce( + (acc, role) => ({ + ...acc, + [role?.id]: userData?.roles.some( + (roleData) => roleData?.id === role?.id + ), + }), + {} + ); + + setSelectedCheckboxes(updatedCheckboxes); + formik.setValues(formData); + toggle(); + setIsEditUser(true); + setUserId(row?.original?.id); + } catch ({ resp }) { + // eslint-disable-next-line no-console + console.error(resp?.data?.message); + } + }; + + const resendPasswordSetupMailer = (row) => { + try { + const response = resendPasswordMailer(row?.original?.email); + if (response.status === 200) { + pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } + }; + + /* ----------------- Revoke Admin Access Request ----------------- */ + const revokeAdminAccessRequest = async () => { + setRevokeAdminAccessRequestModal(true); + }; + + /* ----------------- Handle Checkbox Change ----------------- */ + const handleCheckboxChange = (id, isChecked) => { + setSelectedCheckboxes({ ...selectedCheckboxes, [id]: isChecked }); + }; + + /* ----------------- Table Columns ----------------- */ + const columns = useMemo( + () => [ + { + size: 30, + header: 'S.no.', + Cell: (props) => { + const tableState = props?.table?.getState(); + const serialNumber = ( + props?.row?.index + + 1 + + tableState?.pagination?.pageIndex * tableState?.pagination?.pageSize + ) + ?.toString() + ?.padStart(1, '0'); + return {serialNumber}.; + }, + enableSorting: false, + }, + { + accessorFn: ({ name }) => name || NOT_AVAILABLE_TEXT, + accessorKey: 'name', + header: 'Doctor/Nurse Name', + isBold: true, + }, + // { + // accessorKey: 'roles', + // header: 'User Access', + // enableSorting: false, + // Cell: ({ row }) => ( + //
    + // {row.original.roles && row.original.roles.length > 0 + // ? row.original.roles.map((role, index) => ( + // + // {role.name} + // {index !== row.original.roles.length - 1 ? ', ' : ''} + // + // )) + // : NOT_AVAILABLE_TEXT} + // {row.original.isAdmin && ', Master Admin'} + //
    + // ), + // }, + { + accessorFn: ({ mobile }) => mobile || NOT_AVAILABLE_TEXT, + accessorKey: 'mobile', + header: 'User Type', + }, + { + accessorFn: ({ email }) => email || NOT_AVAILABLE_TEXT, + accessorKey: 'email', + header: 'Specialties', + }, + { + accessorKey: 'createdAt', + Cell: ({ row }) => ( +
    + {format(new Date(row?.original?.createdAt), 'dd MMM, yyyy')} +
    + ), + header: 'Added On', + }, + { + accessorFn: ({ email }) => email || NOT_AVAILABLE_TEXT, + accessorKey: 'email', + header: 'Status', + }, + ], + [] + ); + + const formik = useFormik({ + initialValues: defaultFormData.current, + validationSchema, + onSubmit: (values) => handleSubmit(values, formik), + }); + + /* ----------------- Handle Cancel ----------------- */ + const handleCancel = () => { + toggle(); + formik.resetForm(); + }; + + const deleteUserToggle = (row) => { + setDeleteModal(!deleteModal); + setSelectedUserData(row?.original); + }; + + const makeMasterAdminToggle = (row) => { + setMasterAdminModal((prev) => !prev); + setSelectedUserData(row?.original); + }; + + const revokeAdminAccessToggle = () => { + setRevokeAdminAccessRequestModal(!revokeAdminAccessRequestModal); + }; + + /* ----------------- Transfer Master Admin Access to User ----------------- */ + const handleTransferMasterAdminAccess = async () => { + setButtonLoading(true); + try { + const response = await transferMasterAdminAccess(seletedUserData?.id); + if (response.status === 200) { + pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + ref.current.resetPage(true); + makeMasterAdminToggle(); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error?.response?.data?.message); + } finally { + ref.current.reFetchData(); + setButtonLoading(false); + } + }; + + /* ----------------- Delete User ----------------- */ + const deleteUser = async () => { + setButtonLoading(true); + try { + const response = await deleteUserById(seletedUserData?.id); + if (response.status === 200) { + pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + ref.current.resetPage(true); + deleteUserToggle(); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error?.response?.data?.message); + } finally { + ref.current.reFetchData(); + setButtonLoading(false); + } + }; + /* ----------------- Revoke Admin Access ----------------- */ + const handleRevokeAdminAccessRequest = async () => { + setButtonLoading(true); + try { + const response = await revokeAdminTransferAccess(); + if (response.status === 200) { + pushNotification(response?.data?.message, NOTIFICATION.SUCCESS); + ref.current.resetPage(true); + setRevokeAdminAccessRequestModal(false); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error?.response?.data?.message); + } finally { + ref.current.reFetchData(); + setButtonLoading(false); + } + }; + + const handleuserAccessCancel = () => { + setCheckboxError(false); + setShowUserAccessModal(false); + }; + + const getRowStyle = (row) => + row?.original?.isAdmin ? { backgroundColor: '#E7F4EE !important' } : {}; + + const handleSubmitClick = async () => { + const formikErrors = await formik.validateForm(); + const errors = Object.keys(formikErrors); + + if (errors.length) { + // Find the first invalid field and focus it + const firstErrorField = errors[0]; + const firstErrorRef = fieldRefs[firstErrorField]?.current; + + if (firstErrorRef) { + // Scroll to the first invalid field smoothly + if (typeof firstErrorRef?.scrollIntoView === 'function') { + firstErrorRef?.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + + // Focus the field after a slight delay (to ensure scrolling completes first) + setTimeout(() => firstErrorRef.focus(), 300); + } + + // Show error notification + if (formik?.touched[firstErrorField]) + pushNotification(formikErrors[firstErrorField], NOTIFICATION.ERROR); + } + + formik.handleSubmit(); + }; + + return ( + + } + addButtonDisabled={userTotalCount === 10} + // permissionName={'CREATE_USERS'} + infiniteDropdown + /> + + + , + renderAction: (row) => isBsAdmin && !row?.original?.isAdmin, + }, + { + onClick: revokeAdminAccessRequest, + text: 'Revoke Admin Access Request', + icon: , + // permissionName: "CREATE_USERS", + renderAction: (row) => row?.original?.transferMasterAdminAccess, + }, + { + onClick: editUser, + text: 'Edit', + icon: , + // permissionName: "CREATE_USERS", + }, + { + onClick: (row) => resendPasswordSetupMailer(row), + text: 'Resend password setup mailer', + icon: , + renderAction: (row) => !row?.original?.hasProfileCompleted, + // permissionName: "CREATE_USERS", + }, + { + onClick: deleteUserToggle, + text: 'Delete', + icon: , + renderAction: (row) => !row?.original?.isAdmin, + // permissionName: 'DELETE_USERS', + // isDisabledValue: user?.id, + rowKey: 'id', + }, + ].filter(Boolean)} + /> + + + {/* --------------- ADD User Modal --------------- */} + {showModal && !showUserAccessModal && ( + handleCancel()} + onFooterRightButtonClick={() => handleSubmit()} + title={isEditUser ? 'Edit Doctor/Nurse' : 'Add New Doctor/Nurse'} + isTitleLeft={true} + onClose={() => { + formik.resetForm(); + setShowModal(false); + setSelectedCheckboxes([]); + setIsAdmin(false); + }} + modalBodyFunction={() => ( + +
    + { + formik.setFieldValue('userName', e.target.value.trim()); + formik.handleBlur(e); + }} + inputRef={fieldRefs.userName} + error={Boolean( + formik.errors.userName && formik.touched.userName + )} + helperText={ + formik.errors.userName && formik.touched.userName ? ( + + {formik.errors.userName} + + ) : ( + '' + ) + } + /> + { + formik.setFieldValue('lastName', e.target.value.trim()); + formik.handleBlur(e); + }} + inputRef={fieldRefs.lastName} + error={Boolean( + formik.errors.lastName && formik.touched.lastName + )} + helperText={ + formik.errors.lastName && formik.touched.lastName ? ( + + {formik.errors.lastName} + + ) : ( + '' + ) + } + /> + + + option.name} + searchDebounceTime={500} + fieldName="appointmentType" + placeholderText="Select Appointment Types" + styleForSelectorParent={classes.multiSelectParent} + styleForSelector={classes.multiSelectChild} + inputRef={fieldRefs.appointmentType} + /> + {/* + {formik.errors.email} + + ) : ( + '' + ) + } + /> + { + if (e.target.value.length <= 10) { + formik.handleChange(e); + } + }} + onBlur={formik.handleBlur} + inputRef={fieldRefs.mobile} + error={Boolean(formik.errors.mobile && formik.touched.mobile)} + helperText={ + formik.errors.mobile && formik.touched.mobile ? ( + + {formik.errors.mobile} + + ) : ( + '' + ) + } + inputProps={{ + type: 'string', + pattern: '[0-9]*', + autocomplete: 'password', + form: { + autocomplete: 'off', + }, + }} + /> */} + +
    + )} + /> + )} + + {/* ----------- Choose User Access Modal----------- */} + {showModal && showUserAccessModal && formik.isValid && ( + handleuserAccessCancel()} + onFooterRightButtonClick={() => formik.handleSubmit()} + footerLeftButtonTitle="Cancel" + footerRightButtonTitle="Add" + showFooter + onClose={() => { + formik.resetForm(); + setSelectedCheckboxes([]); + setShowModal(false); + setIsAdmin(false); + }} + backFunctionCall={() => { + setShowUserAccessModal(false); + }} + modalBodyFunction={() => <>} + footerContent={ + + + + setCheckboxError(false)} // Clear error on cancel + > + Cancel + + + + + Add + + + + + } + /> + )} + + {/* ----------- Delete User Modal ----------- */} + {deleteModal && ( + + )} + + {/* ----------- Make User Master Admin Modal ----------- */} + {masterAdminModal && ( + + )} + + {/* ----------- Revoke Admin Access Request ----------- */} + {revokeAdminAccessRequestModal && ( + + )} + + ); +} + +export default Users; diff --git a/src/views/User/userStyles.js b/src/views/User/userStyles.js new file mode 100644 index 0000000..3e1dee6 --- /dev/null +++ b/src/views/User/userStyles.js @@ -0,0 +1,114 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { pxToRem } from '../../theme/typography'; + +export const useStyles = makeStyles((theme) => ({ + tableMainDiv: { + marginTop: theme.spacing(2.0), + }, + form: { + marginTop: theme.spacing(1.5), + padding: `${theme.spacing(2.0)} ${theme.spacing(2.4)}`, + maxWidth: '440px', + }, + passwordCheckListTitle: { + marginLeft: theme.spacing(0.5), + color: 'grey', + fontStyle: 'italic', + }, + passwordCheckList: { + marginLeft: theme.spacing(0.5), + fontStyle: 'italic', + }, + footerMainDiv: { + marginTop: theme.spacing(0.6), + padding: theme.spacing(1.8), + backgroundColor: '#E9EAEB', + }, + modalFooter: { + display: 'flex', + justifyContent: 'flex-end', + paddingRight: theme.spacing(0.6), + '& .MuiButton-root ': { + display: 'flex', + justifyContent: 'center', + }, + }, + cancelButton: { + marginRight: theme.spacing(0.5), + }, + userAccessModalMainBodyDiv: { + backgroundColor: theme.palette.grey[35], + borderRadius: theme.spacing(0.8), + }, + userAccessModalMainBodyOuterDiv: { + padding: `${theme.spacing(2.4)} ${theme.spacing(2.8)}`, + }, + userAccessModalHeaderDiv: { + padding: `${theme.spacing(1.0)} ${theme.spacing(2.4)}`, + }, + checkBoxLabel: { + marginLeft: theme.spacing(1.0), + }, + userAccessModalContainerDiv: { + padding: `${theme.spacing(0.9)} ${theme.spacing(2.4)}`, + }, + checkboxDiv: { + paddingTop: theme.spacing(0.9), + paddingBottom: theme.spacing(0.9), + }, + tableActionIcons: { + marginRight: theme.spacing(1.4), + }, + tableActionIconTile: { + paddingTop: theme.spacing(2.0), + }, + dateStyle: { + color: theme.palette.grey[10], + backgroundColor: theme.palette.grey[11], + borderRadius: theme.spacing(3), + paddingLeft: theme.spacing(1), + paddingRight: theme.spacing(1), + fontSize: pxToRem(14), + display: 'inline-block', + }, + adminTransferRequestApproveImageDiv: { + margin: theme.spacing(0.5), + marginLeft: theme.spacing(2.0), + }, + adminTransferRequestApproveBody: { + margin: theme.spacing(2.0), + height: '80vh', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + adminTransferRequestApproveIconGroup: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + }, + adminTransferRequestApproveIcon: { + width: '119px', + height: '119px', + }, + adminTransferRequestApproveText: { + fontSize: pxToRem(18), + }, + adminTransferRequestApproveContent: { + fontSize: pxToRem(14), + width: '60%', + marginTop: theme.spacing(2.0), + textAlign: 'center', + }, + adminTransferRequestApproveVerificationSuccessfulText: { + marginTop: theme.spacing(1.0), + }, + loginButton: { + fontFamily: 'Inter-Thin', + width: '200px', + height: '42px', + marginTop: theme.spacing(4.0), + }, +})); diff --git a/src/views/components/Loader.jsx b/src/views/components/Loader.jsx new file mode 100644 index 0000000..6210a86 --- /dev/null +++ b/src/views/components/Loader.jsx @@ -0,0 +1,16 @@ +import { Backdrop, Box, CircularProgress } from '@mui/material'; +import React from 'react'; +import { useStyles } from './styles/loaderStyles'; + +function Loader() { + const classes = useStyles(); + return ( + + + + + + ); +} + +export default Loader; diff --git a/src/views/components/styles/loaderStyles.js b/src/views/components/styles/loaderStyles.js new file mode 100644 index 0000000..0b1b1af --- /dev/null +++ b/src/views/components/styles/loaderStyles.js @@ -0,0 +1,12 @@ +import makeStyles from '@mui/styles/makeStyles'; + +export const useStyles = makeStyles(() => ({ + root: { + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, +})); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..2328e17 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react-swc' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +})