aboutsummaryrefslogtreecommitdiffstats
path: root/webui/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'webui/src/pages')
-rw-r--r--webui/src/pages/bug/Bug.tsx30
-rw-r--r--webui/src/pages/bug/CommentForm.tsx110
-rw-r--r--webui/src/pages/list/ListQuery.tsx67
-rw-r--r--webui/src/pages/new/NewBug.graphql7
-rw-r--r--webui/src/pages/new/NewBugPage.tsx118
5 files changed, 229 insertions, 103 deletions
diff --git a/webui/src/pages/bug/Bug.tsx b/webui/src/pages/bug/Bug.tsx
index 8d6d11cc..d85c5296 100644
--- a/webui/src/pages/bug/Bug.tsx
+++ b/webui/src/pages/bug/Bug.tsx
@@ -1,32 +1,28 @@
import React from 'react';
-import Typography from '@material-ui/core/Typography/Typography';
import { makeStyles } from '@material-ui/core/styles';
-import Author from 'src/components/Author';
-import Date from 'src/components/Date';
+import BugTitleForm from 'src/components/BugTitleForm/BugTitleForm';
+import IfLoggedIn from 'src/components/IfLoggedIn/IfLoggedIn';
import Label from 'src/components/Label';
-import IfLoggedIn from 'src/layout/IfLoggedIn';
import { BugFragment } from './Bug.generated';
import CommentForm from './CommentForm';
import TimelineQuery from './TimelineQuery';
+/**
+ * Css in JS Styles
+ */
const useStyles = makeStyles((theme) => ({
main: {
maxWidth: 1000,
margin: 'auto',
marginTop: theme.spacing(4),
+ overflow: 'hidden',
},
header: {
marginLeft: theme.spacing(3) + 40,
- },
- title: {
- ...theme.typography.h5,
- },
- id: {
- ...theme.typography.subtitle1,
- marginLeft: theme.spacing(1),
+ marginRight: theme.spacing(2),
},
container: {
display: 'flex',
@@ -73,17 +69,11 @@ type Props = {
function Bug({ bug }: Props) {
const classes = useStyles();
+
return (
<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'}>
- <Author author={bug.author} />
- {' opened this bug '}
- <Date date={bug.createdAt} />
- </Typography>
+ <BugTitleForm bug={bug} />
</div>
<div className={classes.container}>
@@ -92,7 +82,7 @@ function Bug({ bug }: Props) {
<IfLoggedIn>
{() => (
<div className={classes.commentForm}>
- <CommentForm bugId={bug.id} />
+ <CommentForm bug={bug} />
</div>
)}
</IfLoggedIn>
diff --git a/webui/src/pages/bug/CommentForm.tsx b/webui/src/pages/bug/CommentForm.tsx
index f2a2eb6c..0b97e133 100644
--- a/webui/src/pages/bug/CommentForm.tsx
+++ b/webui/src/pages/bug/CommentForm.tsx
@@ -2,13 +2,13 @@ import React, { useState, useRef } from 'react';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
-import Tab from '@material-ui/core/Tab';
-import Tabs from '@material-ui/core/Tabs';
-import TextField from '@material-ui/core/TextField';
import { makeStyles, Theme } from '@material-ui/core/styles';
-import Content from 'src/components/Content';
+import CommentInput from '../../components/CommentInput/CommentInput';
+import CloseBugButton from 'src/components/CloseBugButton/CloseBugButton';
+import ReopenBugButton from 'src/components/ReopenBugButton/ReopenBugButton';
+import { BugFragment } from './Bug.generated';
import { useAddCommentMutation } from './CommentForm.generated';
import { TimelineDocument } from './TimelineQuery.generated';
@@ -30,40 +30,24 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
display: 'flex',
justifyContent: 'flex-end',
},
+ greenButton: {
+ marginLeft: '8px',
+ backgroundColor: '#2ea44fd9',
+ color: '#fff',
+ '&:hover': {
+ backgroundColor: '#2ea44f',
+ },
+ },
}));
-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 = {
- bugId: string;
+ bug: BugFragment;
};
-function CommentForm({ bugId }: Props) {
+function CommentForm({ bug }: Props) {
const [addComment, { loading }] = useAddCommentMutation();
- const [input, setInput] = useState<string>('');
- const [tab, setTab] = useState(0);
+ const [issueComment, setIssueComment] = useState('');
+ const [inputProp, setInputProp] = useState<any>('');
const classes = useStyles({ loading });
const form = useRef<HTMLFormElement>(null);
@@ -71,8 +55,8 @@ function CommentForm({ bugId }: Props) {
addComment({
variables: {
input: {
- prefix: bugId,
- message: input,
+ prefix: bug.id,
+ message: issueComment,
},
},
refetchQueries: [
@@ -80,60 +64,50 @@ function CommentForm({ bugId }: Props) {
{
query: TimelineDocument,
variables: {
- id: bugId,
+ id: bug.id,
first: 100,
},
},
],
awaitRefetchQueries: true,
- }).then(() => setInput(''));
+ }).then(() => resetForm());
};
+ function resetForm() {
+ setInputProp({
+ value: '',
+ });
+ }
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
- submit();
+ if (issueComment.length > 0) submit();
};
- const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
- // Submit on cmd/ctrl+enter
- if ((e.metaKey || e.altKey) && e.keyCode === 13) {
- submit();
- }
- };
+ function getCloseButton() {
+ return <CloseBugButton bug={bug} disabled={issueComment.length > 0} />;
+ }
+
+ function getReopenButton() {
+ return <ReopenBugButton bug={bug} disabled={issueComment.length > 0} />;
+ }
return (
<Paper className={classes.container}>
<form onSubmit={handleSubmit} ref={form}>
- <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
- onKeyDown={handleKeyDown}
- 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>
+ <CommentInput
+ inputProps={inputProp}
+ loading={loading}
+ onChange={(comment: string) => setIssueComment(comment)}
+ />
<div className={classes.actions}>
+ {bug.status === 'OPEN' ? getCloseButton() : getReopenButton()}
<Button
+ className={classes.greenButton}
variant="contained"
color="primary"
type="submit"
- disabled={loading}
+ disabled={loading || issueComment.length === 0}
>
Comment
</Button>
diff --git a/webui/src/pages/list/ListQuery.tsx b/webui/src/pages/list/ListQuery.tsx
index 7eb6f4c5..87c21e3c 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';
@@ -11,6 +12,8 @@ import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
import Skeleton from '@material-ui/lab/Skeleton';
+import IfLoggedIn from 'src/components/IfLoggedIn/IfLoggedIn';
+
import FilterToolbar from './FilterToolbar';
import List from './List';
import { useListBugsQuery } from './ListQuery.generated';
@@ -40,6 +43,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 +109,13 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
padding: theme.spacing(2, 3),
},
},
+ greenButton: {
+ backgroundColor: '#2ea44fd9',
+ color: '#fff',
+ '&:hover': {
+ backgroundColor: '#2ea44f',
+ },
+ },
}));
function editParams(
@@ -271,21 +292,37 @@ 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>
+ <IfLoggedIn>
+ {() => (
+ <Button
+ className={classes.greenButton}
+ variant="contained"
+ href="/new"
+ >
+ New issue
+ </Button>
+ )}
+ </IfLoggedIn>
</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..c9e268b6
--- /dev/null
+++ b/webui/src/pages/new/NewBugPage.tsx
@@ -0,0 +1,118 @@
+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 '../../components/CommentInput/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',
+ },
+ greenButton: {
+ 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;
+ 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.greenButton}
+ variant="contained"
+ type="submit"
+ disabled={isFormValid() ? false : true}
+ >
+ Submit new issue
+ </Button>
+ </div>
+ </form>
+ </Paper>
+ );
+}
+
+export default NewBugPage;