2022-09-07 08:58:00 -07:00
import { Meteor } from 'meteor/meteor' ;
import React , { useState , useEffect } from 'react' ;
import { useTracker } from 'meteor/react-meteor-data' ;
import { useTheme } from '@mui/material/styles' ;
import _ from 'lodash' ;
import SimpleTable from "/imports/ui/util/SimpleTable" ;
import TextField from "@mui/material/TextField" ;
import Button from "@mui/material/Button" ;
import Select from '@mui/material/Select' ;
import Chip from '@mui/material/Chip' ;
import MenuItem from '@mui/material/MenuItem' ;
import { InputLabel , List , ListItem , ListItemButton , ListItemText } from "@mui/material" ;
import Box from "@mui/material/Box" ;
import OutlinedInput from '@mui/material/OutlinedInput' ;
import FormControl from '@mui/material/FormControl' ;
import ToggleButton from '@mui/material/ToggleButton' ;
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' ;
import Dialog from '@mui/material/Dialog' ;
import DialogActions from '@mui/material/DialogActions' ;
import DialogContent from '@mui/material/DialogContent' ;
import DialogContentText from '@mui/material/DialogContentText' ;
import DialogTitle from '@mui/material/DialogTitle' ;
import { Assets , conditions } from "/imports/api/assets" ;
import { AssetTypes } from "/imports/api/asset-types" ;
import { Students } from "/imports/api/students" ;
import { Staff } from "/imports/api/staff" ;
2023-07-30 14:11:12 -07:00
import { Link , useLocation , useNavigate , useNavigationType } from "react-router-dom" ;
import { Action as NavigationType } from "@remix-run/router/history" ;
import Tab from '@mui/material/Tab' ;
import TabContext from '@mui/lab/TabContext' ;
import TabList from '@mui/lab/TabList' ;
import TabPanel from '@mui/lab/TabPanel' ;
2022-09-07 08:58:00 -07:00
const cssTwoColumnContainer = {
display : 'grid' ,
gridTemplateColumns : "1fr 1fr" ,
columnGap : '1rem' ,
rowGap : '0.4rem' ,
}
const cssEditorField = {
minWidth : '10rem'
}
const AssignmentsByAsset = ( ) => {
2023-07-30 14:11:12 -07:00
const navigate = useNavigate ( )
const navigateType = useNavigationType ( )
const location = useLocation ( )
const state = location . state
2022-09-07 08:58:00 -07:00
const theme = useTheme ( ) ;
const [ assetId , setAssetId ] = useState ( "" )
//Dialog stuff.
const [ openUnassignDialog , setOpenUnassignDialog ] = useState ( false )
2023-06-16 11:52:48 -07:00
const [ unassignDialogEditConditionOnly , setUnassignDialogEditConditionOnly ] = useState ( false )
2022-09-07 08:58:00 -07:00
const [ unassignCondition , setUnassignCondition ] = useState ( conditions [ 2 ] )
const [ unassignComment , setUnassignComment ] = useState ( "" )
const [ unassignConditionDetails , setUnassignConditionDetails ] = useState ( "" )
const [ assetIdInput , setAssetIdInput ] = useState ( undefined )
2023-07-30 14:11:12 -07:00
2022-09-07 08:58:00 -07:00
const { foundAsset } = useTracker ( ( ) => {
let foundAsset = null ;
if ( assetId ) {
foundAsset = Assets . findOne ( { assetId : assetId } ) ;
if ( foundAsset ) {
foundAsset . assetType = AssetTypes . findOne ( { _id : foundAsset . assetTypeId } )
if ( foundAsset . assigneeId )
foundAsset . assignee = foundAsset . assigneeType === "Student" ? Students . findOne ( { _id : foundAsset . assigneeId } ) : Staff . findOne ( { _id : foundAsset . assigneeId } )
}
}
return { foundAsset }
2023-07-30 14:11:12 -07:00
} , [ assetId ] ) ;
// Set a timer function to create history for the browser if the user pauses on an asset long enough.
useEffect ( ( ) => {
let clearTimer ;
// Only setup the timer to update navigation if we have found an asset for the current text input, and that asset is not the same as the one already current in the browser history.
if ( foundAsset && ( ! state || state . assetId !== foundAsset . assetId ) ) {
const prevFoundAssetId = foundAsset . assetId
// If the asset id doesn't change in 3 seconds then add this asset to the browser history so the back functionality works.
const timer = setTimeout ( ( ) => {
if ( foundAsset && foundAsset . assetId === prevFoundAssetId ) navigate ( "/assignments/byAsset" , { replace : false , state : { assetId : foundAsset . assetId } } ) ;
} , 3000 )
clearTimer = ( ) => clearTimeout ( timer )
}
return clearTimer
} , [ foundAsset ] )
const [ usageData , setUsageData ] = useState ( [ ] )
const [ assignmentData , setAssignmentData ] = useState ( [ ] )
2022-09-07 08:58:00 -07:00
2023-07-30 14:11:12 -07:00
// Collect the usage and assignment data when the selected person changes.
useEffect ( ( ) => {
try {
if ( foundAsset ) {
let query = { assetId : foundAsset . assetId }
console . log ( "Requesting asset historical data" )
console . log ( query )
Meteor . call ( 'DataCollection.chromebookData' , query , ( err , result ) => {
if ( err ) console . error ( err )
else setUsageData ( result )
} )
Meteor . call ( 'AssetAssignmentHistory.get' , query , ( err , result ) => {
if ( err ) console . error ( err )
else setAssignmentData ( result )
} )
}
else setUsageData ( { } )
} catch ( e ) { console . log ( "Found error in collecting chromebook history & usage in ByAsset.jsx: " + e ) }
} , [ foundAsset ] )
// Restore the state if the forward/back/refresh functionality of the browser was utilized.
useEffect ( ( ) => {
console . log ( "useEffect - navigation" )
if ( ! state ) {
console . log ( "no state" )
navigate ( "/assignments/byAsset" , { replace : true , state : { asset : null } } )
}
else {
console . log ( navigateType )
console . log ( state )
if ( navigateType === "POP" || navigateType === 'REPLACE' || navigateType === "PUSH" ) {
setAssetId ( state . assetId ? state . assetId : "" )
}
}
} , [ state ] )
//Set focus on initial rendering.
useEffect ( ( ) => {
if ( assetIdInput ) assetIdInput . focus ( )
} , [ assetIdInput ] )
2022-09-07 08:58:00 -07:00
2023-06-16 11:52:48 -07:00
const unassign = ( editConditionOnly ) => {
2022-09-07 08:58:00 -07:00
// Open the dialog to get condition and comment.
2023-06-16 11:52:48 -07:00
setUnassignDialogEditConditionOnly ( editConditionOnly )
2022-09-07 08:58:00 -07:00
setUnassignComment ( "" )
setUnassignCondition ( foundAsset . condition ? foundAsset . condition : conditions [ 2 ] )
setUnassignConditionDetails ( foundAsset . conditionDetails || "" )
setOpenUnassignDialog ( true ) ;
}
const unassignDialogClosed = ( unassign ) => {
setOpenUnassignDialog ( false )
if ( unassign === true ) {
2023-06-16 11:52:48 -07:00
if ( unassignDialogEditConditionOnly ) {
2023-06-16 14:44:21 -07:00
Meteor . call ( 'assets.updateCondition' , foundAsset . _id , unassignCondition , unassignConditionDetails , ( err , result ) => {
2023-06-16 11:52:48 -07:00
if ( err ) console . error ( err )
else if ( assetIdInput ) assetIdInput . focus ( )
} )
}
else {
// Call assets.unassign(assetId, comment, condition, conditionDetails, date)
Meteor . call ( 'assets.unassign' , foundAsset . assetId , unassignComment , unassignCondition , unassignConditionDetails , ( err , result ) => {
if ( err ) console . error ( err )
else if ( assetIdInput ) assetIdInput . focus ( )
} )
}
2022-09-07 08:58:00 -07:00
}
}
2023-07-30 14:11:12 -07:00
const [ tab , setTab ] = useState ( 'assignments' )
const RenderUsage = ( { data } ) => {
return (
< >
< ul >
{ data . map ( ( next , i ) => (
< li key = { next . _id } >
{ next . person && (
< >
User : < Link to = { "/assignments/byPerson" } state = { { search : next . person . lastName , person : next . person } } > { next . person . firstName } { next . person . lastName } { next . person . grade ? "~ " + next . person . grade : "" } ( { next . email } ) < / Link > < br / >
< / >
) }
{ ! next . person && (
< >
User : N / A < br / >
< / >
) }
{ /*Device ID: <Link to={"/search?deviceId=" + encodeURIComponent(next.deviceId)}>{next.deviceId}</Link><br/>*/ }
{ /*{next.asset && (*/ }
{ /* <>Asset ID: <Link to={"/search?assetId=" + encodeURIComponent(next.asset.assetId)}>{next.asset.assetId}</Link><br/></>*/ }
{ /*)}*/ }
{ /*<>Asset Type: {next.assetType ? next.assetType.name : "Unknown"}<br/></>*/ }
{ /*Serial: <Link to={"/search?serial=" + encodeURIComponent(next.serial)}>{next.serial}</Link><br/>*/ }
{ new Date ( next . startTime ) . toLocaleDateString ( "en-US" ) + "-" + new Date ( next . endTime ) . toLocaleDateString ( "en-US" ) + " @ " + new Date ( next . endTime ) . toLocaleTimeString ( "en-US" ) } ( { Math . ceil ( ( ( next . endTime ? next . endTime : new Date ( ) . getTime ( ) ) - next . startTime ) / ( 1000 * 60 * 60 * 24 ) ) } days ) < br / >
{ /*{next.assignedTo && (*/ }
{ /* <>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 : < Link to = { "/assignments/byPerson" } state = { { search : next . assignee . lastName , person : next . assignee } } > { next . assignee . firstName } { next . assignee . lastName } { next . assignee . grade ? "~ " + next . assignee . grade : "" } ( { next . assignee . email } ) < / 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/>*/ }
{ /* </>*/ }
{ /*)}*/ }
{ /*Serial: <Link to={"/search?serial=" + encodeURIComponent(next.serial)}>{next.serial}</Link><br/>*/ }
{ new Date ( next . startDate ) . toLocaleDateString ( "en-US" ) + ( next . endDate ? "-" + new Date ( next . endDate ) . toLocaleDateString ( "en-US" ) : " - Still Assigned" ) } ( { Math . ceil ( ( ( next . endDate ? next . endDate : new Date ( ) . getTime ( ) ) - next . startDate ) / ( 1000 * 60 * 60 * 24 ) ) } days ) < br / >
{ next . comment && (
< > Comment : { next . comment } < br / > < / >
) }
Start Condition : { next . startCondition } < br / >
{ next . startConditionDetails && < > Details : { next . startConditionDetails } < br / > < / > }
{ next . endDate && (
< >
End Condition : { next . endCondition } < br / >
{ next . endConditionDetails && < > Details : { next . endConditionDetails } < br / > < / > }
< / >
) }
< / li >
) ) }
< / ul >
< / >
)
}
2022-09-07 08:58:00 -07:00
return (
< >
< Dialog open = { openUnassignDialog } onClose = { unassignDialogClosed } >
2023-06-16 11:52:48 -07:00
< DialogTitle > { unassignDialogEditConditionOnly ? "Edit Condition" : "Unassign Asset" } < / DialogTitle >
2022-09-07 08:58:00 -07:00
< DialogContent style = { { display : 'flex' , flexDirection : 'column' } } >
< div >
< TextField style = { cssEditorField } select variant = "standard" label = "Condition" value = { unassignCondition } onChange = { ( e ) => { setUnassignCondition ( e . target . value ) } } >
{ conditions . map ( ( condition , i ) => {
return < MenuItem key = { i } value = { condition } > { condition } < / MenuItem >
} ) }
< / TextField >
< / div >
2023-06-16 11:52:48 -07:00
{ ! unassignDialogEditConditionOnly && < TextField style = { { marginTop : '1rem' , minWidth : '30rem' } } variant = "standard" label = "Comment" value = { unassignComment } onChange = { ( e ) => { setUnassignComment ( e . target . value ) } } / > }
2022-09-07 08:58:00 -07:00
< TextField style = { { marginTop : '1rem' , minWidth : '30rem' } } multiline rows = { 4 } variant = "outlined" label = "Condition Details" value = { unassignConditionDetails } onChange = { ( e ) => { setUnassignConditionDetails ( e . target . value ) } } / >
< / DialogContent >
< DialogActions >
2023-06-16 11:52:48 -07:00
< Button onClick = { ( ) => unassignDialogClosed ( true ) } > { unassignDialogEditConditionOnly ? "Save" : "Unassign" } < / Button >
2022-09-07 08:58:00 -07:00
< Button onClick = { ( ) => unassignDialogClosed ( false ) } > Cancel < / Button >
< / DialogActions >
< / Dialog >
< Box style = { { marginTop : '1rem' , ... cssTwoColumnContainer } } >
2023-07-30 14:11:12 -07:00
< TextField style = { { ... cssEditorField , maxWidth : "40rem" , minWidth : "10rem" } } variant = "standard" label = "Asset ID" inputRef = { input => setAssetIdInput ( input ) } value = { assetId } onChange = { ( e ) => { setAssetId ( e . target . value . toUpperCase ( ) ) } } / >
{ foundAsset && (
< div >
< h3 style = { { margin : "0 0 0.5rem 0" } } > Asset ID : { foundAsset . assetId } < / h3 >
< h3 style = { { margin : "0 0 0.5rem 0" } } > Serial : { foundAsset . serial } < / h3 >
< h3 style = { { margin : "0 0 0.5rem 0" } } > Current Condition : { foundAsset . condition } < / h3 >
< TabContext value = { tab } >
< Box sx = { { borderBottom : 1 , borderColor : 'divider' } } >
< TabList onChange = { ( e , v ) => setTab ( v ) } >
< Tab label = "Assignments" value = "assignments" / >
< Tab label = "Assignment History" value = "assignmentHistory" / >
< Tab label = "Usage History" value = "usageHistory" / >
< / TabList >
< / Box >
< TabPanel value = "assignments" >
< div >
< div > Condition Details : { foundAsset . conditionDetails } < / div >
{ foundAsset . assignee && (
< >
< div > Assigned on : { new Date ( foundAsset . assignmentDate ) . toLocaleDateString ( "en-US" ) } ( { Math . ceil ( ( new Date ( ) . getTime ( ) - foundAsset . assignmentDate ) / ( 1000 * 60 * 60 * 24 ) ) } days ) < / div >
< div > Assigned to : < Link to = { "/assignments/byPerson" } state = { { search : foundAsset . assignee . lastName , person : foundAsset . assignee } } > { foundAsset . assignee . firstName } { foundAsset . assignee . lastName } { foundAsset . assignee . grade && foundAsset . assignee . grade } ( { foundAsset . assignee . email } ) < / Link > < / div >
< Button variant = "contained" color = 'secondary' className = "button" onClick = { ( ) => unassign ( false ) } > Unassign < / Button >
{ " " }
< Button variant = "contained" color = 'secondary' className = "button" onClick = { ( ) => unassign ( true ) } > Edit < / Button >
< / >
) }
< / div >
< / TabPanel >
< TabPanel value = "assignmentHistory" >
< RenderAssignments data = { assignmentData } / >
< / TabPanel >
< TabPanel value = "usageHistory" >
< RenderUsage data = { usageData } / >
< / TabPanel >
< / TabContext >
< / div >
) }
2022-09-07 08:58:00 -07:00
< / Box >
< / >
)
}
export default ( ) => {
Meteor . subscribe ( 'students' ) ;
Meteor . subscribe ( 'staff' ) ;
Meteor . subscribe ( 'assetTypes' ) ;
Meteor . subscribe ( 'assets' ) ;
return (
< AssignmentsByAsset / >
)
}