diff --git a/src/components/assessment/assessmentShape.ts b/src/components/assessment/assessmentShape.ts index f03483655c..24598638f2 100644 --- a/src/components/assessment/assessmentShape.ts +++ b/src/components/assessment/assessmentShape.ts @@ -10,6 +10,7 @@ export interface IAssessmentOverview { category: AssessmentCategory; closeAt: string; coverImage: string; + fileName?: string; grade: number; id: number; maxGrade: number; diff --git a/src/components/incubator/EditingOverviewCard.tsx b/src/components/incubator/EditingOverviewCard.tsx index ff20dbe181..2ff1c6070a 100644 --- a/src/components/incubator/EditingOverviewCard.tsx +++ b/src/components/incubator/EditingOverviewCard.tsx @@ -1,5 +1,17 @@ -import { Button, Card, Elevation, Icon, IconName, Intent, Text } from '@blueprintjs/core'; +import { + Button, + Card, + Classes, + Dialog, + Elevation, + Icon, + IconName, + Intent, + MenuItem, + Text +} from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; +import { ItemRenderer, Select } from '@blueprintjs/select'; import * as React from 'react'; import { NavLink } from 'react-router-dom'; import Textarea from 'react-textarea-autosize'; @@ -9,7 +21,11 @@ import { getPrettyDate } from '../../utils/dateHelpers'; import { assessmentCategoryLink } from '../../utils/paramParseHelpers'; import { exportXml } from '../../utils/xmlParser'; -import { IAssessmentOverview } from '../assessment/assessmentShape'; +import { + AssessmentCategories, + AssessmentCategory, + IAssessmentOverview +} from '../assessment/assessmentShape'; import { controlButton } from '../commons'; import Markdown from '../commons/Markdown'; @@ -24,6 +40,7 @@ type Props = { interface IState { editingOverviewField: string; fieldValue: any; + showOptionsOverlay: boolean; } export class EditingOverviewCard extends React.Component { @@ -31,12 +48,18 @@ export class EditingOverviewCard extends React.Component { super(props); this.state = { editingOverviewField: '', - fieldValue: '' + fieldValue: '', + showOptionsOverlay: false }; } public render() { - return
{this.makeEditingOverviewCard(this.props.overview)}
; + return ( +
+ {this.optionsOverlay()} + {this.makeEditingOverviewCard(this.props.overview)} +
+ ); } private saveEditOverview = (field: keyof IAssessmentOverview) => (e: any) => { @@ -67,7 +90,13 @@ export class EditingOverviewCard extends React.Component { } }; - private handleExportXml = () => (e: any) => { + private toggleOptionsOverlay = () => { + this.setState({ + showOptionsOverlay: !this.state.showOptionsOverlay + }); + }; + + private handleExportXml = (e: any) => { exportXml(); }; @@ -127,6 +156,7 @@ export class EditingOverviewCard extends React.Component { : `${getPrettyDate(overview.closeAt)}`} + {this.makeOptionsButton()} {makeOverviewCardButton(overview, this.props.listingPath)} @@ -149,22 +179,66 @@ export class EditingOverviewCard extends React.Component { private makeExportButton = (overview: IAssessmentOverview) => ( ); + + private makeOptionsButton = () => ( + + ); + + private saveCategory = (i: AssessmentCategory, e: any) => { + const overview = { + ...this.props.overview, + category: i + }; + localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(overview)); + this.props.updateEditingOverview(overview); + }; + + private optionsOverlay = () => ( + +
+

Category

+ {categorySelect(this.props.overview.category, this.saveCategory)} +

Story

+
+ {this.state.editingOverviewField === 'story' + ? this.makeEditingOverviewTextarea('story') + : createPlaceholder(this.props.overview.story || '')} +
+
+

Filename

+
+ {this.state.editingOverviewField === 'fileName' + ? this.makeEditingOverviewTextarea('fileName') + : createPlaceholder(this.props.overview.fileName || '')} +
+
+
+ ); } const createPlaceholder = (str: string): string => { if (str.match('^(\n| )*$')) { - return 'Enter Value Here.'; + return 'Enter Value Here (If Applicable)'; } else { return str; } @@ -180,3 +254,32 @@ const makeOverviewCardButton = (overview: IAssessmentOverview, listingPath: stri ); }; + +const assessmentCategoriesArr = [ + AssessmentCategories.Mission, + AssessmentCategories.Path, + AssessmentCategories.Sidequest, + AssessmentCategories.Contest +]; + +const categorySelect = ( + category: AssessmentCategory, + handleSelect = (i: AssessmentCategory, e: React.ChangeEvent) => {} +) => ( + + + {this.manageQuestionTab(index)} +
+
+ + ))} ); } - private manageQuestionTab = () => { - const index = this.props.questionId; + private manageQuestionTab = (index: number) => { return (
{controlButton( - 'Clone Current Question', + `Clone`, IconNames.DOCUMENT, this.confirmSave( - this.makeQuestion(() => - deepCopy(this.props.assessment.questions[this.props.questionId]) - ) + this.makeQuestion(() => deepCopy(this.props.assessment.questions[index]), index) ) )} + {controlButton(`Delete`, IconNames.REMOVE, this.confirmSave(this.deleteQuestion(index)))} + {controlButton( + `Shift Up`, + IconNames.CARET_UP, + this.confirmSave(this.shiftQuestion(-1, index)), + {}, + index === 0 + )} + {controlButton( + `Shift Down`, + IconNames.CARET_DOWN, + this.confirmSave(this.shiftQuestion(1, index)), + {}, + index >= this.props.assessment.questions.length - 1 + )}
{controlButton( 'Insert Programming Question', IconNames.FONT, - this.confirmSave(this.makeQuestion(programmingTemplate)) + this.confirmSave(this.makeQuestion(programmingTemplate, index)) )} {controlButton( 'Insert MCQ Question', IconNames.CONFIRM, - this.confirmSave(this.makeQuestion(mcqTemplate)) - )} -
- {controlButton( - 'Delete Current Question', - IconNames.REMOVE, - this.confirmSave(this.deleteQuestion) + this.confirmSave(this.makeQuestion(mcqTemplate, index)) )}
{index > 0 @@ -87,9 +106,8 @@ export class ManageQuestionTab extends React.Component { ); }; - private shiftQuestion = (dir: number) => () => { + private shiftQuestion = (dir: number, index: number) => () => { const assessment = this.props.assessment; - const index = this.props.questionId; const newIndex = index + dir; if (newIndex >= 0 && newIndex < assessment.questions.length) { const question = assessment.questions[index]; @@ -102,9 +120,9 @@ export class ManageQuestionTab extends React.Component { } }; - private makeQuestion = (template: () => any) => () => { + private makeQuestion = (template: () => any, index: number) => () => { const assessment = this.props.assessment; - const index = this.props.questionId + 1; + index = index + 1; const questions = assessment.questions; questions.splice(index, 0, template()); assessment.questions = questions; @@ -112,10 +130,9 @@ export class ManageQuestionTab extends React.Component { history.push('/incubator/-1/' + index.toString()); }; - private deleteQuestion = () => { + private deleteQuestion = (index: number) => () => { const assessment = this.props.assessment; let questions = assessment.questions; - const index = this.props.questionId; if (questions.length > 1) { questions = questions.slice(0, index).concat(questions.slice(index + 1)); } diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index 536a641991..d982a595bf 100644 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -26,11 +26,13 @@ export type ControlBarProps = { handleReplEval: () => void; handleReplOutputClear: () => void; handleToggleEditorAutorun?: () => void; + handleToggleEditorPersist?: () => void; hasChapterSelect: boolean; hasEditorAutorunButton: boolean; hasSaveButton: boolean; hasShareButton: boolean; hasUnsavedChanges?: boolean; + isEditorPersist?: boolean; isEditorAutorun?: boolean; isRunning: boolean; editingMode?: string; @@ -144,6 +146,14 @@ class ControlBar extends React.PureComponent { this.props.onClickReset !== null ? controlButton('Reset', IconNames.REPEAT, this.props.onClickReset) : undefined; + const editorPersistSwitch = + this.props.handleToggleEditorPersist !== null + ? controlButton( + 'Editor Persistence ' + (this.props.isEditorPersist ? 'Enabled' : 'Disabled'), + this.props.isEditorPersist ? IconNames.TICK : IconNames.CROSS, + this.props.handleToggleEditorPersist + ) + : undefined; return (
{this.props.isEditorAutorun ? undefined : this.props.isRunning ? stopButton : runButton} @@ -151,6 +161,7 @@ class ControlBar extends React.PureComponent { {shareButton} {chapterSelectButton} {externalSelectButton} {this.props.isEditorAutorun ? stopAutorunButton : startAutorunButton} {resetButton} + {editorPersistSwitch}
); } diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index d373ad1ed4..67615e051c 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -30,11 +30,7 @@ const capitalizeFirstLetter = (str: string) => { export const retrieveLocalAssessment = (): IAssessment | null => { const assessment = localStorage.getItem('MissionEditingAssessmentSA'); if (assessment) { - try { - return JSON.parse(assessment); - } catch (err) { - return null; - } + return JSON.parse(assessment); } else { return null; } @@ -43,11 +39,7 @@ export const retrieveLocalAssessment = (): IAssessment | null => { export const retrieveLocalAssessmentOverview = (): IAssessmentOverview | null => { const assessment = localStorage.getItem('MissionEditingOverviewSA'); if (assessment) { - try { - return JSON.parse(assessment); - } catch (err) { - return null; - } + return JSON.parse(assessment); } else { return null; } @@ -84,7 +76,7 @@ const makeAssessmentOverview = ( maxXp: maxXpVal, openAt: rawOverview.startdate, title: rawOverview.title, - reading: task.READING !== null ? task.READING[0] : '', + reading: task.READING ? task.READING[0] : '', shortSummary: task.WEBSUMMARY ? task.WEBSUMMARY[0] : '', status: AssessmentStatuses.attempting, story: rawOverview.story, @@ -225,7 +217,7 @@ export const exportXml = () => { if (assessmentStr && overviewStr) { const assessment: IAssessment = JSON.parse(assessmentStr); const overview: IAssessmentOverview = JSON.parse(overviewStr); - const title = assessment.title; + const filename = overview.fileName || overview.title; const builder = new Builder(); const xmlTask: IXmlParseStrTask = assessmentToXml(assessment, overview); const xml = { @@ -238,7 +230,7 @@ export const exportXml = () => { }; let xmlStr = builder.buildObject(xml); xmlStr = xmlStr.replace(/( )+/g, ''); - download(title + '.xml', xmlStr); + download(filename + '.xml', xmlStr); } };