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

267/display data in classrooms #275

Merged
merged 9 commits into from
Jun 5, 2020
30 changes: 28 additions & 2 deletions public/app/listeners/editClassroom.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
const { EDIT_CLASSROOM_CHANNEL } = require('../config/channels');
const { ERROR_GENERAL } = require('../config/errors');
const { CLASSROOMS_COLLECTION } = require('../db');
const {
CLASSROOMS_COLLECTION,
SPACES_COLLECTION,
ACTIONS_COLLECTION,
APP_INSTANCE_RESOURCES_COLLECTION,
} = require('../db');
const logger = require('../logger');

const editClassroom = (mainWindow, db) => async (event, { name, id }) => {
const editClassroom = (mainWindow, db) => async (
event,
{ name, id, deleteSelection }
) => {
logger.debug('editing classroom');

try {
Expand All @@ -15,6 +23,24 @@ const editClassroom = (mainWindow, db) => async (event, { name, id }) => {
mainWindow.webContents.send(EDIT_CLASSROOM_CHANNEL, ERROR_GENERAL);
}

// delete selected space and their resources
Object.entries(deleteSelection).forEach(([spaceId, selected]) => {
if (selected) {
classroom
.get(SPACES_COLLECTION)
.remove({ id: spaceId })
.write();
classroom
.get(ACTIONS_COLLECTION)
.remove({ spaceId })
.write();
classroom
.get(APP_INSTANCE_RESOURCES_COLLECTION)
.remove({ spaceId })
.write();
}
});

// update data
const now = new Date();
classroom.assign({ name, updatedAt: now }).write();
Expand Down
32 changes: 24 additions & 8 deletions public/app/listeners/editUserInClassroom.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
const { EDIT_USER_IN_CLASSROOM_CHANNEL } = require('../config/channels');
const { ERROR_GENERAL } = require('../config/errors');
const { CLASSROOMS_COLLECTION, USERS_COLLECTION } = require('../db');
const {
CLASSROOMS_COLLECTION,
USERS_COLLECTION,
ACTIONS_COLLECTION,
APP_INSTANCE_RESOURCES_COLLECTION,
} = require('../db');
const logger = require('../logger');

/**
* @param {Object<string,boolean>} deleteSelection : object mapping space id to whether the space data should be deleted
*/
const editUserInClassroom = (mainWindow, db) => async (
event,
{ username, userId, classroomId }
{ username, userId, classroomId, deleteSelection }
) => {
logger.debug('editing user in classroom');

try {
const user = db
.get(CLASSROOMS_COLLECTION)
.find({ id: classroomId })
.get(USERS_COLLECTION)
.find({ id: userId });
const classroom = db.get(CLASSROOMS_COLLECTION).find({ id: classroomId });

const user = classroom.get(USERS_COLLECTION).find({ id: userId });

// check user exists
const found = user.value();
Expand All @@ -25,9 +31,19 @@ const editUserInClassroom = (mainWindow, db) => async (
);
}

// delete space data related to user if selected
const actions = classroom.get(ACTIONS_COLLECTION);
const resources = classroom.get(APP_INSTANCE_RESOURCES_COLLECTION);
Object.entries(deleteSelection).forEach(([spaceId, selected]) => {
if (selected) {
actions.remove({ spaceId, user: userId }).write();
resources.remove({ spaceId, user: userId }).write();
}
});

// update data
const now = new Date();
user.assign({ username, lastUpdatedAt: now }).write();
user.assign({ username, updatedAt: now }).write();

mainWindow.webContents.send(EDIT_USER_IN_CLASSROOM_CHANNEL);
} catch (err) {
Expand Down
40 changes: 31 additions & 9 deletions public/app/listeners/loadSpaceInClassroom.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,35 @@ const loadSpaceInClassroom = (mainWindow, db) => async (
try {
const classroom = db.get(CLASSROOMS_COLLECTION).find({ id: classroomId });

// add user if doesn't exist
let user = classroom
.get(USERS_COLLECTION)
.find({ username })
.value();
if (!user) {
user = addUserInClassroomDatabase(db, { username, id: classroomId });
// username should be defined if add resources or actions
if (isResourcesSelected || isActionsSelected) {
if (!username) {
logger.debug('username not specified');
return mainWindow.webContents.send(
LOAD_SPACE_IN_CLASSROOM_CHANNEL,
ERROR_GENERAL
);
}
}

// add user
let user = null;
if (username) {
user = classroom
.get(USERS_COLLECTION)
.find({ username })
.value();
if (!user) {
try {
user = addUserInClassroomDatabase(db, { username, id: classroomId });
} catch (err) {
logger.debug(err);
return mainWindow.webContents.send(
LOAD_SPACE_IN_CLASSROOM_CHANNEL,
err
);
}
}
}

// todo: check teacher can write in classroom
Expand Down Expand Up @@ -101,8 +123,6 @@ const loadSpaceInClassroom = (mainWindow, db) => async (
clean(extractPath);
}

const { id: userId } = user;

// write resources to database if selected
if (isResourcesSelected) {
if (_.isEmpty(appInstanceResources)) {
Expand All @@ -113,6 +133,7 @@ const loadSpaceInClassroom = (mainWindow, db) => async (
);
}

const { id: userId } = user;
const savedResources = classroom.get(APP_INSTANCE_RESOURCES_COLLECTION);

// remove previous corresponding resources
Expand All @@ -138,6 +159,7 @@ const loadSpaceInClassroom = (mainWindow, db) => async (
);
}

const { id: userId } = user;
const savedActions = classroom.get(ACTIONS_COLLECTION);

// remove previous corresponding actions
Expand Down
25 changes: 19 additions & 6 deletions src/actions/classroom.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import {
} from '../types';
import {
ERROR_GENERAL,
ERROR_ACCESS_DENIED_CLASSROOM,
ERROR_DUPLICATE_CLASSROOM_NAME,
ERROR_LOADING_MESSAGE,
ERROR_INVALID_USERNAME,
ERROR_DUPLICATE_USERNAME_IN_CLASSROOM,
ERROR_NO_USER_TO_DELETE,
Expand Down Expand Up @@ -67,6 +67,8 @@ import {
ERROR_GETTING_SPACE_IN_CLASSROOM_MESSAGE,
ERROR_INVALID_USERNAME_MESSAGE,
SUCCESS_SPACE_LOADED_MESSAGE,
ERROR_LOADING_MESSAGE,
ERROR_ACCESS_DENIED_CLASSROOM_MESSAGE,
} from '../config/messages';
import { createFlag } from './common';
import { createExtractFile, createClearLoadSpace } from './loadSpace';
Expand All @@ -88,10 +90,14 @@ export const getClassrooms = () => dispatch => {
// create listener
window.ipcRenderer.once(GET_CLASSROOMS_CHANNEL, (event, classrooms) => {
// dispatch that the getter has succeeded
dispatch({
type: GET_CLASSROOMS_SUCCEEDED,
payload: classrooms,
});
if (classrooms === ERROR_ACCESS_DENIED_CLASSROOM) {
toastr.error(ERROR_MESSAGE_HEADER, ERROR_ACCESS_DENIED_CLASSROOM_MESSAGE);
} else {
dispatch({
type: GET_CLASSROOMS_SUCCEEDED,
payload: classrooms,
});
}
dispatch(flagGettingClassrooms(false));
});
};
Expand All @@ -110,6 +116,12 @@ export const getClassroom = async payload => async dispatch => {
}

switch (response) {
case ERROR_ACCESS_DENIED_CLASSROOM:
toastr.error(
ERROR_MESSAGE_HEADER,
ERROR_ACCESS_DENIED_CLASSROOM_MESSAGE
);
break;
case ERROR_GENERAL:
toastr.error(ERROR_MESSAGE_HEADER, ERROR_GETTING_CLASSROOM_MESSAGE);
break;
Expand Down Expand Up @@ -206,7 +218,8 @@ export const editClassroom = payload => dispatch => {
if (response === ERROR_GENERAL) {
toastr.error(ERROR_MESSAGE_HEADER, ERROR_EDITING_CLASSROOM_MESSAGE);
} else {
// update saved classrooms in state
// update saved classrooms and current classroom in state
dispatch(getClassroom(payload));
dispatch(getClassrooms());

toastr.success(SUCCESS_MESSAGE_HEADER, SUCCESS_EDITING_CLASSROOM_MESSAGE);
Expand Down
36 changes: 20 additions & 16 deletions src/components/classrooms/AddClassroomButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
import { withTranslation } from 'react-i18next';
import TextField from '@material-ui/core/TextField';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import { addClassroom } from '../../actions';
import {
ADD_CLASSROOM_BUTTON_ID,
ADD_CLASSROOM_NAME_INPUT_ID,
ADD_CLASSROOM_VALIDATE_BUTTON_ID,
ADD_CLASSROOM_CANCEL_BUTTON_ID,
} from '../../config/selectors';
import ClassroomNameTextField from './ClassroomNameTextField';

const styles = theme => ({
fab: {
Expand All @@ -41,18 +40,25 @@ class AddClassroomButton extends Component {

state = (() => {
const { t } = this.props;
const defaultName = t('New Classroom');
return {
open: false,
name: t('New Classroom'),
name: defaultName,
};
})();

isClassroomNameValid = () => {
const { name } = this.state;
// todo: check for special characters
return name.trim().length;
};

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

close = () => {
this.setState({ name: '', open: false });
this.setState({ open: false });
};

handleCancel = () => {
Expand All @@ -62,11 +68,14 @@ class AddClassroomButton extends Component {
handleValidate = () => {
const { name } = this.state;
const { userId, dispatchAddClassroom } = this.props;
dispatchAddClassroom({ name, userId });
this.close();
if (this.isClassroomNameValid()) {
const trimmedName = name.trim();
dispatchAddClassroom({ name: trimmedName, userId });
this.close();
}
};

handleChange = event => {
handleNameChange = event => {
const { target } = event;
this.setState({ name: target.value });
};
Expand Down Expand Up @@ -96,15 +105,9 @@ class AddClassroomButton extends Component {
{t('Enter a name for your new classroom')}
</DialogTitle>
<DialogContent>
<TextField
id={ADD_CLASSROOM_NAME_INPUT_ID}
autoFocus
margin="dense"
label={t("Classroom's Name")}
type="text"
fullWidth
value={name}
onChange={this.handleChange}
<ClassroomNameTextField
name={name}
handleChange={this.handleNameChange}
/>
</DialogContent>
<DialogActions>
Expand All @@ -119,6 +122,7 @@ class AddClassroomButton extends Component {
onClick={this.handleValidate}
color="primary"
id={ADD_CLASSROOM_VALIDATE_BUTTON_ID}
disabled={!this.isClassroomNameValid()}
>
{t('Validate')}
</Button>
Expand Down
43 changes: 43 additions & 0 deletions src/components/classrooms/ClassroomNameTextField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import TextField from '@material-ui/core/TextField';
import { CLASSROOM_NAME_INPUT_ID } from '../../config/selectors';
import { isClassroomNameValid } from '../../utils/classroom';

const ClassroomNameTextField = ({ name, t, handleChange }) => {
const isValid = isClassroomNameValid(name);
let errorProps = {};
if (!isValid) {
errorProps = {
...errorProps,
helperText: t(`Classroom's name is not valid`),
error: true,
};
}

return (
<TextField
id={CLASSROOM_NAME_INPUT_ID}
autoFocus
margin="dense"
label={t("Classroom's Name")}
type="text"
fullWidth
value={name}
onChange={handleChange}
// eslint-disable-next-line react/jsx-props-no-spreading
{...errorProps}
/>
);
};

ClassroomNameTextField.propTypes = {
name: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};

const TranslatedComponent = withTranslation()(ClassroomNameTextField);

export default TranslatedComponent;
Loading