Skip to content

Commit ff0d227

Browse files
authored
Mission editing slang (#505)
* Removed previous button on first question, added divider * Yarn format * Changeded help description * fix import for firefox * max width for globals * symbols aligned * increase abstraction removed numberRange from textArea Reduced save frequency of solutionTemplate * manage questions give warning * UI change for deployment tab * fixed maxGrade and maxXP calculation * added local/global switch * adjust mcq options * split question template tab added suggested answer editing * swap answer and solutionTemplate internal change * add reading * add grading deployment * clone question shift question added * fixed symbol formating * changed manage questions ui * yarn format * added more overview options * add editing persist * Update ManageQuestionTab.tsx
1 parent 319ad02 commit ff0d227

File tree

8 files changed

+198
-61
lines changed

8 files changed

+198
-61
lines changed

src/components/assessment/assessmentShape.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface IAssessmentOverview {
1010
category: AssessmentCategory;
1111
closeAt: string;
1212
coverImage: string;
13+
fileName?: string;
1314
grade: number;
1415
id: number;
1516
maxGrade: number;

src/components/incubator/EditingOverviewCard.tsx

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1-
import { Button, Card, Elevation, Icon, IconName, Intent, Text } from '@blueprintjs/core';
1+
import {
2+
Button,
3+
Card,
4+
Classes,
5+
Dialog,
6+
Elevation,
7+
Icon,
8+
IconName,
9+
Intent,
10+
MenuItem,
11+
Text
12+
} from '@blueprintjs/core';
213
import { IconNames } from '@blueprintjs/icons';
14+
import { ItemRenderer, Select } from '@blueprintjs/select';
315
import * as React from 'react';
416
import { NavLink } from 'react-router-dom';
517
import Textarea from 'react-textarea-autosize';
@@ -9,7 +21,11 @@ import { getPrettyDate } from '../../utils/dateHelpers';
921
import { assessmentCategoryLink } from '../../utils/paramParseHelpers';
1022
import { exportXml } from '../../utils/xmlParser';
1123

12-
import { IAssessmentOverview } from '../assessment/assessmentShape';
24+
import {
25+
AssessmentCategories,
26+
AssessmentCategory,
27+
IAssessmentOverview
28+
} from '../assessment/assessmentShape';
1329
import { controlButton } from '../commons';
1430
import Markdown from '../commons/Markdown';
1531

@@ -24,19 +40,26 @@ type Props = {
2440
interface IState {
2541
editingOverviewField: string;
2642
fieldValue: any;
43+
showOptionsOverlay: boolean;
2744
}
2845

2946
export class EditingOverviewCard extends React.Component<Props, IState> {
3047
public constructor(props: Props) {
3148
super(props);
3249
this.state = {
3350
editingOverviewField: '',
34-
fieldValue: ''
51+
fieldValue: '',
52+
showOptionsOverlay: false
3553
};
3654
}
3755

3856
public render() {
39-
return <div>{this.makeEditingOverviewCard(this.props.overview)}</div>;
57+
return (
58+
<div>
59+
{this.optionsOverlay()}
60+
{this.makeEditingOverviewCard(this.props.overview)}
61+
</div>
62+
);
4063
}
4164

4265
private saveEditOverview = (field: keyof IAssessmentOverview) => (e: any) => {
@@ -67,7 +90,13 @@ export class EditingOverviewCard extends React.Component<Props, IState> {
6790
}
6891
};
6992

70-
private handleExportXml = () => (e: any) => {
93+
private toggleOptionsOverlay = () => {
94+
this.setState({
95+
showOptionsOverlay: !this.state.showOptionsOverlay
96+
});
97+
};
98+
99+
private handleExportXml = (e: any) => {
71100
exportXml();
72101
};
73102

@@ -127,6 +156,7 @@ export class EditingOverviewCard extends React.Component<Props, IState> {
127156
: `${getPrettyDate(overview.closeAt)}`}
128157
</div>
129158
</Text>
159+
{this.makeOptionsButton()}
130160
{makeOverviewCardButton(overview, this.props.listingPath)}
131161
</div>
132162
</div>
@@ -149,22 +179,66 @@ export class EditingOverviewCard extends React.Component<Props, IState> {
149179

150180
private makeExportButton = (overview: IAssessmentOverview) => (
151181
<Button
152-
// disabled={overview.status !== AssessmentStatuses.attempted}
153182
icon={IconNames.EXPORT}
154183
intent={Intent.DANGER}
155184
minimal={true}
156185
// intentional: each menu renders own version of onClick
157186
// tslint:disable-next-line:jsx-no-lambda
158-
onClick={this.handleExportXml()}
187+
onClick={this.handleExportXml}
159188
>
160189
Export XML
161190
</Button>
162191
);
192+
193+
private makeOptionsButton = () => (
194+
<Button icon={IconNames.WRENCH} minimal={true} onClick={this.toggleOptionsOverlay}>
195+
Other Options
196+
</Button>
197+
);
198+
199+
private saveCategory = (i: AssessmentCategory, e: any) => {
200+
const overview = {
201+
...this.props.overview,
202+
category: i
203+
};
204+
localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(overview));
205+
this.props.updateEditingOverview(overview);
206+
};
207+
208+
private optionsOverlay = () => (
209+
<Dialog
210+
canOutsideClickClose={false}
211+
className="assessment-reset"
212+
icon={IconNames.WRENCH}
213+
isCloseButtonShown={true}
214+
isOpen={this.state.showOptionsOverlay}
215+
onClose={this.toggleOptionsOverlay}
216+
title="Other options"
217+
>
218+
<div className={Classes.DIALOG_BODY}>
219+
<h3>Category</h3>
220+
{categorySelect(this.props.overview.category, this.saveCategory)}
221+
<h3>Story</h3>
222+
<div onClick={this.toggleEditField('story')}>
223+
{this.state.editingOverviewField === 'story'
224+
? this.makeEditingOverviewTextarea('story')
225+
: createPlaceholder(this.props.overview.story || '')}
226+
</div>
227+
<br />
228+
<h3>Filename</h3>
229+
<div onClick={this.toggleEditField('fileName')}>
230+
{this.state.editingOverviewField === 'fileName'
231+
? this.makeEditingOverviewTextarea('fileName')
232+
: createPlaceholder(this.props.overview.fileName || '')}
233+
</div>
234+
</div>
235+
</Dialog>
236+
);
163237
}
164238

165239
const createPlaceholder = (str: string): string => {
166240
if (str.match('^(\n| )*$')) {
167-
return 'Enter Value Here.';
241+
return 'Enter Value Here (If Applicable)';
168242
} else {
169243
return str;
170244
}
@@ -180,3 +254,32 @@ const makeOverviewCardButton = (overview: IAssessmentOverview, listingPath: stri
180254
</NavLink>
181255
);
182256
};
257+
258+
const assessmentCategoriesArr = [
259+
AssessmentCategories.Mission,
260+
AssessmentCategories.Path,
261+
AssessmentCategories.Sidequest,
262+
AssessmentCategories.Contest
263+
];
264+
265+
const categorySelect = (
266+
category: AssessmentCategory,
267+
handleSelect = (i: AssessmentCategory, e: React.ChangeEvent<HTMLSelectElement>) => {}
268+
) => (
269+
<CategorySelectComponent
270+
className="pt-minimal"
271+
items={assessmentCategoriesArr}
272+
onItemSelect={handleSelect}
273+
itemRenderer={categoryRenderer}
274+
filterable={false}
275+
>
276+
<Button className="pt-minimal" text={category} rightIcon="double-caret-vertical" />
277+
</CategorySelectComponent>
278+
);
279+
280+
const CategorySelectComponent = Select.ofType<AssessmentCategory>();
281+
282+
const categoryRenderer: ItemRenderer<AssessmentCategory> = (
283+
category,
284+
{ handleClick, modifiers, query }
285+
) => <MenuItem active={false} key={category} onClick={handleClick} text={category} />;

src/components/incubator/EditingWorkspace.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ interface IState {
8181
assessment: IAssessment | null;
8282
activeTab: number;
8383
editingMode: string;
84+
editorPersist: boolean;
8485
hasUnsavedChanges: boolean;
8586
showResetOverlay: boolean;
8687
originalMaxGrade: number;
@@ -94,6 +95,7 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
9495
assessment: retrieveLocalAssessment(),
9596
activeTab: 0,
9697
editingMode: 'question',
98+
editorPersist: false,
9799
hasUnsavedChanges: false,
98100
showResetOverlay: false,
99101
originalMaxGrade: 0,
@@ -142,7 +144,8 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
142144
editorProps:
143145
question.type === QuestionTypes.programming
144146
? {
145-
editorValue: this.props.editorValue || (question.answer as string),
147+
editorValue:
148+
this.props.editorValue || (question as IProgrammingQuestion).solutionTemplate,
146149
handleEditorEval: this.props.handleEditorEval,
147150
handleEditorValueChange: this.props.handleEditorValueChange,
148151
handleUpdateHasUnsavedChanges: this.props.handleUpdateHasUnsavedChanges
@@ -251,10 +254,10 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
251254
? (question as IProgrammingQuestion).solutionTemplate || ''
252255
: null;
253256
this.props.handleUpdateCurrentAssessmentId(assessmentId, questionId);
254-
this.props.handleResetWorkspace({ editorValue });
255257
this.handleRefreshLibrary();
256258
this.props.handleUpdateHasUnsavedChanges(false);
257-
if (editorValue) {
259+
if (editorValue && !this.state.editorPersist) {
260+
this.props.handleResetWorkspace({ editorValue });
258261
this.props.handleEditorValueChange(editorValue);
259262
}
260263
if (this.state.hasUnsavedChanges) {
@@ -289,12 +292,14 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
289292
};
290293

291294
private resetEditorValue = () => {
292-
const question: IQuestion = this.state.assessment!.questions[this.formatedQuestionId()];
293-
const editorValue =
294-
question.type === QuestionTypes.programming
295-
? ((question as IProgrammingQuestion).solutionTemplate as string)
296-
: '//If you see this, this is a bug. Please report bug.';
297-
this.props.handleEditorValueChange(editorValue);
295+
if (!this.state.editorPersist) {
296+
const question: IQuestion = this.state.assessment!.questions[this.formatedQuestionId()];
297+
const editorValue =
298+
question.type === QuestionTypes.programming
299+
? ((question as IProgrammingQuestion).solutionTemplate as string)
300+
: '//If you see this, this is a bug. Please report bug.';
301+
this.props.handleEditorValueChange(editorValue);
302+
}
298303
};
299304

300305
private handleSave = () => {
@@ -366,6 +371,12 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
366371
});
367372
};
368373

374+
private toggleEditorPersist = () => {
375+
this.setState({
376+
editorPersist: !this.state.editorPersist
377+
});
378+
};
379+
369380
/** Pre-condition: IAssessment has been loaded */
370381
private sideContentProps: (p: AssessmentWorkspaceProps, q: number) => SideContentProps = (
371382
props: AssessmentWorkspaceProps,
@@ -550,7 +561,9 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
550561
questionProgress: [questionId + 1, this.state.assessment!.questions.length],
551562
sourceChapter: this.state.assessment!.questions[questionId].library.chapter,
552563
editingMode: this.state.editingMode,
553-
toggleEditMode: this.toggleEditingMode
564+
toggleEditMode: this.toggleEditingMode,
565+
isEditorPersist: this.state.editorPersist,
566+
handleToggleEditorPersist: this.toggleEditorPersist
554567
};
555568
};
556569
}

src/components/incubator/ImportFromFileComponent.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,15 @@ export class ImportFromFileComponent extends React.Component<Props, State> {
5151
);
5252
}
5353

54-
private handleFileRead = (e: any) => {
54+
private handleFileRead = (file: any) => (e: any) => {
5555
const content = this.fileReader.result;
5656
if (content) {
5757
parseString(content, (err: any, result: any) => {
5858
// tslint:disable-next-line:no-console
59-
// console.dir(result);
59+
console.dir(file);
6060
try {
6161
const entireAssessment: [IAssessmentOverview, IAssessment] = makeEntireAssessment(result);
62+
entireAssessment[0].fileName = file.name.slice(0, -4);
6263
localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(entireAssessment[0]));
6364
this.props.updateEditingOverview(entireAssessment[0]);
6465

@@ -82,15 +83,14 @@ export class ImportFromFileComponent extends React.Component<Props, State> {
8283
const files = e.target.files;
8384
if (e.target.files) {
8485
this.fileReader = new FileReader();
85-
this.fileReader.onloadend = this.handleFileRead;
86+
this.fileReader.onloadend = this.handleFileRead(files[0]);
8687
this.fileReader.readAsText(files[0]);
8788
}
8889
};
8990

9091
private makeMission = () => {
9192
localStorage.setItem('MissionEditingOverviewSA', JSON.stringify(overviewTemplate()));
9293
this.props.updateEditingOverview(overviewTemplate());
93-
9494
localStorage.setItem('MissionEditingAssessmentSA', JSON.stringify(assessmentTemplate()));
9595
this.props.newAssessment(assessmentTemplate());
9696
};

src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ const removeSpaces = (str: string) => {
264264
return str.replace(/\s+/g, '');
265265
};
266266

267+
const altEval = (str: string): any => {
268+
return Function('"use strict";return (' + str + ')')();
269+
};
270+
267271
function styliseChapter(chap: number) {
268272
return `Source \xa7${chap}`;
269273
}
@@ -289,10 +293,6 @@ const chapterSelect = (
289293
</ChapterSelectComponent>
290294
);
291295

292-
const altEval = (str: string): any => {
293-
return Function('"use strict";return (' + str + ')')();
294-
};
295-
296296
const ChapterSelectComponent = Select.ofType<IChapter>();
297297

298298
const chapterRenderer: ItemRenderer<IChapter> = (chap, { handleClick, modifiers, query }) => (

0 commit comments

Comments
 (0)