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.
 
 

168 lines
5.1 KiB

  1. 'use client';
  2. import { fetchItemUomConversions, ItemUomConversionRow } from '@/app/api/inventory/actions';
  3. import { InventoryResult } from '@/app/api/inventory';
  4. import { useEffect, useRef, useState } from 'react';
  5. import { useTranslation } from 'react-i18next';
  6. import {
  7. Box,
  8. Checkbox,
  9. CircularProgress,
  10. Paper,
  11. Table,
  12. TableBody,
  13. TableCell,
  14. TableContainer,
  15. TableHead,
  16. TableRow,
  17. Typography,
  18. } from '@mui/material';
  19. interface Props {
  20. inventory: InventoryResult | null;
  21. }
  22. const formatRatio = (value: number) => {
  23. if (!Number.isFinite(value)) return '';
  24. return String(value);
  25. };
  26. const cellSx = {
  27. px: 1.25,
  28. py: 0.75,
  29. fontSize: 13,
  30. lineHeight: 1.4,
  31. } as const;
  32. const numSx = {
  33. ...cellSx,
  34. width: '9%',
  35. whiteSpace: 'nowrap',
  36. fontVariantNumeric: 'tabular-nums',
  37. } as const;
  38. const flagSx = {
  39. ...cellSx,
  40. width: '8%',
  41. textAlign: 'center',
  42. } as const;
  43. const FlagCell = ({ checked }: { checked: boolean }) => (
  44. <TableCell align="center" padding="checkbox" sx={flagSx}>
  45. <Checkbox size="small" checked={checked} disabled sx={{ p: 0 }} />
  46. </TableCell>
  47. );
  48. const InventoryItemUom: React.FC<Props> = ({ inventory }) => {
  49. const { t } = useTranslation('inventory');
  50. const [rows, setRows] = useState<ItemUomConversionRow[]>([]);
  51. const [loading, setLoading] = useState(false);
  52. const requestRef = useRef(0);
  53. const itemId = inventory?.itemId;
  54. useEffect(() => {
  55. if (itemId == null) {
  56. setRows([]);
  57. setLoading(false);
  58. return;
  59. }
  60. const requestId = ++requestRef.current;
  61. setLoading(true);
  62. fetchItemUomConversions(itemId)
  63. .then((res) => {
  64. if (requestId !== requestRef.current) return;
  65. setRows(res ?? []);
  66. })
  67. .catch((error) => {
  68. if (requestId !== requestRef.current) return;
  69. console.error('Failed to load item unit conversions:', error);
  70. setRows([]);
  71. })
  72. .finally(() => {
  73. if (requestId === requestRef.current) setLoading(false);
  74. });
  75. }, [itemId]);
  76. if (!inventory || itemId == null) {
  77. return <Typography sx={{ mt: 2 }}>{t('No items are selected yet.')}</Typography>;
  78. }
  79. const flags = [
  80. t('Base Unit'),
  81. t('Stock Unit'),
  82. t('Picking Unit'),
  83. t('Sales Unit'),
  84. t('Purchase Unit'),
  85. t('Inactive'),
  86. ];
  87. return (
  88. <Box sx={{ mt: 1 }}>
  89. <Typography variant="subtitle1" sx={{ mb: 1, fontWeight: 600 }}>
  90. {`${t('Item selected')}: ${inventory.itemCode} | ${inventory.itemName} (${t(inventory.itemType)})`}
  91. </Typography>
  92. {loading ? (
  93. <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
  94. <CircularProgress size={28} />
  95. </Box>
  96. ) : (
  97. <TableContainer component={Paper} variant="outlined" sx={{ maxHeight: 440, width: '100%' }}>
  98. <Table size="small" stickyHeader sx={{ width: '100%', tableLayout: 'fixed' }}>
  99. <colgroup>
  100. <col style={{ width: '14%' }} />
  101. <col style={{ width: '22%' }} />
  102. <col style={{ width: '9%' }} />
  103. <col style={{ width: '9%' }} />
  104. <col style={{ width: '8%' }} />
  105. <col style={{ width: '8%' }} />
  106. <col style={{ width: '8%' }} />
  107. <col style={{ width: '8%' }} />
  108. <col style={{ width: '8%' }} />
  109. <col style={{ width: '6%' }} />
  110. </colgroup>
  111. <TableHead>
  112. <TableRow>
  113. <TableCell sx={cellSx}>{t('Unit')}</TableCell>
  114. <TableCell sx={cellSx}>{t('Unit Description')}</TableCell>
  115. <TableCell align="right" sx={numSx}>{t('Ratio Numerator')}</TableCell>
  116. <TableCell align="right" sx={numSx}>{t('Ratio Denominator')}</TableCell>
  117. {flags.map((label) => (
  118. <TableCell key={label} align="center" sx={flagSx}>
  119. {label}
  120. </TableCell>
  121. ))}
  122. </TableRow>
  123. </TableHead>
  124. <TableBody>
  125. {rows.length === 0 ? (
  126. <TableRow>
  127. <TableCell colSpan={10} align="center">
  128. {t('No data')}
  129. </TableCell>
  130. </TableRow>
  131. ) : (
  132. rows.map((row) => (
  133. <TableRow key={row.id} hover>
  134. <TableCell sx={cellSx}>{row.unitCode}</TableCell>
  135. <TableCell sx={cellSx}>{row.unitDesc}</TableCell>
  136. <TableCell align="right" sx={numSx}>{formatRatio(row.ratioN)}</TableCell>
  137. <TableCell align="right" sx={numSx}>{formatRatio(row.ratioD)}</TableCell>
  138. <FlagCell checked={row.baseUnit} />
  139. <FlagCell checked={row.stockUnit} />
  140. <FlagCell checked={row.pickingUnit} />
  141. <FlagCell checked={row.salesUnit} />
  142. <FlagCell checked={row.purchaseUnit} />
  143. <FlagCell checked={row.inactive} />
  144. </TableRow>
  145. ))
  146. )}
  147. </TableBody>
  148. </Table>
  149. </TableContainer>
  150. )}
  151. </Box>
  152. );
  153. };
  154. export default InventoryItemUom;