diff --git a/src/components/BMDashboard/ItemList/ItemListView.jsx b/src/components/BMDashboard/ItemList/ItemListView.jsx index 118146a9f5..677ae90af4 100644 --- a/src/components/BMDashboard/ItemList/ItemListView.jsx +++ b/src/components/BMDashboard/ItemList/ItemListView.jsx @@ -19,7 +19,7 @@ export function ItemListView({ children, }) { const darkMode = useSelector(state => state.theme.darkMode); - const [filteredItems, setFilteredItems] = useState(items); + const [filteredItems, setFilteredItems] = useState(items || []); const [selectedProject, setSelectedProject] = useState('all'); const [selectedItem, setSelectedItem] = useState('all'); const [isError, setIsError] = useState(false); @@ -55,7 +55,7 @@ export function ItemListView({ }, [selectedProject, selectedItem, items]); useEffect(() => { - setIsError(Object.entries(errors).length > 0); + setIsError(errors ? Object.keys(errors).length > 0 : false); }, [errors]); useEffect(() => { @@ -204,13 +204,25 @@ export function ItemListView({ )}
- - -
@@ -253,6 +265,7 @@ export function ItemListView({ UpdateItemModal={UpdateItemModal} dynamicColumns={dynamicColumns} darkMode={darkMode} + itemType={itemType} sortConfig={sortConfig} onSort={handleSort} totalItems={totalItems} @@ -293,7 +306,7 @@ ItemListView.propTypes = { errors: PropTypes.shape({ message: PropTypes.string, }), - UpdateItemModal: PropTypes.elementType.isRequired, + UpdateItemModal: PropTypes.elementType, dynamicColumns: PropTypes.arrayOf( PropTypes.shape({ label: PropTypes.string.isRequired, diff --git a/src/components/BMDashboard/ItemList/ItemsTable.jsx b/src/components/BMDashboard/ItemList/ItemsTable.jsx index 278962e77f..a880ebadbd 100644 --- a/src/components/BMDashboard/ItemList/ItemsTable.jsx +++ b/src/components/BMDashboard/ItemList/ItemsTable.jsx @@ -1,25 +1,18 @@ import { useState } from 'react'; -import { Table, Button } from 'reactstrap'; +import PropTypes from 'prop-types'; +import { Table, Button, Badge } from 'reactstrap'; import { BiPencil } from 'react-icons/bi'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSortDown, faSort, faSortUp } from '@fortawesome/free-solid-svg-icons'; import RecordsModal from './RecordsModal'; +import styles from './ItemListView.module.css'; const rowsPerPageOptions = [25, 50, 100]; function generatePageNumbers(current, total) { - if (total <= 7) { - return Array.from({ length: total }, (_, i) => i + 1); - } - - if (current <= 3) { - return [1, 2, 3, 4, 5, '...', total]; - } - - if (current >= total - 2) { - return [1, '...', total - 4, total - 3, total - 2, total - 1, total]; - } - + if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1); + if (current <= 3) return [1, 2, 3, 4, 5, '...', total]; + if (current >= total - 2) return [1, '...', total - 4, total - 3, total - 2, total - 1, total]; return [1, '...', current - 1, current, current + 1, '...', total]; } @@ -30,6 +23,7 @@ export default function ItemsTable({ UpdateItemModal, dynamicColumns, darkMode = false, + itemType, sortConfig, onSort, totalItems, @@ -60,9 +54,8 @@ export default function ItemsTable({ setRecordType(type); }; - const getNestedValue = (obj, path) => { - return path.split('.').reduce((acc, part) => (acc ? acc[part] : null), obj); - }; + const getNestedValue = (obj, path) => + path ? path.split('.').reduce((acc, part) => (acc ? acc[part] : null), obj) : null; const getIconFor = key => { if (!sortConfig?.key || sortConfig.key !== key) return faSort; @@ -77,6 +70,18 @@ export default function ItemsTable({ Hold: 'hold', }; + const numericKeys = new Set(['stockBought', 'stockUsed', 'stockAvailable', 'stockWasted']); + + const getColumnStyle = (key, isAction = false) => { + const base = { verticalAlign: 'middle' }; + if (key && numericKeys.has(key)) base.textAlign = 'right'; + if (isAction) { + base.borderLeft = '2px solid #dee2e6'; + base.textAlign = 'center'; + } + return base; + }; + return ( <> - + {UpdateItemModal && ( + + )}
- - - - - {dynamicColumns.map(({ label }) => { + {(dynamicColumns || []).map(({ label, key }) => { const sortKey = dynamicSortKeyByLabel[label]; const clickable = Boolean(sortKey); - return ( ); })} - - - - + + + - {filteredItems && filteredItems.length > 0 ? ( filteredItems.map(el => ( - - - - {dynamicColumns.map(({ label, key }) => ( - - ))} - - + + {(dynamicColumns || []).map(({ label, key }) => { + const value = getNestedValue(el, key); + if ( + key === 'stockAvailable' && + value !== null && + value !== undefined && + Number(value) < 10 + ) { + return ( + + ); + } + return ( + + ); + })} + - - - - @@ -206,16 +253,13 @@ export default function ItemsTable({ ))} -
{startRow}-{endRow} of {totalItems}
-
- - {generatePageNumbers(currentPage, totalPages).map((p, idx) => typeof p === 'number' ? ( -
onSort?.('project')} className={styles.sortableTh}> + onSort?.('project')} + className={styles.sortableTh} + style={{ verticalAlign: 'middle' }} + > Project onSort?.('name')} className={styles.sortableTh}> + onSort?.('name')} + className={styles.sortableTh} + style={{ verticalAlign: 'middle' }} + > Name onSort?.(sortKey) : undefined} className={clickable ? styles.sortableTh : undefined} + style={getColumnStyle(key)} > {label} {clickable && } Usage RecordUpdatesPurchases + Usage Record + + Updates + + Purchases +
{el.project?.name}{el.itemType?.name}{getNestedValue(el, key)} + {el.project?.name}{el.itemType?.name} + + Low + + {value} + + {value} + + + + No items data