aboutsummaryrefslogtreecommitdiffstats
path: root/webui
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2018-08-15 20:31:53 +0200
committerMichael Muré <batolettre@gmail.com>2018-08-15 20:31:53 +0200
commit1984d4343db770fc2c8e251a81f1ab997a4c4d5e (patch)
treed60630e0d36ea24ee9eaf16653194703f4b46dd8 /webui
parent2530cee1eac225924e1119554cf475cdc46ed7dc (diff)
downloadgit-bug-1984d4343db770fc2c8e251a81f1ab997a4c4d5e.tar.gz
webui: rework of the bug page with a timeline
Diffstat (limited to 'webui')
-rw-r--r--webui/src/App.js8
-rw-r--r--webui/src/bug/Bug.js78
-rw-r--r--webui/src/bug/BugQuery.js (renamed from webui/src/bug/BugPage.js)6
-rw-r--r--webui/src/bug/Comment.js43
-rw-r--r--webui/src/bug/Message.js75
-rw-r--r--webui/src/bug/Timeline.js43
-rw-r--r--webui/src/bug/TimelineQuery.js39
-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