Skip to content

Commit

Permalink
Merge pull request #1758 from OneCommunityGlobal/vishala_comsumables_…
Browse files Browse the repository at this point in the history
…view

Vishala Consumables Page View
  • Loading branch information
tdkent authored Jan 7, 2024
2 parents 3dba53d + e47436c commit 95db05d
Show file tree
Hide file tree
Showing 12 changed files with 565 additions and 5 deletions.
30 changes: 30 additions & 0 deletions src/actions/bmdashboard/consumableActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import axios from "axios";
import { SET_CONSUMABLES } from "constants/bmdashboard/consumableConstants";
import { GET_ERRORS } from "constants/errors";
import { ENDPOINTS } from "utils/URL";

export const setConsumables = payload => {
return {
type: SET_CONSUMABLES,
payload
}
}

export const setErrors = payload => {
return {
type: GET_ERRORS,
payload
}
}

export const fetchAllConsumables = () => {
return async dispatch => {
axios.get(ENDPOINTS.BM_CONSUMABLES)
.then(res => {
dispatch(setConsumables(res.data))
})
.catch(err => {
dispatch(setErrors(err))
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.PageViewContainer {
padding: 0% !important;

}

.Page {
background-color: #E8F4F9;
width: 100%;
height: auto;
margin: 0px;
padding: 1rem 2rem;
font-size: 15px;
}

.Box {
background-color: white;
border-radius: 1rem;
padding: 1.5rem;
height: auto;
}

.modal-open {
padding-right: 0px !important;
}

.ModalViewContainer {
overflow-y: auto;
overflow-x: auto;
max-height: 75vh;
font-size: small;
}

@media (min-width: 1200px) {
.ModalViewContainer {
font-size: medium;
}
}

.InputsMargin {
margin: 0.2rem;
}

.cusorpointer {
cursor: pointer;
}

.BuildingTableHeaderLine {
font-weight: 500;
vertical-align: bottom;
border-bottom: 2px solid #dee2e6;
background-color: #E8F4F9;
border-top: 1px solid #dee2e6;
}

.BuildingTitle {
font-weight: bold;
margin-bottom: 20px;
text-align: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Label, Form, Row, Col } from 'reactstrap';
import { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Select from 'react-select';
import { fetchBMProjects } from 'actions/bmdashboard/projectActions';
import './Consumables.css';

function ConsumablesInputs({ consumable, setConsumable, project, setProject }) {
const dispatch = useDispatch();
const projects = useSelector(state => state.bmProjects);
const [formattedProjects, setFormattedProjects] = useState([]); // For React-Select
const [formattedConsumables, setFormattedConsumables] = useState([]); // For React-Select
const consumables = useSelector(state => state.bmConsumables.consumableslist);

useEffect(() => {
dispatch(fetchBMProjects());
}, []);

useEffect(() => {
let _formattedProjects = [{ label: 'All Projects', value: '0' }];
const tempProjs = projects.map(proj => {
return { label: proj.name, value: proj._id };
});
_formattedProjects = _formattedProjects.concat(tempProjs);
setFormattedProjects(_formattedProjects);
}, [projects]);

useEffect(() => {
let consumablesSet = [];
let _formattedConsumables = [{ label: 'All Consumables', value: '0' }];

if (consumables.length) {
if (project.value === '0')
consumablesSet = [...new Set(consumables.map(rec => rec.itemType.name))];
else
consumablesSet = [
...new Set(
consumables
.filter(rec => rec.project?.name === project.label)
.map(rec => rec.itemType.name),
),
];
}
const temp = consumablesSet.map(con => {
return { label: con, value: con };
});
_formattedConsumables = _formattedConsumables.concat(temp);
setFormattedConsumables(_formattedConsumables);
}, [consumables, project]);

const projectHandler = selected => {
setProject(selected);
setConsumable({ label: 'All Consumables', value: '0' });
};

const consumableHandler = selected => {
setConsumable(selected);
};

return (
<div className="container">
<Form>
<Row className="align-items-center InputsMargin">
<Col className="InputsMargin">
<Row className="justify-content-start align-items-center">
<Label for="selectconsumable" lg={2} md={3}>
Project:
</Label>
<Col lg={10} md={9}>
<Select
onChange={projectHandler}
options={formattedProjects}
value={project}
defaultValue={{ label: 'All Projects', value: '0' }}
/>
</Col>
</Row>
</Col>

<Col className="InputsMargin">
<Row className="justify-content-start align-items-center">
<Label lg={3} md={3} for="selectproject">
Consumable:
</Label>
<Col lg={9} md={9}>
<Select
onChange={consumableHandler}
options={formattedConsumables}
value={consumable}
defaultValue={{ label: 'All Consumables', value: '0' }}
/>
</Col>
</Row>
</Col>
</Row>
</Form>
</div>
);
}

export default ConsumablesInputs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Table, Button } from 'reactstrap';
import { BiPencil } from 'react-icons/bi';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons';
import './Consumables.css';
import ReactTooltip from 'react-tooltip';
import { fetchAllConsumables } from 'actions/bmdashboard/consumableActions';
import ConsumablesViewModal from './ConsumablesViewModal';

function ConsumablesTable({ consumable, project }) {
// Data fetched in the parent component : ConsumablesView
const dispatch = useDispatch();

const consumables = useSelector(state => state.bmConsumables.consumableslist);
const [recordType, setRecordType] = useState(null);
const [modal, setModal] = useState(false);
const [selectedRow, setSelectedRow] = useState(null);
const [sortOrder, setSortOrder] = useState({ project: 'asc', itemType: 'asc' });
const [iconToDisplay, setIconToDisplay] = useState({ project: faSortUp, itemType: faSortUp });
const [consumablesViewData, setConsumablesViewData] = useState(null);

useEffect(() => {
dispatch(fetchAllConsumables());
}, []);

useEffect(() => {
setConsumablesViewData(consumables);
}, [consumables]);

const handleSort = column => {
if (!column || consumables.length === 0) return;
switch (column) {
case 'project': {
setSortOrder({ ...sortOrder, project: sortOrder.project === 'asc' ? 'desc' : 'asc' });
setIconToDisplay({
...iconToDisplay,
project: iconToDisplay.project === faSortUp ? faSortDown : faSortUp,
});
const factor = sortOrder.project === 'asc' ? 1 : -1;
const _consumablesViewData = [...consumables].sort((a, b) => {
return factor * a.project.name.localeCompare(b.project.name);
});
setConsumablesViewData(_consumablesViewData);
break;
}
case 'itemType': {
setSortOrder({ ...sortOrder, itemType: sortOrder.itemType === 'asc' ? 'desc' : 'asc' });
setIconToDisplay({
...iconToDisplay,
itemType: iconToDisplay.itemType === faSortUp ? faSortDown : faSortUp,
});
const factor = sortOrder.itemType === 'asc' ? 1 : -1;
const _consumablesViewData = [...consumables].sort((a, b) => {
return factor * a.itemType.name.localeCompare(b.itemType.name);
});
setConsumablesViewData(_consumablesViewData);
break;
}
default: {
break;
}
}
};

const handleOpenModal = (row, type) => {
setSelectedRow(row); // current row data
setRecordType(type); // UpdatesEdit/UpdatesView/PurchasesEdit/PurchasesView
setModal(true);
};

useEffect(() => {
if (project.value !== '0') {
const _consumables = consumables.filter(rec => rec.project?.name === project.label);
setConsumablesViewData(_consumables);
} else {
setConsumablesViewData([...consumables]);
}
}, [project]);

useEffect(() => {
let _consumables;
if (project.value === '0' && consumable.value === '0') {
setConsumablesViewData([...consumables]);
} else if (project.value !== '0' && consumable.value === '0') {
_consumables = consumables.filter(rec => rec.project?._id === project.value);
setConsumablesViewData([..._consumables]);
} else if (project.value === '0' && consumable.value !== '0') {
_consumables = consumables.filter(rec => rec.itemType?.name === consumable.value);
setConsumablesViewData([..._consumables]);
} else {
_consumables = consumables.filter(
rec => rec.project?._id === project.value && rec.itemType?.name === consumable.value,
);
setConsumablesViewData([..._consumables]);
}
}, [project, consumable]);

return (
<div>
<div>
<ConsumablesViewModal
modal={modal}
setModal={setModal}
recordType={recordType}
record={selectedRow}
/>
<Table responsive>
<thead className="BuildingTableHeaderLine">
<tr>
<th onClick={() => handleSort('project')}>
<div
data-tip={`Sort project ${sortOrder.project}`}
className="d-flex align-self-stretch cusorpointer"
>
<div>Project</div>
<FontAwesomeIcon icon={iconToDisplay.project} size="lg" />
</div>
<ReactTooltip />
</th>
<th onClick={() => handleSort('itemType')}>
<div
data-tip={`Sort name ${sortOrder.itemType}`}
className="d-flex align-items-stretch cusorpointer"
>
<div>Name</div>
<FontAwesomeIcon icon={iconToDisplay.itemType} size="lg" />
</div>
<ReactTooltip />
</th>
<th>Unit</th>
<th>Bought</th>
<th>Used</th>
<th>Available</th>
<th>Waste</th>
<th>Updates</th>
<th>Purchases</th>
</tr>
</thead>
<tbody>
{consumablesViewData && consumablesViewData.length > 0 ? (
consumablesViewData.map(rec => {
return (
<tr key={rec._id}>
<td>{rec.project?.name}</td>
<td>{rec.itemType?.name}</td>
<td>{rec.itemType?.unit}</td>
<td>{rec.stockBought}</td>
<td>{rec.stockUsed}</td>
<td>{rec.stockAvailable}</td>
<td>{rec.stockWasted}</td>

<td className="materials_cell">
<button type="button" onClick={() => handleOpenModal(rec, 'UpdatesEdit')}>
<BiPencil />
</button>
<Button
color="primary"
outline
size="sm"
onClick={() => handleOpenModal(rec, 'UpdatesView')}
>
View
</Button>
</td>
<td>
<Button
color="primary"
outline
size="sm"
onClick={() => handleOpenModal(rec, 'PurchasesView')}
>
View
</Button>
</td>
</tr>
);
})
) : (
<tr>
<td colSpan={11} style={{ textAlign: 'center' }}>
No consumables data available
</td>
</tr>
)}
</tbody>
</Table>
</div>
</div>
);
}

export default ConsumablesTable;
Loading

0 comments on commit 95db05d

Please sign in to comment.