-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1758 from OneCommunityGlobal/vishala_comsumables_…
…view Vishala Consumables Page View
- Loading branch information
Showing
12 changed files
with
565 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
}) | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/components/BMDashboard/Consumables/ConsumablesList/Consumables.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
101 changes: 101 additions & 0 deletions
101
src/components/BMDashboard/Consumables/ConsumablesList/ConsumablesInputs.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
194 changes: 194 additions & 0 deletions
194
src/components/BMDashboard/Consumables/ConsumablesList/ConsumablesTable.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.