diff --git a/src/components/incubator/EditingWorkspace.tsx b/src/components/incubator/EditingWorkspace.tsx index 1e790de1d9..8b59643872 100644 --- a/src/components/incubator/EditingWorkspace.tsx +++ b/src/components/incubator/EditingWorkspace.tsx @@ -393,6 +393,7 @@ class AssessmentWorkspace extends React.Component diff --git a/src/components/incubator/assessmentTemplates.ts b/src/components/incubator/assessmentTemplates.ts index e8684115a3..10a77e5e8c 100644 --- a/src/components/incubator/assessmentTemplates.ts +++ b/src/components/incubator/assessmentTemplates.ts @@ -97,7 +97,7 @@ export const mcqTemplate = (): IMCQQuestion => { id: 2, library: emptyLibrary(), type: 'mcq', - solution: 1, + solution: 0, grader: { name: 'avenger', id: 1 diff --git a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx index e762dc80da..ee51e6c447 100644 --- a/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx @@ -7,6 +7,7 @@ import { sourceChapters } from '../../../reducers/states'; import { ExternalLibraryName, IAssessment, Library } from '../../assessment/assessmentShape'; import { controlButton } from '../../commons'; +import SideContent from '../../workspace/side-content'; import { emptyLibrary } from '../assessmentTemplates'; import { assignToPath, getValueFromPath } from './'; import TextareaContent from './TextareaContent'; @@ -30,10 +31,14 @@ interface IExternal { symbols: string[]; } -export class DeploymentTab extends React.Component { +export class DeploymentTab extends React.Component< + IProps, + { activeTab: number; deploymentEnabled: boolean } +> { public constructor(props: IProps) { super(props); this.state = { + activeTab: 0, deploymentEnabled: false }; } @@ -58,39 +63,40 @@ export class DeploymentTab extends React.Component { const deploymentPath = this.props.pathToLibrary; const deployment = getValueFromPath(deploymentPath, this.props.assessment) as Library; - const deploymentDisp = this.props.isGlobalDeployment ? 'Global Deployment' : 'Local Deployment'; + // const deploymentDisp = this.props.isGlobalDeployment ? 'Global Deployment' : 'Local Deployment'; const symbols = deployment.external.symbols.map((symbol, i) => ( - {this.textareaContent(deploymentPath.concat(['external', 'symbols', 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.textareaContent(deploymentPath.concat(['globals', i, 0]))} +
+ + +
+ {this.globalValueTextareaContent(i)} +
- {this.globalValueTextareaContent(i)} {controlButton('Delete', IconNames.MINUS, this.handleGlobalDelete(i))} )); - const resetLibrary = controlButton('Reload Library', IconNames.REFRESH, () => + const resetLibrary = controlButton('Refresh Library', IconNames.REFRESH, () => this.props.handleRefreshLibrary(deployment) ); - return ( -
- {deploymentDisp} {resetLibrary} -
-
- Interpreter: -
- {chapterSelect(deployment.chapter, this.handleChapterSelect)} -
-
+ const symbolsFragment = ( + External Library:
{externalSelect(deployment.external.name, this.handleExternalSelect!)} @@ -100,16 +106,55 @@ export class DeploymentTab extends React.Component {symbols}
{controlButton('New Symbol', IconNames.PLUS, this.handleNewSymbol)} -
-
+
+ ); + + const globalsFragment = ( +
Globals:

{globals}
{controlButton('New Global', IconNames.PLUS, this.handleNewGlobal)} +
+ ); + + const tabs = [ + { + label: `Library`, + icon: IconNames.BOOK, + body: symbolsFragment + }, + { + label: `Globals`, + icon: IconNames.GLOBE, + body: globalsFragment + } + ]; + + return ( +
+ {/* {deploymentDisp} +
*/} + {resetLibrary} +
+ Interpreter: +
+ {chapterSelect(deployment.chapter, this.handleChapterSelect)} +
); }; + private handleChangeActiveTab = (tab: number) => { + this.setState({ + activeTab: tab + }); + }; + private textareaContent = (path: Array) => { return ( void; } -export class ManageQuestionTab extends React.Component { +interface IState { + showSaveOverlay: boolean; + modifyAssessment: () => void; +} + +export class ManageQuestionTab extends React.Component { public constructor(props: IProps) { super(props); + this.state = { + showSaveOverlay: false, + modifyAssessment: () => {} + }; } public render() { - return this.manageQuestionTab(); + return ( +
+ {this.confirmSaveOverlay()} + {this.manageQuestionTab()} +
+ ); } private manageQuestionTab = () => { return (
{controlButton( - 'Make Programming Question', + 'Insert Programming Question', IconNames.FONT, - this.makeQuestion(programmingTemplate) + this.confirmSave(this.makeQuestion(programmingTemplate)) + )} + {controlButton( + 'Insert MCQ Question', + IconNames.CONFIRM, + this.confirmSave(this.makeQuestion(mcqTemplate)) + )} + {controlButton( + 'Delete Current Question', + IconNames.REMOVE, + this.confirmSave(this.deleteQn) )} - {controlButton('Make MCQ Question', IconNames.CONFIRM, this.makeQuestion(mcqTemplate))} - {controlButton('Delete Question', IconNames.REMOVE, this.deleteQn)}
); }; @@ -56,6 +81,52 @@ export class ManageQuestionTab extends React.Component { assessment.questions = questions; this.props.updateAssessment(assessment); }; + + private confirmSave = (modifyAssessment: () => void) => () => { + if (this.props.hasUnsavedChanges) { + this.setState({ + showSaveOverlay: true, + modifyAssessment + }); + } else { + modifyAssessment(); + } + }; + + /** + * Asks to save work. + */ + private confirmSaveOverlay = () => ( + +
+ +
+
+ + {controlButton('Cancel', null, () => this.setState({ showSaveOverlay: false }), { + minimal: false + })} + {controlButton( + 'Confirm', + null, + () => { + this.state.modifyAssessment(); + this.setState({ + showSaveOverlay: false + }); + }, + { minimal: false, intent: Intent.DANGER } + )} + +
+
+ ); } export default ManageQuestionTab; diff --git a/src/components/incubator/editingWorkspaceSideContent/QuestionTemplateTab.tsx b/src/components/incubator/editingWorkspaceSideContent/QuestionTemplateTab.tsx index cb3bae3f11..5dbef15fa7 100644 --- a/src/components/incubator/editingWorkspaceSideContent/QuestionTemplateTab.tsx +++ b/src/components/incubator/editingWorkspaceSideContent/QuestionTemplateTab.tsx @@ -1,8 +1,10 @@ import { Card } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; import * as React from 'react'; import AceEditor from 'react-ace'; import { IAssessment, IMCQQuestion } from '../../assessment/assessmentShape'; +import { controlButton } from '../../commons'; import { assignToPath, getValueFromPath, limitNumberRange } from './'; import TextareaContent from './TextareaContent'; @@ -117,12 +119,39 @@ export class QuestionTemplateTab extends React.Component { {mcqButton} Solution: {this.textareaContent(['questions', questionId, 'solution'], true, [0, 3])} + {controlButton('Add Option', IconNames.CONFIRM, this.addOption)} + {controlButton('Delete Option', IconNames.REMOVE, this.delOption)}
); }; + private addOption = () => { + const assessment = this.props.assessment; + const questionId = this.props.questionId; + const question = assessment!.questions[questionId] as IMCQQuestion; + const choices = question.choices.concat([ + { + content: 'A', + hint: null + } + ]); + question.choices = choices; + assessment!.questions[questionId] = question; + this.props.updateAssessment(assessment); + }; + + private delOption = () => { + const assessment = this.props.assessment; + const questionId = this.props.questionId; + const question = assessment!.questions[questionId] as IMCQQuestion; + const choices = question.choices.slice(0, question.choices.length - 1); + question.choices = choices; + assessment!.questions[questionId] = question; + this.props.updateAssessment(assessment); + }; + private textareaContent = ( path: Array, isNumber: boolean = false,