@@ -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; |