Cleaned up the L&F; Added table sorting; Cleaned up the table styling.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import {Roles} from 'meteor/alanning:roles';
|
||||
import React, { useState } from 'react';
|
||||
import {createTheme, ThemeProvider} from '@mui/material/styles'
|
||||
import { useTracker } from 'meteor/react-meteor-data';
|
||||
import _ from 'lodash';
|
||||
import {BrowserRouter, Routes, Route} from 'react-router-dom';
|
||||
@@ -11,6 +12,58 @@ import History from './pages/History'
|
||||
import Users from './pages/Users'
|
||||
import Admin from './pages/Admin'
|
||||
|
||||
const appTheme = createTheme({
|
||||
components: {
|
||||
MuiTableSortLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
"&.Mui-active": {
|
||||
color: 'white',
|
||||
"&:hover": {
|
||||
color: '#d2d1d1'
|
||||
},
|
||||
},
|
||||
color: 'white',
|
||||
backgroundColor: '#333447',
|
||||
"&:hover": {
|
||||
color: '#d2d1d1',
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
'& path': {
|
||||
fill: 'white'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MuiTableCell: {
|
||||
styleOverrides: {
|
||||
// root: {
|
||||
// '&:hover': {
|
||||
// color: 'white !important'
|
||||
// }
|
||||
// },
|
||||
head: {
|
||||
color: 'white !important',
|
||||
backgroundColor: '#333447',
|
||||
'&:hover': {
|
||||
color: 'white !important'
|
||||
},
|
||||
hover: {
|
||||
color: 'white !important'
|
||||
}
|
||||
},
|
||||
// hover: {
|
||||
// color: 'white !important',
|
||||
// '&:hover': {
|
||||
// color: 'white !important'
|
||||
// }
|
||||
// }
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export const App = () => {
|
||||
const {user, canManageLaptops, isAdmin} = useTracker(() => {
|
||||
const user = Meteor.user();
|
||||
@@ -25,31 +78,33 @@ export const App = () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Page>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
TODO: Some statistics and such.
|
||||
<ThemeProvider theme={appTheme}>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Page>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
TODO: Some statistics and such.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>}/>
|
||||
<Route path="/assignments/*" element={<Page>
|
||||
{canManageLaptops && <Assignments/>}
|
||||
</Page>}/>
|
||||
<Route path="/assets/*" element={<Page>
|
||||
{isAdmin && <Assets/>}
|
||||
</Page>}/>
|
||||
<Route path="/admin/*" element={<Page>
|
||||
{isAdmin && <Admin/>}
|
||||
</Page>}/>
|
||||
<Route path="/history/*" element={<Page>
|
||||
{canManageLaptops && <History/>}
|
||||
</Page>}/>
|
||||
<Route path="/users/*" element={<Page>
|
||||
{isAdmin && <Users/>}
|
||||
</Page>}/>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
<Route path="/assignments/*" element={<Page>
|
||||
{canManageLaptops && <Assignments/>}
|
||||
</Page>}/>
|
||||
<Route path="/assets/*" element={<Page>
|
||||
{isAdmin && <Assets/>}
|
||||
</Page>}/>
|
||||
<Route path="/admin/*" element={<Page>
|
||||
{isAdmin && <Admin/>}
|
||||
</Page>}/>
|
||||
<Route path="/history/*" element={<Page>
|
||||
{canManageLaptops && <History/>}
|
||||
</Page>}/>
|
||||
<Route path="/users/*" element={<Page>
|
||||
{isAdmin && <Users/>}
|
||||
</Page>}/>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
@@ -48,10 +48,10 @@ export const Page = (props) => {
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 center title">K12 Tempest</div>
|
||||
<div className="col-12 center">
|
||||
<div className="nav-separator"/>
|
||||
</div>
|
||||
<div className="col-12 center title"><div className="k12">K-12</div> Tempest</div>
|
||||
{/*<div className="col-12 center">*/}
|
||||
{/* <div className="nav-separator"/>*/}
|
||||
{/*</div>*/}
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -113,6 +113,15 @@ export default () => {
|
||||
{
|
||||
name: "ID",
|
||||
value: (row) => row.id,
|
||||
descendingComparator: (a, b) => {
|
||||
if(a.id === b.id) return 0
|
||||
else if(!a.id) return -1
|
||||
else if(!b.id) return 1
|
||||
else if(a.id.length < b.id.length) return 1
|
||||
else if(a.id.length > b.id.length) return -1
|
||||
else if(b.id < a.id) return -1
|
||||
else return 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Email",
|
||||
|
||||
@@ -114,6 +114,15 @@ export default () => {
|
||||
{
|
||||
name: "ID",
|
||||
value: (row) => row.id,
|
||||
descendingComparator: (a, b) => {
|
||||
if(a.id === b.id) return 0
|
||||
else if(!a.id) return -1
|
||||
else if(!b.id) return 1
|
||||
else if(a.id.length < b.id.length) return 1
|
||||
else if(a.id.length > b.id.length) return -1
|
||||
else if(b.id < a.id) return -1
|
||||
else return 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Email",
|
||||
@@ -130,6 +139,15 @@ export default () => {
|
||||
{
|
||||
name: "GRD",
|
||||
value: (row) => row.grade,
|
||||
descendingComparator: (a, b) => {
|
||||
if(a.grade === b.grade) return 0
|
||||
else if(!a.grade) return -1
|
||||
else if(!b.grade) return 1
|
||||
else if(a.grade.length < b.grade.length) return 1
|
||||
else if(a.grade.length > b.grade.length) return -1
|
||||
else if(b.grade < a.grade) return -1
|
||||
else return 1
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ const AddAssets = ({assetTypes}) => {
|
||||
</div>
|
||||
<div style={{marginLeft: '1rem', display: 'flex', flexDirection: 'column'}}>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
<TextField style={cssEditorField} variant="standard" label="Asset ID" value={assetId} onChange={(e) => {setAssetId(e.target.value)}}/>
|
||||
<TextField style={cssEditorField} variant="standard" label="Asset ID" value={assetId} onChange={(e) => {setAssetId(e.target.value.toUpperCase())}}/>
|
||||
<TextField style={cssEditorField} variant="standard" label="Serial" value={serial} onChange={(e) => {setSerial(e.target.value)}}/>
|
||||
</div>
|
||||
<div style={{display: 'flex', flexDirection: 'row'}}>
|
||||
|
||||
@@ -105,18 +105,45 @@ export default () => {
|
||||
{
|
||||
name: "Asset ID",
|
||||
value: (row) => row.assetId,
|
||||
descendingComparator: (a, b) => {
|
||||
if(a.assetId === b.assetId) return 0
|
||||
else if(!a.assetId) return -1
|
||||
else if(!b.assetId) return 1
|
||||
else if(a.assetId.length < b.assetId.length) return 1
|
||||
else if(a.assetId.length > b.assetId.length) return -1
|
||||
else if(b.assetId < a.assetId) return -1
|
||||
else return 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Serial",
|
||||
value: (row) => row.serial,
|
||||
descendingComparator: (a, b) => {
|
||||
if(a.serial === b.serial) return 0
|
||||
else if(!a.serial) return -1
|
||||
else if(!b.serial) return 1
|
||||
else if(a.serial.length < b.serial.length) return 1
|
||||
else if(a.serial.length > b.serial.length) return -1
|
||||
else if(b.serial < a.serial) return -1
|
||||
else return 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Condition",
|
||||
value: (row) => row.condition,
|
||||
descendingComparator: (a, b) => {
|
||||
if(b.condition === a.condition) return 0 //Most common case
|
||||
// Find the first matching condition in order.
|
||||
for(let condition of conditions) {
|
||||
if(b.condition === condition) return -1
|
||||
else if(a.condition === condition) return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "AssetType",
|
||||
value: (row) => row.assetType.name,
|
||||
value: (row) => row.assetType ? row.assetType.name : "",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ const AssignmentsByAsset = () => {
|
||||
</Dialog>
|
||||
|
||||
<Box style={{marginTop: '1rem',...cssTwoColumnContainer}}>
|
||||
<TextField style={cssEditorField} variant="standard" label="Asset ID" inputRef={input=>setAssetIdInput(input)} value={assetId} onChange={(e) => {setAssetId(e.target.value)}}/>
|
||||
<TextField style={cssEditorField} variant="standard" label="Asset ID" inputRef={input=>setAssetIdInput(input)} value={assetId} onChange={(e) => {setAssetId(e.target.value.toUpperCase())}}/>
|
||||
</Box>
|
||||
{foundAsset && (
|
||||
<div>
|
||||
|
||||
@@ -5,15 +5,19 @@ import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import TableSortLabel from '@mui/material/TableSortLabel';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import classNames from 'classnames';
|
||||
import Button from "@mui/material/Button";
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
import _ from 'lodash';
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
// let columns = [
|
||||
// {
|
||||
// name: "ID",
|
||||
// value: (row) => row._id
|
||||
// value: (row) => row._id,
|
||||
// descendingComparator: (a, b) => {return value(b) < value(a) ? 1 : value(b) > value(a) ? -1 : 0}
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
@@ -46,6 +50,8 @@ export default ({columns, rows, options}) => {
|
||||
}
|
||||
|
||||
const [edited, setEdited] = useState(undefined);
|
||||
const [order, setOrder] = useState('asc') //'asc' or 'desc'
|
||||
const [orderBy, setOrderBy] = useState(undefined); //Column name being sorted.
|
||||
|
||||
let editRow = (e, row) => {
|
||||
setEdited(row);
|
||||
@@ -75,6 +81,42 @@ export default ({columns, rows, options}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const sort = (e, column) => {
|
||||
const isAscending = orderBy === column && order === 'asc' //Descending if this is the first click on the column or toggling from ascending.
|
||||
setOrder(isAscending ? 'desc' : 'asc')
|
||||
setOrderBy(column)
|
||||
}
|
||||
|
||||
const descendingComparator = (a, b, orderBy) => {
|
||||
let av = orderBy.value(a)
|
||||
let bv = orderBy.value(b)
|
||||
|
||||
if(bv < av) return -1
|
||||
else if(bv > av) return 1
|
||||
else return 0
|
||||
}
|
||||
const getComparator = (order, orderBy) => {
|
||||
if(!orderBy) return undefined
|
||||
else if(orderBy.descendingComparator) return order === 'desc' ? (a,b) => orderBy.descendingComparator(a,b) : (a,b) => -orderBy.descendingComparator(a,b)
|
||||
else return order === 'desc'
|
||||
? (a, b) => descendingComparator(a, b, orderBy)
|
||||
: (a, b) => -descendingComparator(a, b, orderBy);
|
||||
}
|
||||
const stableSort = (array, comparator) => {
|
||||
if(comparator) {
|
||||
const stabilizedThis = array.map((el, index) => [el, index]);
|
||||
stabilizedThis.sort((a, b) => {
|
||||
const order = comparator(a[0], b[0]);
|
||||
if (order !== 0) {
|
||||
return order;
|
||||
}
|
||||
return a[1] - b[1];
|
||||
});
|
||||
return stabilizedThis.map((el) => el[0]);
|
||||
}
|
||||
else return array
|
||||
}
|
||||
// console.log(rows)
|
||||
return (
|
||||
<div className='simpleTableContainer'>
|
||||
{options.add && <div style={cssTopControls}><Button variant="text" className="button" onClick={addRow}>Add</Button></div>}
|
||||
@@ -83,11 +125,17 @@ export default ({columns, rows, options}) => {
|
||||
<TableHead className="sticky">
|
||||
<TableRow>
|
||||
{columns.map((column, i) => {return (
|
||||
<TableCell key={i} className="headerCell">{column.name}</TableCell>
|
||||
<TableCell key={i} className="headerCell" sortDirection={orderBy === column ? order : false}>
|
||||
<TableSortLabel active={orderBy === column} direction={orderBy === column ? order : 'asc'} onClick={(e) => {sort(e, column)}}>
|
||||
{column.name}
|
||||
{orderBy === column.name && (
|
||||
<Box style={{color: 'white'}} component="span" sx={visuallyHidden}>
|
||||
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
)}
|
||||
</TableSortLabel>
|
||||
</TableCell>
|
||||
)})}
|
||||
{/*<TableCell className="headerCell">Name</TableCell>*/}
|
||||
{/*<TableCell className="headerCell">Email</TableCell>*/}
|
||||
{/*<TableCell className="headerCell">Roles</TableCell>*/}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@@ -98,9 +146,7 @@ export default ({columns, rows, options}) => {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{rows.map((row, i)=>{
|
||||
// console.log("Rendering Row " + i)
|
||||
// console.log(row);
|
||||
{stableSort(rows, getComparator(order, orderBy)).map((row, i) => {
|
||||
return (
|
||||
<TableRow key={options.key(row)} className={classNames({tableRow: true, selected: (!edited || options.key(edited) !== options.key(row)) && selected && options.key(selected) === options.key(row)})} onDoubleClick={(e) => {editRow(e, row)}} onClick={(e) => selectRow(e, row)}>
|
||||
{edited && options.key(edited) === options.key(row) ?
|
||||
@@ -117,6 +163,8 @@ export default ({columns, rows, options}) => {
|
||||
|
||||
if(_.isObject(value)) {
|
||||
console.error("Cannot have an object returned as the value in a table.")
|
||||
console.log("Cell value: ")
|
||||
console.log(value)
|
||||
value = JSON.stringify(value)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user