Skip to content

Enhancement lastUpdated cell styling #135

Merged
merged 22 commits into from
Jan 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6e6492b
Added background state varible and created lastUpdatedCell component …
Nov 23, 2020
a6d8132
Removed uncesscary function and variables. Changed name of the state …
Nov 25, 2020
ab75000
Implemented logic for changing cell color based on last updated time.
Dec 2, 2020
6b67902
implemented logic that sets color of lastUpdated cell based on time o…
Dec 3, 2020
c418a5a
Forammting fixes, removed uneccessary overrides, fixed date received…
Dec 3, 2020
4c57b61
Created ItemTableCell and LastUpdated cell components for refactoring…
wrigh393 Dec 16, 2020
ae03e13
Fixed background color not displaying in LastUpdatedCell component
wrigh393 Dec 17, 2020
8ac1d92
Update Python requirements to fix Flask_JWT_Extended and PyJWT
campb303 Dec 30, 2020
7110cda
Update venv-manager to allow 60 second for requirement installation
campb303 Dec 30, 2020
f36ac8e
Moved ternary statements to swtich statement for readability
campb303 Jan 20, 2021
375b679
Formatting
campb303 Jan 20, 2021
55de973
Rename reactTableProps to TableCellProps
campb303 Jan 20, 2021
fc608a6
Renamed folder and update exports
campb303 Jan 20, 2021
0c3f1bb
Formatting
campb303 Jan 20, 2021
813438a
Remove unused backgroundColor prop
campb303 Jan 21, 2021
672b0f9
Refactir timeToBackgroundColor and inject background color into props
campb303 Jan 21, 2021
86b75a1
Rename reactTableProps to ItemTableCellProps
campb303 Jan 21, 2021
e3724de
Make time prop required
campb303 Jan 21, 2021
9df197e
Add docs
campb303 Jan 21, 2021
246e4d9
Add docs
campb303 Jan 21, 2021
1ed9daa
Update docs to show what values the time prop can be
campb303 Jan 21, 2021
1084d86
Merge branch 'staging' into enhancement-lastupdated-styling
campb303 Jan 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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