From b1f189ffc7dfe558d5895c8f0cb103ab3e5c17d7 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Mon, 10 Jul 2023 19:10:34 -0400 Subject: filters and better ui --- src/client/theme.ts | 8 + src/client/util/ReportManager.scss | 409 ---------- src/client/util/ReportManager.tsx | 548 ------------- src/client/util/reportManager/ReportManager.scss | 473 +++++++++++ src/client/util/reportManager/ReportManager.tsx | 570 +++++++++++++ .../util/reportManager/ReportManagerComponents.tsx | 153 ++++ .../util/reportManager/reportManagerSchema.ts | 877 +++++++++++++++++++++ .../util/reportManager/reportManagerUtils.ts | 54 ++ src/client/util/reportManagerSchema.ts | 877 --------------------- src/client/views/MainView.tsx | 2 +- .../CollectionFreeFormLinkView.tsx | 3 + src/client/views/nodes/LoadingBox.scss | 4 + src/client/views/topbar/TopBar.tsx | 2 +- 13 files changed, 2144 insertions(+), 1836 deletions(-) delete mode 100644 src/client/util/ReportManager.scss delete mode 100644 src/client/util/ReportManager.tsx create mode 100644 src/client/util/reportManager/ReportManager.scss create mode 100644 src/client/util/reportManager/ReportManager.tsx create mode 100644 src/client/util/reportManager/ReportManagerComponents.tsx create mode 100644 src/client/util/reportManager/reportManagerSchema.ts create mode 100644 src/client/util/reportManager/reportManagerUtils.ts delete mode 100644 src/client/util/reportManagerSchema.ts (limited to 'src') diff --git a/src/client/theme.ts b/src/client/theme.ts index f0a11fe6e..0ef2e5e2c 100644 --- a/src/client/theme.ts +++ b/src/client/theme.ts @@ -19,5 +19,13 @@ export const theme = createTheme({ }, }, }, + + MuiTooltip: { + styleOverrides: { + tooltip: { + fontSize: '12px', + }, + }, + }, }, }); diff --git a/src/client/util/ReportManager.scss b/src/client/util/ReportManager.scss deleted file mode 100644 index b3bd998e6..000000000 --- a/src/client/util/ReportManager.scss +++ /dev/null @@ -1,409 +0,0 @@ -@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: 450px; - min-width: 300px; - padding: 16px; - padding-top: 32px; - display: flex; - flex-direction: column; - gap: 16px; - background-color: #ffffff; - text-align: left; - position: relative; - - .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; - flex-direction: column; - align-items: stretch; - gap: 16px; - - .report-select { - padding: 8px; - border-color: #c6c6c6; - - .report-opt { - padding: 8px; - } - } - } -} - -.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; - } -} - -// View issues - -.view-issues { - width: 65vw; - min-width: 500px; - display: flex; - 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; - } -} - -// video - -.default-video::-webkit-media-controls { - display: block !important; -} - -// Issue - -.issue { - cursor: pointer; - 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; - 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; - } - } -} - -// 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; - } - } -} - -.file-list { - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - color: #7f7f7f; - width: 100%; - overflow-x: auto; - list-style-type: none; - display: flex; - gap: 16px; - - .file-name { - padding: 8px 12px; - display: flex; - align-items: center; - gap: 16px; - white-space: nowrap; - } -} - -// 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-label { - color: #7f7f7f; - - .issue-link { - cursor: pointer; - color: #4476f7; - } - } - - .issue-title { - font-size: 24px; - margin: 0; - padding: 0; - } - - .issue-content { - font-size: 14px; - color: #7f7f7f; - } -} - -// Media previews - -.report-media-wrapper { - position: relative; - - .close-btn { - position: absolute; - top: 2px; - right: 2px; - opacity: 0; - } - - .report-media-content { - position: relative; - display: inline block; - cursor: pointer; - } - - .report-media-content::after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); /* Adjust the opacity as desired */ - opacity: 0; - transition: opacity 0.3s ease; /* Transition for smooth effect */ - } - - &:hover { - .report-media-content::after { - opacity: 1; - } - - .close-btn { - opacity: 1; - } - } -} - -.report-audio-wrapper { - display: flex; - align-items: center; - gap: 8px; -} - -@media (max-width: 1100px) { - .report-header { - flex-direction: column; - align-items: stretch; - gap: 2rem; - } -} - -// Old code - -// <----------------------------------------------------------------------------> - -// .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 deleted file mode 100644 index 125d20876..000000000 --- a/src/client/util/ReportManager.tsx +++ /dev/null @@ -1,548 +0,0 @@ -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 { 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 ReactMarkdown from 'react-markdown'; -import rehypeRaw from 'rehype-raw'; -import remarkGfm from 'remark-gfm'; -import { theme } from '../theme'; -import v4 = require('uuid/v4'); -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; - -enum ViewState { - VIEW, - CREATE, -} - -interface FileData { - _id: string; - file: File; -} - -// Format reference: "https://browndash.com/files/images/upload_cb31bc0fda59c96ca14193ec494f80cf_o.jpg" /> - -/** - * Class for reporting and viewing Github issues within the app. - */ -@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 fetchingIssues: boolean = false; - @action private setFetchingIssues = (fetching: boolean) => { - this.fetchingIssues = fetching; - }; - - @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; - }); - - // Form state - - @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; - }); - - @observable private mediaFiles: FileData[] = []; - @action private setMediaFiles = (files: FileData[]) => { - this.mediaFiles = files; - }; - - public close = action(() => (this.isOpen = false)); - public open = action(async () => { - this.isOpen = true; - if (this.shownIssues.length === 0) { - this.setFetchingIssues(true); - try { - // load in the issues if not already loaded - const issues = (await this.getAllIssues()) as Issue[]; - this.setShownIssues(issues.filter(issue => issue.state === 'open' && !issue.pull_request)); - } catch (err) { - console.log(err); - } - this.setFetchingIssues(false); - } - }); - - constructor(props: {}) { - super(props); - ReportManager.Instance = this; - - // initializing Github connection - this.octokit = new Octokit({ - auth: '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', - }); - } - - /** - * Fethches issues from Github. - * @returns array of all issues - */ - 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'); - } - } - - /** - * Sends a request to Github to report a new issue with the form data. - * @returns nothing - */ - public async reportIssue(): Promise { - if (this.bugTitle === '' || this.bugDescription === '' || this.bugType === '' || this.bugPriority === '') { - alert('Please fill out all required fields to report an issue.'); - return; - } - this.setSubmitting(true); - - const links = await this.uploadFilesToServer(); - if (!links) { - // error uploading files to the server - return; - } - 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} ${formattedLinks.length > 0 && `\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.'); - return; - } - - // Reset fields - this.setBugTitle(''); - this.setBugDescription(''); - this.setMediaFiles([]); - this.setBugType(''); - this.setBugPriority(''); - this.setSubmitting(false); - alert('Successfully submitted issue.'); - // this.close(); - } - - /** - * Formats issue title. - * - * @param title title of issue - * @param userEmail email of issue submitter - * @returns formatted title - */ - private formatTitle = (title: string, userEmail: string): string => `${title} - ${userEmail.replace('@brown.edu', '')}`; - - // turns an upload link into a servable link - // ex: - // C: /Users/dash/Documents/GitHub/Dash-Web/src/server/public/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2.png - // -> https://browndash.com/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; - }; - - /** - * Gets the server file path. - * - * @param link response from file upload - * @returns server file path - */ - private getServerPath = (link: any): string => { - return link.result.accessPaths.agnostic.server as string; - }; - - /** - * Uploads media files to the server. - * @returns the server paths or undefined on error - */ - private uploadFilesToServer = async (): Promise => { - try { - const links = await Networking.UploadFilesToServer( - this.mediaFiles.map(file => ({ file: file.file })), - true - ); - return (links ?? []).map(this.getServerPath); - } catch (err) { - if (err instanceof Error) { - alert(err.message); - } else { - alert(err); - } - } - }; - - /** - * Handles file upload. - * @param files uploaded files - */ - private onDrop = (files: File[]) => { - this.setMediaFiles([...this.mediaFiles, ...files.map(file => ({ _id: v4(), file }))]); - }; - - /** - * Gets a JSX element to render a media preview - * @param fileData file data - * @returns JSX element of a piece of media (image, video, audio) - */ - private getMediaPreview = (fileData: FileData): JSX.Element => { - const file = fileData.file; - const mimeType = file.type; - const preview = URL.createObjectURL(file); - - if (mimeType.startsWith('image/')) { - return ( -
-
- {`Preview -
- this.setMediaFiles(this.mediaFiles.filter(f => f._id !== fileData._id))} className="close-btn"> - - -
- ); - } else if (mimeType.startsWith('video/')) { - return ( -
-
- -
- this.setMediaFiles(this.mediaFiles.filter(f => f._id !== fileData._id))} className="close-btn"> - - -
- ); - } else if (mimeType.startsWith('audio/')) { - return ( -
-
- ); - } - return <>; - }; - - /** - * @returns the component that dispays all issues - */ - private viewIssuesComponent = () => { - return ( -
-
-
-

Open Issues

- -
- { - this.setQuery(e.target.value); - }} - required - /> -
- {this.fetchingIssues ? ( -
- -
- ) : ( - this.shownIssues - .filter(issue => issue.title.toLowerCase().includes(this.query)) - .map(issue => ( - { - this.setSelectedIssue(issue); - }} - /> - )) - )} -
-
-
{this.selectedIssue ? :
No issue selected
}
- - - -
- ); - }; - - /** - * @returns the form component for submitting issues - */ - private reportIssueComponent = () => { - return ( -
-
- -

Report an Issue

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