diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/theme.ts | 42 | ||||
-rw-r--r-- | src/client/util/ReportManager.scss | 14 | ||||
-rw-r--r-- | src/client/util/ReportManager.tsx | 235 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 96 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss | 12 |
5 files changed, 269 insertions, 130 deletions
diff --git a/src/client/theme.ts b/src/client/theme.ts new file mode 100644 index 000000000..57be370cc --- /dev/null +++ b/src/client/theme.ts @@ -0,0 +1,42 @@ +import '@mui/material/styles'; +import { createTheme } from '@mui/material/styles'; + +export const theme = createTheme({ + palette: { + primary: { + main: 'rgb(0, 149, 246)', + }, + }, + // 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 5a2f2fcad..969e0de74 100644 --- a/src/client/util/ReportManager.scss +++ b/src/client/util/ReportManager.scss @@ -1,5 +1,19 @@ @import '../views/global/globalCssVariables'; +.report-issue { + padding: 16px; + display: flex; + flex-direction: column; + gap: 1rem; + background-color: #ffffff; + text-align: left; + + h2 { + font-size: 24px; + } +} + +// --------------------------------------- .issue-list-wrapper { position: relative; min-width: 250px; diff --git a/src/client/util/ReportManager.tsx b/src/client/util/ReportManager.tsx index 4c1020455..a08ef9979 100644 --- a/src/client/util/ReportManager.tsx +++ b/src/client/util/ReportManager.tsx @@ -17,12 +17,14 @@ import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import './ReportManager.scss'; import { undoBatch } from './UndoManager'; -import { Octokit } from "@octokit/core"; +import { Octokit } from '@octokit/core'; import { CheckBox } from '../views/search/CheckBox'; 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'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -35,21 +37,25 @@ export class ReportManager extends React.Component<{}> { private octokit: Octokit; @observable public issues: any[] = []; - @action setIssues = action((issues: any[]) => { this.issues = issues; }); - + @action setIssues = action((issues: any[]) => { + this.issues = issues; + }); + // undefined is the default - null is if the user is making an issue @observable public selectedIssue: any = undefined; - @action setSelectedIssue = action((issue: any) => { this.selectedIssue = issue; }); + @action setSelectedIssue = action((issue: any) => { + 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())); + this.shownIssues = this.issues.filter(issue => issue.title.toLowerCase().includes(query.toLowerCase())); }); constructor(props: {}) { @@ -57,7 +63,7 @@ export class ReportManager extends React.Component<{}> { ReportManager.Instance = this; this.octokit = new Octokit({ - auth: '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' + auth: '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', }); } @@ -72,25 +78,33 @@ export class ReportManager extends React.Component<{}> { }) .catch(err => console.log(err)); } - (this.isOpen = true) + this.isOpen = true; }); @observable private bugTitle = ''; - @action setBugTitle = action((title: string) => { this.bugTitle = title; }); + @action setBugTitle = action((title: string) => { + this.bugTitle = title; + }); @observable private bugDescription = ''; - @action setBugDescription = action((description: string) => { this.bugDescription = description; }); + @action setBugDescription = action((description: string) => { + this.bugDescription = description; + }); @observable private bugType = ''; - @action setBugType = action((type: string) => { this.bugType = type; }); + @action setBugType = action((type: string) => { + this.bugType = type; + }); @observable private bugPriority = ''; - @action setBugPriority = action((priortiy: string) => { this.bugPriority = priortiy; }); + @action setBugPriority = action((priortiy: string) => { + this.bugPriority = priortiy; + }); // 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', '')}`; + private formatTitle = (title: string, userEmail: string) => `${title} - ${userEmail.replace('@brown.edu', '')}`; - public async getAllIssues() : Promise<any[]> { + public async getAllIssues(): Promise<any[]> { const res = await this.octokit.request('GET /repos/{owner}/{repo}/issues', { owner: 'brown-dash', repo: 'Dash-Web', @@ -105,40 +119,34 @@ export class ReportManager extends React.Component<{}> { } // turns an upload link into a servable link - // ex: + // 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 regex = 'public'; const publicIndex = fileLink.indexOf(regex) + regex.length; const finalUrl = `${serverUrl}${fileLink.substring(publicIndex + 1).replace('.', '_l.')}`; return finalUrl; - } + }; public async reportIssue() { - if (this.bugTitle === '' || this.bugDescription === '' - || this.bugType === '' || this.bugPriority === '') { + if (this.bugTitle === '' || this.bugDescription === '' || this.bugType === '' || this.bugPriority === '') { alert('Please fill out all required fields to report an issue.'); return; } if (this.toGithub) { + const formattedLinks = (this.fileLinks ?? []).map(this.fileLinktoServerLink); - const formattedLinks = (this.fileLinks ?? []).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 - ] + labels: ['from-dash-app', this.bugType, this.bugPriority], }); // 201 status means success @@ -147,8 +155,7 @@ export class ReportManager extends React.Component<{}> { // on error, don't close the modal return; } - } - else { + } else { // if not going to github issues, not sure what to do yet... } @@ -163,9 +170,13 @@ export class ReportManager extends React.Component<{}> { } @observable public fileLinks: any = []; - @action setFileLinks = action((links: any) => { this.fileLinks = links; }); + @action setFileLinks = action((links: any) => { + this.fileLinks = links; + }); - private getServerPath = (link: any) => { return link.result.accessPaths.agnostic.server } + private getServerPath = (link: any) => { + return link.result.accessPaths.agnostic.server; + }; private uploadFiles = (input: any) => { // keep null while uploading @@ -173,112 +184,177 @@ export class ReportManager extends React.Component<{}> { // 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 => { + Networking.UploadFilesToServer(fileArray.map(file => ({ file }))).then(links => { console.log('finshed uploading', links.map(this.getServerPath)); this.setFileLinks((links ?? []).map(this.getServerPath)); - }) + }); } - - } + }; + @observable private age = ''; - private renderIssue = (issue: any) => { + @action private setAge = (e: SelectChangeEvent) => { + this.age = e.target.value as string; + }; + + 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> + ); + }; + private renderIssue = (issue: any) => { const isReportingIssue = issue === null; - return isReportingIssue ? + return isReportingIssue ? ( // report issue - (<div className="settings-content"> - <h3 style={{ 'textDecoration': 'underline'}}>Report an Issue</h3> - <label>Please leave a title for the bug.</label><br /> - <input type="text" placeholder='title' onChange={(e) => this.setBugTitle(e.target.value)} required/> + <div className="settings-content"> + <h3 style={{ textDecoration: 'underline' }}>Report an Issue</h3> + <label>Please leave a title for the bug.</label> + <br /> + <input value={this.bugTitle} type="text" placeholder="title" onChange={e => this.setBugTitle(e.target.value)} required /> + {/* <TextField fullWidth type="text" placeholder="Title..." required onChange={e => this.setBugTitle(e.target.value)} /> */} <br /> <label>Please leave a description for the bug and how it can be recreated.</label> - <textarea placeholder='description' onChange={(e) => this.setBugDescription(e.target.value)} required/> + <textarea value={this.bugDescription} placeholder="description" onChange={e => this.setBugDescription(e.target.value)} required /> <br /> {/* {<label>Send to github issues? </label> <input type="checkbox" onChange={(e) => this.toGithub = e.target.checked} /> <br /> } */} <label>Please label the issue</label> - <div className='flex-select'> - <select name="bugType" onChange={e => this.bugType = e.target.value}> - <option value="" disabled selected>Type</option> - <option value="bug">Bug</option> - <option value="cosmetic">Poor Design or Cosmetic</option> - <option value="documentation">Poor Documentation</option> + <div className="flex-select"> + <select name="bugType" onChange={e => (this.bugType = e.target.value)}> + <option value="" disabled selected> + Type + </option> + <option value="priority-low">Bug</option> + <option value="priority-medium">Poor Design or Cosmetic</option> + <option value="priority-high">Poor Documentation</option> </select> - <select name="bigPriority" onChange={e => this.bugPriority = e.target.value}> - <option value="" disabled selected>Priority</option> + <select name="bigPriority" onChange={e => (this.bugPriority = e.target.value)}> + <option 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> - <div> <label>Upload media that shows the bug (optional)</label> - <input type="file" name="file" multiple accept='audio/*, video/*, image/*' onChange={e => this.uploadFiles(e.target)}/> + <input type="file" name="file" multiple accept="audio/*, video/*, image/*" onChange={e => this.uploadFiles(e.target)} /> </div> <br /> - <button onClick={() => this.reportIssue()} disabled={this.fileLinks === null} style={{ backgroundColor: this.fileLinks === null ? 'grey' : '' }}>{this.fileLinks === null ? 'Uploading...' : 'Submit'}</button> - </div>) - : + <button onClick={() => this.reportIssue()} disabled={this.fileLinks === null} style={{ backgroundColor: this.fileLinks === null ? 'grey' : '' }}> + {this.fileLinks === null ? 'Uploading...' : 'Submit'} + </button> + </div> + ) : ( // view issue - ( - <div className='issue-container'> - <h5 style={{'textAlign': "left"}}><a href={issue.html_url} target="_blank">Issue #{issue.number}</a></h5> - <div className='issue-title'> - {issue.title} - </div> - <ReactMarkdown children={issue.body} className='issue-body' linkTarget={"_blank"} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} /> + <div className="issue-container"> + <h5 style={{ textAlign: 'left' }}> + <a href={issue.html_url} target="_blank"> + Issue #{issue.number} + </a> + </h5> + <div className="issue-title">{issue.title}</div> + <ReactMarkdown children={issue.body} 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'> + <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>)} + <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 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 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> ); } @@ -286,7 +362,8 @@ export class ReportManager extends React.Component<{}> { render() { return ( <MainViewModal - contents={this.reportInterface} + // contents={this.reportInterface} + contents={this.reportIssueComponent()} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ab2e0f7c5..4557e5866 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -65,6 +65,8 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; +import { ThemeProvider } from '@mui/material'; +import { theme } from '../theme'; const _global = (window /* browser */ || global) /* node */ as any; @observer @@ -962,38 +964,39 @@ export class MainView extends React.Component { render() { return ( - <div - className={`mainView-container${this.colorScheme}`} - onScroll={() => (ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)} - ref={r => { - r && - new _global.ResizeObserver( - action(() => { - this._windowWidth = r.getBoundingClientRect().width; - this._windowHeight = r.getBoundingClientRect().height; - }) - ).observe(r); - }}> - {this.inkResources} - <DictationOverlay /> - <SharingManager /> - <ServerStats /> - <RTFMarkup /> - <SettingsManager /> - <ReportManager /> - <CaptureManager /> - <GroupManager /> - <GoogleAuthenticationManager /> - <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfSidebarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} /> - <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} /> - {this._hideUI ? null : <TopBar />} - {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} - {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null} - {this.linkDocPreview} + <ThemeProvider theme={theme}> + <div + className={`mainView-container${this.colorScheme}`} + onScroll={() => (ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)} + ref={r => { + r && + new _global.ResizeObserver( + action(() => { + this._windowWidth = r.getBoundingClientRect().width; + this._windowHeight = r.getBoundingClientRect().height; + }) + ).observe(r); + }}> + {this.inkResources} + <DictationOverlay /> + <SharingManager /> + <ServerStats /> + <RTFMarkup /> + <SettingsManager /> + <ReportManager /> + <CaptureManager /> + <GroupManager /> + <GoogleAuthenticationManager /> + <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfSidebarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} /> + <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} /> + {this._hideUI ? null : <TopBar />} + {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} + {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null} + {this.linkDocPreview} - {((page: string) => { - // prettier-ignore - switch (page) { + {((page: string) => { + // prettier-ignore + switch (page) { default: case 'dashboard': return (<> <div key="dashdiv" style={{ position: 'relative', display: this._hideUI || LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 2001 }}> @@ -1003,22 +1006,23 @@ export class MainView extends React.Component { </> ); case 'home': return <DashboardView />; } - })(Doc.ActivePage)} + })(Doc.ActivePage)} - <PreviewCursor /> - <TaskCompletionBox /> - <ContextMenu /> - <RadialMenu /> - <AnchorMenu /> - <DashFieldViewMenu /> - <MarqueeOptionsMenu /> - <OverlayView /> - <TimelineMenu /> - <RichTextMenu /> - <InkTranscription /> - {this.snapLines} - <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> - </div> + <PreviewCursor /> + <TaskCompletionBox /> + <ContextMenu /> + <RadialMenu /> + <AnchorMenu /> + <DashFieldViewMenu /> + <MarqueeOptionsMenu /> + <OverlayView /> + <TimelineMenu /> + <RichTextMenu /> + <InkTranscription /> + {this.snapLines} + <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> + </div> + </ThemeProvider> ); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss index cb5cef29c..4ada1731f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss @@ -1,11 +1,13 @@ -.collectionfreeformlinksview-svgCanvas{ +// TODO: change z-index to -1 when a modal is active? + +.collectionfreeformlinksview-svgCanvas { position: absolute; top: 0; left: 0; - width: 100%; + width: 100%; height: 100%; pointer-events: none; - } - .collectionfreeformlinksview-container { +} +.collectionfreeformlinksview-container { pointer-events: none; - }
\ No newline at end of file +} |