diff options
Diffstat (limited to 'webui/src/list')
-rw-r--r-- | webui/src/list/BugRow.js | 102 | ||||
-rw-r--r-- | webui/src/list/List.js | 135 | ||||
-rw-r--r-- | webui/src/list/ListPage.js | 45 |
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 |