Finished core functionality.
This commit is contained in:
@@ -9,9 +9,11 @@ import Select from '@mui/material/Select';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import {Assets, conditions} from "/imports/api/assets";
|
||||
import {AssetTypes} from "/imports/api/asset-types";
|
||||
import {Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Grid, Box} from "@mui/material";
|
||||
|
||||
const cssEditorField = {
|
||||
margin: '0.6rem 0',
|
||||
minWidth: '200px'
|
||||
}
|
||||
const cssGridFieldContainer = {
|
||||
display: 'grid',
|
||||
@@ -84,8 +86,21 @@ export default () => {
|
||||
Meteor.subscribe('assetTypes');
|
||||
Meteor.subscribe('assets');
|
||||
|
||||
const [removeRow, setRemoveRow] = useState(undefined)
|
||||
const [assetId, setAssetId] = useState("")
|
||||
const [serial, setSerial] = useState("")
|
||||
const [assetTypeId, setAssetTypeId] = useState("")
|
||||
const assetTypes = [{name: "All", _id: 0}, ...AssetTypes.find({}, {sort: {year: -1}}).fetch()]
|
||||
|
||||
|
||||
const {assets} = useTracker(() => {
|
||||
const assets = Assets.find({}).fetch();
|
||||
let query = {}
|
||||
|
||||
if(assetId) query.assetId = {$regex: assetId, $options: 'i'}
|
||||
if(serial) query.serial = {$regex: serial, $options: 'i'}
|
||||
if(assetTypeId) query.assetTypeId = assetTypeId
|
||||
|
||||
const assets = Assets.find(query).fetch();
|
||||
const assetTypes = AssetTypes.find({}, {sort: {year: -1}}).fetch();
|
||||
const assetTypeNameMap = assetTypes.reduce((map, obj) => {
|
||||
map[obj._id] = obj;
|
||||
@@ -151,11 +166,47 @@ export default () => {
|
||||
key: (row) => row._id,
|
||||
editor: (row, close) => {return (<AssetEditor value={row} close={close}/>)},
|
||||
add: true,
|
||||
remove: (row) => {setRemoveRow(row)},
|
||||
maxHeight: '40rem'
|
||||
}
|
||||
|
||||
const removeAsset = (asset) => {
|
||||
Meteor.call("assets.remove", asset._id);
|
||||
setRemoveRow(undefined)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={removeRow ? true : false} onClose={() => setRemoveRow(undefined)}>
|
||||
<DialogTitle>Remove Asset?</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Are you certain you want to remove the selected Asset?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => removeAsset(removeRow)}>Remove Asset</Button>
|
||||
<Button onClick={() => setRemoveRow(undefined)}>Cancel</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Box component="div" sx={{m: 2, p: 2, border: '1px dashed grey'}}>
|
||||
<h4 style={{margin: 0, padding: 0}}>Filter</h4>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={4}>
|
||||
<TextField style={cssEditorField} variant="standard" label="Asset ID" value={assetId} onChange={(e) => {setAssetId(e.target.value)}}/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<TextField style={cssEditorField} variant="standard" label="Serial" value={serial} onChange={(e) => {setSerial(e.target.value)}}/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<TextField style={cssEditorField} select variant="standard" value={assetTypeId} onChange={(e)=>{setAssetTypeId(e.target.value)}} label="Asset Type">
|
||||
{assetTypes.map((assetType, i) => {
|
||||
return <MenuItem key={i} value={assetType._id}>{assetType.name}</MenuItem>
|
||||
})}
|
||||
</TextField>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<SimpleTable rows={assets} columns={columns} options={options}/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -4,19 +4,18 @@ import { useTracker } from 'meteor/react-meteor-data';
|
||||
import _ from 'lodash';
|
||||
import TabNav from '../util/TabNav';
|
||||
import {Route, Routes} from "react-router-dom";
|
||||
import Search from './History/Search'
|
||||
|
||||
export default () => {
|
||||
let tabs = [
|
||||
{
|
||||
title: "Chromebook Usage",
|
||||
getElement: () => {
|
||||
const ChromebookUsage = lazy(()=>import('./History/ChromebookUsage'))
|
||||
return <ChromebookUsage/>
|
||||
},
|
||||
path: '/chromebookUsage',
|
||||
href: 'chromebookUsage'
|
||||
},
|
||||
// {
|
||||
// title: "Chromebook Usage",
|
||||
// getElement: () => {
|
||||
// const ChromebookUsage = lazy(()=>import('./History/ChromebookUsage'))
|
||||
// return <ChromebookUsage/>
|
||||
// },
|
||||
// path: '/chromebookUsage',
|
||||
// href: 'chromebookUsage'
|
||||
// },
|
||||
{
|
||||
title: "Asset History",
|
||||
getElement: () => {
|
||||
@@ -31,10 +30,6 @@ export default () => {
|
||||
return (
|
||||
<>
|
||||
<TabNav tabs={tabs}/>
|
||||
|
||||
<Routes>
|
||||
<Route path="search" element={<Search/>}/>
|
||||
</Routes>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -30,9 +30,9 @@ import SearchIcon from "@mui/icons-material/Search";
|
||||
|
||||
export default () => {
|
||||
const navigate = useNavigate()
|
||||
const [resultType, setResultType] = useState("usage")
|
||||
const [searchType, setSearchType] = useState("email")
|
||||
const [value, setValue] = useState("")
|
||||
|
||||
const search = () => {
|
||||
if(searchType === 'email' || searchType === 'firstName' || searchType === 'lastName') {
|
||||
if (value && value.length > 1) {
|
||||
@@ -45,18 +45,18 @@ export default () => {
|
||||
setPeopleToPickFrom(all)
|
||||
setOpenPickPersonDialog(true)
|
||||
} else if (all.length === 1) {
|
||||
navigate("/history/search?resultType=" + encodeURIComponent(resultType) + "&" + (students.length ? "studentId" : 'staffId') + "=" + encodeURIComponent(all[0]._id));
|
||||
navigate("/search?" + (students.length ? "studentId" : 'staffId') + "=" + encodeURIComponent(all[0]._id));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(searchType === 'assetID' || searchType === 'serial') {
|
||||
let asset = Assets.findOne(searchType === 'assetID' ? {assetId: value} : {serial : value});
|
||||
|
||||
else if(searchType === 'assetId' || searchType === 'serial') {
|
||||
let asset = Assets.findOne(searchType === 'assetId' ? {assetId: value.toUpperCase()} : {serial : value});
|
||||
console.log(asset)
|
||||
if(asset) {
|
||||
if(searchType === 'assetID')
|
||||
navigate("/history/search?resultType=" + encodeURIComponent(resultType) + "&assetId=" + encodeURIComponent(asset.assetId))
|
||||
if(searchType === 'assetId')
|
||||
navigate("/search?assetId=" + encodeURIComponent(asset.assetId.toUpperCase()))
|
||||
else
|
||||
navigate("/history/search?resultType=" + encodeURIComponent(resultType) + "&serial=" + encodeURIComponent(asset.serial))
|
||||
navigate("/search?serial=" + encodeURIComponent(asset.serial))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,13 @@ export default () => {
|
||||
if(openPickPersonDialog) setOpenPickPersonDialog(false)
|
||||
|
||||
if(person && person._id) {
|
||||
navigate("/history/search?resultType=" + encodeURIComponent(resultType) + "&" + (person.grade ? "studentId" : 'staffId') + "=" + encodeURIComponent(person._id));
|
||||
navigate("/search?" + (person.grade ? "studentId" : 'staffId') + "=" + encodeURIComponent(person._id));
|
||||
}
|
||||
}
|
||||
|
||||
const inputKeyPress = (e) => {
|
||||
if(e.key === 'Enter' && value.length > 0) {
|
||||
search()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,14 +101,14 @@ export default () => {
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<div style={{display: "flex", flexDirection: "column"}} sx={{ p: '2px 4px', display: 'flex', alignItems: 'center', width: 400 }}>
|
||||
<Paper componet='form'>
|
||||
<div style={{marginBottom: "1rem", marginTop: "2rem"}}>
|
||||
<ToggleButtonGroup color="primary" value={resultType} exclusive onChange={(e, type)=>setResultType(type)} aria-label="Result Type">
|
||||
<ToggleButton value="usage">Usage History</ToggleButton>
|
||||
<ToggleButton value="assignment">Assignment History</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
<div style={{display: "flex", flexDirection: "column", marginTop: "1rem"}} sx={{ p: '2px 4px', display: 'flex', alignItems: 'center', width: 400 }}>
|
||||
{/*<Paper componet='form'>*/}
|
||||
{/* <div style={{marginBottom: "1rem", marginTop: "2rem"}}>*/}
|
||||
{/* <ToggleButtonGroup color="primary" value={resultType} exclusive onChange={(e, type)=>setResultType(type)} aria-label="Result Type">*/}
|
||||
{/* <ToggleButton value="usage">Usage History</ToggleButton>*/}
|
||||
{/* <ToggleButton value="assignment">Assignment History</ToggleButton>*/}
|
||||
{/* </ToggleButtonGroup>*/}
|
||||
{/* </div>*/}
|
||||
<div style={{marginBottom: "1rem"}}>
|
||||
<ToggleButtonGroup color="primary" value={searchType} exclusive onChange={(e, type)=>setSearchType(type)} aria-label="Search Type">
|
||||
<ToggleButton value="email">Email</ToggleButton>
|
||||
@@ -113,12 +119,12 @@ export default () => {
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
<div>
|
||||
<InputBase value={value} onChange={(e) => {setValue(e.target.value)}} sx={{ ml: 1, flex: 1 }} placeholder="Value" inputProps={{ 'aria-label': 'Search Value' }}/>
|
||||
<InputBase value={value} onKeyPress={inputKeyPress} onChange={(e) => {setValue(e.target.value)}} sx={{ ml: 1, flex: 1 }} placeholder="Value" inputProps={{ 'aria-label': 'Search Value' }}/>
|
||||
<IconButton type="button" sx={{ p: '10px' }} aria-label="search" onClick={search}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Paper>
|
||||
{/*</Paper>*/}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTracker } from 'meteor/react-meteor-data';
|
||||
import _ from 'lodash';
|
||||
import {Link, useSearchParams} from "react-router-dom";
|
||||
|
||||
const RenderUsage = ({data}) => {
|
||||
return (
|
||||
<>
|
||||
<ul>
|
||||
{data.map((next, i) => (
|
||||
<li key={next._id}>
|
||||
{next.person && (
|
||||
<>
|
||||
User: {next.person.firstName} {next.person.lastName} {next.person.grade ? "~ " + next.person.grade : ""} (<Link to={"/history/search?email=" + encodeURIComponent(next.email)}>{next.email}</Link>)<br/>
|
||||
</>
|
||||
)}
|
||||
Device ID: <Link to={"/history/search?deviceId=" + encodeURIComponent(next.deviceId)}>{next.deviceId}</Link><br/>
|
||||
Serial: <Link to={"/history/search?serial=" + encodeURIComponent(next.serial)}>{next.serial}</Link><br/>
|
||||
{next.asset && (
|
||||
<>
|
||||
Asset ID: <Link to={"/history/search?assetId=" + encodeURIComponent(next.asset.assetId)}>{next.asset.assetId}</Link><br/>
|
||||
</>
|
||||
)}
|
||||
{new Date(next.startTime).toLocaleDateString("en-US") + "-" + new Date(next.endTime).toLocaleDateString("en-US")}
|
||||
{next.assetType && (
|
||||
<>
|
||||
<br/>Asset Type: {next.assetType.name}
|
||||
</>
|
||||
)}
|
||||
{next.assignedTo && (
|
||||
<>
|
||||
<br/>Currently assigned to: {next.assignedTo.firstName} {next.assignedTo.lastName} {next.assignedTo.grade ? "~ " + next.assignedTo.grade : ""} ({next.assignedTo.email})
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const RenderAssignments = ({data}) => {
|
||||
return (
|
||||
<>
|
||||
<ul>
|
||||
{data.map((next, i) => (
|
||||
<li key={next._id}>
|
||||
{next.assignee && (
|
||||
<>
|
||||
User: {next.assignee.firstName} {next.assignee.lastName} {next.assignee.grade ? "~ " + next.assignee.grade : ""} (<Link to={"/history/search?email=" + encodeURIComponent(next.assignee.email)}>{next.assignee.email}</Link>)<br/>
|
||||
</>
|
||||
)}
|
||||
Serial: <Link to={"/history/search?serial=" + encodeURIComponent(next.serial)}>{next.serial}</Link><br/>
|
||||
{next.asset && (
|
||||
<>
|
||||
Asset ID: <Link to={"/history/search?assetId=" + encodeURIComponent(next.asset.assetId)}>{next.asset.assetId}</Link><br/>
|
||||
</>
|
||||
)}
|
||||
{next.assetType && (
|
||||
<>
|
||||
Asset Type: {next.assetType.name}<br/>
|
||||
</>
|
||||
)}
|
||||
{new Date(next.startDate).toLocaleDateString("en-US") + "-" + new Date(next.endDate).toLocaleDateString("en-US")}<br/>
|
||||
Comment: {next.comment}<br/>
|
||||
Start Condition: {next.startCondition}<br/>
|
||||
Details: {next.startConditionDetails}<br/>
|
||||
End Condition: {next.endCondition}<br/>
|
||||
Details: {next.endConditionDetails}<br/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default () => {
|
||||
// const query = queryString.parse(search)
|
||||
const [data, setData] = useState([])
|
||||
const [search, setSearch] = useSearchParams()
|
||||
|
||||
useEffect(() => {
|
||||
let args;
|
||||
|
||||
if(search.get('resultType') === 'usage') {
|
||||
if(search.get('studentId')) {
|
||||
args = {studentId: search.get('studentId')}
|
||||
}
|
||||
else if(search.get('staffId')) {
|
||||
args = {staffId: search.get('staffId')}
|
||||
}
|
||||
else if(search.get('email')) {
|
||||
args = {email: search.get('email')}
|
||||
}
|
||||
else if(search.get('deviceId')) {
|
||||
args = {deviceId: search.get('deviceId')}
|
||||
}
|
||||
else if(search.get('serial')) {
|
||||
args = {serial: search.get('serial')}
|
||||
}
|
||||
else if(search.get('assetId')) {
|
||||
args = {assetId: search.get('assetId')}
|
||||
}
|
||||
|
||||
Meteor.call('DataCollection.chromebookData', args, (err, result) => {
|
||||
if (err) console.error(err)
|
||||
else setData(result)
|
||||
})
|
||||
}
|
||||
else {
|
||||
if(search.get('studentId')) {
|
||||
args = {studentId: search.get('studentId')}
|
||||
}
|
||||
else if(search.get('staffId')) {
|
||||
args = {staffId: search.get('staffId')}
|
||||
}
|
||||
else if(search.get('serial')) {
|
||||
args = {serial: search.get('serial')}
|
||||
}
|
||||
else if(search.get('assetId')) {
|
||||
args = {assetId: search.get('assetId')}
|
||||
}
|
||||
|
||||
Meteor.call('AssetAssignmentHistory.get', args, (err, result) => {
|
||||
if (err) console.error(err)
|
||||
else setData(result)
|
||||
})
|
||||
}
|
||||
|
||||
}, [search])
|
||||
|
||||
return (search.get('resultType') === 'usage' ? <RenderUsage data={data}/> : <RenderAssignments data={data}/>)
|
||||
}
|
||||
160
imports/ui/pages/Search.jsx
Normal file
160
imports/ui/pages/Search.jsx
Normal file
@@ -0,0 +1,160 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTracker } from 'meteor/react-meteor-data';
|
||||
import _ from 'lodash';
|
||||
import {Link, useSearchParams} from "react-router-dom";
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
const RenderUsage = ({data}) => {
|
||||
return (
|
||||
<>
|
||||
<ul>
|
||||
{data.map((next, i) => (
|
||||
<li key={next._id}>
|
||||
{next.person && (
|
||||
<>
|
||||
User: {next.person.firstName} {next.person.lastName} {next.person.grade ? "~ " + next.person.grade : ""} (<Link to={"/search?email=" + encodeURIComponent(next.email)}>{next.email}</Link>)<br/>
|
||||
</>
|
||||
)}
|
||||
Device ID: <Link to={"/search?deviceId=" + encodeURIComponent(next.deviceId)}>{next.deviceId}</Link><br/>
|
||||
Serial: <Link to={"/search?serial=" + encodeURIComponent(next.serial)}>{next.serial}</Link><br/>
|
||||
{next.asset && (
|
||||
<>
|
||||
Asset ID: <Link to={"/search?assetId=" + encodeURIComponent(next.asset.assetId)}>{next.asset.assetId}</Link><br/>
|
||||
</>
|
||||
)}
|
||||
{new Date(next.startTime).toLocaleDateString("en-US") + "-" + new Date(next.endTime).toLocaleDateString("en-US")}
|
||||
{next.assetType && (
|
||||
<>
|
||||
<br/>Asset Type: {next.assetType.name}
|
||||
</>
|
||||
)}
|
||||
{next.assignedTo && (
|
||||
<>
|
||||
<br/>Currently assigned to: {next.assignedTo.firstName} {next.assignedTo.lastName} {next.assignedTo.grade ? "~ " + next.assignedTo.grade : ""} ({next.assignedTo.email})
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const RenderAssignments = ({data}) => {
|
||||
return (
|
||||
<>
|
||||
<ul>
|
||||
{data.map((next, i) => (
|
||||
<li key={next._id + "/" + next.assetId}>
|
||||
{next.assignee && (
|
||||
<>
|
||||
User: {next.assignee.firstName} {next.assignee.lastName} {next.assignee.grade ? "~ " + next.assignee.grade : ""} (<Link to={"/search?email=" + encodeURIComponent(next.assignee.email)}>{next.assignee.email}</Link>)<br/>
|
||||
</>
|
||||
)}
|
||||
Serial: <Link to={"/search?serial=" + encodeURIComponent(next.serial)}>{next.serial}</Link><br/>
|
||||
{next.asset && (
|
||||
<>
|
||||
Asset ID: <Link to={"/search?assetId=" + encodeURIComponent(next.asset.assetId)}>{next.asset.assetId}</Link><br/>
|
||||
</>
|
||||
)}
|
||||
{next.assetType && (
|
||||
<>
|
||||
Asset Type: {next.assetType.name}<br/>
|
||||
</>
|
||||
)}
|
||||
{new Date(next.startDate).toLocaleDateString("en-US") + (next.endDate ? "-" + new Date(next.endDate).toLocaleDateString("en-US") : " - Still Assigned")}<br/>
|
||||
{next.endDate && (
|
||||
<>Comment: {next.comment}<br/></>
|
||||
)}
|
||||
Start Condition: {next.startCondition}<br/>
|
||||
Details: {next.startConditionDetails}<br/>
|
||||
{next.endDate && (
|
||||
<>
|
||||
End Condition: {next.endCondition}<br/>
|
||||
Details: {next.endConditionDetails}<br/>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default () => {
|
||||
// const query = queryString.parse(search)
|
||||
const [usageData, setUsageData] = useState([])
|
||||
const [assignmentData, setAssignmentData] = useState([])
|
||||
const [search, setSearch] = useSearchParams()
|
||||
|
||||
useEffect(() => {
|
||||
let args;
|
||||
|
||||
if(search.get('studentId')) {
|
||||
args = {studentId: search.get('studentId')}
|
||||
}
|
||||
else if(search.get('staffId')) {
|
||||
args = {staffId: search.get('staffId')}
|
||||
}
|
||||
else if(search.get('email')) {
|
||||
args = {email: search.get('email')}
|
||||
}
|
||||
else if(search.get('deviceId')) {
|
||||
args = {deviceId: search.get('deviceId')}
|
||||
}
|
||||
else if(search.get('serial')) {
|
||||
args = {serial: search.get('serial')}
|
||||
}
|
||||
else if(search.get('assetId')) {
|
||||
args = {assetId: search.get('assetId')}
|
||||
}
|
||||
|
||||
Meteor.call('DataCollection.chromebookData', args, (err, result) => {
|
||||
if (err) console.error(err)
|
||||
else setUsageData(result)
|
||||
})
|
||||
|
||||
// if(search.get('studentId')) {
|
||||
// args = {studentId: search.get('studentId')}
|
||||
// }
|
||||
// else if(search.get('staffId')) {
|
||||
// args = {staffId: search.get('staffId')}
|
||||
// }
|
||||
// else if(search.get('serial')) {
|
||||
// args = {serial: search.get('serial')}
|
||||
// }
|
||||
// else if(search.get('assetId')) {
|
||||
// args = {assetId: search.get('assetId')}
|
||||
// }
|
||||
|
||||
Meteor.call('AssetAssignmentHistory.get', args, (err, result) => {
|
||||
if (err) console.error(err)
|
||||
else setAssignmentData(result)
|
||||
})
|
||||
}, [search])
|
||||
|
||||
const [tabIndex, setTabIndex] = useState(0)
|
||||
|
||||
console.log(assignmentData)
|
||||
|
||||
// return (search.get('resultType') === 'usage' ? <RenderUsage data={data}/> : <RenderAssignments data={data}/>)
|
||||
return (
|
||||
<>
|
||||
<Box sx={{width: '100%'}}>
|
||||
<Tabs value={tabIndex} onChange={(e, index) => {setTabIndex(index)}} aria-label='nav tabs'>
|
||||
<Tab label="Usage"/>
|
||||
<Tab label="Assignments"/>
|
||||
</Tabs>
|
||||
</Box>
|
||||
<div role="tabpanel" hidden={tabIndex !== 0}>
|
||||
<RenderUsage data={usageData}/>
|
||||
</div>
|
||||
<div role="tabpanel" hidden={tabIndex !== 1}>
|
||||
<RenderAssignments data={assignmentData}/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user