@@ -16,7 +16,10 @@ | |||
"@mui/icons-material": "^5.15.0", | |||
"@mui/material": "^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", | |||
"dayjs": "^1.11.10", | |||
"i18next": "^23.7.11", | |||
"i18next-resources-to-backend": "^1.2.0", | |||
"next": "14.0.4", | |||
@@ -24,7 +27,8 @@ | |||
"react": "^18", | |||
"react-dom": "^18", | |||
"react-hook-form": "^7.49.2", | |||
"react-i18next": "^13.5.0" | |||
"react-i18next": "^13.5.0", | |||
"react-intl": "^6.5.5" | |||
}, | |||
"devDependencies": { | |||
"@types/node": "^20", | |||
@@ -371,6 +375,92 @@ | |||
"resolved": "https://registry.npmjs.org/@fontsource/plus-jakarta-sans/-/plus-jakarta-sans-5.0.18.tgz", | |||
"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": { | |||
"version": "0.11.13", | |||
"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", | |||
"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": { | |||
"version": "14.0.4", | |||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", | |||
@@ -1039,6 +1219,15 @@ | |||
"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": { | |||
"version": "0.0.29", | |||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", | |||
@@ -1885,6 +2074,11 @@ | |||
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", | |||
"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": { | |||
"version": "4.3.4", | |||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | |||
@@ -3257,6 +3451,17 @@ | |||
"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": { | |||
"version": "3.0.2", | |||
"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": { | |||
"version": "16.13.1", | |||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | |||
@@ -4780,6 +5011,11 @@ | |||
"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": { | |||
"version": "1.22.8", | |||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |||
@@ -5567,7 +5803,7 @@ | |||
"version": "5.3.3", | |||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", | |||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", | |||
"dev": true, | |||
"devOptional": true, | |||
"bin": { | |||
"tsc": "bin/tsc", | |||
"tsserver": "bin/tsserver" | |||
@@ -17,7 +17,10 @@ | |||
"@mui/icons-material": "^5.15.0", | |||
"@mui/material": "^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", | |||
"dayjs": "^1.11.10", | |||
"i18next": "^23.7.11", | |||
"i18next-resources-to-backend": "^1.2.0", | |||
"next": "14.0.4", | |||
@@ -25,7 +28,8 @@ | |||
"react": "^18", | |||
"react-dom": "^18", | |||
"react-hook-form": "^7.49.2", | |||
"react-i18next": "^13.5.0" | |||
"react-i18next": "^13.5.0", | |||
"react-intl": "^6.5.5" | |||
}, | |||
"devDependencies": { | |||
"@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 { useTranslation } from "react-i18next"; | |||
import PageTitle from "../PageTitle/PageTitle"; | |||
import ProgressByClient from "./ProgressByClient"; | |||
import '../../app/global.css'; | |||
const DashboardTabButton: React.FC = () => { | |||
@@ -16,8 +17,8 @@ const DashboardTabButton: React.FC = () => { | |||
return <div>Project Financial Summary</div>; | |||
case 'cashFlow': | |||
return <div>Project Cash Flow</div>; | |||
case 'projectProgress': | |||
return <div>Project Progress by Client</div>; | |||
case 'progressByClient': | |||
return <ProgressByClient/>; | |||
case 'resourceUtilization': | |||
return <div>Project Resource Utilization</div>; | |||
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; |