Skip to content
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

Manage Students Table: add multiple students button #20935

Merged
merged 8 commits into from
Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/i18n/common/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"addSectionName": "Enter a name for your section that will help you remember which classroom it is for. Your students will also be able to see this name.",
"addSectionNameHint": "e.g., Intro to CS - Period 2",
"addStudents": "Add students",
"addStudentsMultiple": "Add multiple students",
"addStudentsMultipleInstructions": "Type or paste the names of your students, one per line.",
"addStudentsManageMyOwn": "Create and manage my own list of students",
"addStudentsSyncThirdParty": "Sync my list of students from an existing classroom section in a third party tool",
"addStudentsToSectionInstructions": "Choose how you want to add your students:",
Expand Down Expand Up @@ -300,6 +302,7 @@
"disableMaker": "Disable Maker Toolkit",
"discountCodeSchoolConfirm": "Before you can receive your code, please verify the school at which you teach:",
"documentation": "Documentation",
"done": "Done",
"dontForget": "Don't forget",
"dragBlocksToMatch": "Drag the blocks to match",
"dropletBlock_addOperator_description": "Add two numbers",
Expand Down Expand Up @@ -825,6 +828,7 @@
"runTooltip": "Run the program defined by the blocks in the workspace.",
"runtimeErrorMsg": "Your program did not run successfully. Please remove line {lineNumber} and try again.",
"save": "Save",
"saveAll": "Save all",
"saveAndPublish": "Save & Publish",
"savedToGallery": "Saved",
"saving": "Saving...",
Expand Down
83 changes: 83 additions & 0 deletions apps/src/templates/manageStudents/AddMultipleStudents.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {addMultipleAddRows} from './manageStudentsRedux';
import Button from '../Button';
import i18n from "@cdo/locale";
import BaseDialog from '../BaseDialog';
import DialogFooter from "../teacherDashboard/DialogFooter";

const styles = {
dialog: {
paddingLeft: 20,
paddingRight: 20,
paddingBottom: 20
}
};

class AddMultipleStudents extends Component {
static propTypes = {
// Provided by redux
addMultipleStudents: PropTypes.func.isRequired,
};

state = {
openDialog: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super-picky nit: I'd name this something like isDialogOpen. openDialog sounds like an action to me. In fact, you have a public method openDialog just below.

};

openDialog = () => {
this.setState({openDialog: true});
};

closeDialog = () => {
this.setState({openDialog: false});
};

add = () => {
const value = this.refs.studentsTextBox.value;
this.props.addMultipleStudents(value.split("\n"));
this.closeDialog();
};

render() {
return (
<div>
<Button
onClick={this.openDialog}
color={Button.ButtonColor.gray}
text={i18n.addStudentsMultiple()}
/>
<BaseDialog
useUpdatedStyles
uncloseable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dialog has a "Cancel" button. Is there a good reason to make it uncloseable? This is worth diverging from spec to provide user-friendly behavior, unless there's a specific reason for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope - I copied this from somewhere and forgot to remove it.

isOpen={this.state.openDialog}
style={styles.dialog}
>
<h2>{i18n.addStudentsMultiple()}</h2>
<div>
{i18n.addStudentsMultipleInstructions()}
</div>
<textarea name="textarea" rows="15" cols="70" ref="studentsTextBox">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May not necessary to fix in this PR, especially if there will be a styling pass later, but it'd be nice to size this textbox to match the dialog width instead of using rows/cols. Also not sure it should be resizable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do a style pass all together 👍

</textarea>
<DialogFooter>
<Button
text={i18n.dialogCancel()}
onClick={this.closeDialog}
color={Button.ButtonColor.gray}
/>
<Button
text={i18n.done()}
onClick={this.add}
color={Button.ButtonColor.orange}
/>
</DialogFooter>
</BaseDialog>
</div>
);
}
}

export default connect(state => ({}), dispatch => ({
addMultipleStudents(names) {
dispatch(addMultipleAddRows(names));
},
}))(AddMultipleStudents);
29 changes: 19 additions & 10 deletions apps/src/templates/manageStudents/ManageStudentsActionsCell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import PopUpMenu, {MenuBreak} from "@cdo/apps/lib/ui/PopUpMenu";
import color from "../../util/color";
import FontAwesome from '../FontAwesome';
import Button from '../Button';
import {startEditingStudent, cancelEditingStudent, removeStudent, saveStudent, addStudent} from './manageStudentsRedux';
import {startEditingStudent, cancelEditingStudent, removeStudent, saveStudent, addStudent, ROW_TYPE} from './manageStudentsRedux';
import {connect} from 'react-redux';
import BaseDialog from '../BaseDialog';
import DialogFooter from "../teacherDashboard/DialogFooter";
Expand All @@ -23,7 +23,7 @@ class ManageStudentActionsCell extends Component {
isEditing: PropTypes.bool,
isSaving: PropTypes.bool,
disableSaving: PropTypes.bool,
isAddRow: PropTypes.bool,
rowType: PropTypes.oneOf(Object.values(ROW_TYPE)),
// Provided by redux
startEditingStudent: PropTypes.func,
cancelEditingStudent: PropTypes.func,
Expand Down Expand Up @@ -66,21 +66,30 @@ class ManageStudentActionsCell extends Component {
};

onCancel = () => {
this.props.cancelEditingStudent(this.props.id);
if (this.props.rowType === ROW_TYPE.newStudent) {
this.props.removeStudent(this.props.id);
} else {
this.props.cancelEditingStudent(this.props.id);
}
};

onSave = () => {
this.props.saveStudent(this.props.id);
if (this.props.rowType === ROW_TYPE.newStudent) {
this.onAdd();
} else {
this.props.saveStudent(this.props.id);
}
};

onAdd = () => {
this.props.addStudent(this.props.id);
};

render() {
const {rowType, isEditing} = this.props;
return (
<div>
{!this.props.isEditing &&
{!isEditing &&
<QuickActionsCell>
<PopUpMenu.Item
onClick={this.onEdit}
Expand All @@ -97,26 +106,26 @@ class ManageStudentActionsCell extends Component {
</PopUpMenu.Item>
</QuickActionsCell>
}
{(this.props.isEditing && !this.props.isAddRow) &&
{(isEditing && (rowType !== ROW_TYPE.add)) &&
<div>
<Button
onClick={this.onSave}
color={Button.ButtonColor.white}
color={Button.ButtonColor.orange}
text={i18n.save()}
disabled={this.props.isSaving || this.props.disableSaving}
/>
<Button
onClick={this.onCancel}
color={Button.ButtonColor.blue}
color={Button.ButtonColor.gray}
text={i18n.cancel()}
/>
</div>
}
{this.props.isAddRow &&
{(rowType === ROW_TYPE.add) &&
<div>
<Button
onClick={this.onAdd}
color={Button.ButtonColor.white}
color={Button.ButtonColor.gray}
text={i18n.add()}
disabled={this.props.isSaving || this.props.disableSaving}
/>
Expand Down
47 changes: 39 additions & 8 deletions apps/src/templates/manageStudents/ManageStudentsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import ManageStudentsNameCell from './ManageStudentsNameCell';
import ManageStudentsAgeCell from './ManageStudentsAgeCell';
import ManageStudentsGenderCell from './ManageStudentsGenderCell';
import ManageStudentsActionsCell from './ManageStudentsActionsCell';
import {convertStudentDataToArray, ADD_STATUS} from './manageStudentsRedux';
import {convertStudentDataToArray, ADD_STATUS, ROW_TYPE} from './manageStudentsRedux';
import { connect } from 'react-redux';
import Notification, {NotificationType} from '../Notification';
import AddMultipleStudents from './AddMultipleStudents';
import Button from '../Button';

export const studentSectionDataPropType = PropTypes.shape({
id: PropTypes.number.isRequired,
Expand All @@ -25,7 +27,7 @@ export const studentSectionDataPropType = PropTypes.shape({
secretPicturePath: PropTypes.string,
sectionId: PropTypes.number,
loginType: PropTypes.string,
isAddRow: PropTypes.bool,
rowType: PropTypes.oneOf(Object.values(ROW_TYPE)),
});

/** @enum {number} */
Expand Down Expand Up @@ -71,20 +73,25 @@ const passwordFormatter = (loginType, {rowData}) => {
};

// The "add row" should always be pinned to the top when sorting.
// The "new student rows" should always be next.
// This function takes into account having multiple "add rows"
const sortRows = (data, columnIndexList, orderList) => {
export const sortRows = (data, columnIndexList, orderList) => {
let addRows = [];
let newStudentRows = [];
let studentRows = [];
for (let i = 0; i<data.length; i++) {
if (data[i].isAddRow) {
if (data[i].rowType === ROW_TYPE.add) {
addRows.push(data[i]);
} else if (data[i].rowType === ROW_TYPE.newStudent) {
newStudentRows.push(data[i]);
} else {
studentRows.push(data[i]);
}
}
addRows = orderBy(addRows, columnIndexList, orderList);
newStudentRows = orderBy(newStudentRows, columnIndexList, orderList);
studentRows = orderBy(studentRows, columnIndexList, orderList);
return addRows.concat(studentRows);
return addRows.concat(newStudentRows).concat(studentRows);
};

class ManageStudentsTable extends Component {
Expand Down Expand Up @@ -151,11 +158,29 @@ class ManageStudentsTable extends Component {
isEditing={rowData.isEditing}
isSaving={rowData.isSaving}
disableSaving={disableSaving}
isAddRow={rowData.isAddRow}
rowType={rowData.rowType}
/>
);
};

actionsHeaderFormatter = () => {
const numberOfEditingRows = Object.keys(this.props.editingData).length;
return (
<div>
{numberOfEditingRows > 1 &&
<Button
onClick={()=>{console.log("save all");}}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the TODO referenced in the description.

color={Button.ButtonColor.orange}
text={i18n.saveAll()}
/>
}
{numberOfEditingRows <= 1 &&
i18n.actions()
}
</div>
);
};

getSortingColumns = () => {
return this.state.sortingColumns || {};
};
Expand Down Expand Up @@ -266,6 +291,7 @@ class ManageStudentsTable extends Component {
property: 'actions',
header: {
label: i18n.actions(),
format: this.actionsHeaderFormatter,
props: {
style: {
...tableLayoutStyles.headerCell,
Expand Down Expand Up @@ -303,24 +329,29 @@ class ManageStudentsTable extends Component {
sort: sortRows,
})(this.props.studentData);

const {addStatus, loginType} = this.props;

return (
<div>
{this.props.addStatus === ADD_STATUS.success &&
{addStatus === ADD_STATUS.success &&
<Notification
type={NotificationType.success}
notice={i18n.manageStudentsNotificationSuccess()}
details={i18n.manageStudentsNotificationAddSuccess()}
dismissible={false}
/>
}
{this.props.addStatus === ADD_STATUS.fail &&
{addStatus === ADD_STATUS.fail &&
<Notification
type={NotificationType.failure}
notice={i18n.manageStudentsNotificationFailure()}
details={i18n.manageStudentsNotificationCannotAdd()}
dismissible={false}
/>
}
{(loginType === SectionLoginType.word || loginType === SectionLoginType.picture) &&
<AddMultipleStudents/>
}
<Table.Provider
columns={columns}
style={tableLayoutStyles.table}
Expand Down