diff options
-rw-r--r-- | package-lock.json | 82 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/client/theme.ts | 47 | ||||
-rw-r--r-- | src/client/util/ReportManager.scss | 383 | ||||
-rw-r--r-- | src/client/util/ReportManager.tsx | 478 | ||||
-rw-r--r-- | src/client/util/reportManagerSchema.ts | 877 |
6 files changed, 1614 insertions, 255 deletions
diff --git a/package-lock.json b/package-lock.json index 895dec389..5b0c4e2f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3871,6 +3871,11 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==" + }, "available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -9291,6 +9296,14 @@ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, + "file-selector": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", + "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", + "requires": { + "tslib": "^2.4.0" + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -19757,6 +19770,16 @@ "prop-types": "^15.8.1" } }, + "react-dropzone": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", + "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "requires": { + "attr-accept": "^2.2.2", + "file-selector": "^0.6.0", + "prop-types": "^15.8.1" + } + }, "react-event-listener": { "version": "0.6.6", "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz", @@ -19880,6 +19903,55 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-loader-spinner": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.3.4.tgz", + "integrity": "sha512-G2vw4ssX+RDZ/vfaeva06yfNqyFViv/u+tVZ3kFLy5TKNlNx2DbuwreBSpRtPespQA+VxinxUJsigwLwG9erOg==", + "requires": { + "react-is": "^18.2.0", + "styled-components": "^5.3.5", + "styled-tools": "^1.7.2" + }, + "dependencies": { + "css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "styled-components": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", + "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + } + } + } + }, "react-loading": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/react-loading/-/react-loading-2.0.3.tgz", @@ -21215,6 +21287,11 @@ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "sharp": { "version": "0.23.4", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.23.4.tgz", @@ -22232,6 +22309,11 @@ } } }, + "styled-tools": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/styled-tools/-/styled-tools-1.7.2.tgz", + "integrity": "sha512-IjLxzM20RMwAsx8M1QoRlCG/Kmq8lKzCGyospjtSXt/BTIIcvgTonaxQAsKnBrsZNwhpHzO9ADx5te0h76ILVg==" + }, "stylis": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", diff --git a/package.json b/package.json index 6cd271c96..39f70ab66 100644 --- a/package.json +++ b/package.json @@ -288,9 +288,11 @@ "react-compound-slider": "^2.5.0", "react-datepicker": "^3.8.0", "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", "react-grid-layout": "^1.3.4", "react-icons": "^4.3.1", "react-jsx-parser": "^1.29.0", + "react-loader-spinner": "^5.3.4", "react-loading": "^2.0.3", "react-markdown": "^8.0.3", "react-measure": "^2.5.2", diff --git a/src/client/theme.ts b/src/client/theme.ts index 57be370cc..f0a11fe6e 100644 --- a/src/client/theme.ts +++ b/src/client/theme.ts @@ -4,39 +4,20 @@ import { createTheme } from '@mui/material/styles'; export const theme = createTheme({ palette: { primary: { - main: 'rgb(0, 149, 246)', + main: '#4476f7', + }, + }, + components: { + MuiButton: { + styleOverrides: { + root: { + fontSize: '14px', + boxShadow: 'none', + '&:hover': { + boxShadow: 'none', + }, + }, + }, }, }, - // components: { - // MuiButton: { - // styleOverrides: { - // root: { - // fontSize: '14px', - // boxShadow: 'none', - // '&:hover': { - // boxShadow: 'none', - // scale: 1, - // }, - // }, - // }, - // }, - // MuiTextField: { - // styleOverrides: { - // root: { - // '& .MuiInputBase-input': { - // fontSize: '16px', - // }, - // '& .MuiInputLabel-root': { - // fontSize: '16px', - // }, - // '& .MuiInputLabel-shrink': { - // fontSize: '16px', - // }, - // '& .MuiFormHelperText-root': { - // fontSize: '16px', - // }, - // }, - // }, - // }, - // }, }); diff --git a/src/client/util/ReportManager.scss b/src/client/util/ReportManager.scss index 969e0de74..4ff86fd9c 100644 --- a/src/client/util/ReportManager.scss +++ b/src/client/util/ReportManager.scss @@ -1,102 +1,349 @@ @import '../views/global/globalCssVariables'; +// header + +.report-header { + display: flex; + justify-content: space-between; + align-items: center; + + h2 { + margin: 0; + padding: 0; + font-size: 24px; + } +} + +.report-header-vertical { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + + h2 { + margin: 0; + padding: 0; + padding-bottom: 8px; + font-size: 24px; + } +} + +// Report + .report-issue { + width: 45vw; padding: 16px; + padding-top: 32px; display: flex; flex-direction: column; - gap: 1rem; + gap: 16px; background-color: #ffffff; text-align: left; + position: relative; - h2 { - font-size: 24px; + .report-label { + font-size: 14px; + font-weight: 400; + color: #747474; + } + + .report-section { + display: flex; + flex-direction: column; + } + + .report-textarea { + width: 100%; + height: 80px; + padding: 8px; + resize: none; + } + + .report-selects { + display: flex; + gap: 16px; + + .report-select { + flex: 1; + padding: 8px; + border-color: #c6c6c6; + + .report-opt { + padding: 8px; + } + } } } -// --------------------------------------- -.issue-list-wrapper { - position: relative; - min-width: 250px; - background-color: $light-blue; - overflow-y: scroll; +.report-input { + border: none; + outline: none; + border-bottom: 1px solid #c6c6c6; + padding: 8px; + padding-left: 0; + transition: all 0.2s ease; + + &:hover { + border-bottom-color: #7f7f7f; + } + &:focus { + border-bottom-color: #4476f7; + } } -.issue-list { +// View issues + +.view-issues { + width: 65vw; + min-width: 600px; display: flex; - align-items: center; - justify-content: space-between; - padding: 5px; - margin: 5px; - border-radius: 5px; - border: 1px solid grey; - background-color: lightgoldenrodyellow; + gap: 16px; + height: 100%; + + .left { + height: 100%; + flex: 1; + padding: 16px; + display: flex; + flex-direction: column; + gap: 16px; + background-color: #ffffff; + text-align: left; + position: relative; + + .issues { + position: relative; + flex-grow: 1; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + gap: 16px; + } + } + + .right { + padding: 16px; + height: 100%; + overflow-y: auto; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + flex: 1; + } } -// issue should pop up when the user hover over the issue -.issue-list:hover { - box-shadow: 2px; +// Issue + +.issue { cursor: pointer; - border: 3px solid #252b33; + padding: 16px; + background-color: #ffffff; + border: 1px solid #d3d3d3; + transition: all 0.1s ease; + display: flex; + flex-direction: column; + gap: 8px; + border-radius: 8px; + transition: all 0.2s ease; + // box-shadow: 0 0 8px #d0d0d07c; + + .issue-label { + cursor: pointer; + font-size: 14px; + font-weight: 400; + letter-spacing: 1px; + color: #7f7f7f; + } + + .issue-title { + font-size: 16px; + font-weight: 500; + padding: 0; + margin: 0; + color: #4476f7; + } + + &:hover { + background-color: #4476f7; + border-color: #4476f7; + color: #ffffff; + + .issue-label { + color: #ffffff; + } + + .issue-title { + color: #ffffff; + } + } } -.issue-content { - background-color: white; - padding: 10px; - flex: 1 1 auto; - overflow-y: scroll; +// Dropzone + +.dropzone { + padding: 2rem; + border-radius: 0.5rem; + border: 2px dashed #ebebeb; + + .dropzone-instructions { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + color: #7f7f7f; + + p { + text-align: center; + } + } } -.issue-title { - font-size: 20px; - font-weight: 600; - color: black; +.files { + font-size: 14px; + color: #7f7f7f; + + .file-list { + width: 100%; + list-style-type: none; + display: flex; + overflow-x: auto; + gap: 16px; + margin: 0; + padding: 0; + + .file-name { + padding: 8px 12px; + display: flex; + align-items: center; + gap: 16px; + white-space: nowrap; + } + } } -.issue-body { - padding: 0 10px; +// Detailed issue view + +.issue-view { + height: 100%; width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + background-color: #ffffff; text-align: left; -} + position: relative; + overflow: auto; -.issue-body > * { - margin-top: 5px; -} + .issue-label { + color: #7f7f7f; -.issue-body img, -.issue-body video { - display: block; - max-width: 100%; -} + .issue-link { + cursor: pointer; + color: #4476f7; + } + } -.report-issue-fab { - position: fixed; - bottom: 20px; - right: 20px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; -} + .issue-title { + font-size: 24px; + margin: 0; + padding: 0; + } -.loading-center { - margin: auto 0; + .issue-content { + font-size: 14px; + color: #7f7f7f; + } } -.settings-content label { - margin-top: 10px; -} +// Old code -.report-disclaimer { - font-size: 8px; - color: grey; - padding-right: 50px; - font-style: italic; - text-align: left; -} +// <----------------------------------------------------------------------------> -.flex-select { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; -} +// .issue-list-wrapper { +// position: relative; +// min-width: 250px; +// background-color: $light-blue; +// overflow-y: scroll; +// } + +// .issue-list { +// display: flex; +// align-items: center; +// justify-content: space-between; +// padding: 5px; +// margin: 5px; +// border-radius: 5px; +// border: 1px solid grey; +// background-color: lightgoldenrodyellow; +// } + +// // issue should pop up when the user hover over the issue +// .issue-list:hover { +// box-shadow: 2px; +// cursor: pointer; +// border: 3px solid #252b33; +// } + +// .issue-content { +// background-color: white; +// padding: 10px; +// flex: 1 1 auto; +// overflow-y: scroll; +// } + +// .issue-title { +// font-size: 20px; +// font-weight: 600; +// color: black; +// } + +// .issue-body { +// padding: 0 10px; +// width: 100%; +// text-align: left; +// } + +// .issue-body > * { +// margin-top: 5px; +// } + +// .issue-body img, +// .issue-body video { +// display: block; +// max-width: 100%; +// } + +// .report-issue-fab { +// position: fixed; +// bottom: 20px; +// right: 20px; +// display: flex; +// align-items: center; +// justify-content: center; +// cursor: pointer; +// } + +// .loading-center { +// margin: auto 0; +// } + +// .settings-content label { +// margin-top: 10px; +// } + +// .report-disclaimer { +// font-size: 8px; +// color: grey; +// padding-right: 50px; +// font-style: italic; +// text-align: left; +// } + +// .flex-select { +// display: flex; +// align-items: center; +// justify-content: center; +// gap: 10px; +// } diff --git a/src/client/util/ReportManager.tsx b/src/client/util/ReportManager.tsx index a08ef9979..3eebb8f15 100644 --- a/src/client/util/ReportManager.tsx +++ b/src/client/util/ReportManager.tsx @@ -1,62 +1,82 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; -import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { BoolCast, Cast, StrCast } from '../../fields/Types'; import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; -import { Networking } from '../Network'; -import { MainViewModal } from '../views/MainViewModal'; import { FontIconBox } from '../views/nodes/button/FontIconBox'; import { DragManager } from './DragManager'; import { GroupManager } from './GroupManager'; +import { CheckBox } from '../views/search/CheckBox'; +import { undoBatch } from './UndoManager'; +import * as React from 'react'; import './SettingsManager.scss'; import './ReportManager.scss'; -import { undoBatch } from './UndoManager'; +import { action, computed, observable, runInAction } from 'mobx'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { BsX } from 'react-icons/bs'; +import { BiX } from 'react-icons/bi'; +import { AiOutlineUpload } from 'react-icons/ai'; +import { HiOutlineArrowLeft } from 'react-icons/hi'; +import { Issue } from './reportManagerSchema'; +import { observer } from 'mobx-react'; +import { Doc } from '../../fields/Doc'; +import { Networking } from '../Network'; +import { MainViewModal } from '../views/MainViewModal'; import { Octokit } from '@octokit/core'; -import { CheckBox } from '../views/search/CheckBox'; +import { Button, IconButton } from '@mui/material'; +import { Oval } from 'react-loader-spinner'; +import Dropzone from 'react-dropzone'; import ReactLoading from 'react-loading'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; -import { Button, MenuItem, Select, SelectChangeEvent, TextField } from '@mui/material'; -import { FormControl, InputLabel } from '@material-ui/core'; +import { theme } from '../theme'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; +enum ViewState { + VIEW, + CREATE, +} + @observer export class ReportManager extends React.Component<{}> { public static Instance: ReportManager; @observable private isOpen = false; + @observable private query = ''; + @action private setQuery = (q: string) => { + this.query = q; + }; + private octokit: Octokit; - @observable public issues: any[] = []; - @action setIssues = action((issues: any[]) => { - this.issues = issues; + @observable viewState: ViewState = ViewState.VIEW; + @action private setViewState = (state: ViewState) => { + this.viewState = state; + }; + @observable submitting: boolean = false; + @action private setSubmitting = (submitting: boolean) => { + this.submitting = submitting; + }; + + @observable + public shownIssues: Issue[] = []; + @action setShownIssues = action((issues: Issue[]) => { + this.shownIssues = issues; }); - // undefined is the default - null is if the user is making an issue - @observable public selectedIssue: any = undefined; - @action setSelectedIssue = action((issue: any) => { + @observable selectedIssue: Issue | undefined = undefined; + @action setSelectedIssue = action((issue: Issue | undefined) => { this.selectedIssue = issue; }); - // only get the open issues - @observable public shownIssues = this.issues.filter(issue => issue.state === 'open'); - - public updateIssueSearch = action((query: string = '') => { - if (query === '') { - this.shownIssues = this.issues.filter(issue => issue.state === 'open'); - return; - } - this.shownIssues = this.issues.filter(issue => issue.title.toLowerCase().includes(query.toLowerCase())); - }); + @observable private mediaFiles: File[] = []; + @action private setMediaFiles = (files: File[]) => { + this.mediaFiles = files; + }; constructor(props: {}) { super(props); @@ -68,17 +88,18 @@ export class ReportManager extends React.Component<{}> { } public close = action(() => (this.isOpen = false)); - public open = action(() => { - if (this.issues.length === 0) { - // load in the issues if not already loaded - this.getAllIssues() - .then(issues => { - this.setIssues(issues); - this.updateIssueSearch(); - }) - .catch(err => console.log(err)); - } + public open = action(async () => { this.isOpen = true; + if (this.shownIssues.length === 0) { + try { + // load in the issues if not already loaded + const issues = (await this.getAllIssues()) as Issue[]; + this.setShownIssues(issues.filter(issue => issue.state === 'open')); + // this.updateIssueSearch(); + } catch (err) { + console.log(err); + } + } }); @observable private bugTitle = ''; @@ -98,6 +119,14 @@ export class ReportManager extends React.Component<{}> { this.bugPriority = priortiy; }); + private showReportIssueScreen = () => { + this.setSelectedIssue(undefined); + }; + + private closeReportIssueScreen = () => { + this.setSelectedIssue(undefined); + }; + // private toGithub = false; // will always be set to true - no alterntive option yet private toGithub = true; @@ -133,30 +162,31 @@ export class ReportManager extends React.Component<{}> { }; public async reportIssue() { + console.log(this.bugTitle); + console.log('reporting issue'); if (this.bugTitle === '' || this.bugDescription === '' || this.bugType === '' || this.bugPriority === '') { alert('Please fill out all required fields to report an issue.'); return; } + this.setSubmitting(true); - if (this.toGithub) { - const formattedLinks = (this.fileLinks ?? []).map(this.fileLinktoServerLink); + console.log('to github'); + const links = await this.uploadFilesToServer(); + const formattedLinks = (links ?? []).map(this.fileLinktoServerLink); - const req = await this.octokit.request('POST /repos/{owner}/{repo}/issues', { - owner: 'brown-dash', - repo: 'Dash-Web', - title: this.formatTitle(this.bugTitle, Doc.CurrentUserEmail), - body: `${this.bugDescription} \n\nfiles:\n${formattedLinks.join('\n')}`, - labels: ['from-dash-app', this.bugType, this.bugPriority], - }); + const req = await this.octokit.request('POST /repos/{owner}/{repo}/issues', { + owner: 'brown-dash', + repo: 'Dash-Web', + title: this.formatTitle(this.bugTitle, Doc.CurrentUserEmail), + body: `${this.bugDescription} \n\nfiles:\n${formattedLinks.join('\n')}`, + labels: ['from-dash-app', this.bugType, this.bugPriority], + }); - // 201 status means success - if (req.status !== 201) { - alert('Error creating issue on github.'); - // on error, don't close the modal - return; - } - } else { - // if not going to github issues, not sure what to do yet... + // 201 status means success + if (req.status !== 201) { + alert('Error creating issue on github.'); + // on error, don't close the modal + return; } // if we're down here, then we're good to go. reset the fields. @@ -166,7 +196,9 @@ export class ReportManager extends React.Component<{}> { this.setFileLinks([]); this.setBugType(''); this.setBugPriority(''); - this.close(); + this.setSubmitting(false); + alert('Successfully submitted issue.'); + // this.close(); } @observable public fileLinks: any = []; @@ -191,61 +223,216 @@ export class ReportManager extends React.Component<{}> { } }; - @observable private age = ''; + private uploadFilesToServer = async () => { + const links = await Networking.UploadFilesToServer(this.mediaFiles.map(file => ({ file }))); + console.log('finshed uploading', links.map(this.getServerPath)); + return (links ?? []).map(this.getServerPath); + // this.setFileLinks((links ?? []).map(this.getServerPath)); + }; - @action private setAge = (e: SelectChangeEvent) => { - this.age = e.target.value as string; + private onDrop = (files: File[]) => { + this.setMediaFiles(files); + }; + + private reportComponent = () => { + if (this.viewState === ViewState.VIEW) { + return this.viewIssuesComponent(); + } else { + return this.reportIssueComponent(); + } + }; + + private viewIssuesComponent = () => { + return ( + <div className="view-issues"> + <div className="left"> + <div className="report-header"> + <h2>Open Issues</h2> + <Button + variant="contained" + onClick={() => { + this.setViewState(ViewState.CREATE); + }}> + report issue + </Button> + </div> + <input + className="report-input" + type="text" + placeholder="Filter..." + onChange={e => { + this.setQuery(e.target.value); + }} + required + /> + <div className="issues"> + {this.shownIssues + .filter(issue => issue.title.toLowerCase().includes(this.query)) + .map(issue => ( + <IssueCard + key={issue.number} + issue={issue} + onSelect={() => { + this.setSelectedIssue(issue); + }} + /> + ))} + </div> + </div> + <div className="right">{this.selectedIssue ? <IssueView issue={this.selectedIssue} /> : <div>No issue selected</div>}</div> + <IconButton sx={{ position: 'absolute', top: '4px', right: '4px' }} onClick={this.close}> + <BiX size="16px" /> + </IconButton> + </div> + ); }; private reportIssueComponent = () => { return ( <div className="report-issue"> - <h2>Report an issue</h2> - <TextField - fullWidth - type="text" - label="Please leave a title for the bug" - placeholder="Title..." - required - sx={{ - '& .MuiInputBase-input': { - fontSize: 'inherit', - }, - }} - onChange={e => this.setBugTitle(e.target.value)} - /> - <label>Please leave a description for the bug and how it can be recreated.</label> - <textarea value={this.bugDescription} placeholder="description" onChange={e => this.setBugDescription(e.target.value as string)} required /> - <FormControl fullWidth> - <InputLabel id="demo-simple-select-label">Age</InputLabel> - <Select labelId="demo-simple-select-label" id="demo-simple-select" value={this.age} label="Age" onChange={this.setAge}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select> - </FormControl> - <FormControl fullWidth> - <InputLabel>Bug Type</InputLabel> - <Select value={this.bugType} label="Bug Type" onChange={e => this.setBugType(e.target.value)}> - <MenuItem value="bug">Bug</MenuItem> - <MenuItem value="Poor design or cosmetic">Poor design or cosmetic</MenuItem> - <MenuItem value="Poor documentation">Poor documentation</MenuItem> - </Select> - </FormControl> - <FormControl> - <InputLabel>Bug Priority</InputLabel> - <Select fullWidth value={this.bugPriority} label="Bug Priority" onChange={e => this.setBugPriority(e.target.value as string)}> - <MenuItem value="bug">Bug</MenuItem> - <MenuItem value="Poor design or cosmetic">Poor design or cosmetic</MenuItem> - <MenuItem value="Poor documentation">Poor documentation</MenuItem> - </Select> - </FormControl> - <Button variant="contained">Submit</Button> + <div className="report-header-vertical"> + <Button + variant="text" + onClick={() => { + this.setViewState(ViewState.VIEW); + }} + sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}> + <HiOutlineArrowLeft color={theme.palette.primary.main} /> + back to view + </Button> + <h2>Report an Issue</h2> + </div> + <div className="report-section"> + <label className="report-label">Please provide a title for the bug</label> + <input className="report-input" value={this.bugTitle} type="text" placeholder="Title..." onChange={e => this.setBugTitle(e.target.value)} required /> + </div> + <div className="report-section"> + <label className="report-label">Please leave a description for the bug and how it can be recreated</label> + <textarea className="report-textarea" value={this.bugDescription} placeholder="Description..." onChange={e => this.setBugDescription(e.target.value)} required /> + </div> + <div className="report-selects"> + <select className="report-select" name="bugType" onChange={e => (this.bugType = e.target.value)}> + <option value="" disabled selected> + Type + </option> + <option className="report-opt" value="priority-low"> + Bug + </option> + <option className="report-opt" value="priority-medium"> + Poor Design or Cosmetic + </option> + <option className="report-opt" value="priority-high"> + Poor Documentation + </option> + </select> + <select className="report-select" name="bigPriority" onChange={e => (this.bugPriority = e.target.value)}> + <option className="report-opt" value="" disabled selected> + Priority + </option> + <option value="priority-low">Low</option> + <option value="priority-medium">Medium</option> + <option value="priority-high">High</option> + </select> + </div> + <Dropzone + onDrop={this.onDrop} + accept={{ + 'image/png': ['.png'], + 'image/jpg': ['.jpg'], + 'image/jpeg': ['.jpeg'], + 'video/mp4': ['.mp4'], + 'video/mpeg': ['.mpeg'], + 'video/webm': ['.webm'], + 'audio/mpeg': ['.mp3'], + 'audio/wav': ['.wav'], + 'audio/ogg': ['.ogg'], + }}> + {({ getRootProps, getInputProps }) => ( + <div {...getRootProps({ className: 'dropzone' })}> + <input {...getInputProps()} /> + <div className="dropzone-instructions"> + <AiOutlineUpload size={25} /> + <p>Drop or select media that shows the bug (optional)</p> + </div> + </div> + )} + </Dropzone> + {this.mediaFiles.length > 0 && ( + <div className="files"> + <ul className="file-list"> + {this.mediaFiles.map((file, i) => ( + <li key={file.name} className="file-name"> + {file.name} + <IconButton + onClick={() => { + this.setMediaFiles(this.mediaFiles.filter(f => f !== file)); + }}> + <BiX /> + </IconButton> + </li> + ))} + </ul> + </div> + )} + + <Button + variant="contained" + sx={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '1rem' }} + onClick={() => { + this.reportIssue(); + }}> + Submit + {this.submitting && <Oval height={20} width={20} color="#ffffff" visible={true} ariaLabel="oval-loading" secondaryColor="#ffffff87" strokeWidth={3} strokeWidthSecondary={3} />} + </Button> + <IconButton sx={{ position: 'absolute', top: '4px', right: '4px' }} onClick={this.close}> + <BiX size={'16px'} /> + </IconButton> </div> ); }; - private renderIssue = (issue: any) => { + private get reportInterface() { + const isReportingIssue = this.selectedIssue === null; + + return ( + <div className="settings-interface"> + <div className="issue-list-wrapper"> + <h3>Current Issues</h3> + <input type="text" placeholder="search issues" onChange={e => this.setQuery(e.target.value)}></input> + <br /> + {this.shownIssues.length === 0 ? ( + <ReactLoading className="loading-center" /> + ) : ( + this.shownIssues.map(issue => ( + <div className="issue-list" key={issue.number} onClick={() => this.setSelectedIssue(issue)}> + {issue.title} + </div> + )) + )} + {/* <div className="settings-user"> + <button onClick={() => this.getAllIssues().then(issues => this.issues = issues)}>Poll Issues</button> + </div> */} + </div> + + <div className="close-button" onClick={this.close}> + <FontAwesomeIcon icon={'times'} color="black" size={'lg'} /> + </div> + + <div className="issue-content" style={{ paddingTop: this.selectedIssue === undefined ? '50px' : 'inherit' }}> + {this.selectedIssue === undefined ? 'no issue selected' : this.renderIssue(this.selectedIssue)} + </div> + + <div className="report-issue-fab"> + <span className="report-disclaimer" hidden={!isReportingIssue}> + Note: issue reporting is not anonymous. + </span> + <button onClick={() => (isReportingIssue ? this.closeReportIssueScreen() : this.showReportIssueScreen())}>{isReportingIssue ? 'Cancel' : 'Report New Issue'}</button> + </div> + </div> + ); + } + + private renderIssue = (issue: Issue) => { const isReportingIssue = issue === null; return isReportingIssue ? ( @@ -275,7 +462,7 @@ export class ReportManager extends React.Component<{}> { <option value="priority-high">Poor Documentation</option> </select> - <select name="bigPriority" onChange={e => (this.bugPriority = e.target.value)}> + <select name="priority" onChange={e => (this.bugPriority = e.target.value)}> <option value="" disabled selected> Priority </option> @@ -304,71 +491,54 @@ export class ReportManager extends React.Component<{}> { </a> </h5> <div className="issue-title">{issue.title}</div> - <ReactMarkdown children={issue.body} className="issue-body" linkTarget={'_blank'} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} /> + <ReactMarkdown children={issue.body as string} className="issue-body" linkTarget={'_blank'} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} /> </div> ); }; - private showReportIssueScreen = () => { - this.setSelectedIssue(null); - }; - - private closeReportIssueScreen = () => { - this.setSelectedIssue(undefined); - }; - - private get reportInterface() { - const isReportingIssue = this.selectedIssue === null; - - return ( - <div className="settings-interface"> - <div className="issue-list-wrapper"> - <h3>Current Issues</h3> - <input type="text" placeholder="search issues" onChange={e => this.updateIssueSearch(e.target.value)}></input> - <br /> - {this.issues.length === 0 ? ( - <ReactLoading className="loading-center" /> - ) : ( - this.shownIssues.map(issue => ( - <div className="issue-list" key={issue.number} onClick={() => this.setSelectedIssue(issue)}> - {issue.title} - </div> - )) - )} - - {/* <div className="settings-user"> - <button onClick={() => this.getAllIssues().then(issues => this.issues = issues)}>Poll Issues</button> - </div> */} - </div> - - <div className="close-button" onClick={this.close}> - <FontAwesomeIcon icon={'times'} color="black" size={'lg'} /> - </div> - - <div className="issue-content" style={{ paddingTop: this.selectedIssue === undefined ? '50px' : 'inherit' }}> - {this.selectedIssue === undefined ? 'no issue selected' : this.renderIssue(this.selectedIssue)} - </div> - - <div className="report-issue-fab"> - <span className="report-disclaimer" hidden={!isReportingIssue}> - Note: issue reporting is not anonymous. - </span> - <button onClick={() => (isReportingIssue ? this.closeReportIssueScreen() : this.showReportIssueScreen())}>{isReportingIssue ? 'Cancel' : 'Report New Issue'}</button> - </div> - </div> - ); - } - render() { return ( <MainViewModal // contents={this.reportInterface} - contents={this.reportIssueComponent()} + contents={this.reportComponent()} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} - dialogueBoxStyle={{ width: 'auto', height: '500px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }} + dialogueBoxStyle={{ width: 'auto', minWidth: '400px', height: '85vh', maxHeight: '90vh', background: '#ffffff', borderRadius: '8px' }} /> ); } } + +interface IssueCardProps { + issue: Issue; + onSelect: () => void; +} +const IssueCard = ({ issue, onSelect }: IssueCardProps) => { + return ( + <div className="issue" onClick={onSelect}> + <label className="issue-label">#{issue.number}</label> + <h3 className="issue-title">{issue.title}</h3> + </div> + ); +}; + +interface IssueViewProps { + issue: Issue; +} + +const IssueView = ({ issue }: IssueViewProps) => { + return ( + <div className="issue-view"> + <span className="issue-label"> + Issue{' '} + <a className="issue-link" href={issue.html_url} target="_blank"> + #{issue.number} + </a> + </span> + <h2 className="issue-title">{issue.title}</h2> + <ReactMarkdown children={issue.body as string} className="issue-content" linkTarget={'_blank'} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} /> + {/* <p className="issue-content">{issue.body}</p> */} + </div> + ); +}; diff --git a/src/client/util/reportManagerSchema.ts b/src/client/util/reportManagerSchema.ts new file mode 100644 index 000000000..28ce6411d --- /dev/null +++ b/src/client/util/reportManagerSchema.ts @@ -0,0 +1,877 @@ +/** + * Issues are a great way to keep track of tasks, enhancements, and bugs for your projects. + */ +export interface Issue { + active_lock_reason?: null | string; + assignee: null | PurpleSimpleUser; + assignees?: AssigneeElement[] | null; + /** + * How the author is associated with the repository. + */ + author_association: AuthorAssociation; + /** + * Contents of the issue + */ + body?: null | string; + body_html?: string; + body_text?: string; + closed_at: Date | null; + closed_by?: null | FluffySimpleUser; + comments: number; + comments_url: string; + created_at: Date; + draft?: boolean; + events_url: string; + html_url: string; + id: number; + /** + * Labels to associate with this issue; pass one or more label names to replace the set of + * labels on this issue; send an empty array to clear all labels from the issue; note that + * the labels are silently dropped for users without push access to the repository + */ + labels: Array<LabelObject | string>; + labels_url: string; + locked: boolean; + milestone: null | Milestone; + node_id: string; + /** + * Number uniquely identifying the issue within its repository + */ + number: number; + performed_via_github_app?: null | GitHubApp; + pull_request?: PullRequest; + reactions?: ReactionRollup; + /** + * A repository on GitHub. + */ + repository?: Repository; + repository_url: string; + /** + * State of the issue; either 'open' or 'closed' + */ + state: string; + /** + * The reason for the current state + */ + state_reason?: StateReason | null; + timeline_url?: string; + /** + * Title of the issue + */ + title: string; + updated_at: Date; + /** + * URL for the issue + */ + url: string; + user: null | TentacledSimpleUser; + [property: string]: any; +} + +/** + * A GitHub user. + */ +export interface PurpleSimpleUser { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +/** + * A GitHub user. + */ +export interface AssigneeElement { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +/** + * How the author is associated with the repository. + */ +export enum AuthorAssociation { + Collaborator = 'COLLABORATOR', + Contributor = 'CONTRIBUTOR', + FirstTimeContributor = 'FIRST_TIME_CONTRIBUTOR', + FirstTimer = 'FIRST_TIMER', + Mannequin = 'MANNEQUIN', + Member = 'MEMBER', + None = 'NONE', + Owner = 'OWNER', +} + +/** + * A GitHub user. + */ +export interface FluffySimpleUser { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +export interface LabelObject { + color?: null | string; + default?: boolean; + description?: null | string; + id?: number; + name?: string; + node_id?: string; + url?: string; + [property: string]: any; +} + +/** + * A collection of related issues and pull requests. + */ +export interface Milestone { + closed_at: Date | null; + closed_issues: number; + created_at: Date; + creator: null | MilestoneSimpleUser; + description: null | string; + due_on: Date | null; + html_url: string; + id: number; + labels_url: string; + node_id: string; + /** + * The number of the milestone. + */ + number: number; + open_issues: number; + /** + * The state of the milestone. + */ + state: State; + /** + * The title of the milestone. + */ + title: string; + updated_at: Date; + url: string; + [property: string]: any; +} + +/** + * A GitHub user. + */ +export interface MilestoneSimpleUser { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +/** + * The state of the milestone. + */ +export enum State { + Closed = 'closed', + Open = 'open', +} + +/** + * GitHub apps are a new way to extend GitHub. They can be installed directly on + * organizations and user accounts and granted access to specific repositories. They come + * with granular permissions and built-in webhooks. GitHub apps are first class actors + * within GitHub. + */ +export interface GitHubApp { + client_id?: string; + client_secret?: string; + created_at: Date; + description: null | string; + /** + * The list of events for the GitHub app + */ + events: string[]; + external_url: string; + html_url: string; + /** + * Unique identifier of the GitHub app + */ + id: number; + /** + * The number of installations associated with the GitHub app + */ + installations_count?: number; + /** + * The name of the GitHub app + */ + name: string; + node_id: string; + owner: null | GitHubAppSimpleUser; + pem?: string; + /** + * The set of permissions for the GitHub app + */ + permissions: GitHubAppPermissions; + /** + * The slug name of the GitHub app + */ + slug?: string; + updated_at: Date; + webhook_secret?: null | string; + [property: string]: any; +} + +/** + * A GitHub user. + */ +export interface GitHubAppSimpleUser { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +/** + * The set of permissions for the GitHub app + */ +export interface GitHubAppPermissions { + checks?: string; + contents?: string; + deployments?: string; + issues?: string; + metadata?: string; +} + +export interface PullRequest { + diff_url: null | string; + html_url: null | string; + merged_at?: Date | null; + patch_url: null | string; + url: null | string; + [property: string]: any; +} + +export interface ReactionRollup { + '+1': number; + '-1': number; + confused: number; + eyes: number; + heart: number; + hooray: number; + laugh: number; + rocket: number; + total_count: number; + url: string; + [property: string]: any; +} + +/** + * A repository on GitHub. + */ +export interface Repository { + /** + * Whether to allow Auto-merge to be used on pull requests. + */ + allow_auto_merge?: boolean; + /** + * Whether to allow forking this repo + */ + allow_forking?: boolean; + /** + * Whether to allow merge commits for pull requests. + */ + allow_merge_commit?: boolean; + /** + * Whether to allow rebase merges for pull requests. + */ + allow_rebase_merge?: boolean; + /** + * Whether to allow squash merges for pull requests. + */ + allow_squash_merge?: boolean; + /** + * Whether or not a pull request head branch that is behind its base branch can always be + * updated even if it is not required to be up to date before merging. + */ + allow_update_branch?: boolean; + /** + * Whether anonymous git access is enabled for this repository + */ + anonymous_access_enabled?: boolean; + archive_url: string; + /** + * Whether the repository is archived. + */ + archived: boolean; + assignees_url: string; + blobs_url: string; + branches_url: string; + clone_url: string; + collaborators_url: string; + comments_url: string; + commits_url: string; + compare_url: string; + contents_url: string; + contributors_url: string; + created_at: Date | null; + /** + * The default branch of the repository. + */ + default_branch: string; + /** + * Whether to delete head branches when pull requests are merged + */ + delete_branch_on_merge?: boolean; + deployments_url: string; + description: null | string; + /** + * Returns whether or not this repository disabled. + */ + disabled: boolean; + downloads_url: string; + events_url: string; + fork: boolean; + forks: number; + forks_count: number; + forks_url: string; + full_name: string; + git_commits_url: string; + git_refs_url: string; + git_tags_url: string; + git_url: string; + /** + * Whether discussions are enabled. + */ + has_discussions?: boolean; + /** + * Whether downloads are enabled. + */ + has_downloads: boolean; + /** + * Whether issues are enabled. + */ + has_issues: boolean; + has_pages: boolean; + /** + * Whether projects are enabled. + */ + has_projects: boolean; + /** + * Whether the wiki is enabled. + */ + has_wiki: boolean; + homepage: null | string; + hooks_url: string; + html_url: string; + /** + * Unique identifier of the repository + */ + id: number; + /** + * Whether this repository acts as a template that can be used to generate new repositories. + */ + is_template?: boolean; + issue_comment_url: string; + issue_events_url: string; + issues_url: string; + keys_url: string; + labels_url: string; + language: null | string; + languages_url: string; + license: null | LicenseSimple; + master_branch?: string; + /** + * The default value for a merge commit message. + * + * - `PR_TITLE` - default to the pull request's title. + * - `PR_BODY` - default to the pull request's body. + * - `BLANK` - default to a blank commit message. + */ + merge_commit_message?: MergeCommitMessage; + /** + * The default value for a merge commit title. + * + * - `PR_TITLE` - default to the pull request's title. + * - `MERGE_MESSAGE` - default to the classic title for a merge message (e.g., Merge pull + * request #123 from branch-name). + */ + merge_commit_title?: MergeCommitTitle; + merges_url: string; + milestones_url: string; + mirror_url: null | string; + /** + * The name of the repository. + */ + name: string; + network_count?: number; + node_id: string; + notifications_url: string; + open_issues: number; + open_issues_count: number; + organization?: null | RepositorySimpleUser; + /** + * A GitHub user. + */ + owner: OwnerObject; + permissions?: RepositoryPermissions; + /** + * Whether the repository is private or public. + */ + private: boolean; + pulls_url: string; + pushed_at: Date | null; + releases_url: string; + /** + * The size of the repository. Size is calculated hourly. When a repository is initially + * created, the size is 0. + */ + size: number; + /** + * The default value for a squash merge commit message: + * + * - `PR_BODY` - default to the pull request's body. + * - `COMMIT_MESSAGES` - default to the branch's commit messages. + * - `BLANK` - default to a blank commit message. + */ + squash_merge_commit_message?: SquashMergeCommitMessage; + /** + * The default value for a squash merge commit title: + * + * - `PR_TITLE` - default to the pull request's title. + * - `COMMIT_OR_PR_TITLE` - default to the commit's title (if only one commit) or the pull + * request's title (when more than one commit). + */ + squash_merge_commit_title?: SquashMergeCommitTitle; + ssh_url: string; + stargazers_count: number; + stargazers_url: string; + starred_at?: string; + statuses_url: string; + subscribers_count?: number; + subscribers_url: string; + subscription_url: string; + svn_url: string; + tags_url: string; + teams_url: string; + temp_clone_token?: string; + template_repository?: null | TemplateRepository; + topics?: string[]; + trees_url: string; + updated_at: Date | null; + url: string; + /** + * Whether a squash merge commit can use the pull request title as default. **This property + * has been deprecated. Please use `squash_merge_commit_title` instead. + */ + use_squash_pr_title_as_default?: boolean; + /** + * The repository visibility: public, private, or internal. + */ + visibility?: string; + watchers: number; + watchers_count: number; + /** + * Whether to require contributors to sign off on web-based commits + */ + web_commit_signoff_required?: boolean; + [property: string]: any; +} + +/** + * License Simple + */ +export interface LicenseSimple { + html_url?: string; + key: string; + name: string; + node_id: string; + spdx_id: null | string; + url: null | string; + [property: string]: any; +} + +/** + * The default value for a merge commit message. + * + * - `PR_TITLE` - default to the pull request's title. + * - `PR_BODY` - default to the pull request's body. + * - `BLANK` - default to a blank commit message. + */ +export enum MergeCommitMessage { + Blank = 'BLANK', + PRBody = 'PR_BODY', + PRTitle = 'PR_TITLE', +} + +/** + * The default value for a merge commit title. + * + * - `PR_TITLE` - default to the pull request's title. + * - `MERGE_MESSAGE` - default to the classic title for a merge message (e.g., Merge pull + * request #123 from branch-name). + */ +export enum MergeCommitTitle { + MergeMessage = 'MERGE_MESSAGE', + PRTitle = 'PR_TITLE', +} + +/** + * A GitHub user. + */ +export interface RepositorySimpleUser { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +/** + * A GitHub user. + */ +export interface OwnerObject { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} + +export interface RepositoryPermissions { + admin: boolean; + maintain?: boolean; + pull: boolean; + push: boolean; + triage?: boolean; + [property: string]: any; +} + +/** + * The default value for a squash merge commit message: + * + * - `PR_BODY` - default to the pull request's body. + * - `COMMIT_MESSAGES` - default to the branch's commit messages. + * - `BLANK` - default to a blank commit message. + */ +export enum SquashMergeCommitMessage { + Blank = 'BLANK', + CommitMessages = 'COMMIT_MESSAGES', + PRBody = 'PR_BODY', +} + +/** + * The default value for a squash merge commit title: + * + * - `PR_TITLE` - default to the pull request's title. + * - `COMMIT_OR_PR_TITLE` - default to the commit's title (if only one commit) or the pull + * request's title (when more than one commit). + */ +export enum SquashMergeCommitTitle { + CommitOrPRTitle = 'COMMIT_OR_PR_TITLE', + PRTitle = 'PR_TITLE', +} + +export interface TemplateRepository { + allow_auto_merge?: boolean; + allow_merge_commit?: boolean; + allow_rebase_merge?: boolean; + allow_squash_merge?: boolean; + allow_update_branch?: boolean; + archive_url?: string; + archived?: boolean; + assignees_url?: string; + blobs_url?: string; + branches_url?: string; + clone_url?: string; + collaborators_url?: string; + comments_url?: string; + commits_url?: string; + compare_url?: string; + contents_url?: string; + contributors_url?: string; + created_at?: string; + default_branch?: string; + delete_branch_on_merge?: boolean; + deployments_url?: string; + description?: string; + disabled?: boolean; + downloads_url?: string; + events_url?: string; + fork?: boolean; + forks_count?: number; + forks_url?: string; + full_name?: string; + git_commits_url?: string; + git_refs_url?: string; + git_tags_url?: string; + git_url?: string; + has_downloads?: boolean; + has_issues?: boolean; + has_pages?: boolean; + has_projects?: boolean; + has_wiki?: boolean; + homepage?: string; + hooks_url?: string; + html_url?: string; + id?: number; + is_template?: boolean; + issue_comment_url?: string; + issue_events_url?: string; + issues_url?: string; + keys_url?: string; + labels_url?: string; + language?: string; + languages_url?: string; + /** + * The default value for a merge commit message. + * + * - `PR_TITLE` - default to the pull request's title. + * - `PR_BODY` - default to the pull request's body. + * - `BLANK` - default to a blank commit message. + */ + merge_commit_message?: MergeCommitMessage; + /** + * The default value for a merge commit title. + * + * - `PR_TITLE` - default to the pull request's title. + * - `MERGE_MESSAGE` - default to the classic title for a merge message (e.g., Merge pull + * request #123 from branch-name). + */ + merge_commit_title?: MergeCommitTitle; + merges_url?: string; + milestones_url?: string; + mirror_url?: string; + name?: string; + network_count?: number; + node_id?: string; + notifications_url?: string; + open_issues_count?: number; + owner?: Owner; + permissions?: TemplateRepositoryPermissions; + private?: boolean; + pulls_url?: string; + pushed_at?: string; + releases_url?: string; + size?: number; + /** + * The default value for a squash merge commit message: + * + * - `PR_BODY` - default to the pull request's body. + * - `COMMIT_MESSAGES` - default to the branch's commit messages. + * - `BLANK` - default to a blank commit message. + */ + squash_merge_commit_message?: SquashMergeCommitMessage; + /** + * The default value for a squash merge commit title: + * + * - `PR_TITLE` - default to the pull request's title. + * - `COMMIT_OR_PR_TITLE` - default to the commit's title (if only one commit) or the pull + * request's title (when more than one commit). + */ + squash_merge_commit_title?: SquashMergeCommitTitle; + ssh_url?: string; + stargazers_count?: number; + stargazers_url?: string; + statuses_url?: string; + subscribers_count?: number; + subscribers_url?: string; + subscription_url?: string; + svn_url?: string; + tags_url?: string; + teams_url?: string; + temp_clone_token?: string; + topics?: string[]; + trees_url?: string; + updated_at?: string; + url?: string; + use_squash_pr_title_as_default?: boolean; + visibility?: string; + watchers_count?: number; + [property: string]: any; +} + +export interface Owner { + avatar_url?: string; + events_url?: string; + followers_url?: string; + following_url?: string; + gists_url?: string; + gravatar_id?: string; + html_url?: string; + id?: number; + login?: string; + node_id?: string; + organizations_url?: string; + received_events_url?: string; + repos_url?: string; + site_admin?: boolean; + starred_url?: string; + subscriptions_url?: string; + type?: string; + url?: string; + [property: string]: any; +} + +export interface TemplateRepositoryPermissions { + admin?: boolean; + maintain?: boolean; + pull?: boolean; + push?: boolean; + triage?: boolean; + [property: string]: any; +} + +export enum StateReason { + Completed = 'completed', + NotPlanned = 'not_planned', + Reopened = 'reopened', +} + +/** + * A GitHub user. + */ +export interface TentacledSimpleUser { + avatar_url: string; + email?: null | string; + events_url: string; + followers_url: string; + following_url: string; + gists_url: string; + gravatar_id: null | string; + html_url: string; + id: number; + login: string; + name?: null | string; + node_id: string; + organizations_url: string; + received_events_url: string; + repos_url: string; + site_admin: boolean; + starred_at?: string; + starred_url: string; + subscriptions_url: string; + type: string; + url: string; + [property: string]: any; +} |