Skip to content

Commit c872bc0

Browse files
authored
Merge pull request #21 from Robinsstudio/resultats
Consultation des résultats par l'enseignant
2 parents 804b206 + b744551 commit c872bc0

14 files changed

Lines changed: 356 additions & 307 deletions

src/AdminView.js

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { Component } from 'react';
22
import ExplorerView from './ExplorerView';
33
import Editor from './Editor';
4+
import SessionView from './SessionView';
45
import request from './request';
56

67
class AdminView extends Component {
@@ -19,6 +20,10 @@ class AdminView extends Component {
1920
model: {
2021
questions: []
2122
}
23+
},
24+
sessionView: {
25+
visible: false,
26+
sessions: []
2227
}
2328
};
2429

@@ -30,6 +35,7 @@ class AdminView extends Component {
3035
this.closeEditor = this.closeEditor.bind(this);
3136
this.updateEditor = this.updateEditor.bind(this);
3237
this.refreshEditor = this.refreshEditor.bind(this);
38+
this.updateSessionView = this.updateSessionView.bind(this);
3339
this.refresh = this.refresh.bind(this);
3440
}
3541

@@ -108,6 +114,15 @@ class AdminView extends Component {
108114
});
109115
}
110116

117+
updateSessionView(data) {
118+
this.setState({
119+
sessionView: {
120+
...this.state.sessionView,
121+
...data
122+
}
123+
});
124+
}
125+
111126
refresh() {
112127
const { editor, folder } = this.state;
113128
if (editor.visible) {
@@ -117,14 +132,30 @@ class AdminView extends Component {
117132
}
118133

119134
render() {
120-
const { folder, files, tags, editor } = this.state;
135+
const { folder, files, tags, editor, sessionView } = this.state;
121136
return (
122137
<div id="app">
123-
<ExplorerView editing={editor.visible} folder={folder} files={files} tags={tags}
124-
requestFolder={this.requestFolder} searchByTags={this.searchByTags}
125-
create={this.create} edit={this.edit} refresh={this.refresh}/>
138+
<ExplorerView
139+
folder={folder}
140+
files={files}
141+
tags={tags}
142+
requestFolder={this.requestFolder}
143+
searchByTags={this.searchByTags}
144+
create={this.create}
145+
edit={this.edit}
146+
updateSessionView={this.updateSessionView}
147+
refresh={this.refresh}
148+
/>
149+
150+
<Editor
151+
editor={editor}
152+
folder={folder}
153+
update={this.updateEditor}
154+
save={this.save}
155+
closeEditor={this.closeEditor}
156+
/>
126157

127-
<Editor editor={editor} folder={folder} update={this.updateEditor} save={this.save} closeEditor={this.closeEditor}/>
158+
<SessionView {...sessionView} updateSessionView={this.updateSessionView}/>
128159
</div>
129160
);
130161
}

src/Editor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ class Editor extends Component {
7878
render() {
7979
const { editor, closeEditor } = this.props;
8080
return (
81-
<div id="editor" className={`view ${editor.visible ? 'editing' : ''}`}>
81+
<div id="editor" className={`view ${editor.visible ? 'visible' : ''}`}>
8282
<div id="editorHeader" className="header">
8383
<span className="ml-3">Éditer un QCM</span>
8484
<div id="buttons" className="mr-3">
8585
<Button color="primary" className="mr-2" onClick={this.save}>Enregistrer</Button>
86-
<Button color="secondary" className="mr-2" onClick={closeEditor}>Annuler</Button>
86+
<Button color="secondary" onClick={closeEditor}>Annuler</Button>
8787
</div>
8888
</div>
8989

src/ExplorerView.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class ExplorerView extends Component {
1010
super(props);
1111
this.state = {
1212
contextMenu: { visible: false },
13+
sessionView: { visible: false, sessions: [] },
1314
displayByList: false
1415
};
1516

@@ -57,20 +58,33 @@ class ExplorerView extends Component {
5758
}
5859

5960
buildFileItem(file) {
60-
const { handleContextMenu, props: { folder, edit, requestFolder, refresh } } = this;
61-
return <File folder={folder} file={file} edit={edit} requestFolder={requestFolder} refresh={refresh} handleContextMenu={handleContextMenu}/>
61+
const { handleContextMenu, props: { folder, edit, requestFolder, refresh, updateSessionView } } = this;
62+
return (
63+
<File
64+
folder={folder}
65+
file={file}
66+
edit={edit}
67+
requestFolder={requestFolder}
68+
refresh={refresh}
69+
handleContextMenu={handleContextMenu}
70+
updateSessionView={updateSessionView}
71+
/>
72+
);
6273
}
6374

6475
toggleDisplay(displayByList) {
6576
this.setState({ displayByList });
6677
}
6778

6879
render() {
69-
const { props: { editing, folder, tags, searchByTags }, state: { contextMenu, displayByList } } = this;
80+
const {
81+
props: { folder, tags, searchByTags },
82+
state: { contextMenu, displayByList }
83+
} = this;
7084
const path = folder.path.concat(folder.active.name ? folder.active : []);
7185

7286
return (
73-
<div id="explorer" className={`view ${editing ? 'editing' : ''}`}>
87+
<div id="explorer" className="view">
7488
<div id="explorerHeader" className="header">
7589
<div id="searchByTags">
7690
<TagInput tags={tags} onChange={searchByTags}/>

src/File.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,25 @@ class File extends Component {
6363
}
6464

6565
handleContextMenu(event) {
66-
const { file: { name, type, url, _id }, refresh } = this.props;
66+
const {
67+
file: { name, type, url, sessions, _id },
68+
refresh,
69+
updateSessionView
70+
} = this.props;
6771

68-
const multipleChoiceItem = url ? {
72+
const sharedLinkItem = url ? {
6973
label: 'Copier le lien partageable',
7074
onClick: () => this.copyToClipboard(`${window.location.href}qcm/${url}`)
7175
} : {
7276
label: 'Générer un lien partageable',
7377
onClick: () => request('/GenerateLink', { _id }).then(() => refresh())
7478
};
7579

80+
const resultsItem = sessions && sessions.length ? {
81+
label: 'Consulter les résultats',
82+
onClick: () => updateSessionView({ visible: true, sessions })
83+
} : [];
84+
7685
const menuItems = [
7786
{ label: 'Ouvrir', onClick: this.open },
7887
{ label: 'Renommer', onClick: this.startRenaming },
@@ -81,7 +90,9 @@ class File extends Component {
8190
onClick: () => Modals.showConfirmModal('Supprimer', `Voulez-vous vraiment supprimer ${name} ?`)
8291
.then(this.remove).catch(() => {})
8392
}
84-
].concat(type === 'qcm' ? multipleChoiceItem : []);
93+
]
94+
.concat(type === 'qcm' ? sharedLinkItem : [])
95+
.concat(type === 'qcm' ? resultsItem : []);
8596

8697
this.props.handleContextMenu(event, menuItems);
8798
}

src/MultipleChoiceView.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import React, { Component, Fragment } from 'react';
2+
import { InputGroup, InputGroupAddon, InputGroupText, Input, Button } from 'reactstrap';
3+
import ReactMarkdown from 'react-markdown';
4+
import CodeRenderer from './CodeRenderer';
5+
6+
class MultipleChoiceView extends Component {
7+
constructor(props) {
8+
super(props);
9+
10+
this.goBack = this.goBack.bind(this);
11+
this.goNext = this.goNext.bind(this);
12+
}
13+
14+
setChecked(index) {
15+
const { questions, current, editable } = this.props;
16+
17+
if (editable) {
18+
const updatedQuestions = questions.slice();
19+
20+
updatedQuestions[current].answers.forEach((answer, i) => {
21+
if (i === index) {
22+
answer.checked = !answer.checked;
23+
}
24+
});
25+
26+
this.fireOnAnswersChanged(updatedQuestions);
27+
}
28+
}
29+
30+
goBack() {
31+
const previous = this.props.current - 1;
32+
33+
if (previous >= 0) {
34+
this.fireOnCurrentQuestionChanged(previous);
35+
}
36+
}
37+
38+
goNext() {
39+
const { questions, current } = this.props;
40+
const next = current + 1;
41+
42+
if (next < questions.length) {
43+
this.fireOnCurrentQuestionChanged(next);
44+
} else {
45+
this.fireOnDone();
46+
}
47+
}
48+
49+
buildTextNextButton() {
50+
const { questions, current } = this.props;
51+
52+
if (current < questions.length - 1) {
53+
return (
54+
<Fragment>
55+
Suivant
56+
<i className="fas fa-arrow-right ml-3"/>
57+
</Fragment>
58+
);
59+
}
60+
61+
return (
62+
<Fragment>
63+
Terminé
64+
<i className="fas fa-check ml-3"/>
65+
</Fragment>
66+
);
67+
}
68+
69+
buildCurrentQuestion() {
70+
const { questions, current, onDone } = this.props;
71+
72+
if (questions.length) {
73+
const question = questions[current];
74+
return (
75+
<Fragment>
76+
<ReactMarkdown source={question.label} renderers={{ code: CodeRenderer }}/>
77+
{ question.answers.map((answer, index) => {
78+
79+
const { checked, correct, label } = answer;
80+
const className = correct ? 'correct' : correct === false && checked ? 'incorrect' : '';
81+
82+
return (
83+
<InputGroup className="mb-3">
84+
<InputGroupAddon addonType="prepend">
85+
<InputGroupText onClick={() => this.setChecked(index)}>
86+
<Input addon type="checkbox" checked={answer.checked = !!checked} readOnly/>
87+
</InputGroupText>
88+
</InputGroupAddon>
89+
<div className={`${className} answer form-control mr-3`} spellCheck="false">{label}</div>
90+
</InputGroup>
91+
);
92+
}) }
93+
<div className="row justify-content-around">
94+
<Button color="primary" className="mt-3 mb-3" onClick={this.goBack}>
95+
<i className="fas fa-arrow-left mr-3"/>
96+
Précédent
97+
</Button>
98+
99+
{(onDone || current < questions.length - 1) &&
100+
<Button color="primary" className="mt-3 mb-3" onClick={this.goNext}>
101+
{ this.buildTextNextButton() }
102+
</Button>}
103+
</div>
104+
</Fragment>
105+
);
106+
}
107+
108+
return null;
109+
}
110+
111+
fireOnAnswersChanged(questions) {
112+
const { onAnswersChanged } = this.props;
113+
if (typeof onAnswersChanged === 'function') {
114+
onAnswersChanged(questions);
115+
}
116+
}
117+
118+
fireOnCurrentQuestionChanged(current) {
119+
const { onCurrentQuestionChanged } = this.props;
120+
if (typeof onCurrentQuestionChanged === 'function') {
121+
onCurrentQuestionChanged(current);
122+
}
123+
}
124+
125+
fireOnDone() {
126+
const { onDone } = this.props;
127+
if (typeof onDone === 'function') {
128+
onDone();
129+
}
130+
}
131+
132+
render() {
133+
return this.buildCurrentQuestion();
134+
}
135+
}
136+
137+
export default MultipleChoiceView;

0 commit comments

Comments
 (0)