| @@ -16,7 +16,10 @@ | |||||
| "@mui/icons-material": "^5.15.0", | "@mui/icons-material": "^5.15.0", | ||||
| "@mui/material": "^5.15.0", | "@mui/material": "^5.15.0", | ||||
| "@mui/material-nextjs": "^5.15.0", | "@mui/material-nextjs": "^5.15.0", | ||||
| "@mui/x-data-grid": "^6.18.7", | |||||
| "@mui/x-date-pickers": "^6.18.7", | |||||
| "@unly/universal-language-detector": "^2.0.3", | "@unly/universal-language-detector": "^2.0.3", | ||||
| "dayjs": "^1.11.10", | |||||
| "i18next": "^23.7.11", | "i18next": "^23.7.11", | ||||
| "i18next-resources-to-backend": "^1.2.0", | "i18next-resources-to-backend": "^1.2.0", | ||||
| "next": "14.0.4", | "next": "14.0.4", | ||||
| @@ -24,7 +27,8 @@ | |||||
| "react": "^18", | "react": "^18", | ||||
| "react-dom": "^18", | "react-dom": "^18", | ||||
| "react-hook-form": "^7.49.2", | "react-hook-form": "^7.49.2", | ||||
| "react-i18next": "^13.5.0" | |||||
| "react-i18next": "^13.5.0", | |||||
| "react-intl": "^6.5.5" | |||||
| }, | }, | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "@types/node": "^20", | "@types/node": "^20", | ||||
| @@ -371,6 +375,92 @@ | |||||
| "resolved": "https://registry.npmjs.org/@fontsource/plus-jakarta-sans/-/plus-jakarta-sans-5.0.18.tgz", | "resolved": "https://registry.npmjs.org/@fontsource/plus-jakarta-sans/-/plus-jakarta-sans-5.0.18.tgz", | ||||
| "integrity": "sha512-poMuIcQ8F7WGXF4mNUviDk49Ewdf0pU7wmCzWQNbWEtus+L46BSp+4OqbWy0LWJEmMLI9F5hUHaSo2maLJwrQw==" | "integrity": "sha512-poMuIcQ8F7WGXF4mNUviDk49Ewdf0pU7wmCzWQNbWEtus+L46BSp+4OqbWy0LWJEmMLI9F5hUHaSo2maLJwrQw==" | ||||
| }, | }, | ||||
| "node_modules/@formatjs/ecma402-abstract": { | |||||
| "version": "1.18.0", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.0.tgz", | |||||
| "integrity": "sha512-PEVLoa3zBevWSCZzPIM/lvPCi8P5l4G+NXQMc/CjEiaCWgyHieUoo0nM7Bs0n/NbuQ6JpXEolivQ9pKSBHaDlA==", | |||||
| "dependencies": { | |||||
| "@formatjs/intl-localematcher": "0.5.2", | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/fast-memoize": { | |||||
| "version": "2.2.0", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", | |||||
| "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", | |||||
| "dependencies": { | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/icu-messageformat-parser": { | |||||
| "version": "2.7.3", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.3.tgz", | |||||
| "integrity": "sha512-X/jy10V9S/vW+qlplqhMUxR8wErQ0mmIYSq4mrjpjDl9mbuGcCILcI1SUYkL5nlM4PJqpc0KOS0bFkkJNPxYRw==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "@formatjs/icu-skeleton-parser": "1.7.0", | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/icu-skeleton-parser": { | |||||
| "version": "1.7.0", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.7.0.tgz", | |||||
| "integrity": "sha512-Cfdo/fgbZzpN/jlN/ptQVe0lRHora+8ezrEeg2RfrNjyp+YStwBy7cqDY8k5/z2LzXg6O0AdzAV91XS0zIWv+A==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/intl": { | |||||
| "version": "2.9.9", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.9.9.tgz", | |||||
| "integrity": "sha512-JI3CNgL2Zdg5lv9ncT2sYKqbAj2RGrCbdzaCckIxMPxn4QuHuOVvYUGmBAXVusBmfG/0sxLmMrnwnBioz+QKdA==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "@formatjs/fast-memoize": "2.2.0", | |||||
| "@formatjs/icu-messageformat-parser": "2.7.3", | |||||
| "@formatjs/intl-displaynames": "6.6.4", | |||||
| "@formatjs/intl-listformat": "7.5.3", | |||||
| "intl-messageformat": "10.5.8", | |||||
| "tslib": "^2.4.0" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "typescript": "5" | |||||
| }, | |||||
| "peerDependenciesMeta": { | |||||
| "typescript": { | |||||
| "optional": true | |||||
| } | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/intl-displaynames": { | |||||
| "version": "6.6.4", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.4.tgz", | |||||
| "integrity": "sha512-ET8KQ+L9Q0K8x1SnJQa4DNssUcbATlMopWqYvGGR8yAvw5qwAQc1fv+DshCoZNIE9pbcue0IGC4kWNAkWqlFag==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "@formatjs/intl-localematcher": "0.5.2", | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/intl-listformat": { | |||||
| "version": "7.5.3", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.3.tgz", | |||||
| "integrity": "sha512-l7EOr0Yh1m8KagytukB90yw81uyzrM7amKFrgxXqphz4KeSIL0KPa68lPsdtZ+JmQB73GaDQRwLOwUKFZ1VZPQ==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "@formatjs/intl-localematcher": "0.5.2", | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@formatjs/intl-localematcher": { | |||||
| "version": "0.5.2", | |||||
| "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.2.tgz", | |||||
| "integrity": "sha512-txaaE2fiBMagLrR4jYhxzFO6wEdEG4TPMqrzBAcbr4HFUYzH/YC+lg6OIzKCHm8WgDdyQevxbAAV1OgcXctuGw==", | |||||
| "dependencies": { | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@humanwhocodes/config-array": { | "node_modules/@humanwhocodes/config-array": { | ||||
| "version": "0.11.13", | "version": "0.11.13", | ||||
| "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", | ||||
| @@ -782,6 +872,96 @@ | |||||
| "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", | "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", | ||||
| "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" | "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" | ||||
| }, | }, | ||||
| "node_modules/@mui/x-data-grid": { | |||||
| "version": "6.18.7", | |||||
| "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.18.7.tgz", | |||||
| "integrity": "sha512-K1A3pMUPxI4/Mt5A4vrK45fBBQK5rZvBVqRMrB5n8zX++Bj+WLWKvLTtfCmlriUtzuadr/Hl7Z+FDRXUJAx6qg==", | |||||
| "dependencies": { | |||||
| "@babel/runtime": "^7.23.2", | |||||
| "@mui/utils": "^5.14.16", | |||||
| "clsx": "^2.0.0", | |||||
| "prop-types": "^15.8.1", | |||||
| "reselect": "^4.1.8" | |||||
| }, | |||||
| "engines": { | |||||
| "node": ">=14.0.0" | |||||
| }, | |||||
| "funding": { | |||||
| "type": "opencollective", | |||||
| "url": "https://opencollective.com/mui" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "@mui/material": "^5.4.1", | |||||
| "@mui/system": "^5.4.1", | |||||
| "react": "^17.0.0 || ^18.0.0", | |||||
| "react-dom": "^17.0.0 || ^18.0.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@mui/x-date-pickers": { | |||||
| "version": "6.18.7", | |||||
| "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.18.7.tgz", | |||||
| "integrity": "sha512-4NoapaCT3jvEk2cuAUjG0ReZvTEk1i4dGDz94Gt1Oc08GuC1AuzYRwCR1/1tdmbDynwkR8ilkKL6AyS3NL1H4A==", | |||||
| "dependencies": { | |||||
| "@babel/runtime": "^7.23.2", | |||||
| "@mui/base": "^5.0.0-beta.22", | |||||
| "@mui/utils": "^5.14.16", | |||||
| "@types/react-transition-group": "^4.4.8", | |||||
| "clsx": "^2.0.0", | |||||
| "prop-types": "^15.8.1", | |||||
| "react-transition-group": "^4.4.5" | |||||
| }, | |||||
| "engines": { | |||||
| "node": ">=14.0.0" | |||||
| }, | |||||
| "funding": { | |||||
| "type": "opencollective", | |||||
| "url": "https://opencollective.com/mui" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "@emotion/react": "^11.9.0", | |||||
| "@emotion/styled": "^11.8.1", | |||||
| "@mui/material": "^5.8.6", | |||||
| "@mui/system": "^5.8.0", | |||||
| "date-fns": "^2.25.0", | |||||
| "date-fns-jalali": "^2.13.0-0", | |||||
| "dayjs": "^1.10.7", | |||||
| "luxon": "^3.0.2", | |||||
| "moment": "^2.29.4", | |||||
| "moment-hijri": "^2.1.2", | |||||
| "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", | |||||
| "react": "^17.0.0 || ^18.0.0", | |||||
| "react-dom": "^17.0.0 || ^18.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/@next/env": { | "node_modules/@next/env": { | ||||
| "version": "14.0.4", | "version": "14.0.4", | ||||
| "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", | ||||
| @@ -1039,6 +1219,15 @@ | |||||
| "tslib": "^2.4.0" | "tslib": "^2.4.0" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/@types/hoist-non-react-statics": { | |||||
| "version": "3.3.5", | |||||
| "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", | |||||
| "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", | |||||
| "dependencies": { | |||||
| "@types/react": "*", | |||||
| "hoist-non-react-statics": "^3.3.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/@types/json5": { | "node_modules/@types/json5": { | ||||
| "version": "0.0.29", | "version": "0.0.29", | ||||
| "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", | "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", | ||||
| @@ -1885,6 +2074,11 @@ | |||||
| "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", | "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", | ||||
| "dev": true | "dev": true | ||||
| }, | }, | ||||
| "node_modules/dayjs": { | |||||
| "version": "1.11.10", | |||||
| "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", | |||||
| "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" | |||||
| }, | |||||
| "node_modules/debug": { | "node_modules/debug": { | ||||
| "version": "4.3.4", | "version": "4.3.4", | ||||
| "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | ||||
| @@ -3257,6 +3451,17 @@ | |||||
| "node": ">= 0.4" | "node": ">= 0.4" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/intl-messageformat": { | |||||
| "version": "10.5.8", | |||||
| "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.8.tgz", | |||||
| "integrity": "sha512-NRf0jpBWV0vd671G5b06wNofAN8tp7WWDogMZyaU8GUAsmbouyvgwmFJI7zLjfAMpm3zK+vSwRP3jzaoIcMbaA==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "@formatjs/fast-memoize": "2.2.0", | |||||
| "@formatjs/icu-messageformat-parser": "2.7.3", | |||||
| "tslib": "^2.4.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/is-array-buffer": { | "node_modules/is-array-buffer": { | ||||
| "version": "3.0.2", | "version": "3.0.2", | ||||
| "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", | "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", | ||||
| @@ -4697,6 +4902,32 @@ | |||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/react-intl": { | |||||
| "version": "6.5.5", | |||||
| "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.5.5.tgz", | |||||
| "integrity": "sha512-cI5UKvBh4tc1zxLIziHBYGMX3dhYWDEFlvUDVN6NfT2i96zTXz/zH2AmM8+2waqgOhwkFUzd+7kK1G9q7fiC2g==", | |||||
| "dependencies": { | |||||
| "@formatjs/ecma402-abstract": "1.18.0", | |||||
| "@formatjs/icu-messageformat-parser": "2.7.3", | |||||
| "@formatjs/intl": "2.9.9", | |||||
| "@formatjs/intl-displaynames": "6.6.4", | |||||
| "@formatjs/intl-listformat": "7.5.3", | |||||
| "@types/hoist-non-react-statics": "^3.3.1", | |||||
| "@types/react": "16 || 17 || 18", | |||||
| "hoist-non-react-statics": "^3.3.2", | |||||
| "intl-messageformat": "10.5.8", | |||||
| "tslib": "^2.4.0" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "react": "^16.6.0 || 17 || 18", | |||||
| "typescript": "5" | |||||
| }, | |||||
| "peerDependenciesMeta": { | |||||
| "typescript": { | |||||
| "optional": true | |||||
| } | |||||
| } | |||||
| }, | |||||
| "node_modules/react-is": { | "node_modules/react-is": { | ||||
| "version": "16.13.1", | "version": "16.13.1", | ||||
| "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | ||||
| @@ -4780,6 +5011,11 @@ | |||||
| "url": "https://github.com/sponsors/ljharb" | "url": "https://github.com/sponsors/ljharb" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/reselect": { | |||||
| "version": "4.1.8", | |||||
| "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", | |||||
| "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" | |||||
| }, | |||||
| "node_modules/resolve": { | "node_modules/resolve": { | ||||
| "version": "1.22.8", | "version": "1.22.8", | ||||
| "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | ||||
| @@ -5567,7 +5803,7 @@ | |||||
| "version": "5.3.3", | "version": "5.3.3", | ||||
| "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", | ||||
| "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", | "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", | ||||
| "dev": true, | |||||
| "devOptional": true, | |||||
| "bin": { | "bin": { | ||||
| "tsc": "bin/tsc", | "tsc": "bin/tsc", | ||||
| "tsserver": "bin/tsserver" | "tsserver": "bin/tsserver" | ||||
| @@ -17,7 +17,10 @@ | |||||
| "@mui/icons-material": "^5.15.0", | "@mui/icons-material": "^5.15.0", | ||||
| "@mui/material": "^5.15.0", | "@mui/material": "^5.15.0", | ||||
| "@mui/material-nextjs": "^5.15.0", | "@mui/material-nextjs": "^5.15.0", | ||||
| "@mui/x-data-grid": "^6.18.7", | |||||
| "@mui/x-date-pickers": "^6.18.7", | |||||
| "@unly/universal-language-detector": "^2.0.3", | "@unly/universal-language-detector": "^2.0.3", | ||||
| "dayjs": "^1.11.10", | |||||
| "i18next": "^23.7.11", | "i18next": "^23.7.11", | ||||
| "i18next-resources-to-backend": "^1.2.0", | "i18next-resources-to-backend": "^1.2.0", | ||||
| "next": "14.0.4", | "next": "14.0.4", | ||||
| @@ -25,7 +28,8 @@ | |||||
| "react": "^18", | "react": "^18", | ||||
| "react-dom": "^18", | "react-dom": "^18", | ||||
| "react-hook-form": "^7.49.2", | "react-hook-form": "^7.49.2", | ||||
| "react-i18next": "^13.5.0" | |||||
| "react-i18next": "^13.5.0", | |||||
| "react-intl": "^6.5.5" | |||||
| }, | }, | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "@types/node": "^20", | "@types/node": "^20", | ||||
| @@ -0,0 +1,161 @@ | |||||
| import * as React from 'react'; | |||||
| import { Card, CardHeader, CardContent, SxProps, Theme } from '@mui/material'; | |||||
| import { DataGrid, GridColDef } from '@mui/x-data-grid'; | |||||
| import { darken, lighten, styled } from '@mui/material/styles'; | |||||
| interface CustomDatagridProps { | |||||
| Title?: string; | |||||
| rows: any[]; | |||||
| columns: any[]; | |||||
| columnWidth?: number; | |||||
| Style?: boolean; | |||||
| sx?: SxProps<Theme>; | |||||
| dataGridHeight?: number; | |||||
| [key: string]: any; | |||||
| } | |||||
| const CustomDatagrid: React.FC<CustomDatagridProps> = ({ | |||||
| Title, | |||||
| rows, | |||||
| columns, | |||||
| columnWidth, | |||||
| Style = true, | |||||
| sx, | |||||
| dataGridHeight, | |||||
| ...props | |||||
| }) => { | |||||
| const modifiedColumns = columns.map((column) => { | |||||
| return { | |||||
| ...column, | |||||
| width: columnWidth ?? 150, | |||||
| }; | |||||
| }); | |||||
| const rowsWithDefaultValues = rows.map((row) => { | |||||
| return { ...row }; | |||||
| }); | |||||
| const getBackgroundColor = (color: string, mode: 'light' | 'dark') => | |||||
| mode === 'dark' ? darken(color, 0.7) : lighten(color, 0.7); | |||||
| const getHoverBackgroundColor = (color: string, mode: 'light' | 'dark') => | |||||
| mode === 'dark' ? darken(color, 0.6) : lighten(color, 0.6); | |||||
| const getSelectedBackgroundColor = (color: string, mode: 'light' | 'dark') => | |||||
| mode === 'dark' ? darken(color, 0.5) : lighten(color, 0.5); | |||||
| const getSelectedHoverBackgroundColor = (color: string, mode: 'light' | 'dark') => | |||||
| mode === 'dark' ? darken(color, 0.4) : lighten(color, 0.4); | |||||
| const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ | |||||
| '& .super-app-theme--Open': { | |||||
| backgroundColor: getBackgroundColor(theme.palette.info.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getHoverBackgroundColor(theme.palette.info.main, theme.palette.mode) | |||||
| }, | |||||
| '&.Mui-selected': { | |||||
| backgroundColor: getSelectedBackgroundColor(theme.palette.info.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getSelectedHoverBackgroundColor(theme.palette.info.main, theme.palette.mode) | |||||
| } | |||||
| } | |||||
| }, | |||||
| '& .super-app-theme--finish': { | |||||
| backgroundColor: getBackgroundColor(theme.palette.success.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getHoverBackgroundColor(theme.palette.success.main, theme.palette.mode) | |||||
| }, | |||||
| '&.Mui-selected': { | |||||
| backgroundColor: getSelectedBackgroundColor(theme.palette.success.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getSelectedHoverBackgroundColor(theme.palette.success.main, theme.palette.mode) | |||||
| } | |||||
| } | |||||
| }, | |||||
| '& .super-app-theme--danger': { | |||||
| backgroundColor: getBackgroundColor(theme.palette.warning.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getHoverBackgroundColor(theme.palette.warning.main, theme.palette.mode) | |||||
| }, | |||||
| '&.Mui-selected': { | |||||
| backgroundColor: getSelectedBackgroundColor(theme.palette.warning.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getSelectedHoverBackgroundColor(theme.palette.warning.main, theme.palette.mode) | |||||
| } | |||||
| } | |||||
| }, | |||||
| '& .super-app-theme--warning': { | |||||
| backgroundColor: getBackgroundColor(theme.palette.error.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getHoverBackgroundColor(theme.palette.error.main, theme.palette.mode) | |||||
| }, | |||||
| '&.Mui-selected': { | |||||
| backgroundColor: getSelectedBackgroundColor(theme.palette.error.main, theme.palette.mode), | |||||
| '&:hover': { | |||||
| backgroundColor: getSelectedHoverBackgroundColor(theme.palette.error.main, theme.palette.mode) | |||||
| } | |||||
| } | |||||
| } | |||||
| })); | |||||
| return ( | |||||
| <div className="mt-5" style={{ height: dataGridHeight ?? 400, width: '100%' }}> | |||||
| <Card> | |||||
| {Title && <CardHeader title={Title} />} | |||||
| <CardContent style={{ display: "flex", alignItems: "center", justifyContent: "center" }}> | |||||
| {Style ? ( | |||||
| <StyledDataGrid | |||||
| rows={rowsWithDefaultValues} | |||||
| columns={modifiedColumns} | |||||
| editMode="row" | |||||
| initialState={{ | |||||
| pagination: { paginationModel: { pageSize: 10 } }, | |||||
| }} | |||||
| className="customDataGrid" | |||||
| sx={{ | |||||
| boxShadow: 2, | |||||
| border: 2, | |||||
| borderColor: 'primary.light', | |||||
| '& .MuiDataGrid-cell:hover': { | |||||
| color: 'primary.main' | |||||
| }, | |||||
| height: dataGridHeight ?? 400, | |||||
| '& .MuiDataGrid-root': { | |||||
| overflow: 'auto', | |||||
| }, | |||||
| ...sx | |||||
| }} | |||||
| {...props} | |||||
| /> | |||||
| ) : ( | |||||
| <DataGrid | |||||
| rows={rowsWithDefaultValues} | |||||
| columns={modifiedColumns} | |||||
| editMode="row" | |||||
| initialState={{ | |||||
| pagination: { paginationModel: { pageSize: 10 } }, | |||||
| }} | |||||
| className="customDataGrid" | |||||
| sx={{ | |||||
| boxShadow: 2, | |||||
| border: 2, | |||||
| borderColor: 'primary.light', | |||||
| '& .MuiDataGrid-cell:hover': { | |||||
| color: 'primary.main' | |||||
| }, | |||||
| height: 400, | |||||
| '& .MuiDataGrid-root': { | |||||
| overflow: 'auto', | |||||
| }, | |||||
| ...sx | |||||
| }} | |||||
| {...props} | |||||
| /> | |||||
| )} | |||||
| </CardContent> | |||||
| </Card> | |||||
| </div> | |||||
| ); | |||||
| }; | |||||
| export default CustomDatagrid; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./CustomDatagrid"; | |||||
| @@ -0,0 +1,263 @@ | |||||
| "use client"; | |||||
| import React, { useState, FC } from 'react'; | |||||
| import { | |||||
| Stack, | |||||
| Typography, | |||||
| FormControlLabel, | |||||
| Checkbox, | |||||
| Autocomplete, | |||||
| Button, | |||||
| Grid, | |||||
| TextField, | |||||
| CardHeader, | |||||
| Card, | |||||
| FormControl, | |||||
| InputLabel, | |||||
| Select, | |||||
| MenuItem, | |||||
| ThemeProvider | |||||
| } from '@mui/material'; | |||||
| import { useForm, Controller } from 'react-hook-form'; | |||||
| import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; | |||||
| import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; | |||||
| import dayjs from 'dayjs'; | |||||
| import SearchIcon from '@mui/icons-material/Search'; | |||||
| import RefreshIcon from '@mui/icons-material/Refresh'; | |||||
| import { DemoItem } from '@mui/x-date-pickers/internals/demo'; | |||||
| import { DatePicker } from '@mui/x-date-pickers/DatePicker'; | |||||
| interface Field { | |||||
| id: any; | |||||
| label: any; | |||||
| type: string; | |||||
| options?: Array<{ id: string; label: string }>; | |||||
| setValue: ((value: any) => void) | Array<(value: any) => void>; | |||||
| value?: any; | |||||
| required?: boolean; | |||||
| } | |||||
| interface FormComponentProps { | |||||
| fields: Field[]; | |||||
| onSubmit: (data: any) => void; | |||||
| resetForm: () => void; | |||||
| sx?: any; | |||||
| } | |||||
| interface SearchFormProps { | |||||
| applySearch: (data: any) => void; | |||||
| fields: Field[]; | |||||
| title?: string; | |||||
| sx?: any; | |||||
| } | |||||
| const FormComponent: FC<FormComponentProps> = ({ fields, onSubmit, resetForm, sx }) => { | |||||
| const { reset, register, handleSubmit, control } = useForm(); | |||||
| const [fromDate, setFromDate] = useState<dayjs.Dayjs | null>(null); | |||||
| const [dayRangeFromDate, setDayRangeFromDate] = useState<dayjs.Dayjs | null>(null); | |||||
| const [dayRangeToDate, setDayRangeToDate] = useState<dayjs.Dayjs | null>(null); | |||||
| const [value, setValue] = useState<{ [key: string]: any }>({}); | |||||
| const [checkbox1, setCheckbox1] = useState(false); | |||||
| const handleFormSubmit = (data: any) => { | |||||
| if (fromDate != null || fromDate != undefined) { | |||||
| data.fromDate = dayjs(fromDate).format('YYYY-MM-DD'); | |||||
| } | |||||
| if (value !== null) { | |||||
| data.dropdownCombo = value | |||||
| } | |||||
| if (value !== null) { | |||||
| data.checkbox = checkbox1; | |||||
| } | |||||
| if (dayRangeFromDate != null || dayRangeFromDate != undefined) { | |||||
| data.dayRangeFromDate = dayjs(dayRangeFromDate).format('YYYY-MM-DD'); | |||||
| } | |||||
| if (dayRangeToDate != null || dayRangeToDate != undefined) { | |||||
| data.dayRangeToDate = dayjs(dayRangeToDate).format('YYYY-MM-DD'); | |||||
| } | |||||
| onSubmit(data); | |||||
| }; | |||||
| const handleFormReset = () => { | |||||
| reset(); | |||||
| resetForm(); | |||||
| setFromDate(null); | |||||
| fields.forEach((field) => { | |||||
| if (typeof(field.setValue) === "function") { | |||||
| field.setValue(typeof (field.value) === "boolean" ? false : null); | |||||
| } else if (Array.isArray(field.setValue)) { | |||||
| field.setValue.forEach((setFunc) => { | |||||
| setFunc(null); | |||||
| }); | |||||
| } | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <form onSubmit={handleSubmit(handleFormSubmit)}> | |||||
| <Grid container alignItems="center"> | |||||
| {fields.map((field) => { | |||||
| if (field.type === 'dropdown') { | |||||
| return ( | |||||
| <Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel id={`${field.id}-label`}>{field.label}</InputLabel> | |||||
| <Controller | |||||
| name={field.id} | |||||
| control={control} | |||||
| defaultValue="" | |||||
| render={({ field: { onChange, value } }) => ( | |||||
| <Select | |||||
| labelId={`${field.id}-label`} | |||||
| id={field.id} | |||||
| value={value} | |||||
| onChange={(e) => { | |||||
| onChange(e.target.value); | |||||
| }} | |||||
| > | |||||
| {field.options?.map((option) => ( | |||||
| <MenuItem | |||||
| value={option.id ?? JSON.stringify(option)} | |||||
| key={option.id ?? JSON.stringify(option)} | |||||
| > | |||||
| {option.id !== undefined ? option.label : JSON.stringify(option)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| ); | |||||
| } else if (field.type === 'date') { | |||||
| return ( | |||||
| <Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <DemoItem> | |||||
| <DatePicker | |||||
| slotProps={{ | |||||
| textField: { | |||||
| id:field.id, | |||||
| }, | |||||
| }} | |||||
| label={field.label} | |||||
| value={fromDate === null ? null : dayjs(fromDate)} | |||||
| onChange={(newValue) => { | |||||
| setFromDate(newValue); | |||||
| }} | |||||
| /> | |||||
| </DemoItem> | |||||
| </LocalizationProvider> | |||||
| </Grid> | |||||
| ); | |||||
| } else if (field.type === 'checkbox') { | |||||
| return ( | |||||
| <Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}> | |||||
| <FormControlLabel | |||||
| control={ | |||||
| <Checkbox | |||||
| id={field.id} | |||||
| checked={field.value} | |||||
| onChange={(event) => { | |||||
| if (typeof field.setValue === 'function') { | |||||
| field.setValue(event.target.checked); | |||||
| setCheckbox1(event.target.checked); | |||||
| } | |||||
| }} | |||||
| color="primary" | |||||
| /> | |||||
| } | |||||
| label={<Typography style={{fontSize:"1.15em"}}>{field.label}</Typography>} | |||||
| /> | |||||
| </Grid> | |||||
| ) | |||||
| } else if (field.type === 'dateRange') { | |||||
| return ( | |||||
| <Grid container key={field.id[0]}> | |||||
| <Grid item xs={12} sm={7} md={7} lg={7} sx={{ ml: 3, mr: 3, mb: 3 }}> | |||||
| <Grid container> | |||||
| <Grid> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <DatePicker | |||||
| label={field.label[0]} | |||||
| value={field.value[0]} | |||||
| onChange={(newValue) => setDayRangeFromDate(newValue)} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| </Grid> | |||||
| <Grid item xs={1.5} sm={1.5} md={1.5} lg={1.5} sx={{ display: 'flex', justifyContent: "center", alignItems: 'center' }}> | |||||
| To | |||||
| </Grid> | |||||
| <Grid item xs={5.25} sm={5.25} md={5.25} lg={5.25}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <DatePicker | |||||
| label={field.label[1]} | |||||
| value={field.value[1]} | |||||
| onChange={(newValue) => setDayRangeToDate(newValue)} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Grid> | |||||
| ) | |||||
| } | |||||
| return ( | |||||
| <Grid item xs={12} sm={5.5} md={5.5} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }} key={field.id}> | |||||
| <TextField | |||||
| fullWidth | |||||
| {...register(field.id)} | |||||
| id={field.id} | |||||
| label={field.label} | |||||
| defaultValue={field.value !== undefined && field.value !== null ? `${field.value}` : ''} | |||||
| required={field.required === true ? field.required : false} | |||||
| sx={{ ...sx }} | |||||
| /> | |||||
| </Grid> | |||||
| ); | |||||
| })} | |||||
| </Grid> | |||||
| <Grid container maxWidth="lg" justifyContent="space-between" style={{marginTop:-20}}> | |||||
| <Stack direction="row"> | |||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}> | |||||
| <Button className="h-12 w-32" style={{backgroundColor:"#92c1e9",color:"white",fontSize:"1.15em",fontWeight:100}} type="submit"> | |||||
| <SearchIcon/> Search | |||||
| </Button> | |||||
| </Grid> | |||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}> | |||||
| <Button className="h-12 w-32" style={{backgroundColor:"#f890a5",color:"white",fontSize:"1.15em",fontWeight:100}} onClick={handleFormReset}> | |||||
| <RefreshIcon/> Reset | |||||
| </Button> | |||||
| </Grid> | |||||
| </Stack> | |||||
| </Grid> | |||||
| </form> | |||||
| ); | |||||
| }; | |||||
| const CustomSearchForm: FC<SearchFormProps> = ({ applySearch, fields, title, sx }) => { | |||||
| const Title = title || "Searching Criteria"; | |||||
| const handleSubmit = (data: any) => { | |||||
| if (applySearch) { | |||||
| applySearch(data); | |||||
| } else { | |||||
| console.log('applySearch function is null'); | |||||
| } | |||||
| }; | |||||
| const handleFormReset = () => { | |||||
| console.log('Form Reset'); | |||||
| }; | |||||
| return ( | |||||
| <Card style={{marginRight:20}}> | |||||
| <CardHeader className="text-slate-500" style={{marginTop:-20}} titleTypographyProps={{ variant: 'h5' }} title={Title}></CardHeader> | |||||
| <FormComponent fields={fields} onSubmit={handleSubmit} resetForm={handleFormReset} sx={sx} /> | |||||
| </Card> | |||||
| ); | |||||
| }; | |||||
| export default CustomSearchForm; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./CustomSearchForm"; | |||||
| @@ -5,6 +5,7 @@ import Paper from "@mui/material/Paper"; | |||||
| import { TFunction } from "i18next"; | import { TFunction } from "i18next"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import PageTitle from "../PageTitle/PageTitle"; | import PageTitle from "../PageTitle/PageTitle"; | ||||
| import ProgressByClient from "./ProgressByClient"; | |||||
| import '../../app/global.css'; | import '../../app/global.css'; | ||||
| const DashboardTabButton: React.FC = () => { | const DashboardTabButton: React.FC = () => { | ||||
| @@ -16,8 +17,8 @@ const DashboardTabButton: React.FC = () => { | |||||
| return <div>Project Financial Summary</div>; | return <div>Project Financial Summary</div>; | ||||
| case 'cashFlow': | case 'cashFlow': | ||||
| return <div>Project Cash Flow</div>; | return <div>Project Cash Flow</div>; | ||||
| case 'projectProgress': | |||||
| return <div>Project Progress by Client</div>; | |||||
| case 'progressByClient': | |||||
| return <ProgressByClient/>; | |||||
| case 'resourceUtilization': | case 'resourceUtilization': | ||||
| return <div>Project Resource Utilization</div>; | return <div>Project Resource Utilization</div>; | ||||
| case 'staffUtilization': | case 'staffUtilization': | ||||
| @@ -0,0 +1,115 @@ | |||||
| "use client"; | |||||
| import * as React from "react"; | |||||
| import Grid from "@mui/material/Grid"; | |||||
| import { useState } from 'react' | |||||
| import Paper from "@mui/material/Paper"; | |||||
| import { TFunction } from "i18next"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import {Card,CardHeader} from '@mui/material'; | |||||
| import CustomSearchForm from "../CustomSearchForm/CustomSearchForm"; | |||||
| import CustomDatagrid from '../CustomDatagrid/CustomDatagrid'; | |||||
| import '../../app/global.css'; | |||||
| const ProgressByClient: React.FC = () => { | |||||
| const [activeTab, setActiveTab] = useState('financialSummary'); | |||||
| const [SearchCriteria, setSearchCriteria] = React.useState({}) | |||||
| const { t } = useTranslation("dashboard"); | |||||
| const [clientCode, setClientCode] = useState(''); | |||||
| const [clientName, setClientName] = useState(''); | |||||
| const [subsidiaryClientCode, setSubsidiaryClientCode] = useState(''); | |||||
| const [subsidiaryClientName, setSubsidiaryClientName] = useState(''); | |||||
| const [dropdownDemo, setDropdownDemo] = useState(''); | |||||
| const [dateDemo, setDateDemo] = useState(null); | |||||
| const [checkboxDemo, setCheckboxDemo] = useState(false); | |||||
| const [receiptFromDate, setReceiptFromDate] = useState(null); | |||||
| const [receiptToDate, setReceiptToDate] = useState(null); | |||||
| const rows = [{id: 1,processNo:"1",processDescription:"把豬頸背肉絲解凍及洗淨隔乾水份", mat:"豬頸背肉絲", matLot:"Lot001", actlQty:"11.30",unit: "kg", equipment:"解凍盤",operator:"HKPC01"}, | |||||
| {id: 2,processNo:"2",processDescription:"加入調味料作腌製", mat:"洋葱", matLot:"Lot002", actlQty:"2.20",unit: "kg", equipment:"調味盤",operator:"HKPC01"}, | |||||
| {id: 3,processNo:"3",processDescription:"加入調味料作腌製", mat:"茄膏", matLot:"Lot003", actlQty:"50.00",unit: "g", equipment:"調味盤",operator:"HKPC01"}, | |||||
| {id: 4,processNo:"4",processDescription:"加入調味料作腌製", mat:"家樂牌黃汁粉", matLot:"Lot004", actlQty:"500.00",unit: "g", equipment:"調味盤",operator:"HKPC01"}, | |||||
| {id: 5,processNo:"5",processDescription:"放入0-4度雪櫃暫存備用", mat:"-", matLot:"-", actlQty:"-",unit: "-", equipment:"雪櫃",operator:"HKPC01"}, | |||||
| {id: 6,processNo:"6",processDescription:"洋葱洗淨切碎備用", mat:"洋葱", matLot:"Lot002", actlQty:"131.00",unit: "g", equipment:"切割機",operator:"HKPC01"}, | |||||
| {id: 7,processNo:"7",processDescription:"用蒸焗爐煮至熟透", mat:"-", matLot:"-", actlQty:"-",unit: "-", equipment:"蒸焗爐",operator:"HKPC01"},] | |||||
| const columns = [ | |||||
| { | |||||
| id: 'processNo', | |||||
| field: 'processNo', | |||||
| headerName: "工序次序", | |||||
| flex: 0.7, | |||||
| }, | |||||
| { | |||||
| id: 'processDescription', | |||||
| field: 'processDescription', | |||||
| headerName: "工序描述", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'mat', | |||||
| field: 'mat', | |||||
| headerName: "原料", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'matLot', | |||||
| field: 'matLot', | |||||
| headerName: "原料批次", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'actlQty', | |||||
| field: 'actlQty', | |||||
| headerName: "投入數量", | |||||
| flex: 0.7, | |||||
| align: "right", | |||||
| headerAlign: 'right', | |||||
| }, | |||||
| { | |||||
| id: 'unit', | |||||
| field: 'unit', | |||||
| headerName: "單位", | |||||
| flex: 0.7, | |||||
| align: "left", | |||||
| headerAlign: 'left', | |||||
| }, | |||||
| { | |||||
| id: 'equipment', | |||||
| field: 'equipment', | |||||
| headerName: "設備", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'operator', | |||||
| field: 'operator', | |||||
| headerName: "操作人員", | |||||
| flex: 1, | |||||
| }, | |||||
| ]; | |||||
| const InputFields = [ | |||||
| { id: "clientCode", label: "Client Code", type: 'text', value: clientCode, setValue: setClientCode }, | |||||
| { id: "clientName", label: "Client Name", type: 'text', value: clientName, setValue: setClientName }, | |||||
| { id: "subsidiaryClientCode", label: "Subsidiary Client Code", type:'text', value:subsidiaryClientCode, setValue: setSubsidiaryClientCode}, | |||||
| { id: "subsidiaryClientName", label: "Subsidiary Client Name", type:'text', value:subsidiaryClientName, setValue: setSubsidiaryClientName}, | |||||
| // { id: 'dropdownDemo', label: "dropdownDemo", type: 'dropdown', options: [{id:"1", label:"1"}], value: dropdownDemo, setValue: setDropdownDemo }, | |||||
| // { id: 'dateDemo', label:'dateDemo', type: 'date', value: dateDemo, setValue: setDateDemo }, | |||||
| // { id: 'checkboxDemo', label:'checkboxDemo', type: 'checkbox', value: checkboxDemo, setValue: setCheckboxDemo }, | |||||
| // { id: ['receiptFromDate','receiptToDate'], label: ["收貨日期","收貨日期"], value: [receiptFromDate ? receiptFromDate : null, receiptToDate ? receiptToDate : null], | |||||
| // setValue: [setReceiptFromDate, setReceiptToDate],type: 'dateRange' }, | |||||
| ]; | |||||
| const applySearch = (data: any) => { | |||||
| console.log(data) | |||||
| setSearchCriteria(data) | |||||
| } | |||||
| return ( | |||||
| <Grid item sm> | |||||
| <CustomSearchForm applySearch={applySearch} fields={InputFields}/> | |||||
| <CustomDatagrid Title={"工單工序詳情"} rows={rows} columns={columns} columnWidth={200} /> | |||||
| </Grid> | |||||
| ); | |||||
| }; | |||||
| export default ProgressByClient; | |||||