From c8eb4ac0242181744d3268b1052582b61dbaf477 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Sat, 8 Jul 2023 02:58:04 -0400 Subject: feat: updated github key and ui --- src/client/theme.ts | 47 +- src/client/util/ReportManager.scss | 383 +++++++++++--- src/client/util/ReportManager.tsx | 478 ++++++++++++------ src/client/util/reportManagerSchema.ts | 877 +++++++++++++++++++++++++++++++++ 4 files changed, 1530 insertions(+), 255 deletions(-) create mode 100644 src/client/util/reportManagerSchema.ts (limited to 'src') 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 ( +
+
+
+

Open Issues

+ +
+ { + this.setQuery(e.target.value); + }} + required + /> +
+ {this.shownIssues + .filter(issue => issue.title.toLowerCase().includes(this.query)) + .map(issue => ( + { + this.setSelectedIssue(issue); + }} + /> + ))} +
+
+
{this.selectedIssue ? :
No issue selected
}
+ + + +
+ ); }; private reportIssueComponent = () => { return (
-

Report an issue

- this.setBugTitle(e.target.value)} - /> - -