aboutsummaryrefslogtreecommitdiffstats
path: root/webui
diff options
context:
space:
mode:
Diffstat (limited to 'webui')
-rw-r--r--webui/package-lock.json6
-rw-r--r--webui/package.json2
-rw-r--r--webui/src/App.tsx2
-rw-r--r--webui/src/pages/bug/CommentInput.tsx96
-rw-r--r--webui/src/pages/list/ListQuery.tsx57
-rw-r--r--webui/src/pages/new/NewBug.graphql7
-rw-r--r--webui/src/pages/new/NewBugPage.tsx121
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;