1import { Button, Typography } from '@mui/material';
  2import makeStyles from '@mui/styles/makeStyles';
  3import { useRef, useState } from 'react';
  4import { Link } from 'react-router';
  5
  6import { TimelineDocument } from '../../pages/bug/TimelineQuery.generated';
  7import IfLoggedIn from '../IfLoggedIn/IfLoggedIn';
  8import Author from 'src/components/Author';
  9import Date from 'src/components/Date';
 10import { BugFragment } from 'src/pages/bug/Bug.generated';
 11
 12import BugTitleInput from './BugTitleInput';
 13import { useSetTitleMutation } from './SetTitle.generated';
 14
 15/**
 16 * Css in JS styles
 17 */
 18const useStyles = makeStyles((theme) => ({
 19  header: {
 20    display: 'flex',
 21    flexDirection: 'column',
 22  },
 23  headerTitle: {
 24    display: 'flex',
 25    flexDirection: 'row',
 26    justifyContent: 'space-between',
 27  },
 28  readOnlyTitle: {
 29    ...theme.typography.h5,
 30  },
 31  readOnlyId: {
 32    ...theme.typography.subtitle1,
 33    marginLeft: theme.spacing(1),
 34  },
 35  editButtonContainer: {
 36    display: 'flex',
 37    flexDirection: 'row',
 38    justifyContent: 'flex-start',
 39    alignItems: 'center',
 40    minWidth: 200,
 41    marginLeft: theme.spacing(2),
 42  },
 43  greenButton: {
 44    marginLeft: theme.spacing(1),
 45    backgroundColor: theme.palette.success.main,
 46    color: theme.palette.success.contrastText,
 47    '&:hover': {
 48      backgroundColor: theme.palette.success.dark,
 49      color: theme.palette.primary.contrastText,
 50    },
 51  },
 52  saveButton: {
 53    marginRight: theme.spacing(1),
 54  },
 55  author: {
 56    fontWeight: 'bold',
 57    color: theme.palette.text.secondary,
 58  },
 59}));
 60
 61interface Props {
 62  bug: BugFragment;
 63}
 64
 65/**
 66 * Component for bug title change
 67 * @param bug Selected bug in list page
 68 */
 69function BugTitleForm({ bug }: Props) {
 70  const [bugTitleEdition, setbugTitleEdition] = useState(false);
 71  const [setTitle, { loading, error }] = useSetTitleMutation();
 72  const [issueTitle, setIssueTitle] = useState(bug.title);
 73  const classes = useStyles();
 74  const issueTitleInput = useRef<HTMLInputElement>(null);
 75
 76  function isFormValid() {
 77    if (issueTitleInput.current) {
 78      return issueTitleInput.current.value.length > 0;
 79    } else {
 80      return false;
 81    }
 82  }
 83
 84  function submitNewTitle() {
 85    if (!isFormValid()) return;
 86    if (bug.title === issueTitleInput.current?.value) {
 87      cancelChange();
 88      return;
 89    }
 90    setTitle({
 91      variables: {
 92        input: {
 93          prefix: bug.id,
 94          title: issueTitleInput.current!!.value,
 95        },
 96      },
 97      refetchQueries: [
 98        // TODO: update the cache instead of refetching
 99        {
100          query: TimelineDocument,
101          variables: {
102            id: bug.id,
103            first: 100,
104          },
105        },
106      ],
107      awaitRefetchQueries: true,
108    }).then(() => setbugTitleEdition(false));
109  }
110
111  function cancelChange() {
112    setIssueTitle(bug.title);
113    setbugTitleEdition(false);
114  }
115
116  function editableBugTitle() {
117    return (
118      <form className={classes.headerTitle}>
119        <BugTitleInput
120          inputRef={issueTitleInput}
121          label="Title"
122          variant="outlined"
123          fullWidth
124          margin="dense"
125          value={issueTitle}
126          onChange={(event: any) => setIssueTitle(event.target.value)}
127        />
128        <div className={classes.editButtonContainer}>
129          <Button
130            className={classes.saveButton}
131            size="small"
132            variant="contained"
133            onClick={() => submitNewTitle()}
134            disabled={issueTitle.length === 0}
135          >
136            Save
137          </Button>
138          <Button size="small" onClick={() => cancelChange()}>
139            Cancel
140          </Button>
141        </div>
142      </form>
143    );
144  }
145
146  function readonlyBugTitle() {
147    return (
148      <div className={classes.headerTitle}>
149        <div>
150          <span className={classes.readOnlyTitle}>{bug.title}</span>
151          <span className={classes.readOnlyId}>{bug.humanId}</span>
152        </div>
153        <IfLoggedIn>
154          {() => (
155            <div className={classes.editButtonContainer}>
156              <Button
157                size="small"
158                variant="contained"
159                onClick={() => setbugTitleEdition(!bugTitleEdition)}
160              >
161                Edit
162              </Button>
163              <Button
164                className={classes.greenButton}
165                size="small"
166                variant="contained"
167                component={Link}
168                to="/new"
169              >
170                New bug
171              </Button>
172            </div>
173          )}
174        </IfLoggedIn>
175      </div>
176    );
177  }
178
179  if (loading) return <div>Loading...</div>;
180  if (error) return <div>Error</div>;
181
182  return (
183    <div className={classes.header}>
184      {bugTitleEdition ? editableBugTitle() : readonlyBugTitle()}
185      <div className="classes.headerSubtitle">
186        <Typography color={'textSecondary'}>
187          <Author author={bug.author} className={classes.author} />
188          {' opened this bug '}
189          <Date date={bug.createdAt} />
190        </Typography>
191      </div>
192    </div>
193  );
194}
195
196export default BugTitleForm;