From 77fe31249a016e025f544d2572e0b5698af9631d Mon Sep 17 00:00:00 2001 From: Josh Holland Date: Wed, 28 Aug 2019 14:05:44 +0000 Subject: [PATCH] Display which users a choice conflicts with on the finalisation screen Previously, if a conflicting choice was made, the two offending rows would turn BRIGHT RED, but no other feedback was visible -- since the other row would frequently be scrolled off the screen, this is not exactly the most helpful way the application could tell the user that something is wrong ("you did something wrong, but I'm not going to tell you what it is"). Now, the name of the user(s) with which a choice conflicts is/are displayed on the right-hand side of the table. That space is reserved even if there is currently no conflict, since having the table jump around underneath you if you choose a conflicting project is even worse than the existing behaviour! See #14. --- src/components/choice_editor.js | 55 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/components/choice_editor.js b/src/components/choice_editor.js index d850ded..23cb63e 100644 --- a/src/components/choice_editor.js +++ b/src/components/choice_editor.js @@ -19,7 +19,7 @@ along with this program. If not, see . */ -import React, {Component} from 'react'; +import React, {Component, Fragment} from 'react'; import { connect } from 'react-redux'; import {DropdownButton, MenuItem} from 'react-bootstrap'; import ClassNames from 'classnames'; @@ -75,30 +75,37 @@ class ChoiceEditor extends Component { this.props.onClick(studentID, {type, id: parseInt(id, 10)}); } - + // Produces an object mapping user IDs to lists of conflicting + // users. If a user's choice does not conflict, it will not be + // present in the keys of the returned object. invalidChoices() { - return Object.keys(this.props.students).filter(userID => { + return Object.keys(this.props.students).reduce((obj, userID) => { const userOption = this.props.choices[userID]; if (!userOption) { - return false; + return obj; } if (userOption.type === "user") { - return false; + return obj; } - return Object.keys(this.props.students).some(otherUserID => { + const conflictingUsers = Object.keys(this.props.students).map(otherUserID => { if (otherUserID === userID) { - return false; + return null; } const otherUserOption = this.props.choices[otherUserID]; if (!otherUserOption) { - return false; + return null; } if (otherUserOption.type === "user") { - return false; + return null; } - return userOption.id === otherUserOption.id; - }); - }); + if (userOption.id !== otherUserOption.id) { + return null; + } + return otherUserID; + }).filter(x => x != null); + obj[userID] = conflictingUsers; + return obj; + }, {}); } renderSaveButtons(submitDisabled, saveDisabled) { @@ -182,14 +189,15 @@ class ChoiceEditor extends Component { const [id, userAll] = kv; const user = userAll.data; const projectIDs = this.getUserChoices(user); + const conflicts = invalidUsers[id] || []; return ( -
-
{user.name}
+
+
{user.name}
{this.props.showPriority && (
{user.priority}
)} -
+
    {projectIDs.map((projectID, i) => { const title = projectID != null ? this.getProjectTitle(projectID) : "(No choice)"; @@ -216,6 +224,15 @@ class ChoiceEditor extends Component {
{showButtons && this.renderDropdowns(id)}
+
+ { + conflicts.length ? + Choice conflicts with {conflicts.map(uid => + this.props.users[uid].data.name + ).join(", ")} + : null + } +
); }); @@ -223,9 +240,9 @@ class ChoiceEditor extends Component { render() { const showButtons = Boolean(this.props.choices); - const invalidUsers = showButtons? this.invalidChoices(): []; + const invalidUsers = showButtons? this.invalidChoices(): {}; return ( -
+
Student
{this.props.showPriority && ( @@ -236,8 +253,8 @@ class ChoiceEditor extends Component { {this.renderStudentChoices(invalidUsers)} {showButtons && this.renderSaveButtons( - invalidUsers.length || Object.keys(this.props.choices).length < Object.keys(this.props.students).length, - Boolean(invalidUsers.length) + Object.keys(invalidUsers).length || Object.keys(this.props.choices).length < Object.keys(this.props.students).length, + Boolean(Object.keys(invalidUsers).length) )}
);