Skip to content

an update #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/incubator/EditingWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
body: (
<ManageQuestionTab
assessment={assessment}
hasUnsavedChanges={this.state.hasUnsavedChanges}
questionId={questionId}
updateAssessment={this.updateAndSaveAssessment}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/incubator/assessmentTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const mcqTemplate = (): IMCQQuestion => {
id: 2,
library: emptyLibrary(),
type: 'mcq',
solution: 1,
solution: 0,
grader: {
name: 'avenger',
id: 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -30,10 +31,14 @@ interface IExternal {
symbols: string[];
}

export class DeploymentTab extends React.Component<IProps, { deploymentEnabled: boolean }> {
export class DeploymentTab extends React.Component<
IProps,
{ activeTab: number; deploymentEnabled: boolean }
> {
public constructor(props: IProps) {
super(props);
this.state = {
activeTab: 0,
deploymentEnabled: false
};
}
Expand All @@ -58,39 +63,40 @@ export class DeploymentTab extends React.Component<IProps, { deploymentEnabled:
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 deploymentDisp = this.props.isGlobalDeployment ? 'Global Deployment' : 'Local Deployment';
const symbols = deployment.external.symbols.map((symbol, i) => (
<tr key={i}>
<td>{this.textareaContent(deploymentPath.concat(['external', 'symbols', i]))}</td>
<td style={{ width: '520px' }}>
{this.textareaContent(deploymentPath.concat(['external', 'symbols', i]))}
</td>
<td>{controlButton('Delete', IconNames.MINUS, this.handleSymbolDelete(i))}</td>
</tr>
));

const globals = deployment.globals.map((symbol, i) => (
<tr key={i}>
<td className="col-xs-3">
{this.textareaContent(deploymentPath.concat(['globals', i, 0]))}
<td className="col-xs-3" style={{ height: '2rem', width: '10rem', overflow: 'auto' }}>
<div style={{ height: '2rem', width: '10rem', overflow: 'auto' }}>
{this.textareaContent(deploymentPath.concat(['globals', i, 0]))}
</div>
</td>
<td className="col-xs-7" style={{ height: '2rem', width: '20rem', overflow: 'auto' }}>
<div style={{ height: '2rem', width: '20rem', overflow: 'auto' }}>
{this.globalValueTextareaContent(i)}
</div>
</td>
<td className="col-xs-7">{this.globalValueTextareaContent(i)}</td>
<td className="col-xs-2">
{controlButton('Delete', IconNames.MINUS, this.handleGlobalDelete(i))}
</td>
</tr>
));

const resetLibrary = controlButton('Reload Library', IconNames.REFRESH, () =>
const resetLibrary = controlButton('Refresh Library', IconNames.REFRESH, () =>
this.props.handleRefreshLibrary(deployment)
);

return (
<div>
{deploymentDisp} {resetLibrary}
<br />
<br />
Interpreter:
<br />
{chapterSelect(deployment.chapter, this.handleChapterSelect)}
<br />
<br />
const symbolsFragment = (
<React.Fragment>
External Library:
<br />
{externalSelect(deployment.external.name, this.handleExternalSelect!)}
Expand All @@ -100,16 +106,55 @@ export class DeploymentTab extends React.Component<IProps, { deploymentEnabled:
<br />
<table style={{ width: '100%' }}>{symbols}</table>
{controlButton('New Symbol', IconNames.PLUS, this.handleNewSymbol)}
<br />
<br />
</React.Fragment>
);

const globalsFragment = (
<React.Fragment>
<div>Globals:</div>
<br />
<table style={{ width: '100%' }}>{globals}</table>
{controlButton('New Global', IconNames.PLUS, this.handleNewGlobal)}
</React.Fragment>
);

const tabs = [
{
label: `Library`,
icon: IconNames.BOOK,
body: symbolsFragment
},
{
label: `Globals`,
icon: IconNames.GLOBE,
body: globalsFragment
}
];

return (
<div>
{/* {deploymentDisp}
<br /> */}
{resetLibrary}
<br />
Interpreter:
<br />
{chapterSelect(deployment.chapter, this.handleChapterSelect)}
<SideContent
activeTab={this.state.activeTab}
handleChangeActiveTab={this.handleChangeActiveTab}
tabs={tabs}
/>
</div>
);
};

private handleChangeActiveTab = (tab: number) => {
this.setState({
activeTab: tab
});
};

private textareaContent = (path: Array<string | number>) => {
return (
<TextareaContent
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
import { ButtonGroup, Classes, Dialog, Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import * as React from 'react';

import { IAssessment } from '../../assessment/assessmentShape';
import { controlButton } from '../../commons';
import Markdown from '../../commons/Markdown';
import { mcqTemplate, programmingTemplate } from '../../incubator/assessmentTemplates';

interface IProps {
assessment: IAssessment;
hasUnsavedChanges: boolean;
questionId: number;
updateAssessment: (assessment: IAssessment) => void;
}

export class ManageQuestionTab extends React.Component<IProps, {}> {
interface IState {
showSaveOverlay: boolean;
modifyAssessment: () => void;
}

export class ManageQuestionTab extends React.Component<IProps, IState> {
public constructor(props: IProps) {
super(props);
this.state = {
showSaveOverlay: false,
modifyAssessment: () => {}
};
}

public render() {
return this.manageQuestionTab();
return (
<div>
{this.confirmSaveOverlay()}
{this.manageQuestionTab()}
</div>
);
}

private manageQuestionTab = () => {
return (
<div>
{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)}
</div>
);
};
Expand All @@ -56,6 +81,52 @@ export class ManageQuestionTab extends React.Component<IProps, {}> {
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 = () => (
<Dialog
className="assessment-reset"
icon={IconNames.ERROR}
isCloseButtonShown={false}
isOpen={this.state.showSaveOverlay}
title="Confirmation: Save unsaved changes?"
>
<div className={Classes.DIALOG_BODY}>
<Markdown content="Are you sure you want to save over your unsaved changes?" />
</div>
<div className={Classes.DIALOG_FOOTER}>
<ButtonGroup>
{controlButton('Cancel', null, () => this.setState({ showSaveOverlay: false }), {
minimal: false
})}
{controlButton(
'Confirm',
null,
() => {
this.state.modifyAssessment();
this.setState({
showSaveOverlay: false
});
},
{ minimal: false, intent: Intent.DANGER }
)}
</ButtonGroup>
</div>
</Dialog>
);
}

export default ManageQuestionTab;
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -117,12 +119,39 @@ export class QuestionTemplateTab extends React.Component<IProps, IState> {
{mcqButton}
Solution:
{this.textareaContent(['questions', questionId, 'solution'], true, [0, 3])}
{controlButton('Add Option', IconNames.CONFIRM, this.addOption)}
{controlButton('Delete Option', IconNames.REMOVE, this.delOption)}
</div>
</Card>
</div>
);
};

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<string | number>,
isNumber: boolean = false,
Expand Down