Skip to content

Commit

Permalink
Merge pull request #76 from schulcloud/174-implement-paging-functiona…
Browse files Browse the repository at this point in the history
…lity-for-administration-tables

Use paging for administration tables
  • Loading branch information
Niklas Kiefer authored Feb 2, 2017
2 parents 8d3be8e + 7546df0 commit b3f854d
Show file tree
Hide file tree
Showing 25 changed files with 1,116 additions and 288 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"formsy-react-components": "^0.9.0",
"jquery": "^3.1.1",
"oauth-1.0a": "^2.0.0",
"rc-pagination": "^1.6.5",
"rc-select": "^6.7.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-dropzone": "^3.9.0",
Expand Down
75 changes: 51 additions & 24 deletions src/modules/administration/actions/administration.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,69 @@
import { Permissions, Server } from '../../core/helpers/';

const schoolService = Server.service('/schools');
const classService = Server.service('/classes');
const courseService = Server.service('/courses');
const userService = Server.service('/users');
const classService = Server.service('/classes');

const indexArrayByKey = (array, key) => {
const result = {};
array.forEach((obj) => {
result[obj[key]] = obj;
});
return result;
};

export default {
updateSchool: (data) => {
if(data._id) return schoolService.patch(data._id, data);

return schoolService.create(data);
loadContent: (serviceName, query) => {
const service = Server.service(serviceName);
return service.find({query})
.then(result => {
return Promise.resolve({
records: indexArrayByKey(result.data, '_id'),
pagination: {total: result.total, skip: result.skip}
});
});
},


updateCourse: (data) => {
if(data._id) return courseService.patch(data._id, data);

return courseService.create(data);
updateRecord: (serviceName, data) => {
const service = Server.service(serviceName);
if(data._id) return service.patch(data._id, data);
return service.create(data);
},

removeCourse: (data) => {
return courseService.remove(data._id);
removeRecord: (serviceName, data) => {
const id = data._id;
if(!id) throw new Error("_id not set!");
const service = Server.service(serviceName);
return service.remove(id);
},


updateClass: (data) => {
if(data._id) return classService.patch(data._id, data);

return classService.create(data);
populateFields: (serviceName, _id, fields) => {
const service = Server.service(serviceName);
return service.find({query: {
_id,
$populate: fields
}})
.then(result => Promise.resolve(result.data[0]));
},

removeClass: (data) => {
return classService.remove(data._id);
_loadTeachers: (schoolId) => {
return userService.find({
query: {
schoolId,
roles: ['teacher'],
$limit: 1000
}
})
.then(result => Promise.resolve(result.data));
},

updateUser: (data) => {
if(data._id) return userService.patch(data._id, data);

return userService.create(data);
_loadClasses: (schoolId) => {
return classService.find({
query: {
schoolId,
$limit: 1000
}
})
.then(result => Promise.resolve(result.data));
}
};
102 changes: 96 additions & 6 deletions src/modules/administration/components/admin-section.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import ReactDOM from 'react-dom';
import Pagination from 'rc-pagination';
import Select from 'rc-select';
import '../styles/rc-pagination.scss';
import '../styles/rc-select.scss';

import ModalForm from './modal-form';
import Table from './table';
Expand All @@ -12,14 +16,27 @@ class AdminSection extends React.Component {
title: '',
addLabel: '',
editLabel: '',
submitCallback: () => {}
}
};

this.state = {
record: {}
record: {},
records: [],
numberOfPages: 1,
itemsPerPage: 10,
};

this.defaultRecord = {};
this.loadContentFromServer = null;
this.serviceName = null;
}

componentDidMount() {
this.loadContent(1, this.state.itemsPerPage);
}

// return the query passed to actions.loadContent along with pagination options, e.g. {schoolId: 123456}
contentQuery() {
throw new TypeError("contentQuery() has to be implemented by AdminSection subclasses.");
}

modalFormUI(record) {
Expand All @@ -33,7 +50,7 @@ class AdminSection extends React.Component {
ref="edit-modal"
title={title}
content={this.modalFormUI.bind(this)()}
submitCallback={this.options.submitCallback.bind(this)}
submitCallback={this.updateRecord.bind(this)}
{...this.options}
/>
);
Expand Down Expand Up @@ -63,14 +80,87 @@ class AdminSection extends React.Component {
key={index}
className={action.class}
onClick={action.action.bind(this, record)}>
<i className={`fa fa-${action.icon}`} />
<button className={`btn btn-default btn-sm`}>
<i className={`fa fa-${action.icon}`} /> {action.label || ""}
</button>
</a>
);
})}
</div>
);
}

onPageSizeChange(currentPage, itemsPerPage) {
this.setState({itemsPerPage});
this.loadContent(1, itemsPerPage);
}

loadContent(page, itemsPerPage) {
const paginationOptions = {$skip: (page - 1) * itemsPerPage, $limit: itemsPerPage};
const query = Object.assign({}, paginationOptions, this.contentQuery());
this.loadContentFromServer(query)
.then((result) => {
const numberOfPages = Math.ceil(result.pagination.total / this.state.itemsPerPage);
Object.assign(result, {numberOfPages});
this.setState(result);
});
}

loadTeachers() {
this.props.actions.loadTeachers()
.then(teachers => this.setState({teachers}));
}

loadClasses() {
this.props.actions.loadClasses()
.then(classes => this.setState({classes}));
}

onPageChange(page) {
this.loadContent(page, this.state.itemsPerPage);
}

updateRecord(data) {
console.info(`Replacing \n${JSON.stringify(this.state.records[data._id])} with \n${JSON.stringify(data)}`);
this.props.actions.updateRecord(this.serviceName, data)
.then(this.customizeRecordBeforeInserting.bind(this))
.then(savedData => {
let records = this.state.records;
records[data._id] = savedData;
this.setState({records});
});
}

// override point to customize records before they are inserted into the table,
// e.g. to populate fields (resolve ids)
customizeRecordBeforeInserting(data) {
return Promise.resolve(data);
}

removeRecord(data) {
this.props.actions.removeRecord(this.serviceName, data)
.then(_ => {
let records = this.state.records;
delete records[data._id];
this.setState({records});
});
}

getPaginationControl() {
//if (this.state.numberOfPages < 2) return null;
return (<Pagination
selectComponentClass={Select}
locale={require('rc-pagination/lib/locale/en_US')}
showSizeChanger
defaultPageSize={this.state.itemsPerPage}
defaultCurrent={1}
pageSizeOptions={['10', '25', '50', '100']}
onShowSizeChange={this.onPageSizeChange.bind(this)}
onChange={this.onPageChange.bind(this)}
total={this.state.numberOfPages}
/>);
}

render() {
return (
<section className="section-courses section-default">
Expand All @@ -80,13 +170,13 @@ class AdminSection extends React.Component {
<h5>{this.options.title}</h5>

<Table head={this.getTableHead()} body={this.getTableBody()} />
{this.getPaginationControl()}
<button type="submit" className="btn btn-primary" onClick={this.openModal.bind(this, this.defaultRecord)}>
{this.options.addLabel}
</button>
</div>
</div>
</div>

{this.modalUI()}
</section>
);
Expand Down
10 changes: 5 additions & 5 deletions src/modules/administration/components/administration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import LayoutBase from '../../base/containers/layout';
import SectionTitle from '../../base/components/title'; /* only for base */
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';

import SectionSchool from './school';
import SectionCourses from './courses';
import SectionClasses from './classes';
import SectionTeachers from './teachers';
import SectionStudents from './students';
import SectionSchool from '../containers/school';
import SectionCourses from '../containers/courses';
import SectionClasses from '../containers/classes';
import SectionTeachers from '../containers/teachers';
import SectionStudents from '../containers/students';

require('../styles/administration.scss');

Expand Down
48 changes: 36 additions & 12 deletions src/modules/administration/components/classes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,44 @@ class SectionClasses extends AdminSection {
constructor(props) {
super(props);

this.options = {
const options = {
title: 'Klassen',
addLabel: 'Klasse hinzufügen',
editLabel: 'Klasse bearbeiten',
submitCallback: (data) => {
this.props.actions.updateClass(data);
}
editLabel: 'Klasse bearbeiten'
};
Object.assign(this.options, options);

this.actions = [
{
action: this.openModal.bind(this),
icon: 'edit'
},
{
action: this.props.actions.removeClass.bind(this),
action: this.removeRecord,
icon: 'trash-o'
}
]
];

Object.assign(this.state, {
teachers: [],
classes: []
});

this.loadContentFromServer = this.props.actions.loadContent.bind(this, '/classes');
this.serviceName = '/classes';
}

componentDidMount() {
super.componentDidMount();
this.loadTeachers();
}

contentQuery() {
const schoolId = this.props.schoolId;
return {
schoolId,
$populate: ['teacherIds']
};
}

getTableHead() {
Expand All @@ -39,20 +58,25 @@ class SectionClasses extends AdminSection {
];
}

customizeRecordBeforeInserting(data) {
return this.props.actions.populateFields('/classes', data._id, ['teacherIds']);
}

getTableBody() {
return this.props.classes.map((c) => {
return Object.keys(this.state.records).map((id) => {
const c = this.state.records[id];
return [
c.name,
c.teacherIds.map(id => (this.props.teachersById[id] || {}).lastName).join(', '),
c.teacherIds.map(teacher => teacher.lastName).join(', '),
this.getTableActions(this.actions, c)
];
});
}

getTeacherOptions() {
return this.props.teachers.map((r) => {
return this.state.teachers.map((r) => {
return {
label: r.lastName || r._id,
label: `${r.firstName || r._id} ${r.lastName || ""}`,
value: r._id
};
});
Expand All @@ -74,7 +98,7 @@ class SectionClasses extends AdminSection {
name="schoolId"
type="hidden"
layout="elementOnly"
value={this.props.school._id}
value={this.props.schoolId}
/>

<Input
Expand Down
Loading

0 comments on commit b3f854d

Please sign in to comment.