diff --git a/package-lock.json b/package-lock.json index bd37505..2d43568 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12382,6 +12382,11 @@ "react-is": "^16.9.0" } }, + "react-relative-time": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/react-relative-time/-/react-relative-time-0.0.7.tgz", + "integrity": "sha1-n39AdChuvmE1tTPu4a3qBV35cYM=" + }. "react-router": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", diff --git a/package.json b/package.json index 8df2bc7..56190c6 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "material-table": "^1.63.1", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-relative-time": "0.0.7", "react-scripts": "3.4.1", "react-table": "^7.5.1", "react-router-dom": "^5.2.0" diff --git a/src/App.js b/src/App.js index 9999793..c166231 100644 --- a/src/App.js +++ b/src/App.js @@ -94,7 +94,7 @@ function App() { /> } - + ); } diff --git a/src/components/Assignment/Assignment.js b/src/components/Assignment/Assignment.js new file mode 100644 index 0000000..d17385f --- /dev/null +++ b/src/components/Assignment/Assignment.js @@ -0,0 +1,21 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Typography } from "@material-ui/core"; +import RelativeTime from "react-relative-time"; + +export default function Assignment({ by, to, datetime }){ + return ( + + {by} assigned this to {to} . + + ); +} + +Assignment.propTypes = { + /** The person who changed the assignment. */ + "by": PropTypes.string.isRequired, + /** The person the item was assigned to. */ + "to": PropTypes.string.isRequired, + /** The time the assignment was changed in ISO 8601 format. */ + "datetime": PropTypes.string.isRequired +} \ No newline at end of file diff --git a/src/components/Assignment/Assignment.md b/src/components/Assignment/Assignment.md new file mode 100644 index 0000000..eaaa150 --- /dev/null +++ b/src/components/Assignment/Assignment.md @@ -0,0 +1,26 @@ +Renders a string representation of an assignment with a relative time. + +```jsx +import Assignment from "./Assignment"; + +const example_assignment = { + "type": "assignment", + "datetime": "2020-06-23T13:27:00-0400", + "by": "campb303", + "to": "campb303" +}; + + + +``` + +```jsx static +const example_assignment = { + "type": "assignment", + "datetime": "2020-06-23T13:27:00-0400", + "by": "campb303", + "to": "campb303" +}; + + +``` \ No newline at end of file diff --git a/src/components/Assignment/index.js b/src/components/Assignment/index.js new file mode 100644 index 0000000..f3e1b8b --- /dev/null +++ b/src/components/Assignment/index.js @@ -0,0 +1 @@ +export { default } from "./Assignment"; \ No newline at end of file diff --git a/src/components/CurrentBreakPoint/CurrentBreakPoint.js b/src/components/CurrentBreakPoint/CurrentBreakPoint.js new file mode 100644 index 0000000..1074891 --- /dev/null +++ b/src/components/CurrentBreakPoint/CurrentBreakPoint.js @@ -0,0 +1,44 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { useTheme, useMediaQuery, makeStyles } from '@material-ui/core'; + +export default function CurrentBreakPoint(){ + const theme = useTheme(); + const useStyles = makeStyles({ + true: { + color: theme.palette.success.main + }, + false: { + color: theme.palette.error.main + } + }); + const classes = useStyles(); + + const mediaQueries = { + "xs": useMediaQuery(theme.breakpoints.up('xs')), + "sm": useMediaQuery(theme.breakpoints.up('sm')), + "md": useMediaQuery(theme.breakpoints.up('md')), + "lg": useMediaQuery(theme.breakpoints.up('lg')), + "xl": useMediaQuery(theme.breakpoints.up('xl')), + } + + return ( +

+ { Object.keys(mediaQueries).map( (key) => { + return( + + {`${key.toUpperCase()}: ${ mediaQueries[key] ? "true" : "false"}\n\n\n\n`} + + ) + })} +

+ ); +} + +CurrentBreakPoint.propTypes = { + +}; + +CurrentBreakPoint.defaultProps = { + +}; \ No newline at end of file diff --git a/src/components/CurrentBreakPoint/CurrentBreakPoint.md b/src/components/CurrentBreakPoint/CurrentBreakPoint.md new file mode 100644 index 0000000..e171028 --- /dev/null +++ b/src/components/CurrentBreakPoint/CurrentBreakPoint.md @@ -0,0 +1,30 @@ +Displays the MUI theme breakpoints and whether or not they are active. This is a utility component meant to make UI development easier, not as a UI element for production code. + +It must be used under a [ThemeProvider](https://material-ui.com/styles/api/#themeprovider). + +--- + +```jsx +import { ThemeProvider } from '@material-ui/core/styles'; +import { Paper, makeStyles } from '@material-ui/core'; +import webqueue2Theme from "../../theme.js"; +import CurrentBreakPoint from "./CurrentBreakPoint"; + +const theme = webqueue2Theme(false); + +const useStyles = makeStyles({ + paperPadding: { + padding: theme.spacing(1) + } +}); +const classes = useStyles(); + + + + + + +``` +```jsx static + +``` \ No newline at end of file diff --git a/src/components/CurrentBreakPoint/index.js b/src/components/CurrentBreakPoint/index.js new file mode 100644 index 0000000..4720032 --- /dev/null +++ b/src/components/CurrentBreakPoint/index.js @@ -0,0 +1 @@ +export { default } from "./CurrentBreakPoint"; \ No newline at end of file diff --git a/src/components/DirectoryInformation/DirectoryInformation.js b/src/components/DirectoryInformation/DirectoryInformation.js new file mode 100644 index 0000000..6991e5e --- /dev/null +++ b/src/components/DirectoryInformation/DirectoryInformation.js @@ -0,0 +1,50 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { TableContainer, Table, TableRow, TableCell, Paper, makeStyles, useTheme } from "@material-ui/core"; + +export default function DirectoryInformation({ section }) { + + const theme = useTheme(); + const useStyles = makeStyles((theme) => ({ + headerCell: { + borderBottom: "none", + paddingTop: theme.spacing(0.5), + paddingBottom: theme.spacing(0.5), + }, + bodyCell: { + wordBreak: "break-word", + borderBottom: "none", + paddingTop: theme.spacing(0.5), + paddingBottom: theme.spacing(0.5), + }, + stripedRow: { + '&:nth-of-type(odd)': { + backgroundColor: theme.palette.action.hover, + }, + }, + })); + const classes = useStyles(theme); + + return ( + + + {Object.keys(section).map((key) => ( + key === "type" ? "" : + + {key} + {section[key]} + + ))} +
+
+ ); +}; + +DirectoryInformation.propTypes = { + /** The object containing directory information. */ + "section": PropTypes.object +}; + +DirectoryInformation.defaultProps = { + "section": {} +}; \ No newline at end of file diff --git a/src/components/DirectoryInformation/DirectoryInformation.md b/src/components/DirectoryInformation/DirectoryInformation.md new file mode 100644 index 0000000..a71c076 --- /dev/null +++ b/src/components/DirectoryInformation/DirectoryInformation.md @@ -0,0 +1,28 @@ +Displays the directory information as a table. + + +```jsx +import DirectoryInformation from "./DirectoryInformation"; + +const example_directory_data = { + "type": "directory_information", + "Name": "Heyi Feng", + "Login": "feng293", + "Computer": "civil4147pc2.ecn", + "Location": "HAMP 4147", + "Email": "feng293@purdue.edu", + "Phone": "5039154835", + "Office": "", + "UNIX Dir": "None", + "Zero Dir": "U=\\\\myhome.itap.purdue.edu\\myhome\\%username%", + "User ECNDB": "http://eng.purdue.edu/jump/2e29495", + "Host ECNDB": "http://eng.purdue.edu/jump/2eccc3f", + "Subject": "Upgrade system and Abaqus" +}; + + +``` + +```jsx static + +``` \ No newline at end of file diff --git a/src/components/DirectoryInformation/index.js b/src/components/DirectoryInformation/index.js new file mode 100644 index 0000000..d67daa5 --- /dev/null +++ b/src/components/DirectoryInformation/index.js @@ -0,0 +1 @@ +export { default } from "./DirectoryInformation"; \ No newline at end of file diff --git a/src/components/EmailHeader/EmailHeader.js b/src/components/EmailHeader/EmailHeader.js index 3af9c0c..d3c5ccf 100644 --- a/src/components/EmailHeader/EmailHeader.js +++ b/src/components/EmailHeader/EmailHeader.js @@ -1,19 +1,26 @@ import React from "react"; import PropTypes from "prop-types"; -import { Card, CardHeader, makeStyles } from "@material-ui/core"; +import { Paper, Grid, makeStyles, useTheme, Typography } from "@material-ui/core"; +import RelativeTime from "react-relative-time"; import UserAvatar from "../UserAvatar/"; -import webqueue2Theme from "../../theme"; -export default function EmailHeader({name, date, email}){ +export default function EmailHeader({ datetime, from_name, from_email, to, cc }){ - const theme = webqueue2Theme(false); + const theme = useTheme(); const useStyles = makeStyles({ - "verticalPadding": { - paddingTop: theme.spacing(1), - paddingBottom: theme.spacing(1), + "paperWrapper": { + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, + margin: `${theme.spacing(1)}px 0`, + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0 }, - "removeCardHeaderPadding": { - padding: "0" + "removeNegativeMargin": { + margin: 0 + }, + "secondaryInformation": { + backgroundColor: theme.palette.type === "light" ? theme.palette.grey[100] : theme.palette.grey[700], + margin: `${theme.spacing(1)}px ${-theme.spacing(2)}px ${-theme.spacing(1)}px ${-theme.spacing(2)}px`, + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px` } }); const classes = useStyles(); @@ -31,43 +38,88 @@ export default function EmailHeader({name, date, email}){ * return "" * @returns {string} */ - function buildTitle(name, email){ - let title = ""; + function buildEmailNameString(name, email){ + let emailNameString = ""; const isNotEmpty = (value) => { return value === "" || value === null ? false : true; }; if (isNotEmpty(name)){ - title += `${name}`; + emailNameString += `${name}`; }; if (isNotEmpty(email)){ - title += ` <${email}>` + emailNameString += ` <${email}>` } - return title; + return emailNameString; + }; + + /** + * Returns a string of emails with names. + * @param {array} Array of objects containing names and emails. + * @returns {string} String of email with names. + */ + function buildRecipientString(recipients){ + if (recipients.length === 0){ + return "Tits"; + } + + let recipientString = ""; + recipients.map( (recipient, index) => { + recipientString += buildEmailNameString(recipient.name, recipient.email); + index < recipients.length - 1 ? recipientString += ", " : recipientString += ""; + return true; + }) + return recipientString; }; return( - - } - title={buildTitle(name, email)} - subheader={Date(date)} - classes={{root: classes.removeCardHeaderPadding}} - /> - + + + + + + + + {buildEmailNameString(from_name, from_email)} + + + + + + + { (to.length === 0 && cc.length === 0) ? null : + + + {to.length === 0 ? null : <>To: {buildRecipientString(to)}} + + + {cc.length === 0 ? null : <>Cc: {buildRecipientString(cc)} } + + + } + ); }; EmailHeader.propTypes = { - /** Name of the user. */ - "name": PropTypes.string, - /** Date of the message. */ - "date": PropTypes.string, - /** Email address of the user. */ - "email": PropTypes.string + /** Name of message sender. */ + "from_name": PropTypes.string, + /** Date the message was sent in ISO 8601 format. */ + "datetime": PropTypes.string, + /** Email address of message sender. */ + "from_email": PropTypes.string, + /** Array of people the message was sent to */ + "to": PropTypes.array, + /** Array of people the message was cc'd to */ + "cc": PropTypes.array }; EmailHeader.defaultProps = { - "name": "", - "date": "", - "email": "" + "from_name": "Name Unavailable", + "datetime": new Date(1970, 1, 1, 0, 0, 0, 0), + "from_email": "Email Unavailable", + "to": [], + "cc": [] }; \ No newline at end of file diff --git a/src/components/EmailHeader/EmailHeader.md b/src/components/EmailHeader/EmailHeader.md index e163f38..522c3dd 100644 --- a/src/components/EmailHeader/EmailHeader.md +++ b/src/components/EmailHeader/EmailHeader.md @@ -1,9 +1,64 @@ -Description of EmailHeader. +Displays an email header with name, email, time sent, and recipients if present. +--- + +Default usage, without props. ```jsx import EmailHeader from "./EmailHeader"; ``` ```jsx static +``` + +With `datetime`, `from_name` and `from_email` props. When `first_name` is passed, the [UserAvatar](/#/Components/UserAvatar) will display the first letter of the name. +```jsx +import EmailHeader from "./EmailHeader"; + +const demo_data = { + "datetime": "2020-05-11T13:44:12+0000", + "from_name": "Di Wang", + "from_email": "wang2039@purdue.edu", +}; + + + +``` +```jsx static + +``` + +You can add to/cc data by passing `to` and `cc` props. +```jsx +import EmailHeader from "./EmailHeader"; + +const demo_data = { + "datetime": "2020-05-11T13:44:12+0000", + "from_name": "Di Wang", + "from_email": "wang2039@purdue.edu", + "to": [ + { + "name": "'cesite@ecn.purdue.edu'", + "email": "cesite@ecn.purdue.edu" + } + ], + "cc": [ + { + "name": "Zavattieri, Pablo D", + "email": "zavattie@purdue.edu" + } + ] +}; + + + +``` +```jsx static + ``` \ No newline at end of file diff --git a/src/components/ItemBodyView/ItemBodyView.js b/src/components/ItemBodyView/ItemBodyView.js index bed15c1..19e48fb 100644 --- a/src/components/ItemBodyView/ItemBodyView.js +++ b/src/components/ItemBodyView/ItemBodyView.js @@ -2,50 +2,50 @@ import React from "react"; import PropTypes from "prop-types"; import { Timeline, TimelineItem, TimelineSeparator, TimelineConnector, TimelineContent, TimelineDot } from '@material-ui/lab'; import { Typography, makeStyles } from "@material-ui/core"; -import webqueue2Theme from "../../theme"; -import EmailHeader from "../EmailHeader/"; +import DirectoryInformation from "../DirectoryInformation/"; +import Assignment from "../Assignment/"; +import TimelineActionCard from "../TimelineActionCard/"; +import MessageView from "../MessageView/"; import { objectIsEmpty } from "../../utilities"; export default function ItemBodyView({ item }) { - const theme = webqueue2Theme(false); - const useStyles = makeStyles((theme) => ({ - missingOppositeContent: { + const useStyles = makeStyles(() => ({ + "Timeline-root": { + paddingLeft: "0", + paddingRight: "0", + }, + "TimelineContent-root": { + paddingRight: "0", + }, + "TimelineItem-missingOppositeContent": { '&:before': { content: '""', flex: 0, padding: '0', }, - }, + } })); - const classes = useStyles(theme); + const classes = useStyles(); const generateTimelineItem = (section) => { - switch(section.type) { - case "directoryInformation": - if (section.content.length === 0){ - return "No Directory Information"; - } else { - return "Directory Information Present" - } - case "initialMessage": - return( + switch (section.type) { + case "directory_information": + return ( + + ); + case "initial_message": + return ( <> - - {section.content.map((line, index) => {line})} + ); case "edit": - return( - <> - - {`${section.by} assigned thisat ${Date(section.datetime)}`} - - {section.content.map((line) => {line})} - + return ( + ); case "status": - return( + return ( <> {`${section.by} update the status to at ${Date(section.datetime)}`} @@ -53,27 +53,17 @@ export default function ItemBodyView({ item }) { {section.content.map((line) => {line})} ); - case "assign": + case "assignment": return ( - - {`${section.by} assigned this to ${section.to} at ${Date(section.datetime)}`} - + ); - case "replyToUser": - return( - <> - - {`${section.by} replied ${Date(section.datetime)}`} - - {section.content.map((line) => {line})} - + case "reply_to_user": + return ( + ); - case "replyFromUser": - return( - <> - - {section.content.map((line, index) => {line})} - + case "reply_from_user": + return ( + ); default: return "No Match Found"; @@ -81,20 +71,24 @@ export default function ItemBodyView({ item }) { }; return ( - - {objectIsEmpty(item) ? "" : item.content.map((section, index) => { - return ( - - - - - - - {generateTimelineItem(section)} - - - ); - })} + + {objectIsEmpty(item) ? "" : item.content.map((section) => ( + + + + + + + {generateTimelineItem(section)} + + + ))} ); }; diff --git a/src/components/ItemBodyView/ItemBodyView.md b/src/components/ItemBodyView/ItemBodyView.md index 2c224d1..6a741ea 100644 --- a/src/components/ItemBodyView/ItemBodyView.md +++ b/src/components/ItemBodyView/ItemBodyView.md @@ -11,8 +11,7 @@ The ItemBodyView displays the seven actions possible in an item: ```jsx import ItemBodyView from "./ItemBodyView"; -const demoItem = {"queue": "ce", "number": 100, "lastUpdated": "08-27-20 09:49 PM", "headers": [{"type": "Merged-Time", "content": "Tue, 23 Jun 2020 13:31:53 -0400"}, {"type": "Merged-By", "content": "campb303"}, {"type": "QTime", "content": "1"}, {"type": "QTime-Updated-Time", "content": "Tue, 23 Jun 2020 13:28:50 EDT"}, {"type": "QTime-Updated-By", "content": "campb303"}, {"type": "Time", "content": "1"}, {"type": "Time-Updated-Time", "content": "Tue, 23 Jun 2020 13:28:50 EDT"}, {"type": "Time-Updated-By", "content": "campb303"}, {"type": "Replied-Time", "content": "Tue, 23 Jun 2020 13:28:48 -0400"}, {"type": "Replied-By", "content": "campb303"}, {"type": "Edited-Time", "content": "Tue, 23 Jun 2020 13:27:56 -0400"}, {"type": "Edited-By", "content": "campb303"}, {"type": "QAssigned-To", "content": "campb303"}, {"type": "QAssigned-To-Updated-Time", "content": "Tue, 23 Jun 2020 13:27:00 EDT"}, {"type": "QAssigned-To-Updated-By", "content": "campb303"}, {"type": "Assigned-To", "content": "campb303"}, {"type": "Assigned-To-Updated-Time", "content": "Tue, 23 Jun 2020 13:27:00 EDT"}, {"type": "Assigned-To-Updated-By", "content": "campb303"}, {"type": "QStatus", "content": "Dont Delete"}, {"type": "QStatus-Updated-Time", "content": "Tue, 23 Jun 2020 13:26:55 EDT"}, {"type": "QStatus-Updated-By", "content": "campb303"}, {"type": "Status", "content": "Dont Delete"}, {"type": "Status-Updated-Time", "content": "Tue, 23 Jun 2020 13:26:55 EDT"}, {"type": "Status-Updated-By", "content": "campb303"}, {"type": "Date", "content": "Tue, 23 Jun 2020 13:25:51 -0400"}, {"type": "From", "content": "Justin Campbell "}, {"type": "Message-ID", "content": "<911CE050-3240-4980-91DD-9C3EBB8DBCF8@purdue.edu>"}, {"type": "Subject", "content": "Beepboop"}, {"type": "To", "content": "cesite@ecn.purdue.edu"}, {"type": "Content-Type", "content": "text/plain; charset=\"utf-8\""}, {"type": "X-ECN-Queue-Original-Path", "content": "/home/pier/e/queue/Attachments/inbox/2020-06-23/208-original.txt"}, {"type": "X-ECN-Queue-Original-URL", "content": "https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/208-original.txt"}], "content": [{"type": "directoryInformation", "content": []}, {"type": "initialMessage", "datetime": "2020-06-23T13:25:51-0400", "userName": "Justin Campbell", "userEmail": "campb303@purdue.edu", "ccRecipients": [], "content": ["Testtest\n"]}, {"type": "assign", "by": "campb303", "datetime": "2020-06-23T13:27:00-0400", "to": "campb303"}, {"type": "status", "by": "campb303", "datetime": "2020-06-23T13:26:55", "content": ["*** Status updated by: campb303 at: 6/23/2020 13:26:55 ***\n", "Dont Delete\n"]}, {"type": "edit", "by": "campb303", "datetime": "2020-06-23T13:27:56", "content": ["*** Edited by: campb303 at: 06/23/20 13:27:56 ***\n", "\n", "This be an edit my boy\n", "\n", "\n", "\n"]}, {"type": "replyToUser", "by": "campb303", "datetime": "2020-06-23T13:28:18", "content": ["*** Replied by: campb303 at: 06/23/20 13:28:18 ***\n", "\n", "This be a reply my son\n", "\n", "Justin\n", "ECN\n", "\n"]}, {"type": "replyFromUser", "datetime": "2020-06-23T13:30:45-0400", "subject": "Re: Beepboop", "userName": "Justin Campbell", "userEmail": "campb303@purdue.edu", "content": ["=== Additional information supplied by user ===\n", "\n", "Subject: Re: Beepboop\n", "From: Justin Campbell \n", "Date: Tue, 23 Jun 2020 13:30:45 -0400\n", "X-ECN-Queue-Original-Path: /home/pier/e/queue/Attachments/inbox/2020-06-23/212-original.txt\n", "X-ECN-Queue-Original-URL: https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/212-original.txt\n", "\n", "Huzzah!\n", "\n", "===============================================\n", "\n"], "ccRecipients": []}], "isLocked": "ce 100 is locked by knewell using qvi", "userEmail": "campb303@purdue.edu", "userName": "Justin Campbell", "userAlias": "campb303", "assignedTo": "campb303", "subject": "Beepboop", "status": "Dont Delete", "priority": "", "department": "", "building": "", "dateReceived": "2020-06-23T13:25:51-0400"}; - +const demoItem = {"queue": "ce", "number": 100, "lastUpdated": "09-28-20 01:26 PM", "headers": [{"type": "Merged-Time", "content": "Tue, 23 Jun 2020 13:31:53 -0400"}, {"type": "Merged-By", "content": "campb303"}, {"type": "QTime", "content": "1"}, {"type": "QTime-Updated-Time", "content": "Tue, 23 Jun 2020 13:28:50 EDT"}, {"type": "QTime-Updated-By", "content": "campb303"}, {"type": "Time", "content": "1"}, {"type": "Time-Updated-Time", "content": "Tue, 23 Jun 2020 13:28:50 EDT"}, {"type": "Time-Updated-By", "content": "campb303"}, {"type": "Replied-Time", "content": "Tue, 23 Jun 2020 13:28:48 -0400"}, {"type": "Replied-By", "content": "campb303"}, {"type": "Edited-Time", "content": "Tue, 23 Jun 2020 13:27:56 -0400"}, {"type": "Edited-By", "content": "campb303"}, {"type": "QAssigned-To", "content": "campb303"}, {"type": "QAssigned-To-Updated-Time", "content": "Tue, 23 Jun 2020 13:27:00 EDT"}, {"type": "QAssigned-To-Updated-By", "content": "campb303"}, {"type": "Assigned-To", "content": "campb303"}, {"type": "Assigned-To-Updated-Time", "content": "Tue, 23 Jun 2020 13:27:00 EDT"}, {"type": "Assigned-To-Updated-By", "content": "campb303"}, {"type": "QStatus", "content": "Dont Delete"}, {"type": "QStatus-Updated-Time", "content": "Tue, 23 Jun 2020 13:26:55 EDT"}, {"type": "QStatus-Updated-By", "content": "campb303"}, {"type": "Status", "content": "Dont Delete"}, {"type": "Status-Updated-Time", "content": "Tue, 23 Jun 2020 13:26:55 EDT"}, {"type": "Status-Updated-By", "content": "campb303"}, {"type": "Date", "content": "Tue, 23 Jun 2020 13:25:51 -0400"}, {"type": "From", "content": "Justin Campbell "}, {"type": "Message-ID", "content": "<911CE050-3240-4980-91DD-9C3EBB8DBCF8@purdue.edu>"}, {"type": "Subject", "content": "Beepboop"}, {"type": "To", "content": "cesite@ecn.purdue.edu"}, {"type": "Content-Type", "content": "text/plain; charset=\"utf-8\""}, {"type": "X-ECN-Queue-Original-Path", "content": "/home/pier/e/queue/Attachments/inbox/2020-06-23/208-original.txt"}, {"type": "X-ECN-Queue-Original-URL", "content": "https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/208-original.txt"}], "content": [{"type": "directory_information", "Name": "Heyi Feng", "Login": "feng293", "Computer": "civil4147pc2.ecn", "Location": "HAMP 4147", "Email": "feng293@purdue.edu", "Phone": "5039154835", "Office": "", "UNIX Dir": "None", "Zero Dir": "U=\\\\myhome.itap.purdue.edu\\myhome\\%username%", "User ECNDB": "http://eng.purdue.edu/jump/2e29495", "Host ECNDB": "http://eng.purdue.edu/jump/2eccc3f", "Subject": "Upgrade system and Abaqus"}, {"type": "assignment", "datetime": "2020-06-23T13:27:00-0400", "by": "campb303", "to": "campb303"}, {"type": "initial_message", "datetime": "2020-06-23T13:25:51-0400", "from_name": "Justin Campbell", "user_email": "campb303@purdue.edu", "to": [{"name": "", "email": "cesite@ecn.purdue.edu"}], "cc": [], "content": ["Testtest\n"]}, {"type": "status", "datetime": "2020-06-23T13:26:55", "by": "campb303", "content": ["Dont Delete\n"]}, {"type": "edit", "datetime": "2020-06-23T13:27:56", "by": "campb303", "content": ["This be an edit my boy\n"]}, {"type": "reply_to_user", "datetime": "2020-06-23T13:28:18", "by": "campb303", "content": ["This be a reply my son\n", "\n", "Justin\n", "ECN\n"]}, {"type": "reply_from_user", "datetime": "2020-06-23T13:30:45-0400", "from_name": "Justin Campbell", "from_email": "campb303@purdue.edu", "cc": [], "content": ["X-ECN-Queue-Original-Path: /home/pier/e/queue/Attachments/inbox/2020-06-23/212-original.txt\n", "X-ECN-Queue-Original-URL: https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/212-original.txt\n", "\n", "Huzzah!\n"]}], "isLocked": "ce 100 is locked by knewell using qvi", "userEmail": "campb303@purdue.edu", "userName": "Justin Campbell", "userAlias": "campb303", "assignedTo": "campb303", "subject": "Beepboop", "status": "Dont Delete", "priority": "", "department": "", "building": "", "dateReceived": "2020-06-23T13:25:51-0400"};
diff --git a/src/components/ItemMetadataView/ItemMetadataView.js b/src/components/ItemMetadataView/ItemMetadataView.js index e0f27bc..05f6357 100644 --- a/src/components/ItemMetadataView/ItemMetadataView.js +++ b/src/components/ItemMetadataView/ItemMetadataView.js @@ -1,14 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { makeStyles, Typography } from '@material-ui/core'; +import { makeStyles, Typography, useTheme } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; -import webqueue2Theme from "../../theme"; -import EmailHeader from '../EmailHeader/EmailHeader'; export default function ItemMetadataView({item}){ - const theme = webqueue2Theme(false); + const theme = useTheme(); const useStyles = makeStyles({ "verticalSpacing": { marginTop: theme.spacing(1), @@ -36,8 +34,6 @@ export default function ItemMetadataView({item}){ {item.subject}
- - Status, assignments, priority, refile, archive and delete controls coming soon to a theater near you. diff --git a/src/components/ItemTableAppBar/ItemTableAppBar.md b/src/components/ItemTableAppBar/ItemTableAppBar.md index 8434018..947a46e 100644 --- a/src/components/ItemTableAppBar/ItemTableAppBar.md +++ b/src/components/ItemTableAppBar/ItemTableAppBar.md @@ -2,11 +2,11 @@ The ItemTableAppBar is the primary toolbar for the [ItemTable](/#/Components/Ite ```jsx import React, { useState } from "react"; -import webqueue2Theme from "../../theme.js"; +import { useTheme } from '@material-ui/core'; const [darkMode, setDarkMode] = useState(false); -const theme = webqueue2Theme(darkMode); +const theme = useTheme(); ``` diff --git a/src/components/ItemView/ItemView.js b/src/components/ItemView/ItemView.js index f86bb39..3113d50 100644 --- a/src/components/ItemView/ItemView.js +++ b/src/components/ItemView/ItemView.js @@ -1,23 +1,26 @@ import React from 'react'; import PropTypes from "prop-types"; -import { Paper, makeStyles } from '@material-ui/core'; +import { Paper, makeStyles, useTheme } from '@material-ui/core'; import ItemMetadataView from "../ItemMetadataView/" import ItemBodyView from "../ItemBodyView"; export default function ItemView({ activeItem }){ - const useStyles = makeStyles((theme) => ({ + const theme = useTheme(); + const useStyles = makeStyles({ "paperPadding": { - padding: theme.spacing(2), + paddingTop: theme.spacing(1), + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + border: "none" } - })); - + }); const classes = useStyles(); return( - + ); }; diff --git a/src/components/ItemViewAppBar/ItemViewAppBar.md b/src/components/ItemViewAppBar/ItemViewAppBar.md index 3eba2e4..738202e 100644 --- a/src/components/ItemViewAppBar/ItemViewAppBar.md +++ b/src/components/ItemViewAppBar/ItemViewAppBar.md @@ -3,9 +3,9 @@ The ItemViewAppBar is the primary toolbar for the [ItemView](/#/Components/ItemV ```jsx import React, { useState } from "react"; import ItemViewAppBar from "./ItemViewAppBar"; -import webqueue2Theme from "../../theme.js"; +import { useTheme } from '@material-ui/core'; -const theme = webqueue2Theme(false); +const theme = useTheme(); const [sidebarOpen, setSidebarOpen] = useState(false); diff --git a/src/components/MessageView/MessageView.js b/src/components/MessageView/MessageView.js new file mode 100644 index 0000000..e902a79 --- /dev/null +++ b/src/components/MessageView/MessageView.js @@ -0,0 +1,51 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Paper, Typography, makeStyles, useTheme } from "@material-ui/core"; +import EmailHeader from "../EmailHeader/"; + +export default function MessageView({ datetime, from_name, from_email, to, cc, content }){ + + const theme = useTheme(); + const useStyles = makeStyles({ + "content": { + border: "none", + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px ${theme.spacing(2)}px ${theme.spacing(2)}px` + } + }); + const classes = useStyles(); + + return ( + + + + {content.map((line) => ( + line === "\n" ?
: {line} + ))} +
+
+ ); +}; + +MessageView.propTypes = { + /** Name of message sender. */ + "from_name": PropTypes.string, + /** Date the message was sent in ISO 8601 format. */ + "datetime": PropTypes.string, + /** Email address of message sender. */ + "from_email": PropTypes.string, + /** Array of people the message was sent to. */ + "to": PropTypes.array, + /** Array of people the message was cc'd to. */ + "cc": PropTypes.array, + /** The content of the message as an array of line with non-printable characters. */ + "content": PropTypes.array +}; + +MessageView.defaultProps = { + "from_name": "Name Unavailable", + "datetime": new Date(1970, 1, 1, 0, 0, 0, 0), + "from_email": "Email Unavailable", + "to": [], + "cc": [], + "content": [] +}; \ No newline at end of file diff --git a/src/components/MessageView/MessageView.md b/src/components/MessageView/MessageView.md new file mode 100644 index 0000000..ab2b2b4 --- /dev/null +++ b/src/components/MessageView/MessageView.md @@ -0,0 +1,53 @@ +The MessageView displays an [EmailHeader](/#/Components/EmailHeader) and the content of a message. + +--- + +```jsx +import MessageView from "./MessageView"; + +
+ +
+``` +```jsx static + +``` + +```jsx +import MessageView from "./MessageView"; + +demo_data = { + "datetime": "2020-04-15T15:45:45+0000", + "from_name": "Ricksy, Jennifer R", + "from_email": "jricksy@purdue.edu", + "to": [ + { + "name": "csite@ecn.purdue.edu", + "email": "csite@ecn.purdue.edu" + } + ], + "cc": [ + + ], + "content": [ + "I still can't get this to work. And now the \"trial\" version that I was\n", + "using has expired so I can't do anything with a pdf.\n", + "\n", + "Jenny\n", + "\n", + "Jenny Ricksy\n", + "Burke Graduate Program Administrator\n", + "Lyles School of Civil Engineering\n", + "Ph: 765-494-2436 Fax: 765-494-0395\n", + "HAMP 1141G\n" + ] +}; + + +
+ +
+``` +```jsx static + +``` \ No newline at end of file diff --git a/src/components/MessageView/index.js b/src/components/MessageView/index.js new file mode 100644 index 0000000..7c7e2e9 --- /dev/null +++ b/src/components/MessageView/index.js @@ -0,0 +1 @@ +export { default } from "./MessageView"; \ No newline at end of file diff --git a/src/components/TeamMemberCard/TeamMemberCard.js b/src/components/TeamMemberCard/TeamMemberCard.js index febfa5e..7a7b4b5 100644 --- a/src/components/TeamMemberCard/TeamMemberCard.js +++ b/src/components/TeamMemberCard/TeamMemberCard.js @@ -1,13 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types' import { Card, CardHeader, Avatar, IconButton, CardContent, - CardActions, Link, makeStyles } from '@material-ui/core'; + CardActions, Link, makeStyles, useTheme } from '@material-ui/core'; import WebsiteIcon from '@material-ui/icons/Language'; -import webqueue2Theme from "../../theme"; + export default function TeamMemberCard({firstName, lastName, imageUrl, websiteUrl, children}){ - const theme = webqueue2Theme(); + const theme = useTheme(); const useStyles = () => makeStyles({ "avatarImageLarge": { diff --git a/src/components/TimelineActionCard/TimelineActionCard.js b/src/components/TimelineActionCard/TimelineActionCard.js new file mode 100644 index 0000000..431cd3e --- /dev/null +++ b/src/components/TimelineActionCard/TimelineActionCard.js @@ -0,0 +1,65 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Typography, Paper, makeStyles, useTheme } from '@material-ui/core'; +import clsx from "clsx"; +import RelativeTime from "react-relative-time"; + +export default function TimelineActionCard({ type, datetime, by, content }){ + + const theme = useTheme(); + + const types = { + "edit": { + "verbage": "added an edit", + "coloring": theme.palette.edit.main + }, + "reply_to_user": { + "verbage": "replied", + "coloring": theme.palette.reply_to_user.main + } + } + + const useStyles = makeStyles({ + "Paper-root": { + overflow: "hidden" + }, + "headerColor": { + backgroundColor: types[type].coloring + }, + "padding": { + padding: theme.spacing(1) + } + + }); + const classes = useStyles(); + + return( + +
+ {by} {types[type].verbage} +
+
+ {content.map((line) => ( + line === "\n" ?
: {line} + ))} +
+
+ ); +} + +TimelineActionCard.propTypes = { + "type": PropTypes.oneOf([ + "edit", + "reply_to_user" + ]), + /** ISO 8601 formatted time string. */ + "datetime": PropTypes.string.isRequired, + /** The name of the person who added the edit. */ + "by": PropTypes.string.isRequired, + /** An array of strings containing the content of the edit. */ + "content": PropTypes.array.isRequired +}; + +TimelineActionCard.defaultProps = { + "type": "edit", +}; \ No newline at end of file diff --git a/src/components/TimelineActionCard/TimelineActionCard.md b/src/components/TimelineActionCard/TimelineActionCard.md new file mode 100644 index 0000000..7c8a8ee --- /dev/null +++ b/src/components/TimelineActionCard/TimelineActionCard.md @@ -0,0 +1,60 @@ +Renders a card like view for an action with free form text content like an Edit or Reply. + +```jsx +import { ThemeProvider } from "@material-ui/core/styles"; +import webqueue2Theme from "../../theme"; +import TimelineActionCard from "./TimelineActionCard"; + +const theme = webqueue2Theme(false); + + +
+
+ +
+
+ +
+
+
+``` + +```jsx static + + + +``` \ No newline at end of file diff --git a/src/components/TimelineActionCard/index.js b/src/components/TimelineActionCard/index.js new file mode 100644 index 0000000..48454f2 --- /dev/null +++ b/src/components/TimelineActionCard/index.js @@ -0,0 +1 @@ +export { default } from "./TimelineActionCard"; \ No newline at end of file diff --git a/src/components/UserAvatar/UserAvatar.js b/src/components/UserAvatar/UserAvatar.js index 9a56370..652ce37 100644 --- a/src/components/UserAvatar/UserAvatar.js +++ b/src/components/UserAvatar/UserAvatar.js @@ -2,20 +2,19 @@ import React from "react"; import PropTypes from "prop-types"; import { Avatar } from "@material-ui/core"; -export default function UserAvatar({userName, userAlias}){ - +export default function UserAvatar({ name, ...avatarProps }){ return( - - {userName === "" ? null : userName.charAt(0)} + + {name === "" ? null : name.charAt(0)} ); }; UserAvatar.propTypes = { /** The name of the user. */ - "userName": PropTypes.string + "name": PropTypes.string }; UserAvatar.defaultProps = { - "userName": "" + "name": "" }; \ No newline at end of file diff --git a/src/components/UserAvatar/UserAvatar.md b/src/components/UserAvatar/UserAvatar.md index 40c9a48..36669b6 100644 --- a/src/components/UserAvatar/UserAvatar.md +++ b/src/components/UserAvatar/UserAvatar.md @@ -1,23 +1,36 @@ -The UserAvatar displays a graphical representation of a person and is meant to be used in conjunction with other identifiers like names or emails. +The UserAvatar displays a graphical representation of a person. --- -The UserAvatar will the first letter of the `userName` prop. +## Default Usage +Used without any props, the UserAvatar will display a generic person icon. ```jsx import UserAvatar from "./UserAvatar"; - -``` + +``` ```jsx static - + ``` -If no value, a null value, or an empty string is passed to the UserAvatar, it will fall back to a generic icon. +## With A Name +If the `name` prop is passed, the UserAvatar will display the first letter of the name. ```jsx import UserAvatar from "./UserAvatar"; - + + ``` +```jsx static + +``` + +## Other Props +All props other than `name` are passed to the underlying [MUI Avatar component](https://material-ui.com/api/avatar/). +```jsx +import UserAvatar from "./UserAvatar"; + +``` ```jsx static - + ``` \ No newline at end of file diff --git a/src/theme.js b/src/theme.js index bb9281a..685626a 100644 --- a/src/theme.js +++ b/src/theme.js @@ -20,9 +20,14 @@ export default function theme(darkMode = false) { }, "type": darkMode ? "dark" : "light", - "edit": { - main: "#ffe56433", + main: "rgba(255, 229, 100, 0.2)", + }, + "reply_to_user": { + main: "rgba(99, 125, 255, 0.2)", + }, + "reply_from_user": { + main: "rgba(99, 255, 151, 0.2)", } }, })