Skip to content

Commit 089e79b

Browse files
author
Martin Krulis
committed
Updating main results table on GroupDetail page to display interactive dialog when clicking on particular points. Related code refactored as needed and discovered bugs fixed along.
1 parent 1bca9c5 commit 089e79b

File tree

28 files changed

+577
-289
lines changed

28 files changed

+577
-289
lines changed

src/components/Assignments/ShadowAssignmentPointsTable/ShadowAssignmentPointsTable.js

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import PropTypes from 'prop-types';
33
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
44

55
import { Table, Modal } from 'react-bootstrap';
6-
import { defaultMemoize } from 'reselect';
7-
import moment from 'moment';
86

97
import UsersNameContainer from '../../../containers/UsersNameContainer';
10-
import EditShadowAssignmentPointsForm from '../../forms/EditShadowAssignmentPointsForm';
8+
import EditShadowAssignmentPointsForm, {
9+
getPointsFormInitialValues,
10+
transformPointsFormSubmitData,
11+
} from '../../forms/EditShadowAssignmentPointsForm';
1112
import Box from '../../widgets/Box';
1213
import DateTime from '../../widgets/DateTime';
1314
import Button from '../../widgets/FlatButton';
@@ -16,10 +17,8 @@ import Icon, { EditIcon, DeleteIcon } from '../../icons';
1617
import { createUserNameComparator } from '../../helpers/users';
1718
import { arrayToObject, safeGet } from '../../../helpers/common';
1819

19-
//
20-
2120
class ShadowAssignmentPointsTable extends Component {
22-
state = { dialogOpen: false, dialogStudentId: null, dialogPointsId: null };
21+
state = { dialogStudentId: null, dialogAssignmentId: null };
2322

2423
openDialog = (studentId, pointsId = null) =>
2524
this.setState({
@@ -28,45 +27,19 @@ class ShadowAssignmentPointsTable extends Component {
2827
dialogPointsId: pointsId,
2928
});
3029

31-
closeDialog = () => this.setState({ dialogOpen: false });
32-
33-
getPointsFormInitialValues = defaultMemoize((awardeeId, pointsId) => {
34-
const studentPoints = pointsId && this.props.points.find(({ id }) => id === pointsId);
35-
return studentPoints === null
36-
? {
37-
pointsId: null,
38-
awardeeId,
39-
points: 0,
40-
awardedAt: moment().startOf('minute'),
41-
note: '',
42-
}
43-
: {
44-
pointsId,
45-
points: studentPoints.points,
46-
awardedAt: moment.unix(studentPoints.awardedAt),
47-
note: studentPoints.note,
48-
};
49-
});
30+
closeDialog = () => {
31+
this.setState({ dialogOpen: false });
32+
return Promise.resolve();
33+
};
5034

51-
submitPointsForm = ({ pointsId, awardedAt, ...formData }) => {
52-
const { createPoints, updatePoints } = this.props;
53-
const promise =
54-
pointsId === null
55-
? createPoints({
56-
awardedAt: moment(awardedAt).unix(),
57-
...formData,
58-
})
59-
: updatePoints({
60-
pointsId,
61-
awardedAt: moment(awardedAt).unix(),
62-
...formData,
63-
});
64-
return promise.then(this.closeDialog);
35+
submitPointsForm = formData => {
36+
const { setPoints } = this.props;
37+
return setPoints(transformPointsFormSubmitData(formData)).then(this.closeDialog);
6538
};
6639

67-
removePoints = pointsId => {
40+
removePoints = (pointsId, awardeeId) => {
6841
const { removePoints } = this.props;
69-
return removePoints(pointsId);
42+
return removePoints(pointsId, awardeeId);
7043
};
7144

7245
render() {
@@ -148,7 +121,7 @@ class ShadowAssignmentPointsTable extends Component {
148121
{permissionHints.removePoints && (
149122
<Confirm
150123
id={`remove-${pointsId}`}
151-
onConfirmed={() => this.removePoints(pointsId)}
124+
onConfirmed={() => this.removePoints(pointsId, student.id)}
152125
question={
153126
<FormattedMessage
154127
id="app.shadowAssignmentPointsTable.removePointsButtonConfirmation"
@@ -181,7 +154,10 @@ class ShadowAssignmentPointsTable extends Component {
181154
<UsersNameContainer userId={this.state.dialogStudentId} showEmail="icon" large />
182155
<hr />
183156
<EditShadowAssignmentPointsForm
184-
initialValues={this.getPointsFormInitialValues(this.state.dialogStudentId, this.state.dialogPointsId)}
157+
initialValues={getPointsFormInitialValues(
158+
this.state.dialogPointsId && this.props.points.find(({ id }) => id === this.state.dialogPointsId),
159+
this.state.dialogStudentId
160+
)}
185161
onSubmit={this.submitPointsForm}
186162
maxPoints={maxPoints}
187163
/>
@@ -198,8 +174,7 @@ ShadowAssignmentPointsTable.propTypes = {
198174
points: PropTypes.array.isRequired,
199175
permissionHints: PropTypes.object.isRequired,
200176
maxPoints: PropTypes.number.isRequired,
201-
createPoints: PropTypes.func.isRequired,
202-
updatePoints: PropTypes.func.isRequired,
177+
setPoints: PropTypes.func.isRequired,
203178
removePoints: PropTypes.func.isRequired,
204179
intl: intlShape.isRequired,
205180
};

src/components/Assignments/SolutionsTable/SolutionsTable.js

Lines changed: 67 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,93 +4,95 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
44
import { FormattedMessage } from 'react-intl';
55
import { Table } from 'react-bootstrap';
66

7-
import Box from '../../widgets/Box';
87
import NoSolutionYetTableRow from './NoSolutionYetTableRow';
98
import SolutionsTableRow from './SolutionsTableRow';
109
import { LoadingIcon } from '../../icons';
1110

1211
import styles from './SolutionsTable.less';
1312

1413
const SolutionsTable = ({
15-
title,
1614
assignmentId,
15+
groupId,
1716
solutions,
1817
runtimeEnvironments,
1918
noteMaxlen = null,
2019
compact = false,
2120
}) => (
22-
<Box title={title} collapsable isOpen noPadding unlimitedHeight>
23-
<Table responsive className={styles.solutionsTable}>
24-
<thead>
25-
<tr>
26-
<th />
21+
<Table responsive className={styles.solutionsTable}>
22+
<thead>
23+
<tr>
24+
<th />
25+
<th>
26+
<FormattedMessage id="app.solutionsTable.submissionDate" defaultMessage="Date of submission" />
27+
</th>
28+
<th className="text-center">
29+
<FormattedMessage id="app.solutionsTable.solutionValidity" defaultMessage="Validity" />
30+
</th>
31+
<th className="text-center">
32+
<FormattedMessage id="app.solutionsTable.receivedPoints" defaultMessage="Points" />
33+
</th>
34+
<th className="text-center">
35+
<FormattedMessage id="app.solutionsTable.environment" defaultMessage="Target language" />
36+
</th>
37+
{!compact && (
2738
<th>
28-
<FormattedMessage id="app.solutionsTable.submissionDate" defaultMessage="Date of submission" />
39+
<FormattedMessage id="app.solutionsTable.note" defaultMessage="Note" />
2940
</th>
30-
<th className="text-center">
31-
<FormattedMessage id="app.solutionsTable.solutionValidity" defaultMessage="Validity" />
32-
</th>
33-
<th className="text-center">
34-
<FormattedMessage id="app.solutionsTable.receivedPoints" defaultMessage="Points" />
35-
</th>
36-
<th className="text-center">
37-
<FormattedMessage id="app.solutionsTable.environment" defaultMessage="Target language" />
38-
</th>
39-
{!compact && (
40-
<th>
41-
<FormattedMessage id="app.solutionsTable.note" defaultMessage="Note" />
42-
</th>
41+
)}
42+
<td className="text-right text-muted small">
43+
{solutions.size > 5 && (
44+
<FormattedMessage
45+
id="app.solutionsTable.rowsCount"
46+
defaultMessage="Total records: {count}"
47+
values={{ count: solutions.size }}
48+
/>
4349
)}
44-
<th />
45-
</tr>
46-
</thead>
47-
{solutions.size === 0 ? (
48-
<NoSolutionYetTableRow />
49-
) : (
50-
solutions.map((data, idx) => {
51-
if (!data) {
52-
return (
53-
<tbody key={idx}>
54-
<tr>
55-
<td colSpan={compact ? 6 : 7} className="text-center">
56-
<LoadingIcon size="xs" />
57-
</td>
58-
</tr>
59-
</tbody>
60-
);
61-
}
62-
63-
const id = data.id;
64-
const runtimeEnvironment =
65-
data.runtimeEnvironmentId &&
66-
runtimeEnvironments &&
67-
runtimeEnvironments.find(({ id }) => id === data.runtimeEnvironmentId);
68-
50+
</td>
51+
</tr>
52+
</thead>
53+
{solutions.size === 0 ? (
54+
<NoSolutionYetTableRow />
55+
) : (
56+
solutions.map((data, idx) => {
57+
if (!data) {
6958
return (
70-
<SolutionsTableRow
71-
key={id}
72-
id={id}
73-
status={data.lastSubmission ? data.lastSubmission.evaluationStatus : null}
74-
runtimeEnvironment={runtimeEnvironment}
75-
assignmentId={assignmentId}
76-
noteMaxlen={noteMaxlen}
77-
compact={compact}
78-
{...data}
79-
/>
59+
<tbody key={idx}>
60+
<tr>
61+
<td colSpan={compact ? 6 : 7} className="text-center">
62+
<LoadingIcon size="xs" />
63+
</td>
64+
</tr>
65+
</tbody>
8066
);
81-
})
82-
)}
83-
</Table>
84-
</Box>
67+
}
68+
69+
const id = data.id;
70+
const runtimeEnvironment =
71+
data.runtimeEnvironmentId &&
72+
runtimeEnvironments &&
73+
runtimeEnvironments.find(({ id }) => id === data.runtimeEnvironmentId);
74+
75+
return (
76+
<SolutionsTableRow
77+
key={id}
78+
id={id}
79+
status={data.lastSubmission ? data.lastSubmission.evaluationStatus : null}
80+
runtimeEnvironment={runtimeEnvironment}
81+
assignmentId={assignmentId}
82+
groupId={groupId}
83+
noteMaxlen={noteMaxlen}
84+
compact={compact}
85+
{...data}
86+
/>
87+
);
88+
})
89+
)}
90+
</Table>
8591
);
8692

8793
SolutionsTable.propTypes = {
88-
title: PropTypes.oneOfType([
89-
PropTypes.string,
90-
PropTypes.shape({ type: PropTypes.oneOf([FormattedMessage]) }),
91-
PropTypes.element,
92-
]).isRequired,
9394
assignmentId: PropTypes.string.isRequired,
95+
groupId: PropTypes.string.isRequired,
9496
solutions: ImmutablePropTypes.list.isRequired,
9597
runtimeEnvironments: PropTypes.array,
9698
noteMaxlen: PropTypes.number,

src/components/Assignments/SolutionsTable/SolutionsTableRow.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const showScoreAndPoints = status => status === 'done' || status === 'failed';
2323
const SolutionsTableRow = ({
2424
id,
2525
assignmentId,
26+
groupId,
2627
status = null,
2728
note,
2829
lastSubmission,
@@ -124,7 +125,9 @@ const SolutionsTableRow = ({
124125
{permissionHints && permissionHints.setAccepted && (
125126
<AcceptSolutionContainer id={id} locale={locale} shortLabel bsSize="xs" />
126127
)}
127-
{permissionHints && permissionHints.delete && <DeleteSolutionButtonContainer id={id} bsSize="xs" />}
128+
{permissionHints && permissionHints.delete && (
129+
<DeleteSolutionButtonContainer id={id} groupId={groupId} bsSize="xs" />
130+
)}
128131
</td>
129132
</tr>
130133

@@ -146,6 +149,7 @@ const SolutionsTableRow = ({
146149
SolutionsTableRow.propTypes = {
147150
id: PropTypes.string.isRequired,
148151
assignmentId: PropTypes.string.isRequired,
152+
groupId: PropTypes.string.isRequired,
149153
status: PropTypes.string,
150154
note: PropTypes.any.isRequired,
151155
maxPoints: PropTypes.number.isRequired,

0 commit comments

Comments
 (0)