From 3c1457e07a276ebb8d8a9e3a0cae968688d40711 Mon Sep 17 00:00:00 2001 From: Shuming Date: Mon, 18 Feb 2019 21:14:07 +0800 Subject: [PATCH 01/14] Added Template Reset Feature --- .../assessment/AssessmentWorkspace.tsx | 81 ++++++++++++++++--- .../AssessmentWorkspace.tsx.snap | 48 +++++++++++ src/components/workspace/ControlBar.tsx | 9 ++- src/styles/_academy.scss | 3 +- 4 files changed, 127 insertions(+), 14 deletions(-) diff --git a/src/components/assessment/AssessmentWorkspace.tsx b/src/components/assessment/AssessmentWorkspace.tsx index 3bc08155e7..84bbbfecaf 100644 --- a/src/components/assessment/AssessmentWorkspace.tsx +++ b/src/components/assessment/AssessmentWorkspace.tsx @@ -1,4 +1,13 @@ -import { Button, Card, Dialog, NonIdealState, Spinner } from '@blueprintjs/core' +import { + Button, + ButtonGroup, + Card, + Classes, + Dialog, + Intent, + NonIdealState, + Spinner +} from '@blueprintjs/core' import { IconNames } from '@blueprintjs/icons' import * as React from 'react' @@ -6,6 +15,7 @@ import { InterpreterOutput, IWorkspaceState } from '../../reducers/states' import { beforeNow } from '../../utils/dateHelpers' import { history } from '../../utils/history' import { assessmentCategoryLink } from '../../utils/paramParseHelpers' +import { controlButton } from '../commons' import Markdown from '../commons/Markdown' import Workspace, { WorkspaceProps } from '../workspace' import { ControlBarProps } from '../workspace/ControlBar' @@ -67,13 +77,15 @@ export type DispatchProps = { class AssessmentWorkspace extends React.Component< AssessmentWorkspaceProps, - { showOverlay: boolean } + { showOverlay: boolean; showResetOverlay: boolean } > { public constructor(props: AssessmentWorkspaceProps) { super(props) this.state = { - showOverlay: false + showOverlay: false, + showResetOverlay: false } + this.props.handleEditorValueChange('') } /** @@ -86,6 +98,20 @@ class AssessmentWorkspace extends React.Component< if (this.props.questionId === 0 && this.props.notAttempted) { this.setState({ showOverlay: true }) } + if (this.props.assessment) { + const question: IQuestion = this.props.assessment.questions[ + this.props.questionId >= this.props.assessment.questions.length + ? this.props.assessment.questions.length - 1 + : this.props.questionId + ] + this.props.handleEditorValueChange( + question.type === QuestionTypes.programming + ? question.answer !== null + ? ((question as IProgrammingQuestion).answer as string) + : (question as IProgrammingQuestion).solutionTemplate + : '' + ) + } } /** @@ -119,24 +145,53 @@ class AssessmentWorkspace extends React.Component< ) + + const resetOverlay = ( + +
+ + +
+
+ + {controlButton('Cancel', null, () => this.setState({ showResetOverlay: false }), { + minimal: false + })} + {controlButton( + 'Confirm', + null, + () => { + this.setState({ showResetOverlay: false }) + this.props.handleEditorValueChange( + (this.props.assessment!.questions[questionId] as IProgrammingQuestion) + .solutionTemplate + ) + this.props.handleUpdateHasUnsavedChanges(true) + }, + { minimal: false, intent: Intent.DANGER } + )} + +
+
+ ) /* If questionId is out of bounds, set it to the max. */ const questionId = this.props.questionId >= this.props.assessment.questions.length ? this.props.assessment.questions.length - 1 : this.props.questionId const question: IQuestion = this.props.assessment.questions[questionId] - const editorValue = - question.type === QuestionTypes.programming - ? question.answer !== null - ? ((question as IProgrammingQuestion).answer as string) - : (question as IProgrammingQuestion).solutionTemplate - : null const workspaceProps: WorkspaceProps = { controlBarProps: this.controlBarProps(this.props, questionId), editorProps: question.type === QuestionTypes.programming ? { - editorValue: editorValue!, + editorValue: this.props.editorValue!, handleEditorEval: this.props.handleEditorEval, handleEditorValueChange: this.props.handleEditorValueChange, handleUpdateHasUnsavedChanges: this.props.handleUpdateHasUnsavedChanges @@ -165,6 +220,7 @@ class AssessmentWorkspace extends React.Component< return (
{overlay} + {resetOverlay}
) @@ -194,7 +250,7 @@ class AssessmentWorkspace extends React.Component< ? question.answer !== null ? ((question as IProgrammingQuestion).answer as string) : (question as IProgrammingQuestion).solutionTemplate - : null + : '' this.props.handleUpdateCurrentAssessmentId(assessmentId, questionId) this.props.handleResetWorkspace({ editorValue }) this.props.handleClearContext(question.library) @@ -285,6 +341,9 @@ class AssessmentWorkspace extends React.Component< this.props.assessment!.questions[questionId].id, this.props.editorValue! ), + onClickReset: () => { + this.setState({ showResetOverlay: true }) + }, questionProgress: [questionId + 1, this.props.assessment!.questions.length], sourceChapter: this.props.assessment!.questions[questionId].library.chapter } diff --git a/src/components/assessment/__tests__/__snapshots__/AssessmentWorkspace.tsx.snap b/src/components/assessment/__tests__/__snapshots__/AssessmentWorkspace.tsx.snap index dc954779c5..a3a3c8643e 100644 --- a/src/components/assessment/__tests__/__snapshots__/AssessmentWorkspace.tsx.snap +++ b/src/components/assessment/__tests__/__snapshots__/AssessmentWorkspace.tsx.snap @@ -10,6 +10,22 @@ exports[`AssessmentWorkspace page with MCQ question renders correctly 1`] = ` + +
+ + +
+
+ + + Cancel + + + Confirm + + +
+
" `; @@ -22,6 +38,22 @@ exports[`AssessmentWorkspace page with overdue assessment renders correctly 1`] + +
+ + +
+
+ + + Cancel + + + Confirm + + +
+
" `; @@ -34,6 +66,22 @@ exports[`AssessmentWorkspace page with programming question renders correctly 1` + +
+ + +
+
+ + + Cancel + + + Confirm + + +
+
" `; diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index 98c4f18176..56f3b4fb58 100644 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -34,6 +34,7 @@ export type ControlBarProps = { onClickPrevious?(): any onClickReturn?(): any onClickSave?(): any + onClickReset?(): any } interface IChapter { @@ -59,7 +60,8 @@ class ControlBar extends React.PureComponent { hasShareButton: true, onClickNext: () => {}, onClickPrevious: () => {}, - onClickSave: () => {} + onClickSave: () => {}, + onClickReset: () => {} } private shareInputElem: HTMLInputElement @@ -127,10 +129,13 @@ class ControlBar extends React.PureComponent { this.props.hasChapterSelect && this.props.externalLibraryName !== undefined ? externalSelect(this.props.externalLibraryName, this.props.handleExternalSelect!) : undefined + const resetButton = this.props.hasSaveButton + ? controlButton('Reset', IconNames.REPEAT, this.props.onClickReset) + : undefined return (
{this.props.isRunning ? stopButton : runButton} {saveButton} - {shareButton} {chapterSelectButton} {externalSelectButton} + {shareButton} {chapterSelectButton} {externalSelectButton} {resetButton}
) } diff --git a/src/styles/_academy.scss b/src/styles/_academy.scss index e584891821..1905c06a4a 100644 --- a/src/styles/_academy.scss +++ b/src/styles/_academy.scss @@ -129,7 +129,8 @@ } } -.betcha-dialog { +.betcha-dialog, +.assessment-reset { span.warning { font-weight: bold; color: firebrick; From 8d729a0106ad78ef535e8ef50da888d012608ed3 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Wed, 20 Mar 2019 19:17:58 +0800 Subject: [PATCH 02/14] Update GlobalDeploymentTab.tsx nicer symbol editing --- .../GlobalDeploymentTab.tsx | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx b/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx index 643f9283a0..28f17760cc 100644 --- a/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx @@ -37,10 +37,14 @@ export class GlobalDeploymentTab extends React.Component { private deploymentTab = () => { const deployment = this.props.assessment.globalDeployment!; const symbols = deployment.external.symbols.map((symbol, i) => ( -
- {this.symbolTextareaContent(i)} -
-
+ + + {this.symbolTextareaContent(i)} + + + {controlButton('Delete', IconNames.MINUS, this.handleSymbolDelete(i))} + + )); return ( @@ -61,7 +65,9 @@ export class GlobalDeploymentTab extends React.Component { Symbols:

+ {symbols} +
{controlButton('New Symbol', IconNames.PLUS, this.handleNewSymbol)} ); @@ -88,6 +94,13 @@ export class GlobalDeploymentTab extends React.Component { this.props.updateAssessment(assessment); }; + private handleSymbolDelete = (index: number) => () => { + const assessment = this.props.assessment; + const symbols = assessment.globalDeployment!.external.symbols; + symbols.splice(index, 1); + this.props.updateAssessment(assessment); + }; + private handleNewSymbol = () => { const assessment = this.props.assessment; const symbols = assessment.globalDeployment!.external.symbols; @@ -104,6 +117,7 @@ export class GlobalDeploymentTab extends React.Component { private handleExternalSelect = (i: IExternal, e: React.ChangeEvent) => { const assessment = this.props.assessment; assessment.globalDeployment!.external.name = i.name; + assessment.globalDeployment!.external.symbols = externalLibraries.get(i.name)!; this.props.updateAssessment(assessment); }; } From 55c54d07ade0c6a6542be6f8da83e8a976676dd4 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Wed, 20 Mar 2019 21:17:16 +0800 Subject: [PATCH 03/14] nicer buttons --- .../incubator/ImportFromFileComponent.tsx | 32 ++++++++++++------- .../GlobalDeploymentTab.tsx | 18 ++++------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/components/incubator/ImportFromFileComponent.tsx b/src/components/incubator/ImportFromFileComponent.tsx index 46ae34b08c..cc0ae71a66 100644 --- a/src/components/incubator/ImportFromFileComponent.tsx +++ b/src/components/incubator/ImportFromFileComponent.tsx @@ -1,7 +1,10 @@ +import { FileInput } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; import * as React from 'react'; import { parseString } from 'xml2js'; import { IAssessment, IAssessmentOverview } from '../../components/assessment/assessmentShape'; import { makeEntireAssessment, retrieveLocalAssessment } from '../../utils/xmlParser'; +import { controlButton } from '../commons'; import { assessmentTemplate, overviewTemplate } from '../incubator/assessmentTemplates'; type Props = { @@ -9,7 +12,11 @@ type Props = { updateEditingOverview: (overview: IAssessmentOverview) => void; }; -export class ImportFromFileComponent extends React.Component { +type State = { + fileInputText: string; +}; + +export class ImportFromFileComponent extends React.Component { private fileReader: FileReader; public constructor(props: any) { super(props); @@ -17,7 +24,7 @@ export class ImportFromFileComponent extends React.Component
Please ensure that the xml uploaded is trustable.
- - - {this.state.isInvalidXml ? ( -
The xml uploaded is invalid.
- ) : ( -
You can edit this card
- )} +
+ +
+
{controlButton('Make New Mission', IconNames.NEW_OBJECT, this.makeMission)}
); } @@ -57,13 +65,13 @@ export class ImportFromFileComponent extends React.Component { + private makeMission = () => { localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(overviewTemplate)); this.props.updateEditingOverview(overviewTemplate); diff --git a/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx b/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx index 28f17760cc..c8799187e3 100644 --- a/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx @@ -38,12 +38,8 @@ export class GlobalDeploymentTab extends React.Component { const deployment = this.props.assessment.globalDeployment!; const symbols = deployment.external.symbols.map((symbol, i) => ( - - {this.symbolTextareaContent(i)} - - - {controlButton('Delete', IconNames.MINUS, this.handleSymbolDelete(i))} - + {this.symbolTextareaContent(i)} + {controlButton('Delete', IconNames.MINUS, this.handleSymbolDelete(i))} )); @@ -65,9 +61,7 @@ export class GlobalDeploymentTab extends React.Component { Symbols:

- - {symbols} -
+ {symbols}
{controlButton('New Symbol', IconNames.PLUS, this.handleNewSymbol)} ); @@ -95,7 +89,7 @@ export class GlobalDeploymentTab extends React.Component { }; private handleSymbolDelete = (index: number) => () => { - const assessment = this.props.assessment; + const assessment = this.props.assessment; const symbols = assessment.globalDeployment!.external.symbols; symbols.splice(index, 1); this.props.updateAssessment(assessment); @@ -117,7 +111,9 @@ export class GlobalDeploymentTab extends React.Component { private handleExternalSelect = (i: IExternal, e: React.ChangeEvent) => { const assessment = this.props.assessment; assessment.globalDeployment!.external.name = i.name; - assessment.globalDeployment!.external.symbols = externalLibraries.get(i.name)!; + assessment.globalDeployment!.external.symbols = JSON.parse( + JSON.stringify(externalLibraries.get(i.name)!) + ); this.props.updateAssessment(assessment); }; } From 2c2ba57a88d8ea4ad66a8334b854f095930d6149 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Thu, 21 Mar 2019 14:32:57 +0800 Subject: [PATCH 04/14] made local deployment and added globals editing --- src/components/incubator/EditingWorkspace.tsx | 20 +- .../incubator/assessmentTemplates.ts | 2 +- .../DeploymentTab.tsx | 264 ++++++++++++++++++ .../GlobalDeploymentTab.tsx | 179 ------------ .../TextareaContent.tsx | 4 + .../editingWorkspaceSideContent/index.tsx | 6 +- 6 files changed, 289 insertions(+), 186 deletions(-) create mode 100644 src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx delete mode 100644 src/components/incubator/editingWorkspaceSideContent/GlobalDeploymentTab.tsx diff --git a/src/components/incubator/EditingWorkspace.tsx b/src/components/incubator/EditingWorkspace.tsx index f094553811..a0bfa6b4fe 100644 --- a/src/components/incubator/EditingWorkspace.tsx +++ b/src/components/incubator/EditingWorkspace.tsx @@ -19,7 +19,7 @@ import { ControlBarProps } from '../workspace/ControlBar'; import { SideContentProps } from '../workspace/side-content'; import ToneMatrix from '../workspace/side-content/ToneMatrix'; import { - GlobalDeploymentTab, + DeploymentTab, GradingTab, ManageQuestionTab, QuestionTemplateTab, @@ -293,11 +293,25 @@ class AssessmentWorkspace extends React.Component + ) + }, + { + label: `Manage Local Deployment`, + icon: IconNames.HOME, + body: ( + ) } diff --git a/src/components/incubator/assessmentTemplates.ts b/src/components/incubator/assessmentTemplates.ts index 423896f8d0..c8a3f56b5c 100644 --- a/src/components/incubator/assessmentTemplates.ts +++ b/src/components/incubator/assessmentTemplates.ts @@ -9,7 +9,7 @@ import { } from '../../components/assessment/assessmentShape'; import { mock2DRuneLibrary } from '../../mocks/assessmentAPI'; -const emptyLibrary: Library = { +export const emptyLibrary: Library = { chapter: -1, external: { name: 'NONE', diff --git a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx new file mode 100644 index 0000000000..5fa5f9fbf0 --- /dev/null +++ b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx @@ -0,0 +1,264 @@ +import { Button, MenuItem, Switch } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import { ItemRenderer, Select } from '@blueprintjs/select'; +import * as React from 'react'; +import { externalLibraries } from '../../../reducers/externalLibraries'; +import { sourceChapters } from '../../../reducers/states'; + +import { ExternalLibraryName, IAssessment, Library } from '../../assessment/assessmentShape'; +import { controlButton } from '../../commons'; +import { emptyLibrary } from '../assessmentTemplates'; +import { assignToPath, getValueFromPath } from './'; +import TextareaContent from './TextareaContent'; + +interface IProps { + assessment: IAssessment; + pathToLibrary: Array; + updateAssessment: (assessment: IAssessment) => void; + isGlobalDeployment: boolean; +} + +interface IChapter { + chapter: number; + displayName: string; +} + +interface IExternal { + key: number; + name: ExternalLibraryName; + symbols: string[]; +} + +export class DeploymentTab extends React.Component { + public constructor(props: IProps) { + super(props); + this.state = { + deploymentEnabled: false + }; + } + + public render() { + if(this.props.isGlobalDeployment) { + return this.deploymentTab(); + } else { + return
+ + {this.state.deploymentEnabled ? this.deploymentTab() : null} +
; + } + } + + private deploymentTab = () => { + const deploymentPath = this.props.pathToLibrary; + const deployment = getValueFromPath(deploymentPath, this.props.assessment) as Library; + const deploymentDisp = this.props.isGlobalDeployment ? "Global Deployment" : "Local Deployment"; + const symbols = deployment.external.symbols.map((symbol, i) => ( + + {this.textareaContent(deploymentPath.concat(['external', 'symbols', i]))} + {controlButton('Delete', IconNames.MINUS, this.handleSymbolDelete(i))} + + )); + const globals = deployment.globals.map((symbol, i) => ( + + {this.textareaContent(deploymentPath.concat(['globals', i, 0]))} + {this.globalValueTextareaContent(i)} + {controlButton('Delete', IconNames.MINUS, this.handleGlobalDelete(i))} + + )); + + return ( +
+ {deploymentDisp} +
+
+ Interpreter: +
+ {chapterSelect(deployment.chapter, this.handleChapterSelect)} +
+
+ External Library: +
+ {externalSelect(deployment.external.name, this.handleExternalSelect!)} +
+
+
Symbols:
+
+ {symbols}
+ {controlButton('New Symbol', IconNames.PLUS, this.handleNewSymbol)} +
+
+
Globals:
+
+ {globals}
+ {controlButton('New Global', IconNames.PLUS, this.handleNewGlobal)} +
+ ); + }; + + + private textareaContent = (path: Array) => { + return ( + + ); + }; + + private globalValueTextareaContent = (i: number) => { + const pathVal = this.props.pathToLibrary.concat(['globals', i, 2]); + return ( + + ); + }; + + private handleGlobalValueUpdate = (i: number) => (assessment: IAssessment) => { + const deployment = getValueFromPath(this.props.pathToLibrary, this.props.assessment) as Library; + const global = deployment.globals[i]; + try { + global[1] = altEval(global[2]!); + this.props.updateAssessment(assessment); + } catch (e) { + global[2] = "Invalid Expression"; + } + }; + + private handleSymbolDelete = (index: number) => () => { + const assessment = this.props.assessment; + const deployment = getValueFromPath(this.props.pathToLibrary, assessment) as Library; + const symbols = deployment.external.symbols; + symbols.splice(index, 1); + this.props.updateAssessment(assessment); + }; + + private handleNewSymbol = () => { + const assessment = this.props.assessment; + const deployment = getValueFromPath(this.props.pathToLibrary, assessment) as Library; + const symbols = deployment.external.symbols; + symbols.push('new symbol'); + this.props.updateAssessment(assessment); + }; + + private handleGlobalDelete = (index: number) => () => { + const assessment = this.props.assessment; + const deployment = getValueFromPath(this.props.pathToLibrary, assessment) as Library; + deployment.globals.splice(index, 1); + this.props.updateAssessment(assessment); + }; + + private handleNewGlobal = () => { + const assessment = this.props.assessment; + const deployment = getValueFromPath(this.props.pathToLibrary, assessment) as Library; + deployment.globals.push(["new_global", null, "null"]); + this.props.updateAssessment(assessment); + }; + + private handleChapterSelect = (i: IChapter, e: React.ChangeEvent) => { + const assessment = this.props.assessment; + const deployment = getValueFromPath(this.props.pathToLibrary, assessment) as Library; + deployment.chapter = i.chapter; + this.props.updateAssessment(assessment); + }; + + private handleExternalSelect = (i: IExternal, e: React.ChangeEvent) => { + const assessment = this.props.assessment; + const deployment = getValueFromPath(this.props.pathToLibrary, assessment) as Library; + deployment.external.name = i.name; + deployment.external.symbols = JSON.parse( + JSON.stringify(externalLibraries.get(i.name)!) + ); + this.props.updateAssessment(assessment); + }; + + private handleSwitchDeployment = () => { + const assessment = this.props.assessment; + if (this.state.deploymentEnabled) { + assignToPath(this.props.pathToLibrary, JSON.parse(JSON.stringify(emptyLibrary)), assessment); + } else { + assignToPath(this.props.pathToLibrary.concat(["chapter"]), 1, assessment); + } + this.setState({ + deploymentEnabled: !this.state.deploymentEnabled + }); + this.props.updateAssessment(assessment); + } +} + +const removeSpaces = (str: string) => { + return str.replace(/\s+/g, ''); +}; + +function styliseChapter(chap: number) { + return `Source \xa7${chap}`; +} + +const chapters = sourceChapters.map(chap => ({ displayName: styliseChapter(chap), chapter: chap })); + +const chapterSelect = ( + currentChap: number, + handleSelect = (i: IChapter, e: React.ChangeEvent) => {} +) => ( + + - - + {controlButton( + 'Make Programming Question', + IconNames.FONT, + this.makeQuestion(programmingTemplate) + )} + {controlButton('Make MCQ Question', IconNames.CONFIRM, this.makeQuestion(mcqTemplate))} + {controlButton('Make Programming Question', IconNames.REMOVE, this.deleteQn)} ); }; From 00877c28a2c35d29bab4f44f6e685efd430d5c5d Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Sat, 23 Mar 2019 09:15:49 +0800 Subject: [PATCH 09/14] Update NavigationBar.tsx.snap --- src/components/__tests__/__snapshots__/NavigationBar.tsx.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/__tests__/__snapshots__/NavigationBar.tsx.snap b/src/components/__tests__/__snapshots__/NavigationBar.tsx.snap index 818fd0c450..a0223b7d27 100644 --- a/src/components/__tests__/__snapshots__/NavigationBar.tsx.snap +++ b/src/components/__tests__/__snapshots__/NavigationBar.tsx.snap @@ -26,7 +26,7 @@ exports[`NavigationBar renders "Not logged in" correctly 1`] = `
- Incubator + Mission-Control
@@ -72,7 +72,7 @@ exports[`NavigationBar renders correctly with username 1`] = `
- Incubator + Mission-Control
From 010d55d15bda5388dbed992f35480777cdca8881 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Sat, 23 Mar 2019 09:18:45 +0800 Subject: [PATCH 10/14] Update EditingWorkspace.tsx --- src/components/incubator/EditingWorkspace.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/incubator/EditingWorkspace.tsx b/src/components/incubator/EditingWorkspace.tsx index 70b332d3d0..8e4ced7835 100644 --- a/src/components/incubator/EditingWorkspace.tsx +++ b/src/components/incubator/EditingWorkspace.tsx @@ -351,7 +351,6 @@ class AssessmentWorkspace extends React.Component ) From 8940b15270a66d0a551e5416a6c62c7d91675fa7 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Sat, 23 Mar 2019 18:41:00 +0800 Subject: [PATCH 11/14] Reset button added --- .../assessment/AssessmentWorkspace.tsx | 60 +++++++------- src/components/incubator/EditingWorkspace.tsx | 78 +++++++++++++++---- .../TextareaContent.tsx | 1 - src/components/workspace/ControlBar.tsx | 2 +- 4 files changed, 94 insertions(+), 47 deletions(-) diff --git a/src/components/assessment/AssessmentWorkspace.tsx b/src/components/assessment/AssessmentWorkspace.tsx index af82a86b8e..0cd778bd64 100644 --- a/src/components/assessment/AssessmentWorkspace.tsx +++ b/src/components/assessment/AssessmentWorkspace.tsx @@ -7,20 +7,20 @@ import { Intent, NonIdealState, Spinner -} from '@blueprintjs/core' -import { IconNames } from '@blueprintjs/icons' -import * as React from 'react' +} from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import * as React from 'react'; -import { InterpreterOutput, IWorkspaceState } from '../../reducers/states' -import { beforeNow } from '../../utils/dateHelpers' -import { history } from '../../utils/history' -import { assessmentCategoryLink } from '../../utils/paramParseHelpers' -import { controlButton } from '../commons' -import Markdown from '../commons/Markdown' -import Workspace, { WorkspaceProps } from '../workspace' -import { ControlBarProps } from '../workspace/ControlBar' -import { SideContentProps } from '../workspace/side-content' -import ToneMatrix from '../workspace/side-content/ToneMatrix' +import { InterpreterOutput, IWorkspaceState } from '../../reducers/states'; +import { beforeNow } from '../../utils/dateHelpers'; +import { history } from '../../utils/history'; +import { assessmentCategoryLink } from '../../utils/paramParseHelpers'; +import { controlButton } from '../commons'; +import Markdown from '../commons/Markdown'; +import Workspace, { WorkspaceProps } from '../workspace'; +import { ControlBarProps } from '../workspace/ControlBar'; +import { SideContentProps } from '../workspace/side-content'; +import ToneMatrix from '../workspace/side-content/ToneMatrix'; import { IAssessment, IMCQQuestion, @@ -84,8 +84,8 @@ class AssessmentWorkspace extends React.Component< this.state = { showOverlay: false, showResetOverlay: false - } - this.props.handleEditorValueChange('') + }; + this.props.handleEditorValueChange(''); } /** @@ -103,14 +103,14 @@ class AssessmentWorkspace extends React.Component< this.props.questionId >= this.props.assessment.questions.length ? this.props.assessment.questions.length - 1 : this.props.questionId - ] + ]; this.props.handleEditorValueChange( question.type === QuestionTypes.programming ? question.answer !== null ? ((question as IProgrammingQuestion).answer as string) : (question as IProgrammingQuestion).solutionTemplate : '' - ) + ); } } @@ -144,7 +144,7 @@ class AssessmentWorkspace extends React.Component< /> - ) + ); const resetOverlay = ( { - this.setState({ showResetOverlay: false }) + this.setState({ showResetOverlay: false }); this.props.handleEditorValueChange( (this.props.assessment!.questions[questionId] as IProgrammingQuestion) .solutionTemplate - ) - this.props.handleUpdateHasUnsavedChanges(true) + ); + this.props.handleUpdateHasUnsavedChanges(true); }, { minimal: false, intent: Intent.DANGER } )} - ) + ); /* If questionId is out of bounds, set it to the max. */ const questionId = this.props.questionId >= this.props.assessment.questions.length ? this.props.assessment.questions.length - 1 - : this.props.questionId - const question: IQuestion = this.props.assessment.questions[questionId] + : this.props.questionId; + const question: IQuestion = this.props.assessment.questions[questionId]; const workspaceProps: WorkspaceProps = { controlBarProps: this.controlBarProps(this.props, questionId), editorProps: @@ -250,11 +250,11 @@ class AssessmentWorkspace extends React.Component< ? question.answer !== null ? ((question as IProgrammingQuestion).answer as string) : (question as IProgrammingQuestion).solutionTemplate - : '' - this.props.handleUpdateCurrentAssessmentId(assessmentId, questionId) - this.props.handleResetWorkspace({ editorValue }) - this.props.handleClearContext(question.library) - this.props.handleUpdateHasUnsavedChanges(false) + : ''; + this.props.handleUpdateCurrentAssessmentId(assessmentId, questionId); + this.props.handleResetWorkspace({ editorValue }); + this.props.handleClearContext(question.library); + this.props.handleUpdateHasUnsavedChanges(false); if (editorValue) { this.props.handleEditorValueChange(editorValue); } @@ -343,7 +343,7 @@ class AssessmentWorkspace extends React.Component< this.props.editorValue! ), onClickReset: () => { - this.setState({ showResetOverlay: true }) + this.setState({ showResetOverlay: true }); }, questionProgress: [questionId + 1, this.props.assessment!.questions.length], sourceChapter: this.props.assessment!.questions[questionId].library.chapter diff --git a/src/components/incubator/EditingWorkspace.tsx b/src/components/incubator/EditingWorkspace.tsx index 8e4ced7835..d732aa2bc9 100644 --- a/src/components/incubator/EditingWorkspace.tsx +++ b/src/components/incubator/EditingWorkspace.tsx @@ -1,4 +1,4 @@ -import { NonIdealState, Spinner } from '@blueprintjs/core'; +import { ButtonGroup, Classes, Dialog, Intent, NonIdealState, Spinner } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import * as React from 'react'; @@ -19,6 +19,8 @@ import { Library, QuestionTypes } from '../assessment/assessmentShape'; +import { controlButton } from '../commons'; +import Markdown from '../commons/Markdown'; import Workspace, { WorkspaceProps } from '../workspace'; import { ControlBarProps } from '../workspace/ControlBar'; import { SideContentProps } from '../workspace/side-content'; @@ -80,6 +82,7 @@ interface IState { assessment: IAssessment | null; activeTab: number; hasUnsavedChanges: boolean; + showResetOverlay: boolean; originalMaxGrade: number; originalMaxXp: number; } @@ -91,6 +94,7 @@ class AssessmentWorkspace extends React.Component= this.props.assessment.questions.length + ? this.props.assessment.questions.length - 1 + : this.props.questionId + ]; + const editorValue = question.type === QuestionTypes.programming + ? ((question as IProgrammingQuestion).answer as string) + : 'you aint seeing this'; + this.props.handleEditorValueChange(editorValue); this.setState({ - originalMaxGrade: assessment.questions[this.props.questionId].maxGrade, - originalMaxXp: assessment.questions[this.props.questionId].maxXp + originalMaxGrade: question.maxGrade, + originalMaxXp: question.maxXp }); } } @@ -129,24 +141,21 @@ class AssessmentWorkspace extends React.Component ); } + + + /* If questionId is out of bounds, set it to the max. */ const questionId = this.props.questionId >= this.state.assessment.questions.length ? this.state.assessment.questions.length - 1 : this.props.questionId; const question: IQuestion = this.state.assessment.questions[questionId]; - const editorValue = - question.type === QuestionTypes.programming - ? question.answer !== null - ? ((question as IProgrammingQuestion).answer as string) - : (question as IProgrammingQuestion).solutionTemplate - : null; const workspaceProps: WorkspaceProps = { controlBarProps: this.controlBarProps(this.props, questionId), editorProps: question.type === QuestionTypes.programming ? { - editorValue: editorValue!, + editorValue: this.props.editorValue || question.answer as string, handleEditorEval: this.props.handleEditorEval, handleEditorValueChange: this.props.handleEditorValueChange, handleUpdateHasUnsavedChanges: this.props.handleUpdateHasUnsavedChanges @@ -174,11 +183,49 @@ class AssessmentWorkspace extends React.Component + {this.resetOverlay(questionId)} ); } + private resetOverlay = (questionId: number) => ( + +
+ +
+
+ + {controlButton('Cancel', null, () => this.setState({ showResetOverlay: false }), { + minimal: false + })} + {controlButton( + 'Confirm', + null, + () => { + const assessment = retrieveLocalAssessment()!; + const question = assessment.questions[questionId] as IQuestion; + this.setState({ + assessment, + hasUnsavedChanges: false, + showResetOverlay: false, + originalMaxGrade: question.maxGrade, + originalMaxXp: question.maxXp + }); + }, + { minimal: false, intent: Intent.DANGER } + )} + +
+
+ ); + /** * Checks if there is a need to reset the workspace, then executes * a dispatch (in the props) if needed. @@ -204,9 +251,7 @@ class AssessmentWorkspace extends React.Component history.push(listingPath), onClickSave: this.handleSave, + onClickReset: () => { + this.setState({ showResetOverlay: this.state.hasUnsavedChanges }); + }, questionProgress: [questionId + 1, this.state.assessment!.questions.length], sourceChapter: this.state.assessment!.questions[questionId].library.chapter }; diff --git a/src/components/incubator/editingWorkspaceSideContent/TextareaContent.tsx b/src/components/incubator/editingWorkspaceSideContent/TextareaContent.tsx index 9d84982776..615952c6aa 100644 --- a/src/components/incubator/editingWorkspaceSideContent/TextareaContent.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/TextareaContent.tsx @@ -10,7 +10,6 @@ interface IProps { isNumber?: boolean; numberRange?: number[]; path: Array; - processResults?: (str: string | number) => string | number; useRawValue?: boolean; processResults?: (newVal: string | number) => string | number; updateAssessment: (assessment: IAssessment) => void; diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index 384957ee52..a5b46bd610 100644 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -37,7 +37,7 @@ export type ControlBarProps = { onClickPrevious?(): any; onClickReturn?(): any; onClickSave?(): any; - onClickReset?(): any + onClickReset?(): any; }; interface IChapter { From cdfdfe0abfa5badbbe516225381a4f8908991f4f Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Sun, 24 Mar 2019 09:44:32 +0800 Subject: [PATCH 12/14] fixed bugs globals fixed previous button fixed new template fixed added manual library refresh fixed graderdeployment --- src/components/assessment/assessmentShape.ts | 3 +- src/components/incubator/EditingWorkspace.tsx | 69 +++++-- .../incubator/ImportFromFileComponent.tsx | 8 +- .../incubator/assessmentTemplates.ts | 184 ++++++++++-------- .../DeploymentTab.tsx | 13 +- .../ManageQuestionTab.tsx | 6 +- src/components/workspace/ControlBar.tsx | 2 +- src/utils/xmlParser.ts | 31 ++- 8 files changed, 196 insertions(+), 120 deletions(-) diff --git a/src/components/assessment/assessmentShape.ts b/src/components/assessment/assessmentShape.ts index a0c93f0560..6bd85067f7 100644 --- a/src/components/assessment/assessmentShape.ts +++ b/src/components/assessment/assessmentShape.ts @@ -46,7 +46,7 @@ export type GradingStatus = keyof typeof GradingStatuses; export interface IAssessment { category: AssessmentCategory; globalDeployment?: Library; - graderDeployment?: any; + graderDeployment?: Library; id: number; longSummary: string; missionPDF: string; @@ -84,6 +84,7 @@ export interface IQuestion { content: string; id: number; library: Library; + graderLibrary?: Library; type: QuestionType; grader: { name: string; diff --git a/src/components/incubator/EditingWorkspace.tsx b/src/components/incubator/EditingWorkspace.tsx index d732aa2bc9..adc0b6c550 100644 --- a/src/components/incubator/EditingWorkspace.tsx +++ b/src/components/incubator/EditingWorkspace.tsx @@ -108,9 +108,7 @@ class AssessmentWorkspace extends React.Component= this.props.assessment.questions.length - ? this.props.assessment.questions.length - 1 - : this.props.questionId + this.formatedQuestionId() ]; const editorValue = question.type === QuestionTypes.programming ? ((question as IProgrammingQuestion).answer as string) @@ -142,13 +140,7 @@ class AssessmentWorkspace extends React.Component= this.state.assessment.questions.length - ? this.state.assessment.questions.length - 1 - : this.props.questionId; + const questionId = this.formatedQuestionId(); const question: IQuestion = this.state.assessment.questions[questionId]; const workspaceProps: WorkspaceProps = { controlBarProps: this.controlBarProps(this.props, questionId), @@ -189,6 +181,20 @@ class AssessmentWorkspace extends React.Component { + let questionId = this.props.questionId; + if (questionId < 0) { + questionId = 0; + } else if (questionId >= this.state.assessment!.questions.length) { + questionId = this.state.assessment!.questions.length - 1; + } + return questionId; + } + + /** + * Resets to last save. + */ private resetOverlay = (questionId: number) => ( { const assessment = retrieveLocalAssessment()!; const question = assessment.questions[questionId] as IQuestion; + this.handleRefreshLibrary(); this.setState({ assessment, hasUnsavedChanges: false, @@ -238,24 +245,20 @@ class AssessmentWorkspace extends React.Component { + const question = this.state.assessment!.questions[this.formatedQuestionId()]; + let library = + question.library.chapter === -1 + ? this.state.assessment!.globalDeployment! + : question.library; + if (library && library.globals.length > 0) { + const globalsVal = library.globals.map((x: any) => x[0]); + const symbolsVal = library.external.symbols.concat(globalsVal); + library = { + ...library, + external: { + name: library.external.name, + symbols: uniq(symbolsVal) + } + }; + } + this.props.handleClearContext(library); + } + private handleSave = () => { this.setState({ hasUnsavedChanges: false }); storeLocalAssessment(this.state.assessment!); + this.handleRefreshLibrary(); this.handleSaveGradeAndXp(); }; private handleSaveGradeAndXp = () => { + const questionId = this.formatedQuestionId(); const assessment = this.state.assessment!; - const curGrade = assessment.questions[this.props.questionId].maxGrade; + const curGrade = assessment.questions[questionId].maxGrade; const changeGrade = curGrade - this.state.originalMaxGrade; - const curXp = assessment.questions[this.props.questionId].maxXp; + const curXp = assessment.questions[questionId].maxXp; const changeXp = curXp - this.state.originalMaxXp; if (changeGrade !== 0 || changeXp !== 0) { const overview = this.props.assessmentOverview; @@ -382,6 +407,7 @@ class AssessmentWorkspace extends React.Component seen.hasOwnProperty(item) ? false : (seen[item] = true) + ); +} + export default AssessmentWorkspace; diff --git a/src/components/incubator/ImportFromFileComponent.tsx b/src/components/incubator/ImportFromFileComponent.tsx index cc0ae71a66..5d2971e9fc 100644 --- a/src/components/incubator/ImportFromFileComponent.tsx +++ b/src/components/incubator/ImportFromFileComponent.tsx @@ -88,11 +88,11 @@ export class ImportFromFileComponent extends React.Component { }; private makeMission = () => { - localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(overviewTemplate)); - this.props.updateEditingOverview(overviewTemplate); + localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(overviewTemplate())); + this.props.updateEditingOverview(overviewTemplate()); - localStorage.setItem('MissionEditingAssessmentSA', JSON.stringify(assessmentTemplate)); - this.props.newAssessment(assessmentTemplate); + localStorage.setItem('MissionEditingAssessmentSA', JSON.stringify(assessmentTemplate())); + this.props.newAssessment(assessmentTemplate()); }; } diff --git a/src/components/incubator/assessmentTemplates.ts b/src/components/incubator/assessmentTemplates.ts index 7e0b27d944..72844b11cc 100644 --- a/src/components/incubator/assessmentTemplates.ts +++ b/src/components/incubator/assessmentTemplates.ts @@ -7,96 +7,118 @@ import { IProgrammingQuestion, Library } from '../../components/assessment/assessmentShape'; -import { mock2DRuneLibrary } from '../../mocks/assessmentAPI'; -export const emptyLibrary: Library = { - chapter: -1, - external: { - name: 'NONE', - symbols: [] - }, - globals: [] +export const emptyLibrary = () :Library => { + return { + chapter: -1, + external: { + name: 'NONE', + symbols: [] + }, + globals: [] + }; }; -export const overviewTemplate: IAssessmentOverview = { - category: AssessmentCategories.Mission, - closeAt: '2100-12-01T00:00+08', - coverImage: 'https://fakeimg.pl/300/', - grade: 1, - id: -1, - maxGrade: 0, - maxXp: 0, - openAt: '2000-01-01T00:00+08', - title: 'Insert title here', - shortSummary: 'Insert short summary here', - status: AssessmentStatuses.not_attempted, - story: 'mission', - xp: 0, - gradingStatus: 'none' +export const normalLibrary = () :Library => { + return { + chapter: 1, + external: { + name: 'NONE', + symbols: [] + }, + globals: [] + }; }; -export const programmingTemplate: IProgrammingQuestion = { - answer: '//1st question mock solution template', - comment: '`Great Job` **young padawan**', - content: 'Enter content here', - id: 0, - library: emptyLibrary, - solutionTemplate: '//This is a mock solution template', - type: 'programming', - grader: { - name: 'avenger', - id: 1 - }, - gradedAt: '2038-06-18T05:24:26.026Z', - xp: 0, - grade: 0, - maxGrade: 0, - maxXp: 0 +export const overviewTemplate = (): IAssessmentOverview => { + return { + category: AssessmentCategories.Mission, + closeAt: '2100-12-01T00:00+08', + coverImage: 'https://fakeimg.pl/300/', + grade: 1, + id: -1, + maxGrade: 0, + maxXp: 0, + openAt: '2000-01-01T00:00+08', + title: 'Insert title here', + shortSummary: 'Insert short summary here', + status: AssessmentStatuses.not_attempted, + story: 'mission', + xp: 0, + gradingStatus: 'none' + }; }; -export const mcqTemplate: IMCQQuestion = { - answer: 3, - comment: null, - content: 'This is a mock MCQ question', - choices: [ - { - content: 'A', - hint: null - }, - { - content: 'B', - hint: null +export const programmingTemplate = (): IProgrammingQuestion => { + return { + answer: '//1st question mock solution template', + comment: '`Great Job` **young padawan**', + content: 'Enter content here', + id: 0, + library: emptyLibrary(), + graderLibrary: emptyLibrary(), + solutionTemplate: '//This is a mock solution template', + type: 'programming', + grader: { + name: 'avenger', + id: 1 }, - { - content: 'C', - hint: null + gradedAt: '2038-06-18T05:24:26.026Z', + xp: 0, + grade: 0, + maxGrade: 0, + maxXp: 0 + }; +}; + +export const mcqTemplate = (): IMCQQuestion =>{ + return { + answer: 3, + comment: null, + content: 'This is a mock MCQ question', + choices: [ + { + content: 'A', + hint: null + }, + { + content: 'B', + hint: null + }, + { + content: 'C', + hint: null + }, + { + content: 'D', + hint: null + } + ], + id: 2, + library: emptyLibrary(), + type: 'mcq', + solution: 1, + grader: { + name: 'avenger', + id: 1 }, - { - content: 'D', - hint: null - } - ], - id: 2, - library: emptyLibrary, - type: 'mcq', - solution: 1, - grader: { - name: 'avenger', - id: 1 - }, - gradedAt: '2038-06-18T05:24:26.026Z', - xp: 0, - grade: 0, - maxGrade: 0, - maxXp: 0 + gradedAt: '2038-06-18T05:24:26.026Z', + xp: 0, + grade: 0, + maxGrade: 0, + maxXp: 0 + }; }; -export const assessmentTemplate: IAssessment = { - category: 'Mission', - globalDeployment: mock2DRuneLibrary, - id: -1, - longSummary: 'Insert mission briefing here', - missionPDF: 'www.google.com', - questions: [programmingTemplate], - title: 'Insert title here' +export const assessmentTemplate= (): IAssessment => { + return{ + category: 'Mission', + globalDeployment: normalLibrary(), + graderDeployment: emptyLibrary(), + id: -1, + longSummary: 'Insert mission briefing here', + missionPDF: 'www.google.com', + questions: [programmingTemplate()], + title: 'Insert title here' + }; }; diff --git a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx index 0a34a229cb..1cb09d8fdf 100644 --- a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx @@ -15,6 +15,7 @@ interface IProps { assessment: IAssessment; pathToLibrary: Array; updateAssessment: (assessment: IAssessment) => void; + handleRefreshLibrary: (library: Library) => void; isGlobalDeployment: boolean; } @@ -76,9 +77,15 @@ export class DeploymentTab extends React.Component )); + const resetLibrary = controlButton( + "Reload Library", + IconNames.REFRESH, + () =>this.props.handleRefreshLibrary(deployment) + ); + return (
- {deploymentDisp} + {deploymentDisp} {resetLibrary}

Interpreter: @@ -152,7 +159,7 @@ export class DeploymentTab extends React.Component { const assessment = this.props.assessment; if (this.state.deploymentEnabled) { - assignToPath(this.props.pathToLibrary, JSON.parse(JSON.stringify(emptyLibrary)), assessment); + assignToPath(this.props.pathToLibrary, emptyLibrary(), assessment); } else { assignToPath(this.props.pathToLibrary.concat(['chapter']), 1, assessment); } diff --git a/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx b/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx index 043a646d72..c39d68022a 100644 --- a/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx @@ -29,18 +29,18 @@ export class ManageQuestionTab extends React.Component { this.makeQuestion(programmingTemplate) )} {controlButton('Make MCQ Question', IconNames.CONFIRM, this.makeQuestion(mcqTemplate))} - {controlButton('Make Programming Question', IconNames.REMOVE, this.deleteQn)} + {controlButton('Delete Question', IconNames.REMOVE, this.deleteQn)}
); }; - private makeQuestion = (template: any) => () => { + private makeQuestion = (template: () => any ) => () => { const assessment = this.props.assessment; const index = this.props.questionId; let questions = assessment.questions; questions = questions .slice(0, index) - .concat([template]) + .concat([template()]) .concat(questions.slice(index)); assessment.questions = questions; this.props.updateAssessment(assessment); diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index a5b46bd610..0d2de4ee04 100644 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -207,7 +207,7 @@ class ControlBar extends React.PureComponent { } private hasPreviousButton() { - return this.props.questionProgress && this.props.questionProgress[0] > 0; + return this.props.questionProgress && this.props.questionProgress[0] > 1; } private hasReturnButton() { diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index 81a713f66c..cbe827acee 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -30,7 +30,11 @@ const capitalizeFirstLetter = (str: string) => { export const retrieveLocalAssessment = (): IAssessment | null => { const assessment = localStorage.getItem('MissionEditingAssessmentSA'); if (assessment) { - return JSON.parse(assessment); + try { + return JSON.parse(assessment); + } catch (err) { + return null; + } } else { return null; } @@ -39,7 +43,11 @@ export const retrieveLocalAssessment = (): IAssessment | null => { export const retrieveLocalAssessmentOverview = (): IAssessmentOverview | null => { const assessment = localStorage.getItem('MissionEditingOverviewSA'); if (assessment) { - return JSON.parse(assessment); + try { + return JSON.parse(assessment); + } catch (err) { + return null; + } } else { return null; } @@ -88,13 +96,12 @@ const makeAssessment = (result: any): [IAssessment, number, number] => { const task: IXmlParseStrTask = result.CONTENT.TASK[0]; const rawOverview: IXmlParseStrOverview = task.$; const questionArr = makeQuestions(task); - const libraryVal = makeLibrary(task.DEPLOYMENT); return [ { category: capitalizeFirstLetter(rawOverview.kind) as AssessmentCategories, id: editingId, - globalDeployment: libraryVal, - graderDeployment: task.GRADERDEPLOYMENT[0], + globalDeployment: makeLibrary(task.DEPLOYMENT), + graderDeployment: makeLibrary(task.GRADERDEPLOYMENT), longSummary: task.TEXT[0], missionPDF: 'google.com', questions: questionArr[0], @@ -145,14 +152,14 @@ const makeQuestions = (task: IXmlParseStrTask): [IQuestion[], number, number] => let maxXp = 0; const questions: Array = []; task.PROBLEMS[0].PROBLEM.forEach((problem: IXmlParseStrProblem, curId: number) => { - const libraryVal = makeLibrary(problem.DEPLOYMENT); const localMaxXp = problem.$.maxxp ? parseInt(problem.$.maxxp, 10) : 0; const question: IQuestion = { answer: null, comment: null, content: problem.TEXT[0], id: curId, - library: libraryVal, + library: makeLibrary(problem.DEPLOYMENT), + graderLibrary: makeLibrary(problem.GRADERDEPLOYMENT), type: problem.$.type, grader: { name: 'fake person', @@ -286,8 +293,9 @@ export const assessmentToXml = ( task.PROBLEMS = { PROBLEM: [] }; task.DEPLOYMENT = exportLibrary(assessment.globalDeployment!); - if (assessment.graderDeployment) { - task.GRADERDEPLOYMENT = assessment.graderDeployment; + + if (assessment.graderDeployment!.chapter !== -1) { + task.GRADERDEPLOYMENT = exportLibrary(assessment.graderDeployment!); } assessment.questions.forEach((question: IProgrammingQuestion | IMCQQuestion) => { @@ -308,6 +316,11 @@ export const assessmentToXml = ( problem.$['DEPLOYMENT'] = exportLibrary(question.library); } + if (question.graderLibrary!.chapter !== -1) { + /* tslint:disable:no-string-literal */ + problem.$['GRADERDEPLOYMENT'] = exportLibrary(question.graderLibrary!); + } + if (question.maxXp) { /* tslint:disable:no-string-literal */ problem.$['maxxp'] = question.maxXp; From ec1a2e04b4d1188782dca8fb90ba19b24b5f7d97 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Sun, 24 Mar 2019 09:47:04 +0800 Subject: [PATCH 13/14] yarn format --- src/components/incubator/EditingWorkspace.tsx | 32 ++++++++----------- .../incubator/assessmentTemplates.ts | 10 +++--- .../DeploymentTab.tsx | 6 ++-- .../ManageQuestionTab.tsx | 2 +- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/components/incubator/EditingWorkspace.tsx b/src/components/incubator/EditingWorkspace.tsx index adc0b6c550..1e790de1d9 100644 --- a/src/components/incubator/EditingWorkspace.tsx +++ b/src/components/incubator/EditingWorkspace.tsx @@ -82,7 +82,7 @@ interface IState { assessment: IAssessment | null; activeTab: number; hasUnsavedChanges: boolean; - showResetOverlay: boolean; + showResetOverlay: boolean; originalMaxGrade: number; originalMaxXp: number; } @@ -107,12 +107,11 @@ class AssessmentWorkspace extends React.Component { const question = this.state.assessment!.questions[this.formatedQuestionId()]; let library = - question.library.chapter === -1 - ? this.state.assessment!.globalDeployment! - : question.library; + question.library.chapter === -1 ? this.state.assessment!.globalDeployment! : question.library; if (library && library.globals.length > 0) { const globalsVal = library.globals.map((x: any) => x[0]); const symbolsVal = library.external.symbols.concat(globalsVal); @@ -295,7 +292,7 @@ class AssessmentWorkspace extends React.Component { this.setState({ @@ -494,9 +491,8 @@ class AssessmentWorkspace extends React.Component seen.hasOwnProperty(item) ? false : (seen[item] = true) - ); + const seen = {}; + return a.filter(item => (seen.hasOwnProperty(item) ? false : (seen[item] = true))); } export default AssessmentWorkspace; diff --git a/src/components/incubator/assessmentTemplates.ts b/src/components/incubator/assessmentTemplates.ts index 72844b11cc..e8684115a3 100644 --- a/src/components/incubator/assessmentTemplates.ts +++ b/src/components/incubator/assessmentTemplates.ts @@ -8,7 +8,7 @@ import { Library } from '../../components/assessment/assessmentShape'; -export const emptyLibrary = () :Library => { +export const emptyLibrary = (): Library => { return { chapter: -1, external: { @@ -19,7 +19,7 @@ export const emptyLibrary = () :Library => { }; }; -export const normalLibrary = () :Library => { +export const normalLibrary = (): Library => { return { chapter: 1, external: { @@ -71,7 +71,7 @@ export const programmingTemplate = (): IProgrammingQuestion => { }; }; -export const mcqTemplate = (): IMCQQuestion =>{ +export const mcqTemplate = (): IMCQQuestion => { return { answer: 3, comment: null, @@ -110,8 +110,8 @@ export const mcqTemplate = (): IMCQQuestion =>{ }; }; -export const assessmentTemplate= (): IAssessment => { - return{ +export const assessmentTemplate = (): IAssessment => { + return { category: 'Mission', globalDeployment: normalLibrary(), graderDeployment: emptyLibrary(), diff --git a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx index 1cb09d8fdf..e762dc80da 100644 --- a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx @@ -77,10 +77,8 @@ export class DeploymentTab extends React.Component )); - const resetLibrary = controlButton( - "Reload Library", - IconNames.REFRESH, - () =>this.props.handleRefreshLibrary(deployment) + const resetLibrary = controlButton('Reload Library', IconNames.REFRESH, () => + this.props.handleRefreshLibrary(deployment) ); return ( diff --git a/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx b/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx index c39d68022a..15cf713006 100644 --- a/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx @@ -34,7 +34,7 @@ export class ManageQuestionTab extends React.Component { ); }; - private makeQuestion = (template: () => any ) => () => { + private makeQuestion = (template: () => any) => () => { const assessment = this.props.assessment; const index = this.props.questionId; let questions = assessment.questions; From 5035673dbeb64c0bc7f3242d4d4618ae4eb40df8 Mon Sep 17 00:00:00 2001 From: Ger Hean Date: Sun, 24 Mar 2019 10:14:47 +0800 Subject: [PATCH 14/14] placeholder for overview card --- src/components/incubator/EditingOverviewCard.tsx | 12 ++++++++++-- src/utils/xmlParser.ts | 12 ++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/incubator/EditingOverviewCard.tsx b/src/components/incubator/EditingOverviewCard.tsx index fdeecdf25c..1336cd65f1 100644 --- a/src/components/incubator/EditingOverviewCard.tsx +++ b/src/components/incubator/EditingOverviewCard.tsx @@ -105,7 +105,7 @@ export class EditingOverviewCard extends React.Component { {this.state.editingOverviewField === 'shortSummary' ? ( this.makeEditingOverviewTextarea('shortSummary') ) : ( - + )}
@@ -138,7 +138,7 @@ export class EditingOverviewCard extends React.Component {

{this.state.editingOverviewField === 'title' ? this.makeEditingOverviewTextarea('title') - : title}{' '} + : createPlaceholder(title)}

{this.makeExportButton(overview)}
@@ -160,6 +160,14 @@ export class EditingOverviewCard extends React.Component { ); } +const createPlaceholder = (str: string): string => { + if (str.match('^\n*$')) { + return 'Enter Value Here.'; + } else { + return str; + } +}; + const makeOverviewCardButton = (overview: IAssessmentOverview, listingPath: string | undefined) => { const icon: IconName = IconNames.EDIT; const label: string = 'Edit mission'; diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index cbe827acee..6f86b0c464 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; }