FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

126 lines
3.6 KiB

  1. 'use client';
  2. import { fetchItemMovements, ItemMovementRow } from '@/app/api/inventory/actions';
  3. import { InventoryResult } from '@/app/api/inventory';
  4. import { useEffect, useMemo, useRef, useState } from 'react';
  5. import { useTranslation } from 'react-i18next';
  6. import { Box, CircularProgress, Typography } from '@mui/material';
  7. import { Column } from '../SearchResults';
  8. import SearchResults, { defaultPagingController } from '../SearchResults/SearchResults';
  9. interface Props {
  10. inventory: InventoryResult | null;
  11. }
  12. const InventoryItemLedger: React.FC<Props> = ({ inventory }) => {
  13. const { t } = useTranslation('inventory');
  14. const [rows, setRows] = useState<ItemMovementRow[]>([]);
  15. const [total, setTotal] = useState(0);
  16. const [loading, setLoading] = useState(false);
  17. const [pagingController, setPagingController] = useState(defaultPagingController);
  18. const requestRef = useRef(0);
  19. const itemId = inventory?.itemId;
  20. useEffect(() => {
  21. if (itemId == null) {
  22. setRows([]);
  23. setTotal(0);
  24. setLoading(false);
  25. return;
  26. }
  27. const requestId = ++requestRef.current;
  28. setLoading(true);
  29. fetchItemMovements({
  30. itemId,
  31. pageNum: pagingController.pageNum - 1,
  32. pageSize: pagingController.pageSize,
  33. })
  34. .then((res) => {
  35. if (requestId !== requestRef.current) return;
  36. setRows(res?.records ?? []);
  37. setTotal(res?.total ?? 0);
  38. })
  39. .catch((error) => {
  40. if (requestId !== requestRef.current) return;
  41. console.error('Failed to load item movements:', error);
  42. setRows([]);
  43. setTotal(0);
  44. })
  45. .finally(() => {
  46. if (requestId === requestRef.current) setLoading(false);
  47. });
  48. }, [itemId, pagingController]);
  49. const columns = useMemo<Column<ItemMovementRow>[]>(
  50. () => [
  51. { name: 'trnDate', label: t('Transaction Date') },
  52. {
  53. name: 'type',
  54. label: t('Type'),
  55. renderCell: (row) => (row.type ? t(row.type) : ''),
  56. },
  57. { name: 'lotNo', label: t('Lot No') },
  58. { name: 'expiryDate', label: t('Expiry Date') },
  59. {
  60. name: 'openingQty',
  61. label: t('Opening Qty'),
  62. align: 'right',
  63. headerAlign: 'right',
  64. type: 'integer',
  65. },
  66. {
  67. name: 'inQty',
  68. label: t('In Qty'),
  69. align: 'right',
  70. headerAlign: 'right',
  71. type: 'integer',
  72. },
  73. {
  74. name: 'outQty',
  75. label: t('Out Qty'),
  76. align: 'right',
  77. headerAlign: 'right',
  78. type: 'integer',
  79. },
  80. {
  81. name: 'balanceQty',
  82. label: t('Balance Qty'),
  83. align: 'right',
  84. headerAlign: 'right',
  85. type: 'integer',
  86. },
  87. { name: 'orderRefNo', label: t('Reference No') },
  88. { name: 'storeLocation', label: t('Store Location') },
  89. ],
  90. [t],
  91. );
  92. if (!inventory || itemId == null) {
  93. return <Typography sx={{ mt: 2 }}>{t('No items are selected yet.')}</Typography>;
  94. }
  95. return (
  96. <Box sx={{ mt: 1 }}>
  97. <Typography variant="h6" sx={{ mb: 1 }}>
  98. {`${t('Item selected')}: ${inventory.itemCode} | ${inventory.itemName} (${t(inventory.itemType)})`}
  99. </Typography>
  100. {loading && rows.length === 0 ? (
  101. <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
  102. <CircularProgress size={28} />
  103. </Box>
  104. ) : (
  105. <SearchResults<ItemMovementRow>
  106. items={rows}
  107. columns={columns}
  108. pagingController={pagingController}
  109. setPagingController={setPagingController}
  110. totalCount={total}
  111. isAutoPaging={false}
  112. />
  113. )}
  114. </Box>
  115. );
  116. };
  117. export default InventoryItemLedger;