Skip to content

Commit

Permalink
Merge pull request #135 from ECN/enhancement-lastupdated-styling
Browse files Browse the repository at this point in the history
Enhancement lastUpdated cell styling
  • Loading branch information
campb303 authored Jan 22, 2021
2 parents 2bd362c + 1084d86 commit f3aa06f
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 20 deletions.
7 changes: 6 additions & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ Flask-RESTful
python-dateutil
Flask-JWT-Extended
# Prevent upgrade to 2.x until Flask-JWT-Extended is updated to support it
PyJWT == 1.*
PyJWT == 1.*

# API Documentation
mkdocs
mkdocs-material
mkautodoc
56 changes: 39 additions & 17 deletions src/components/ItemTable/ItemTable.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTable, useFilters, useFlexLayout, useSortBy } from "react-table";
import { Table, TableBody, TableCell, TableHead, TableRow, TableContainer, Paper, Grid, ButtonGroup, IconButton, makeStyles, useTheme } from "@material-ui/core";
import { Table, TableBody, TableCell, TableHead, TableRow, TableContainer, Paper, Grid, ButtonGroup, IconButton, makeStyles, useTheme, } from "@material-ui/core";
import { useHistory } from "react-router-dom";
import RelativeTime from "react-relative-time";
import ItemTableFilter from "../ItemTableFilter/"
import { ArrowDownward, ArrowUpward } from "@material-ui/icons";

import ItemTableCell from "../ItemTableCell";
import LastUpdatedCell from "../LastUpdatedCell/";

export default function ItemTable({ data, rowCanBeSelected }) {
const [selectedRow, setSelectedRow] = useState({ queue: null, number: null});
Expand Down Expand Up @@ -38,7 +39,6 @@ export default function ItemTable({ data, rowCanBeSelected }) {
const classes = useStyles();

const history = useHistory();

const columns = React.useMemo(
() => [
{ Header: 'Queue', accessor: 'queue', },
Expand All @@ -47,10 +47,10 @@ export default function ItemTable({ data, rowCanBeSelected }) {
{ Header: 'Subject', accessor: 'subject' },
{ Header: 'Status', accessor: 'status', },
{ Header: 'Priority', accessor: 'priority' },
{ Header: 'Last Updated', accessor: 'lastUpdated', Cell: ({ value }) => <RelativeTime value={value} /> },
{ Header: 'Last Updated', accessor: 'lastUpdated', },
{ Header: 'Department', accessor: 'department' },
{ Header: 'Building', accessor: 'building' },
{ Header: 'Date Received', accessor: 'dateReceived', Cell: ({ value }) => <RelativeTime value={value} /> },
{ Header: 'Date Received', accessor: 'dateReceived', },
], []);

const tableInstance = useTable(
Expand All @@ -67,16 +67,20 @@ export default function ItemTable({ data, rowCanBeSelected }) {
onChange={(event) => setFilter(event.target.value)}
/>
);
}
},
},
},
useFilters, useFlexLayout, useSortBy,
);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, } = tableInstance;

return (
<TableContainer component={Paper} >
<Table {...getTableProps} aria-label="Table of Queue Items" >
<TableContainer component={Paper}>
<Table
{...getTableProps}
aria-label="Table of Queue Items"
size="small"
>
<TableHead>
{headerGroups.map(headerGroup => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
Expand Down Expand Up @@ -123,7 +127,7 @@ export default function ItemTable({ data, rowCanBeSelected }) {
))}
</TableHead>
<TableBody {...getTableBodyProps()}>
{rows.map((row, i) => {
{rows.map((row) => {
prepareRow(row);
let isSelected = selectedRow.queue === row.original.queue && selectedRow.number === row.original.number
return (
Expand All @@ -136,15 +140,33 @@ export default function ItemTable({ data, rowCanBeSelected }) {
// overriding the selected class but this applied the secondary color at 0.08% opacity.
// Overridding the root class is a workaround.
classes={{ root: (isSelected && rowCanBeSelected) ? classes.rowSelected : classes.bandedRows }}
{...row.getRowProps()} >
{...row.getRowProps()}
>
{row.cells.map(cell => (
<TableCell
classes={{ root: classes.columnBorders }}
{...cell.getCellProps()}
>
{cell.render("Cell")}
</TableCell>
))}
cell.render(_ => {
switch (cell.column.id) {
case "dateReceived":
return (
<ItemTableCell TableCellProps={cell.getCellProps()}>
<RelativeTime value={cell.value} />
</ItemTableCell>
);
case "lastUpdated":
return (
<LastUpdatedCell
time={cell.value}
ItemTableCellProps={cell.getCellProps()}
/>
);
default:
return (
<ItemTableCell TableCellProps={cell.getCellProps()}>
{cell.value}
</ItemTableCell>
);
}
})
))};
</TableRow>
);
})}
Expand Down
37 changes: 37 additions & 0 deletions src/components/ItemTableCell/ItemTableCell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import PropTypes from "prop-types";
import { makeStyles, TableCell, useTheme } from '@material-ui/core'

export default function ItemTableCell({ children, TableCellProps }) {
const theme = useTheme();
const useStyles = makeStyles({
columnBorders: {
// Add column borders
borderLeftWidth: "1px",
borderLeftStyle: "solid",
borderColor: theme.palette.type === "light" ? theme.palette.grey[300] : theme.palette.grey[500]
},
})

const classes = useStyles();
return (
<TableCell
classes={{ root: classes.columnBorders }}
{...TableCellProps}
>
{children}
</TableCell>
);
}

ItemTableCell.propTypes = {
/** Child object passed to display cell data. */
"children": PropTypes.object,
/** Props applied to the TableCell component. */
"TableCellProps": PropTypes.object
};

ItemTableCell.defaultProps = {
"children": {},
"TableCellProps": {},
};
38 changes: 38 additions & 0 deletions src/components/ItemTableCell/ItemTableCell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
The ItemTableCell wraps an [MUI TableCell](https://material-ui.com/api/table-cell/) and adds styling.

## Default Usage
```jsx
import { Paper } from '@material-ui/core';
import ItemTableCell from "./ItemTableCell";

<Paper>
<ItemTableCell>
Hello, moto!
</ItemTableCell>
</Paper>
```

```jsx static
<Paper>
<ItemTableCell>
Hello, moto!
</ItemTableCell>
</Paper>
```

## Forwarded TableCell Props
Props can be passed to the TableCell component using the TableCellProps prop.
```jsx
import { Paper } from '@material-ui/core';
import ItemTableCell from "./ItemTableCell";

<ItemTableCell TableCellProps={{ component: Paper, variant: "footer" }}>
Hello, moto!
</ItemTableCell>
```

```jsx static
<ItemTableCell TableCellProps={{ component: Paper, variant: "footer" }}>
Hello, moto!
</ItemTableCell>
```
3 changes: 3 additions & 0 deletions src/components/ItemTableCell/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ItemTableCell from "./ItemTableCell";

export default ItemTableCell;
69 changes: 69 additions & 0 deletions src/components/LastUpdatedCell/LastUpdatedCell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react'
import PropTypes from "prop-types";
import { red } from '@material-ui/core/colors';
import RelativeTime from 'react-relative-time';
import ItemTableCell from '../ItemTableCell';

/**
* Returns a color showing how old an item is.
* @param {string} time ISO 8601 formatted time string.
* @example
* // Returns "#e57373"
* timeToBackgroundColor("2021-01-04T11:47:00-0500")
* @returns {string} Hex color code.
*/
const timeToBackgroundColor = (time) => {
const lastUpdated = new Date(time).getTime();
const now = new Date().getTime();
const timeDelta = now - lastUpdated;

const day = 24 * 60 * 60 * 1000;
const week = day * 7;
const month = week * 4;

let backgroundColor = "white";

// 1-6 days old
if (timeDelta > day && timeDelta <= week) {
backgroundColor = red[100];
}
// 7-28 days old
else if (timeDelta > week && timeDelta <= month) {
backgroundColor = red[300];
}
// 29+ days old
else if (timeDelta > month) {
backgroundColor = red[500];
}

return backgroundColor;
}

export default function LastUpdatedCell({ time, ItemTableCellProps }) {
// Insert the calculated background color into props so it isn't overriden.
// Inspired by: https://github.com/mui-org/material-ui/issues/19479
ItemTableCellProps = {
...ItemTableCellProps,
style: {
...ItemTableCellProps.style,
backgroundColor: timeToBackgroundColor(time)
}
};

return (
<ItemTableCell TableCellProps={ItemTableCellProps}>
<RelativeTime value={time} />
</ItemTableCell>
);
};

LastUpdatedCell.propTypes = {
/** ISO 8601 formatted time string, Date object or UNIX time. See: https://www.npmjs.com/package/react-relative-time */
"time": PropTypes.string.isRequired,
/** Props to be applied to the ItemTableCell. */
"ItemTableCellProps": PropTypes.object,
};

LastUpdatedCell.defaultProps = {
"ItemTableCellProps": {},
};
38 changes: 38 additions & 0 deletions src/components/LastUpdatedCell/LastUpdatedCell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
A table cell that takes a time value and returns a relative time with a background color to indicate how old an item is.

The LastUpdatedCell wraps an [ItemTableCell](/#/Components/ItemTableCell)

## Default Usage
```jsx
import { Paper } from '@material-ui/core';
import LastUpdatedCell from "./LastUpdatedCell";

let today = new Date();
let threeDaysAgo = new Date().setDate(today.getDate() - 3);
let lastWeek = new Date().setDate(today.getDate() - 8);
let lastMonth = new Date().setDate(today.getDate() - 28);

<Paper>
{ /* Today */ }
<LastUpdatedCell time={today} />
{ /* Three Days Ago */ }
<LastUpdatedCell time={threeDaysAgo} />
{ /* Last Week */ }
<LastUpdatedCell time={lastWeek} />
{ /* Last Month */ }
<LastUpdatedCell time={lastMonth} />
</Paper>
```

```jsx static
<Paper>
{ /* Today */ }
<LastUpdatedCell time={today} />
{ /* Three Days Ago */ }
<LastUpdatedCell time={threeDaysAgo} />
{ /* Last Week */ }
<LastUpdatedCell time={lastWeek} />
{ /* Last Month */ }
<LastUpdatedCell time={lastMonth} />
</Paper>
```
3 changes: 3 additions & 0 deletions src/components/LastUpdatedCell/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import LastUpdatedCell from "./LastUpdatedCell";

export default LastUpdatedCell;
4 changes: 2 additions & 2 deletions utils/venv-manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def get_args() -> argparse.Namespace:
return parser.parse_args()


def run_logged_subprocess(command: Union[str, list], timeout: int = 10, shell: bool = True) -> tuple:
def run_logged_subprocess(command: Union[str, list], timeout: int = 60, shell: bool = True) -> tuple:
"""Executes a shell command using subprocess with logging.
stderr is redirected to stdout and stdout is pipped to logger.
Expand All @@ -96,7 +96,7 @@ def run_logged_subprocess(command: Union[str, list], timeout: int = 10, shell: b
Args:
command (Union): The command to run. If shell=False, pass a list with the first item being the command and the subsequent items being arguments. If shell=True, pass a string as you would type it into a shell.
timeout (int): The number of seconds to wait for a program before killing it
timeout (int): The number of seconds to wait for a program before killing it. Defaults to 60.
Returns:
tuple: With the first value being the return code and second being the combined stdout+stderr
Expand Down

0 comments on commit f3aa06f

Please sign in to comment.