FPSMS-frontend
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

256 linhas
6.9 KiB

  1. "use client";
  2. import {
  3. FooterPropsOverrides,
  4. GridActionsCellItem,
  5. GridCellParams,
  6. GridRowId,
  7. GridRowIdGetter,
  8. GridRowModel,
  9. GridRowModes,
  10. GridRowModesModel,
  11. GridToolbarContainer,
  12. useGridApiRef,
  13. } from "@mui/x-data-grid";
  14. import {
  15. Dispatch,
  16. MutableRefObject,
  17. SetStateAction,
  18. useCallback,
  19. useEffect,
  20. useMemo,
  21. useState,
  22. } from "react";
  23. import StyledDataGrid from "../StyledDataGrid";
  24. import { GridColDef } from "@mui/x-data-grid";
  25. import { Box, Button, Grid, Icon, Typography } from "@mui/material";
  26. import { useTranslation } from "react-i18next";
  27. import { Add } from "@mui/icons-material";
  28. import SaveIcon from "@mui/icons-material/Save";
  29. import DeleteIcon from "@mui/icons-material/Delete";
  30. import CancelIcon from "@mui/icons-material/Cancel";
  31. import FactCheckIcon from "@mui/icons-material/FactCheck";
  32. import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
  33. // import PoQcModal from "./PoQcModal";
  34. import PlayArrowIcon from "@mui/icons-material/PlayArrow";
  35. import { useSearchParams } from "next/navigation";
  36. import { decimalFormatter } from "@/app/utils/formatUtil";
  37. import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
  38. import HighlightOffIcon from '@mui/icons-material/HighlightOff';
  39. import { ProdScheduleLineBomMaterialResult, ScheduleType } from "@/app/api/scheduling";
  40. interface ResultWithId {
  41. id: number;
  42. }
  43. interface Props {
  44. bomMaterial: ProdScheduleLineBomMaterialResult[];
  45. type: ScheduleType
  46. }
  47. export type BomMaterialEntryError = {
  48. [field in keyof any]?: string;
  49. };
  50. export type BomMaterialRow = Partial<
  51. any & {
  52. isActive: boolean | undefined;
  53. _isNew: boolean;
  54. _error: BomMaterialEntryError;
  55. } & ResultWithId
  56. >;
  57. class ProcessRowUpdateError extends Error {
  58. public readonly row: BomMaterialRow;
  59. public readonly errors: BomMaterialEntryError | undefined;
  60. constructor(row: BomMaterialRow, message?: string, errors?: BomMaterialEntryError) {
  61. super(message);
  62. this.row = row;
  63. this.errors = errors;
  64. Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
  65. }
  66. }
  67. function BomMaterialTable({ bomMaterial }: Props) {
  68. const { t } = useTranslation("schedule");
  69. const apiRef = useGridApiRef();
  70. const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
  71. const getRowId = useCallback<GridRowIdGetter<BomMaterialRow>>(
  72. (row) => row.id as number,
  73. []
  74. );
  75. const [entries, setEntries] = useState<BomMaterialRow[]>(bomMaterial || []);
  76. const columns = useMemo<GridColDef[]>(
  77. () => [
  78. {
  79. field: "code",
  80. headerName: t("Code"),
  81. flex: 1,
  82. },
  83. {
  84. field: "name",
  85. headerName: t("Name"),
  86. flex: 1,
  87. },
  88. {
  89. field: "type",
  90. headerName: t("type"),
  91. flex: 1,
  92. renderCell: (row) => {
  93. return t(row.value)
  94. }
  95. },
  96. {
  97. field: "availableQty",
  98. headerName: t("Available Qty"),
  99. flex: 0.5,
  100. type: "number",
  101. editable: true,
  102. align: "right",
  103. headerAlign: "right",
  104. renderCell: (row) => {
  105. return decimalFormatter.format(row.value)
  106. }
  107. // replace with tooltip + content
  108. },
  109. {
  110. field: "demandQty",
  111. headerName: t("Demand Qty"),
  112. flex: 0.5,
  113. editable: true,
  114. align: "right",
  115. headerAlign: "right",
  116. renderCell: (row) => {
  117. return decimalFormatter.format(row.value)
  118. }
  119. },
  120. {
  121. field: "status",
  122. headerName: t("status"),
  123. flex: 0.5,
  124. editable: true,
  125. align: "center",
  126. headerAlign: "center",
  127. renderCell: (params) => {
  128. return params.row.availableQty - params.row.demandQty >= 0 ?
  129. <CheckCircleOutlineIcon
  130. color="success"
  131. fontSize="small"
  132. /> :
  133. <HighlightOffIcon
  134. color="error"
  135. fontSize="small"
  136. />
  137. }
  138. },
  139. ],
  140. []
  141. );
  142. const validation = useCallback(
  143. (
  144. newRow: GridRowModel<BomMaterialRow>
  145. ): BomMaterialEntryError | undefined => {
  146. const error: BomMaterialEntryError = {};
  147. console.log(newRow);
  148. return Object.keys(error).length > 0 ? error : undefined;
  149. },
  150. []
  151. );
  152. const processRowUpdate = useCallback(
  153. (newRow: GridRowModel<BomMaterialRow>, originalRow: GridRowModel<BomMaterialRow>) => {
  154. const errors = validation(newRow); // change to validation
  155. if (errors) {
  156. throw new ProcessRowUpdateError(
  157. originalRow,
  158. "validation error",
  159. errors
  160. );
  161. }
  162. const { _isNew, _error, ...updatedRow } = newRow;
  163. const rowToSave = {
  164. ...updatedRow,
  165. } satisfies BomMaterialRow;
  166. const newEntries = entries.map((e) =>
  167. getRowId(e) === getRowId(originalRow) ? rowToSave : e
  168. );
  169. setEntries(newEntries);
  170. //update remaining qty
  171. const total = newEntries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0);
  172. return rowToSave;
  173. },
  174. [getRowId, entries]
  175. );
  176. const onProcessRowUpdateError = useCallback(
  177. (updateError: ProcessRowUpdateError) => {
  178. const errors = updateError.errors;
  179. const oldRow = updateError.row;
  180. apiRef.current.updateRows([{ ...oldRow, _error: errors }]);
  181. },
  182. [apiRef]
  183. );
  184. return (
  185. <>
  186. <StyledDataGrid
  187. getRowId={getRowId}
  188. apiRef={apiRef}
  189. autoHeight
  190. sx={{
  191. "--DataGrid-overlayHeight": "100px",
  192. ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
  193. border: "1px solid",
  194. borderColor: "error.main",
  195. },
  196. ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
  197. border: "1px solid",
  198. borderColor: "warning.main",
  199. },
  200. }}
  201. disableColumnMenu
  202. editMode="row"
  203. rows={entries}
  204. rowModesModel={rowModesModel}
  205. onRowModesModelChange={setRowModesModel}
  206. processRowUpdate={processRowUpdate}
  207. onProcessRowUpdateError={onProcessRowUpdateError}
  208. columns={columns}
  209. getCellClassName={(params: GridCellParams<BomMaterialRow>) => {
  210. let classname = "";
  211. if (params.row._error) {
  212. classname = "hasError";
  213. }
  214. return classname;
  215. }}
  216. // slots={{
  217. // footer: FooterToolbar,
  218. // noRowsOverlay: NoRowsOverlay,
  219. // }}
  220. // slotProps={{
  221. // footer: { child: footer },
  222. // }}
  223. />
  224. </>
  225. );
  226. }
  227. const NoRowsOverlay: React.FC = () => {
  228. const { t } = useTranslation("home");
  229. return (
  230. <Box
  231. display="flex"
  232. justifyContent="center"
  233. alignItems="center"
  234. height="100%"
  235. >
  236. <Typography variant="caption">{t("Add some entries!")}</Typography>
  237. </Box>
  238. );
  239. };
  240. const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
  241. return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
  242. };
  243. export default BomMaterialTable;