diff options
-rw-r--r-- | webui/src/App.js | 8 | ||||
-rw-r--r-- | webui/src/bug/Bug.js | 78 | ||||
-rw-r--r-- | webui/src/bug/BugQuery.js (renamed from webui/src/bug/BugPage.js) | 6 | ||||
-rw-r--r-- | webui/src/bug/Comment.js | 43 | ||||
-rw-r--r-- | webui/src/bug/Message.js | 75 | ||||
-rw-r--r-- | webui/src/bug/Timeline.js | 43 | ||||
-rw-r--r-- | webui/src/bug/TimelineQuery.js | 39 | ||||
-rw-r--r-- | webui/src/list/ListQuery.js (renamed from webui/src/list/ListPage.js) | 7 |
8 files changed, 231 insertions, 68 deletions
diff --git a/webui/src/App.js b/webui/src/App.js index a8157662..47a31e45 100644 --- a/webui/src/App.js +++ b/webui/src/App.js @@ -7,8 +7,8 @@ import React from 'react' import { Route, Switch, withRouter } from 'react-router' import { Link } from 'react-router-dom' -import BugPage from './bug/BugPage' -import ListPage from './list/ListPage' +import BugQuery from './bug/BugQuery' +import ListQuery from './list/ListQuery' const styles = theme => ({ appTitle: { @@ -30,8 +30,8 @@ const App = ({location, classes}) => ( </Toolbar> </AppBar> <Switch> - <Route path="/" exact component={ListPage}/> - <Route path="/bug/:id" exact component={BugPage}/> + <Route path="/" exact component={ListQuery}/> + <Route path="/bug/:id" exact component={BugQuery}/> </Switch> </React.Fragment> ) diff --git a/webui/src/bug/Bug.js b/webui/src/bug/Bug.js index 33ecdd79..3847c755 100644 --- a/webui/src/bug/Bug.js +++ b/webui/src/bug/Bug.js @@ -1,39 +1,89 @@ import { withStyles } from '@material-ui/core/styles' +import Tooltip from '@material-ui/core/Tooltip/Tooltip' +import Typography from '@material-ui/core/Typography/Typography' import gql from 'graphql-tag' +import * as moment from 'moment' import React from 'react' - -import Comment from './Comment' +import TimelineQuery from './TimelineQuery' const styles = theme => ({ main: { maxWidth: 600, margin: 'auto', marginTop: theme.spacing.unit * 4 + }, + header: {}, + title: { + ...theme.typography.headline + }, + id: { + ...theme.typography.subheading, + marginLeft: 15, + }, + container: { + display: 'flex' + }, + timeline: { + width: '70%', + marginTop: 20, + marginRight: 20, + }, + sidebar: { + width: '30%' + }, + label: { + backgroundColor: '#da9898', + borderRadius: '3px', + paddingLeft: '10px', + margin: '2px 20px auto 2px', + fontWeight: 'bold', } }) const Bug = ({bug, classes}) => ( <main className={classes.main}> + <div className={classes.header}> + <span className={classes.title}>{bug.title}</span> + <span className={classes.id}>{bug.humanId}</span> + + <Typography color={'textSecondary'}> + <Tooltip title={bug.author.email}><span>{bug.author.name}</span></Tooltip> + <span> opened this bug </span> + <Tooltip title={moment(bug.createdAt).format('MMMM D, YYYY, h:mm a')}> + <span> {moment(bug.createdAt).fromNow()} </span> + </Tooltip> + </Typography> + </div> - {bug.comments.edges.map(({cursor, node}) => ( - <Comment key={cursor} comment={node}/> - ))} + <div className={classes.container}> + <div className={classes.timeline}> + <TimelineQuery id={bug.id}/> + </div> + <div className={classes.sidebar}> + <Typography variant={'subheading'}>Labels</Typography> + {bug.labels.map(l => ( + <Typography key={l} className={classes.label}> + {l} + </Typography> + ))} + </div> + </div> </main> ) Bug.fragment = gql` fragment Bug on Bug { - comments(first: 10) { - edges { - cursor - node { - ...Comment - } - } + id + humanId + status + title + labels + createdAt + author { + email + name } } - - ${Comment.fragment} ` export default withStyles(styles)(Bug) diff --git a/webui/src/bug/BugPage.js b/webui/src/bug/BugQuery.js index a91030ab..22421414 100644 --- a/webui/src/bug/BugPage.js +++ b/webui/src/bug/BugQuery.js @@ -17,14 +17,14 @@ const QUERY = gql` ${Bug.fragment} ` -const BugPage = ({match}) => ( +const BugQuery = ({match}) => ( <Query query={QUERY} variables={{id: match.params.id}}> {({loading, error, data}) => { if (loading) return <CircularProgress/> - if (error) return <p>Error.</p> + if (error) return <p>Error: {error}</p> return <Bug bug={data.defaultRepository.bug}/> }} </Query> ) -export default BugPage +export default BugQuery diff --git a/webui/src/bug/Comment.js b/webui/src/bug/Comment.js deleted file mode 100644 index bc108083..00000000 --- a/webui/src/bug/Comment.js +++ /dev/null @@ -1,43 +0,0 @@ -import Avatar from '@material-ui/core/Avatar' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import CardHeader from '@material-ui/core/CardHeader' -import { withStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' -import gql from 'graphql-tag' -import React from 'react' - -const styles = theme => ({ - comment: { - marginBottom: theme.spacing.unit - } -}) - -const Comment = withStyles(styles)(({comment, classes}) => ( - <Card className={classes.comment}> - <CardHeader - avatar={ - <Avatar aria-label={comment.author.name}> - {comment.author.name[0].toUpperCase()} - </Avatar> - } - title={comment.author.name} - subheader={comment.author.email} - /> - <CardContent> - <Typography component="p">{comment.message}</Typography> - </CardContent> - </Card> -)) - -Comment.fragment = gql` - fragment Comment on Comment { - message - author { - name - email - } - } -` - -export default withStyles(styles)(Comment) diff --git a/webui/src/bug/Message.js b/webui/src/bug/Message.js new file mode 100644 index 00000000..04c7dfab --- /dev/null +++ b/webui/src/bug/Message.js @@ -0,0 +1,75 @@ +import { withStyles } from '@material-ui/core/styles' +import Tooltip from '@material-ui/core/Tooltip/Tooltip' +import Typography from '@material-ui/core/Typography' +import gql from 'graphql-tag' +import * as moment from 'moment' +import React from 'react' + +const styles = theme => ({ + header: { + ...theme.typography.body2, + padding: '3px 3px 3px 6px', + backgroundColor: '#f1f8ff', + border: '1px solid #d1d5da', + borderTopLeftRadius: 3, + borderTopRightRadius: 3, + }, + author: { + ...theme.typography.body2, + fontWeight: 'bold' + }, + message: { + borderLeft: '1px solid #d1d5da', + borderRight: '1px solid #d1d5da', + borderBottom: '1px solid #d1d5da', + borderBottomLeftRadius: 3, + borderBottomRightRadius: 3, + backgroundColor: '#fff', + minHeight: 50 + } +}) + +const Message = ({message, classes}) => ( + <div> + <div className={classes.header}> + <Tooltip title={message.author.email}> + <span className={classes.author}>{message.author.name}</span> + </Tooltip> + <span> commented </span> + <Tooltip title={moment(message.date).format('MMMM D, YYYY, h:mm a')}> + <span> {moment(message.date).fromNow()} </span> + </Tooltip> + </div> + <div className={classes.message}> + <Typography>{message.message}</Typography> + </div> + </div> +) + +Message.createFragment = gql` + fragment Create on Operation { + ... on CreateOperation { + date + author { + name + email + } + message + } + } +` + +Message.commentFragment = gql` + fragment Comment on Operation { + ... on AddCommentOperation { + date + author { + name + email + } + message + } + } +` + +export default withStyles(styles)(Message) diff --git a/webui/src/bug/Timeline.js b/webui/src/bug/Timeline.js new file mode 100644 index 00000000..0c4100ec --- /dev/null +++ b/webui/src/bug/Timeline.js @@ -0,0 +1,43 @@ +import { withStyles } from '@material-ui/core/styles' +import React from 'react' +import Message from './Message' + +const styles = theme => ({ + main: { + '& > *:not(:last-child)': { + marginBottom: 10 + } + } +}) + +class Timeline extends React.Component { + + props: { + ops: Array, + fetchMore: (any) => any, + classes: any, + } + + render() { + const {ops, classes} = this.props + + return ( + <div className={classes.main}> + { ops.map((op, index) => { + switch (op.__typename) { + case 'CreateOperation': + return <Message key={index} message={op}/> + case 'AddCommentOperation': + return <Message key={index} message={op}/> + + default: + console.log('unsupported operation type ' + op.__typename) + return null + } + })} + </div> + ) + } +} + +export default withStyles(styles)(Timeline) diff --git a/webui/src/bug/TimelineQuery.js b/webui/src/bug/TimelineQuery.js new file mode 100644 index 00000000..e773aac0 --- /dev/null +++ b/webui/src/bug/TimelineQuery.js @@ -0,0 +1,39 @@ +import CircularProgress from '@material-ui/core/CircularProgress' +import gql from 'graphql-tag' +import React from 'react' +import { Query } from 'react-apollo' +import Timeline from './Timeline' +import Message from './Message' + +const QUERY = gql` + query($id: String!, $first: Int = 10, $after: String) { + defaultRepository { + bug(prefix: $id) { + operations(first: $first, after: $after) { + nodes { + ...Create + ...Comment + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + } + ${Message.createFragment} + ${Message.commentFragment} +` + +const TimelineQuery = ({id}) => ( + <Query query={QUERY} variables={{id}}> + {({loading, error, data, fetchMore}) => { + if (loading) return <CircularProgress/> + if (error) return <p>Error: {error}</p> + return <Timeline ops={data.defaultRepository.bug.operations.nodes} fetchMore={fetchMore}/> + }} + </Query> +) + +export default TimelineQuery diff --git a/webui/src/list/ListPage.js b/webui/src/list/ListQuery.js index b7de735f..3b57fcc2 100644 --- a/webui/src/list/ListPage.js +++ b/webui/src/list/ListQuery.js @@ -3,7 +3,6 @@ import CircularProgress from '@material-ui/core/CircularProgress' import gql from 'graphql-tag' import React from 'react' import { Query } from 'react-apollo' - import BugRow from './BugRow' import List from './List' @@ -32,14 +31,14 @@ const QUERY = gql` ${BugRow.fragment} ` -const ListPage = () => ( +const ListQuery = () => ( <Query query={QUERY}> {({loading, error, data, fetchMore}) => { if (loading) return <CircularProgress/> - if (error) return <p>Error.</p> + if (error) return <p>Error: {error}</p> return <List bugs={data.defaultRepository.bugs} fetchMore={fetchMore}/> }} </Query> ) -export default ListPage +export default ListQuery |