import { ColorState, SketchPicker } from 'react-color'; 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 { 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 { 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 { 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 { 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 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; }); @observable selectedIssue: Issue | undefined = undefined; @action setSelectedIssue = action((issue: Issue | undefined) => { this.selectedIssue = issue; }); @observable private mediaFiles: File[] = []; @action private setMediaFiles = (files: File[]) => { this.mediaFiles = files; }; constructor(props: {}) { super(props); ReportManager.Instance = this; this.octokit = new Octokit({ auth: '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', }); } public close = action(() => (this.isOpen = false)); 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 = ''; @action setBugTitle = action((title: string) => { this.bugTitle = title; }); @observable private bugDescription = ''; @action setBugDescription = action((description: string) => { this.bugDescription = description; }); @observable private bugType = ''; @action setBugType = action((type: string) => { this.bugType = type; }); @observable private bugPriority = ''; @action setBugPriority = action((priortiy: string) => { 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; private formatTitle = (title: string, userEmail: string) => `${title} - ${userEmail.replace('@brown.edu', '')}`; public async getAllIssues(): Promise { const res = await this.octokit.request('GET /repos/{owner}/{repo}/issues', { owner: 'brown-dash', repo: 'Dash-Web', }); // 200 status means success if (res.status === 200) { return res.data; } else { throw new Error('Error getting issues'); } } // turns an upload link into a servable link // ex: // C: /Users/dash/Documents/GitHub/Dash-Web/src/server/public/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2.png // -> http://localhost:1050/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2_l.png private fileLinktoServerLink = (fileLink: string) => { const serverUrl = 'https://browndash.com/'; const regex = 'public'; const publicIndex = fileLink.indexOf(regex) + regex.length; const finalUrl = `${serverUrl}${fileLink.substring(publicIndex + 1).replace('.', '_l.')}`; return finalUrl; }; 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); 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], }); // 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. this.setBugTitle(''); this.setBugDescription(''); // this.toGithub = false; this.setFileLinks([]); this.setBugType(''); this.setBugPriority(''); this.setSubmitting(false); alert('Successfully submitted issue.'); // this.close(); } @observable public fileLinks: any = []; @action setFileLinks = action((links: any) => { this.fileLinks = links; }); private getServerPath = (link: any) => { return link.result.accessPaths.agnostic.server; }; private uploadFiles = (input: any) => { // keep null while uploading this.setFileLinks(null); // upload the files to the server if (input.files && input.files.length !== 0) { const fileArray: File[] = Array.from(input.files); Networking.UploadFilesToServer(fileArray.map(file => ({ file }))).then(links => { console.log('finshed uploading', links.map(this.getServerPath)); this.setFileLinks((links ?? []).map(this.getServerPath)); }); } }; 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)); }; 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)} required />