aboutsummaryrefslogtreecommitdiffstats
path: root/webui/src/list
diff options
context:
space:
mode:
Diffstat (limited to 'webui/src/list')
-rw-r--r--webui/src/list/BugRow.js102
-rw-r--r--webui/src/list/List.js135
-rw-r--r--webui/src/list/ListPage.js45
3 files changed, 282 insertions, 0 deletions
diff --git a/webui/src/list/BugRow.js b/webui/src/list/BugRow.js
new file mode 100644
index 00000000..1ce5ea06
--- /dev/null
+++ b/webui/src/list/BugRow.js
@@ -0,0 +1,102 @@
+import { withStyles } from '@material-ui/core/styles'
+import TableCell from '@material-ui/core/TableCell/TableCell'
+import TableRow from '@material-ui/core/TableRow/TableRow'
+import Tooltip from '@material-ui/core/Tooltip/Tooltip'
+import Typography from '@material-ui/core/Typography'
+import ErrorOutline from '@material-ui/icons/ErrorOutline'
+import gql from 'graphql-tag'
+import * as moment from 'moment'
+import React from 'react'
+import { Link } from 'react-router-dom'
+
+const Open = ({className}) => <Tooltip title="Open">
+ <ErrorOutline nativeColor='#28a745' className={className}/>
+</Tooltip>
+
+const Closed = ({className}) => <Tooltip title="Closed">
+ <ErrorOutline nativeColor='#cb2431' className={className}/>
+</Tooltip>
+
+const Status = ({status, className}) => {
+ switch (status) {
+ case 'OPEN':
+ return <Open className={className}/>
+ case 'CLOSED':
+ return <Closed className={className}/>
+ default:
+ return 'unknown status ' + status
+ }
+}
+
+const styles = theme => ({
+ cell: {
+ display: 'flex',
+ alignItems: 'center'
+ },
+ status: {
+ margin: 10
+ },
+ expand: {
+ width: '100%'
+ },
+ title: {
+ display: 'inline-block',
+ textDecoration: 'none'
+ },
+ labels: {
+ display: 'inline-block',
+ paddingLeft: theme.spacing.unit,
+ '&>span': {
+ padding: '0 4px',
+ margin: '0 1px',
+ backgroundColor: '#da9898',
+ borderRadius: '3px'
+ }
+ }
+})
+
+const BugRow = ({bug, classes}) => (
+ <TableRow hover>
+ <TableCell className={classes.cell}>
+ <Status status={bug.status} className={classes.status}/>
+ <div className={classes.expand}>
+ <Link to={'bug/' + bug.humanId}>
+ <div className={classes.expand}>
+
+ <Typography variant={'title'} className={classes.title}>
+ {bug.title}
+ </Typography>
+ <span className={classes.labels}>
+ {bug.labels.map(l => (
+ <span key={l}>{l}</span>)
+ )}
+ </span>
+ </div>
+ </Link>
+ <Typography color={'textSecondary'}>
+ {bug.humanId} opened
+ <Tooltip title={moment(bug.createdAt).format('MMMM D, YYYY, h:mm a')}>
+ <span> {moment(bug.createdAt).fromNow()} </span>
+ </Tooltip>
+ by {bug.author.name}
+ </Typography>
+ </div>
+ </TableCell>
+ </TableRow>
+)
+
+BugRow.fragment = gql`
+ fragment BugRow on Bug {
+ id
+ humanId
+ title
+ status
+ createdAt
+ labels
+ author {
+ name
+ }
+ }
+`
+
+export default withStyles(styles)(BugRow)
diff --git a/webui/src/list/List.js b/webui/src/list/List.js
new file mode 100644
index 00000000..880782c7
--- /dev/null
+++ b/webui/src/list/List.js
@@ -0,0 +1,135 @@
+import { withStyles } from '@material-ui/core/styles'
+import Table from '@material-ui/core/Table/Table'
+import TableBody from '@material-ui/core/TableBody/TableBody'
+import TablePagination from '@material-ui/core/TablePagination/TablePagination'
+import React from 'react'
+import BugRow from './BugRow'
+
+const styles = theme => ({
+ main: {
+ maxWidth: 600,
+ margin: 'auto',
+ marginTop: theme.spacing.unit * 4
+ }
+})
+
+class List extends React.Component {
+
+ props: {
+ bugs: Array,
+ fetchMore: (any) => any,
+ classes: any,
+ }
+
+ state = {
+ page: 0,
+ rowsPerPage: 10,
+ lastQuery: {}
+ }
+
+ handleChangePage = (event, page) => {
+ const {bugs, fetchMore} = this.props
+ const {rowsPerPage} = this.state
+ const pageInfo = bugs.pageInfo
+
+ if (page === this.state.page + 1) {
+ if (!pageInfo.hasNextPage) {
+ return
+ }
+
+ const variables = {
+ after: pageInfo.endCursor,
+ first: rowsPerPage
+ }
+
+ fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
+ })
+
+ this.setState({page, lastQuery: variables})
+ return
+ }
+
+ if (page === this.state.page - 1) {
+ if (!pageInfo.hasPreviousPage) {
+ return
+ }
+
+ const variables = {
+ before: pageInfo.startCursor,
+ last: rowsPerPage
+ }
+
+ fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
+ })
+
+ this.setState({page, lastQuery: variables})
+ return
+ }
+
+ throw new Error('non neighbour page pagination is not supported')
+ }
+
+ handleChangeRowsPerPage = event => {
+ const {fetchMore} = this.props
+ const {lastQuery} = this.state
+ const rowsPerPage = event.target.value
+
+ const variables = lastQuery
+
+ if (lastQuery.first) {
+ variables.first = rowsPerPage
+ } else if (lastQuery.last) {
+ variables.last = rowsPerPage
+ } else {
+ variables.first = rowsPerPage
+ }
+
+ fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
+ })
+
+ this.setState({rowsPerPage, lastQuery: variables})
+ }
+
+ updateQuery = (previousResult, {fetchMoreResult}) => {
+ return fetchMoreResult ? fetchMoreResult : previousResult
+ }
+
+ render() {
+ const {classes, bugs} = this.props
+ const {page, rowsPerPage} = this.state
+
+ return (
+ <main className={classes.main}>
+ <Table className={classes.table}>
+ <TableBody>
+ {bugs.edges.map(({cursor, node}) => (
+ <BugRow bug={node} key={cursor}/>
+ ))}
+ </TableBody>
+ </Table>
+ <TablePagination
+ component="div"
+ count={bugs.totalCount}
+ rowsPerPage={rowsPerPage}
+ page={page}
+ backIconButtonProps={{
+ 'aria-label': 'Previous Page'
+ }}
+ nextIconButtonProps={{
+ 'aria-label': 'Next Page'
+ }}
+ onChangePage={this.handleChangePage}
+ onChangeRowsPerPage={this.handleChangeRowsPerPage}
+ />
+ </main>
+ )
+ }
+}
+
+export default withStyles(styles)(List)
diff --git a/webui/src/list/ListPage.js b/webui/src/list/ListPage.js
new file mode 100644
index 00000000..b7de735f
--- /dev/null
+++ b/webui/src/list/ListPage.js
@@ -0,0 +1,45 @@
+// @flow
+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'
+
+const QUERY = gql`
+ query($first: Int = 10, $last: Int, $after: String, $before: String) {
+ defaultRepository {
+ bugs: allBugs(first: $first, last: $last, after: $after, before: $before) {
+ totalCount
+ edges {
+ cursor
+ node {
+ ...BugRow
+ }
+ }
+ pageInfo{
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+ }
+ }
+ }
+ }
+
+
+ ${BugRow.fragment}
+`
+
+const ListPage = () => (
+ <Query query={QUERY}>
+ {({loading, error, data, fetchMore}) => {
+ if (loading) return <CircularProgress/>
+ if (error) return <p>Error.</p>
+ return <List bugs={data.defaultRepository.bugs} fetchMore={fetchMore}/>
+ }}
+ </Query>
+)
+
+export default ListPage