FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

PoInputGrid.tsx 20 KiB

6ヶ月前
6ヶ月前
6ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
7ヶ月前
6ヶ月前
7ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
7ヶ月前
6ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
7ヶ月前
7ヶ月前
6ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
6ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  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, 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 { QcItemWithChecks } from "src/app/api/qc";
  34. import PlayArrowIcon from "@mui/icons-material/PlayArrow";
  35. import { PurchaseOrderLine, StockInLine } from "@/app/api/po";
  36. import {
  37. createStockInLine,
  38. PurchaseQcResult,
  39. } from "@/app/api/po/actions";
  40. import { useSearchParams } from "next/navigation";
  41. import {
  42. returnWeightUnit,
  43. calculateWeight,
  44. stockInLineStatusMap,
  45. } from "@/app/utils/formatUtil";
  46. import PoQcStockInModal from "./PoQcStockInModal";
  47. import NotificationImportantIcon from "@mui/icons-material/NotificationImportant";
  48. import { WarehouseResult } from "@/app/api/warehouse";
  49. import LooksOneIcon from "@mui/icons-material/LooksOne";
  50. import LooksTwoIcon from "@mui/icons-material/LooksTwo";
  51. import Looks3Icon from "@mui/icons-material/Looks3";
  52. import axiosInstance from "@/app/(main)/axios/axiosInstance";
  53. // import axios, { AxiosRequestConfig } from "axios";
  54. import { BASE_API_URL, NEXT_PUBLIC_API_URL } from "@/config/api";
  55. import qs from "qs";
  56. import QrCodeIcon from "@mui/icons-material/QrCode";
  57. import { downloadFile } from "@/app/utils/commonUtil";
  58. import { fetchPoQrcode } from "@/app/api/pdf/actions";
  59. import { fetchQcResult } from "@/app/api/qc/actions";
  60. interface ResultWithId {
  61. id: number;
  62. }
  63. interface Props {
  64. qc: QcItemWithChecks[];
  65. setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
  66. setProcessedQty: Dispatch<SetStateAction<number>>;
  67. itemDetail: PurchaseOrderLine;
  68. stockInLine: StockInLine[];
  69. warehouse: WarehouseResult[];
  70. }
  71. export type StockInLineEntryError = {
  72. [field in keyof StockInLine]?: string;
  73. };
  74. export type StockInLineRow = Partial<
  75. StockInLine & {
  76. isActive: boolean | undefined;
  77. _isNew: boolean;
  78. _error: StockInLineEntryError;
  79. } & ResultWithId
  80. >;
  81. class ProcessRowUpdateError extends Error {
  82. public readonly row: StockInLineRow;
  83. public readonly errors: StockInLineEntryError | undefined;
  84. constructor(
  85. row: StockInLineRow,
  86. message?: string,
  87. errors?: StockInLineEntryError
  88. ) {
  89. super(message);
  90. this.row = row;
  91. this.errors = errors;
  92. Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
  93. }
  94. }
  95. function PoInputGrid({
  96. qc,
  97. setRows,
  98. setProcessedQty,
  99. itemDetail,
  100. stockInLine,
  101. warehouse,
  102. }: Props) {
  103. console.log(itemDetail);
  104. const { t } = useTranslation("home");
  105. const apiRef = useGridApiRef();
  106. const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
  107. const getRowId = useCallback<GridRowIdGetter<StockInLineRow>>(
  108. (row) => row.id as number,
  109. []
  110. );
  111. console.log(stockInLine);
  112. const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []);
  113. const [modalInfo, setModalInfo] = useState<StockInLine & {qcResult?: PurchaseQcResult[]}>();
  114. const [qcOpen, setQcOpen] = useState(false);
  115. const [escalOpen, setEscalOpen] = useState(false);
  116. const [stockInOpen, setStockInOpen] = useState(false);
  117. const [putAwayOpen, setPutAwayOpen] = useState(false);
  118. const [currQty, setCurrQty] = useState(() => {
  119. const total = entries.reduce(
  120. (acc, curr) => acc + (curr.acceptedQty || 0),
  121. 0
  122. );
  123. return total;
  124. });
  125. useEffect(() => {
  126. const completedList = entries.filter((e) => e.status === "completed");
  127. const processedQty = completedList.reduce(
  128. (acc, curr) => acc + (curr.acceptedQty || 0),
  129. 0
  130. );
  131. setProcessedQty(processedQty);
  132. }, [entries]);
  133. const handleDelete = useCallback(
  134. (id: GridRowId) => () => {
  135. setEntries((es) => es.filter((e) => getRowId(e) !== id));
  136. },
  137. [getRowId]
  138. );
  139. const handleStart = useCallback(
  140. (id: GridRowId, params: any) => () => {
  141. setRowModesModel((prev) => ({
  142. ...prev,
  143. [id]: { mode: GridRowModes.View },
  144. }));
  145. setTimeout(async () => {
  146. // post stock in line
  147. console.log("delayed");
  148. console.log(params);
  149. const oldId = params.row.id;
  150. console.log(oldId);
  151. console.log(params.row);
  152. const postData = {
  153. itemId: params.row.itemId,
  154. itemNo: params.row.itemNo,
  155. itemName: params.row.itemName,
  156. purchaseOrderId: params.row.purchaseOrderId,
  157. purchaseOrderLineId: params.row.purchaseOrderLineId,
  158. acceptedQty: params.row.acceptedQty,
  159. };
  160. const res = await createStockInLine(postData);
  161. console.log(res);
  162. setEntries((prev) =>
  163. prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p))
  164. );
  165. // do post directly to test
  166. // openStartModal();
  167. }, 200);
  168. },
  169. [createStockInLine]
  170. );
  171. const fetchQcDefaultValue = useCallback(async () => {
  172. // const authHeader = axiosInstance.defaults.headers['Authorization'];
  173. // if (!authHeader) {
  174. // return; // Exit the function if the token is not set
  175. // }
  176. // console.log(authHeader)
  177. // console.log(NEXT_PUBLIC_API_URL)
  178. // const res = await axiosInstance.get<QcItemWithChecks[]>(`${NEXT_PUBLIC_API_URL}/qcResult/${itemDetail.id}`)
  179. // const res = await testFetch2(itemDetail.id)
  180. const res = await fetchQcResult(itemDetail.id);
  181. console.log(res);
  182. return res
  183. }, [axiosInstance]);
  184. const handleQC = useCallback(
  185. (id: GridRowId, params: any) => async () => {
  186. setRowModesModel((prev) => ({
  187. ...prev,
  188. [id]: { mode: GridRowModes.View },
  189. }));
  190. const qcResult = await fetchQcDefaultValue();
  191. console.log(qcResult)
  192. setModalInfo({
  193. ...params.row,
  194. qcResult: qcResult
  195. });
  196. // set default values
  197. setTimeout(() => {
  198. // open qc modal
  199. console.log("delayed");
  200. openQcModal();
  201. }, 200);
  202. },
  203. [fetchQcDefaultValue]
  204. );
  205. const handleEscalation = useCallback(
  206. (id: GridRowId, params: any) => () => {
  207. setRowModesModel((prev) => ({
  208. ...prev,
  209. [id]: { mode: GridRowModes.View },
  210. }));
  211. setModalInfo(params.row);
  212. setTimeout(() => {
  213. // open qc modal
  214. console.log("delayed");
  215. openEscalationModal();
  216. }, 200);
  217. },
  218. []
  219. );
  220. const handleStockIn = useCallback(
  221. (id: GridRowId, params: any) => () => {
  222. setRowModesModel((prev) => ({
  223. ...prev,
  224. [id]: { mode: GridRowModes.View },
  225. }));
  226. setModalInfo(params.row);
  227. setTimeout(() => {
  228. // open stock in modal
  229. openStockInModal();
  230. // return the record with its status as pending
  231. // update layout
  232. console.log("delayed");
  233. }, 200);
  234. },
  235. []
  236. );
  237. const handlePutAway = useCallback(
  238. (id: GridRowId, params: any) => () => {
  239. setRowModesModel((prev) => ({
  240. ...prev,
  241. [id]: { mode: GridRowModes.View },
  242. }));
  243. setModalInfo(params.row);
  244. setTimeout(() => {
  245. // open stock in modal
  246. openPutAwayModal();
  247. // return the record with its status as pending
  248. // update layout
  249. console.log("delayed");
  250. }, 200);
  251. },
  252. []
  253. );
  254. const printQrcode = useCallback(
  255. async (row: any) => {
  256. console.log(row.id);
  257. const postData = { stockInLineIds: [row.id] };
  258. // const postData = { stockInLineIds: [42,43,44] };
  259. const response = await fetchPoQrcode(postData);
  260. if (response) {
  261. console.log(response);
  262. downloadFile(new Uint8Array(response.blobValue), response.filename!!);
  263. }
  264. },
  265. [fetchPoQrcode, downloadFile]
  266. );
  267. const handleQrCode = useCallback(
  268. (id: GridRowId, params: any) => () => {
  269. setRowModesModel((prev) => ({
  270. ...prev,
  271. [id]: { mode: GridRowModes.View },
  272. }));
  273. setModalInfo(params.row);
  274. setTimeout(() => {
  275. // open stock in modal
  276. // openPutAwayModal();
  277. // return the record with its status as pending
  278. // update layout
  279. console.log("delayed");
  280. printQrcode(params.row);
  281. }, 200);
  282. },
  283. []
  284. );
  285. const closeQcModal = useCallback(() => {
  286. setQcOpen(false);
  287. }, []);
  288. const openQcModal = useCallback(() => {
  289. setQcOpen(true);
  290. }, []);
  291. const closeStockInModal = useCallback(() => {
  292. setStockInOpen(false);
  293. }, []);
  294. const openStockInModal = useCallback(() => {
  295. setStockInOpen(true);
  296. }, []);
  297. const closePutAwayModal = useCallback(() => {
  298. setPutAwayOpen(false);
  299. }, []);
  300. const openPutAwayModal = useCallback(() => {
  301. setPutAwayOpen(true);
  302. }, []);
  303. const closeEscalationModal = useCallback(() => {
  304. setEscalOpen(false);
  305. }, []);
  306. const openEscalationModal = useCallback(() => {
  307. setEscalOpen(true);
  308. }, []);
  309. const columns = useMemo<GridColDef[]>(
  310. () => [
  311. {
  312. field: "itemNo",
  313. flex: 0.8,
  314. },
  315. {
  316. field: "itemName",
  317. flex: 1,
  318. },
  319. {
  320. field: "acceptedQty",
  321. headerName: "qty",
  322. flex: 0.5,
  323. type: "number",
  324. editable: true,
  325. // replace with tooltip + content
  326. },
  327. {
  328. field: "uom",
  329. headerName: "uom",
  330. flex: 0.5,
  331. renderCell: (params) => {
  332. return params.row.uom.code;
  333. },
  334. },
  335. {
  336. field: "weight",
  337. headerName: "weight",
  338. flex: 0.5,
  339. renderCell: (params) => {
  340. const weight = calculateWeight(
  341. params.row.acceptedQty,
  342. params.row.uom
  343. );
  344. const weightUnit = returnWeightUnit(params.row.uom);
  345. return `${weight} ${weightUnit}`;
  346. },
  347. },
  348. {
  349. field: "status",
  350. flex: 0.5,
  351. // editable: true,
  352. },
  353. {
  354. field: "actions",
  355. type: "actions",
  356. headerName: "start | qc | escalation | stock in | putaway | delete",
  357. flex: 1,
  358. cellClassName: "actions",
  359. getActions: (params) => {
  360. console.log(params.row.status);
  361. const status = params.row.status.toLowerCase();
  362. return [
  363. <GridActionsCellItem
  364. icon={<PlayArrowIcon />}
  365. label="start"
  366. sx={{
  367. color: "primary.main",
  368. // marginRight: 1,
  369. }}
  370. disabled={!(stockInLineStatusMap[status] === 0)}
  371. // set _isNew to false after posting
  372. // or check status
  373. onClick={handleStart(params.row.id, params)}
  374. color="inherit"
  375. key="edit"
  376. />,
  377. <GridActionsCellItem
  378. icon={<FactCheckIcon />}
  379. label="qc"
  380. sx={{
  381. color: "primary.main",
  382. // marginRight: 1,
  383. }}
  384. disabled={stockInLineStatusMap[status] < 1}
  385. // set _isNew to false after posting
  386. // or check status
  387. onClick={handleQC(params.row.id, params)}
  388. color="inherit"
  389. key="edit"
  390. />,
  391. <GridActionsCellItem
  392. icon={<NotificationImportantIcon />}
  393. label="escalation"
  394. sx={{
  395. color: "primary.main",
  396. // marginRight: 1,
  397. }}
  398. disabled={
  399. stockInLineStatusMap[status] <= 0 ||
  400. stockInLineStatusMap[status] >= 5
  401. }
  402. // set _isNew to false after posting
  403. // or check status
  404. onClick={handleEscalation(params.row.id, params)}
  405. color="inherit"
  406. key="edit"
  407. />,
  408. <GridActionsCellItem
  409. icon={<ShoppingCartIcon />}
  410. label="stockin"
  411. sx={{
  412. color: "primary.main",
  413. // marginRight: 1,
  414. }}
  415. disabled={stockInLineStatusMap[status] !== 6}
  416. // set _isNew to false after posting
  417. // or check status
  418. onClick={handleStockIn(params.row.id, params)}
  419. color="inherit"
  420. key="edit"
  421. />,
  422. <GridActionsCellItem
  423. icon={<ShoppingCartIcon />}
  424. label="putaway"
  425. sx={{
  426. color: "primary.main",
  427. // marginRight: 1,
  428. }}
  429. disabled={stockInLineStatusMap[status] < 7}
  430. // set _isNew to false after posting
  431. // or check status
  432. onClick={handlePutAway(params.row.id, params)}
  433. color="inherit"
  434. key="edit"
  435. />,
  436. <GridActionsCellItem
  437. icon={<QrCodeIcon />}
  438. label="putaway"
  439. sx={{
  440. color: "primary.main",
  441. // marginRight: 1,
  442. }}
  443. disabled={stockInLineStatusMap[status] !== 8}
  444. // set _isNew to false after posting
  445. // or check status
  446. onClick={handleQrCode(params.row.id, params)}
  447. color="inherit"
  448. key="edit"
  449. />,
  450. <GridActionsCellItem
  451. icon={<DeleteIcon />}
  452. label="Delete"
  453. sx={{
  454. color: "error.main",
  455. }}
  456. disabled={stockInLineStatusMap[status] !== 0}
  457. // disabled={Boolean(params.row.status)}
  458. onClick={handleDelete(params.row.id)}
  459. color="inherit"
  460. key="edit"
  461. />,
  462. ];
  463. },
  464. },
  465. ],
  466. []
  467. );
  468. const addRow = useCallback(() => {
  469. console.log(itemDetail);
  470. const newEntry = {
  471. id: Date.now(),
  472. _isNew: true,
  473. itemId: itemDetail.itemId,
  474. purchaseOrderId: itemDetail.purchaseOrderId,
  475. purchaseOrderLineId: itemDetail.id,
  476. itemNo: itemDetail.itemNo,
  477. itemName: itemDetail.itemName,
  478. acceptedQty: itemDetail.qty - currQty, // this bug
  479. uom: itemDetail.uom,
  480. status: "draft",
  481. };
  482. setEntries((e) => [...e, newEntry]);
  483. setRowModesModel((model) => ({
  484. ...model,
  485. [getRowId(newEntry)]: {
  486. mode: GridRowModes.Edit,
  487. // fieldToFocus: "projectId",
  488. },
  489. }));
  490. }, [currQty, getRowId]);
  491. const validation = useCallback(
  492. (
  493. newRow: GridRowModel<StockInLineRow>
  494. // rowModel: GridRowSelectionModel
  495. ): StockInLineEntryError | undefined => {
  496. const error: StockInLineEntryError = {};
  497. console.log(newRow);
  498. console.log(currQty);
  499. if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) {
  500. error["acceptedQty"] = "qty cannot be greater than remaining qty";
  501. }
  502. return Object.keys(error).length > 0 ? error : undefined;
  503. },
  504. [currQty]
  505. );
  506. const processRowUpdate = useCallback(
  507. (
  508. newRow: GridRowModel<StockInLineRow>,
  509. originalRow: GridRowModel<StockInLineRow>
  510. ) => {
  511. const errors = validation(newRow); // change to validation
  512. if (errors) {
  513. throw new ProcessRowUpdateError(
  514. originalRow,
  515. "validation error",
  516. errors
  517. );
  518. }
  519. const { _isNew, _error, ...updatedRow } = newRow;
  520. const rowToSave = {
  521. ...updatedRow,
  522. } satisfies StockInLineRow;
  523. const newEntries = entries.map((e) =>
  524. getRowId(e) === getRowId(originalRow) ? rowToSave : e
  525. );
  526. setEntries(newEntries);
  527. //update remaining qty
  528. const total = newEntries.reduce(
  529. (acc, curr) => acc + (curr.acceptedQty || 0),
  530. 0
  531. );
  532. setCurrQty(total);
  533. return rowToSave;
  534. },
  535. [getRowId, entries]
  536. );
  537. const onProcessRowUpdateError = useCallback(
  538. (updateError: ProcessRowUpdateError) => {
  539. const errors = updateError.errors;
  540. const oldRow = updateError.row;
  541. apiRef.current.updateRows([{ ...oldRow, _error: errors }]);
  542. },
  543. [apiRef]
  544. );
  545. // useEffect(() => {
  546. // const total = entries.reduce(
  547. // (acc, curr) => acc + (curr.acceptedQty || 0),
  548. // 0
  549. // );
  550. // setDefaultQty(itemDetail.qty - total);
  551. // }, [entries]);
  552. const footer = (
  553. <Box display="flex" gap={2} alignItems="center">
  554. <Button
  555. disableRipple
  556. variant="outlined"
  557. startIcon={<Add />}
  558. disabled={itemDetail.qty - currQty <= 0}
  559. onClick={addRow}
  560. size="small"
  561. >
  562. {t("Record pol")}
  563. </Button>
  564. </Box>
  565. );
  566. return (
  567. <>
  568. <StyledDataGrid
  569. getRowId={getRowId}
  570. apiRef={apiRef}
  571. autoHeight
  572. sx={{
  573. "--DataGrid-overlayHeight": "100px",
  574. ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
  575. border: "1px solid",
  576. borderColor: "error.main",
  577. },
  578. ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
  579. border: "1px solid",
  580. borderColor: "warning.main",
  581. },
  582. }}
  583. disableColumnMenu
  584. editMode="row"
  585. rows={entries}
  586. rowModesModel={rowModesModel}
  587. onRowModesModelChange={setRowModesModel}
  588. processRowUpdate={processRowUpdate}
  589. onProcessRowUpdateError={onProcessRowUpdateError}
  590. columns={columns}
  591. isCellEditable={(params) => {
  592. const status = params.row.status.toLowerCase();
  593. return (
  594. stockInLineStatusMap[status] >= 0 ||
  595. stockInLineStatusMap[status] <= 1
  596. );
  597. }}
  598. getCellClassName={(params: GridCellParams<StockInLineRow>) => {
  599. let classname = "";
  600. if (params.row._error) {
  601. classname = "hasError";
  602. }
  603. return classname;
  604. }}
  605. slots={{
  606. footer: FooterToolbar,
  607. noRowsOverlay: NoRowsOverlay,
  608. }}
  609. slotProps={{
  610. footer: { child: footer },
  611. }}
  612. />
  613. <>
  614. <PoQcStockInModal
  615. type={"qc"}
  616. setEntries={setEntries}
  617. qc={qc}
  618. open={qcOpen}
  619. onClose={closeQcModal}
  620. itemDetail={modalInfo!!}
  621. />
  622. </>
  623. <>
  624. <PoQcStockInModal
  625. type={"escalation"}
  626. setEntries={setEntries}
  627. // qc={qc}
  628. open={escalOpen}
  629. onClose={closeEscalationModal}
  630. itemDetail={modalInfo!!}
  631. />
  632. </>
  633. <>
  634. <PoQcStockInModal
  635. type={"stockIn"}
  636. setEntries={setEntries}
  637. // qc={qc}
  638. open={stockInOpen}
  639. onClose={closeStockInModal}
  640. itemDetail={modalInfo!!}
  641. />
  642. </>
  643. <>
  644. <PoQcStockInModal
  645. type={"putaway"}
  646. setEntries={setEntries}
  647. open={putAwayOpen}
  648. warehouse={warehouse}
  649. onClose={closePutAwayModal}
  650. itemDetail={modalInfo!!}
  651. />
  652. </>
  653. </>
  654. );
  655. }
  656. const NoRowsOverlay: React.FC = () => {
  657. const { t } = useTranslation("home");
  658. return (
  659. <Box
  660. display="flex"
  661. justifyContent="center"
  662. alignItems="center"
  663. height="100%"
  664. >
  665. <Typography variant="caption">{t("Add some entries!")}</Typography>
  666. </Box>
  667. );
  668. };
  669. const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
  670. return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
  671. };
  672. export default PoInputGrid;