diff options
Diffstat (limited to 'webui')
-rw-r--r-- | webui/package-lock.json | 6 | ||||
-rw-r--r-- | webui/package.json | 2 | ||||
-rw-r--r-- | webui/src/App.tsx | 2 | ||||
-rw-r--r-- | webui/src/pages/bug/CommentInput.tsx | 96 | ||||
-rw-r--r-- | webui/src/pages/list/ListQuery.tsx | 57 | ||||
-rw-r--r-- | webui/src/pages/new/NewBug.graphql | 7 | ||||
-rw-r--r-- | webui/src/pages/new/NewBugPage.tsx | 121 |
7 files changed, 272 insertions, 19 deletions
diff --git a/webui/package-lock.json b/webui/package-lock.json index 47d5e17d..8809a0ba 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -17570,9 +17570,9 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "prettier": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", - "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true }, "prettier-linter-helpers": { diff --git a/webui/package.json b/webui/package.json index 4f2a8da4..39696a25 100644 --- a/webui/package.json +++ b/webui/package.json @@ -38,7 +38,7 @@ "eslint-config-prettier": "^6.12.0", "eslint-plugin-graphql": "^4.0.0", "eslint-plugin-prettier": "^3.1.4", - "prettier": "^2.1.2" + "prettier": "^2.2.1" }, "scripts": { "start": "npm run generate && react-scripts start", diff --git a/webui/src/App.tsx b/webui/src/App.tsx index 16663870..3a5ef025 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -4,12 +4,14 @@ import { Route, Switch } from 'react-router'; import Layout from './layout'; import BugPage from './pages/bug'; import ListPage from './pages/list'; +import NewBugPage from './pages/new/NewBugPage'; export default function App() { return ( <Layout> <Switch> <Route path="/" exact component={ListPage} /> + <Route path="/new" exact component={NewBugPage} /> <Route path="/bug/:id" exact component={BugPage} /> </Switch> </Layout> diff --git a/webui/src/pages/bug/CommentInput.tsx b/webui/src/pages/bug/CommentInput.tsx new file mode 100644 index 00000000..540a53f7 --- /dev/null +++ b/webui/src/pages/bug/CommentInput.tsx @@ -0,0 +1,96 @@ +import React, { useState, useEffect } from 'react'; + +import Tab from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; +import TextField from '@material-ui/core/TextField'; +import { makeStyles } from '@material-ui/core/styles'; + +import Content from 'src/components/Content'; + +const useStyles = makeStyles((theme) => ({ + container: { + margin: theme.spacing(2, 0), + padding: theme.spacing(0, 2, 2, 2), + }, + textarea: {}, + tabContent: { + margin: theme.spacing(2, 0), + }, + preview: { + borderBottom: `solid 3px ${theme.palette.grey['200']}`, + minHeight: '5rem', + }, + actions: { + display: 'flex', + justifyContent: 'flex-end', + }, +})); + +type TabPanelProps = { + children: React.ReactNode; + value: number; + index: number; +} & React.HTMLProps<HTMLDivElement>; +function TabPanel({ children, value, index, ...props }: TabPanelProps) { + return ( + <div + role="tabpanel" + hidden={value !== index} + id={`editor-tabpanel-${index}`} + aria-labelledby={`editor-tab-${index}`} + {...props} + > + {value === index && children} + </div> + ); +} + +const a11yProps = (index: number) => ({ + id: `editor-tab-${index}`, + 'aria-controls': `editor-tabpanel-${index}`, +}); + +type Props = { + loading: boolean; + onChange: (comment: string) => void; +}; + +function CommentInput({ loading, onChange }: Props) { + const [input, setInput] = useState<string>(''); + const [tab, setTab] = useState(0); + const classes = useStyles(); + + useEffect(() => { + onChange(input); + }, [input, onChange]); + + return ( + <div> + <Tabs value={tab} onChange={(_, t) => setTab(t)}> + <Tab label="Write" {...a11yProps(0)} /> + <Tab label="Preview" {...a11yProps(1)} /> + </Tabs> + <div className={classes.tabContent}> + <TabPanel value={tab} index={0}> + <TextField + fullWidth + label="Comment" + placeholder="Leave a comment" + className={classes.textarea} + multiline + value={input} + variant="filled" + rows="4" // TODO: rowsMin support + onChange={(e: any) => setInput(e.target.value)} + disabled={loading} + /> + </TabPanel> + <TabPanel value={tab} index={1} className={classes.preview}> + <Content markdown={input} /> + </TabPanel> + </div> + </div> + ); +} + +export default CommentInput; diff --git a/webui/src/pages/list/ListQuery.tsx b/webui/src/pages/list/ListQuery.tsx index 7eb6f4c5..424ffac0 100644 --- a/webui/src/pages/list/ListQuery.tsx +++ b/webui/src/pages/list/ListQuery.tsx @@ -2,6 +2,7 @@ import { ApolloError } from '@apollo/client'; import React, { useState, useEffect, useRef } from 'react'; import { useLocation, useHistory, Link } from 'react-router-dom'; +import { Button } from '@material-ui/core'; import IconButton from '@material-ui/core/IconButton'; import InputBase from '@material-ui/core/InputBase'; import Paper from '@material-ui/core/Paper'; @@ -40,6 +41,17 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({ alignItems: 'center', justifyContent: 'space-between', }, + filterissueLabel: { + fontSize: '14px', + fontWeight: 'bold', + paddingRight: '12px', + }, + filterissueContainer: { + display: 'flex', + flexDirection: 'row', + alignItems: 'flex-start', + justifyContents: 'left', + }, search: { borderRadius: theme.shape.borderRadius, borderColor: fade(theme.palette.primary.main, 0.2), @@ -95,6 +107,13 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({ padding: theme.spacing(2, 3), }, }, + greenButton: { + backgroundColor: '#2ea44fd9', + color: '#fff', + '&:hover': { + backgroundColor: '#2ea44f', + }, + }, })); function editParams( @@ -271,21 +290,29 @@ function ListQuery() { return ( <Paper className={classes.main}> <header className={classes.header}> - <h1>Issues</h1> - <form onSubmit={formSubmit}> - <InputBase - placeholder="Filter" - value={input} - onInput={(e: any) => setInput(e.target.value)} - classes={{ - root: classes.search, - focused: classes.searchFocused, - }} - /> - <button type="submit" hidden> - Search - </button> - </form> + <div className="filterissueContainer"> + <form onSubmit={formSubmit}> + <label className={classes.filterissueLabel} htmlFor="issuefilter"> + Filter + </label> + <InputBase + id="issuefilter" + placeholder="Filter" + value={input} + onInput={(e: any) => setInput(e.target.value)} + classes={{ + root: classes.search, + focused: classes.searchFocused, + }} + /> + <button type="submit" hidden> + Search + </button> + </form> + </div> + <Button className={classes.greenButton} variant="contained" href="/new"> + New issue + </Button> </header> <FilterToolbar query={query} queryLocation={queryLocation} /> {content} diff --git a/webui/src/pages/new/NewBug.graphql b/webui/src/pages/new/NewBug.graphql new file mode 100644 index 00000000..92df016e --- /dev/null +++ b/webui/src/pages/new/NewBug.graphql @@ -0,0 +1,7 @@ +mutation newBug($input: NewBugInput!) { + newBug(input: $input) { + bug { + humanId + } + } +}
\ No newline at end of file diff --git a/webui/src/pages/new/NewBugPage.tsx b/webui/src/pages/new/NewBugPage.tsx new file mode 100644 index 00000000..c8e68e7b --- /dev/null +++ b/webui/src/pages/new/NewBugPage.tsx @@ -0,0 +1,121 @@ +import React, { FormEvent, useState } from 'react'; + +import { Button } from '@material-ui/core'; +import Paper from '@material-ui/core/Paper'; +import TextField from '@material-ui/core/TextField/TextField'; +import { fade, makeStyles, Theme } from '@material-ui/core/styles'; + +import CommentInput from '../bug/CommentInput'; + +import { useNewBugMutation } from './NewBug.generated'; + +/** + * Css in JS styles + */ +const useStyles = makeStyles((theme: Theme) => ({ + main: { + maxWidth: 800, + margin: 'auto', + marginTop: theme.spacing(4), + marginBottom: theme.spacing(4), + padding: theme.spacing(2), + overflow: 'hidden', + }, + titleInput: { + borderRadius: theme.shape.borderRadius, + borderColor: fade(theme.palette.primary.main, 0.2), + borderStyle: 'solid', + borderWidth: '1px', + backgroundColor: fade(theme.palette.primary.main, 0.05), + padding: theme.spacing(0, 0), + transition: theme.transitions.create([ + 'width', + 'borderColor', + 'backgroundColor', + ]), + }, + form: { + display: 'flex', + flexDirection: 'column', + }, + actions: { + display: 'flex', + justifyContent: 'flex-end', + }, + gitbugButton: { + backgroundColor: '#2ea44fd9', + color: '#fff', + '&:hover': { + backgroundColor: '#2ea44f', + }, + }, +})); + +/** + * Form to create a new issue + */ +function NewBugPage() { + const [newBug, { loading, error }] = useNewBugMutation(); + const [issueTitle, setIssueTitle] = useState(''); + const [issueComment, setIssueComment] = useState(''); + const classes = useStyles(); + let issueTitleInput: any; + + function submitNewIssue(e: FormEvent) { + e.preventDefault(); + if (!isFormValid()) return; + console.log('submitNewISsue'); + console.log('title: ', issueTitle); + console.log('comment: ', issueComment); + newBug({ + variables: { + input: { + title: issueTitle, + message: issueComment, + }, + }, + }); + issueTitleInput.value = ''; + } + + function isFormValid() { + return issueTitle.length > 0 && issueComment.length > 0 ? true : false; + } + + if (loading) return <div>Loading</div>; + if (error) return <div>Error</div>; + + return ( + <Paper className={classes.main}> + <form className={classes.form} onSubmit={submitNewIssue}> + <TextField + inputRef={(node) => { + issueTitleInput = node; + }} + label="Title" + className={classes.titleInput} + variant="outlined" + fullWidth + margin="dense" + onChange={(event: any) => setIssueTitle(event.target.value)} + /> + <CommentInput + loading={false} + onChange={(comment: string) => setIssueComment(comment)} + /> + <div className={classes.actions}> + <Button + className={classes.gitbugButton} + variant="contained" + type="submit" + disabled={isFormValid() ? false : true} + > + Submit new issue + </Button> + </div> + </form> + </Paper> + ); +} + +export default NewBugPage; |