Skip to content

Commit 59ecbde

Browse files
committed
2 parents 0cb8435 + 2284f7d commit 59ecbde

File tree

4 files changed

+365
-78
lines changed

4 files changed

+365
-78
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
warnings_ara_errors: false
1+
warnings_are_errors: false
22
language: node_js
33
node_js:
44
- "stable"
55
cache:
66
directories:
77
- node_modules
88
script:
9+
- unset CI
910
- npm install
1011
- npm run build
1112
deploy:

src/courses/courses.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import StudentsManager from './studentsManager';
2929
import RichTextInput from 'ra-input-rich-text';
3030
import BookIcon from '@material-ui/icons/Book';
3131
import ResourcesManager from './resourcesManager';
32+
import ExamsManager from './examsManager';
3233

3334
export const CourseIcon = BookIcon;
3435

@@ -125,6 +126,9 @@ export const CourseEdit = props => (
125126
<FormTab label="resources">
126127
<CourseResources/>
127128
</FormTab>
129+
<FormTab label="exams">
130+
<CourseExams/>
131+
</FormTab>
128132
</TabbedForm>
129133
</Edit>
130134
);
@@ -159,4 +163,12 @@ const CourseResources = ({record}) => {
159163
<ResourcesManager source={record}/>
160164
</div>
161165
);
166+
};
167+
168+
const CourseExams = ({record}) => {
169+
return (
170+
<div>
171+
<ExamsManager source={record}/>
172+
</div>
173+
);
162174
};

src/courses/examsManager.js

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
import React, { Component } from 'react';
2+
import {getList, getManyReference, isIdExists, setManyReference} from '../utils/api';
3+
import Checkbox from '@material-ui/core/Checkbox';
4+
5+
6+
import classNames from 'classnames';
7+
import PropTypes from 'prop-types';
8+
import { withStyles } from '@material-ui/core/styles';
9+
import Table from '@material-ui/core/Table';
10+
import TableBody from '@material-ui/core/TableBody';
11+
import TableCell from '@material-ui/core/TableCell';
12+
import TableHead from '@material-ui/core/TableHead';
13+
import TablePagination from '@material-ui/core/TablePagination';
14+
import TableRow from '@material-ui/core/TableRow';
15+
import TableSortLabel from '@material-ui/core/TableSortLabel';
16+
import Toolbar from '@material-ui/core/Toolbar';
17+
import Typography from '@material-ui/core/Typography';
18+
import Paper from '@material-ui/core/Paper';
19+
import IconButton from '@material-ui/core/IconButton';
20+
import Tooltip from '@material-ui/core/Tooltip';
21+
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
22+
import FilterListIcon from '@material-ui/icons/FilterList';
23+
import { lighten } from '@material-ui/core/styles/colorManipulator';
24+
25+
26+
function getSorting(order, orderBy) {
27+
return order === 'desc' ?
28+
(a, b) => (b[orderBy] < a[orderBy] ? -1 : 1):
29+
(a, b) => (a[orderBy] < b[orderBy] ? -1 : 1);
30+
}
31+
32+
33+
const columnData = [
34+
{ id: 'Id', numeric: false, disablePadding: false, label: 'Id'},
35+
{ id: 'Exam Name', numeric: false, disablePadding: false, label: 'Exam Name'},
36+
{ id: 'Date', numeric: false, disablePadding: false, label: 'Date' },
37+
];
38+
39+
class EnhancedTableHead extends React.Component {
40+
createSortHandler = property => event => {
41+
this.props.onRequestSort(event, property);
42+
};
43+
44+
render() {
45+
const { onSelectAllClick, order, orderBy, numSelected, rowCount } = this.props;
46+
47+
return (
48+
<TableHead>
49+
<TableRow>
50+
<TableCell padding="checkbox">
51+
<Checkbox
52+
indeterminate={numSelected > 0 && numSelected < rowCount}
53+
checked={numSelected === rowCount}
54+
onChange={onSelectAllClick}
55+
/>
56+
</TableCell>
57+
{columnData.map(column => {
58+
return (
59+
<TableCell
60+
key={column.id}
61+
numeric={column.numeric}
62+
padding={column.disablePadding ? 'none' : 'default'}
63+
sortDirection={orderBy === column.id ? order : false}
64+
>
65+
<Tooltip
66+
title="Sort"
67+
placement={column.numeric ? 'bottom-end' : 'bottom-start'}
68+
enterDelay={300}
69+
>
70+
<TableSortLabel
71+
active={orderBy === column.id}
72+
direction={order}
73+
onClick={this.createSortHandler(column.id)}
74+
>
75+
{column.label}
76+
</TableSortLabel>
77+
</Tooltip>
78+
</TableCell>
79+
);
80+
}, this)}
81+
</TableRow>
82+
</TableHead>
83+
);
84+
}
85+
}
86+
87+
EnhancedTableHead.propTypes = {
88+
numSelected: PropTypes.number.isRequired,
89+
onRequestSort: PropTypes.func.isRequired,
90+
onSelectAllClick: PropTypes.func.isRequired,
91+
order: PropTypes.string.isRequired,
92+
orderBy: PropTypes.string.isRequired,
93+
rowCount: PropTypes.number.isRequired,
94+
};
95+
96+
97+
const toolbarStyles = theme => ({
98+
root: {
99+
paddingRight: theme.spacing.unit,
100+
},
101+
highlight:
102+
theme.palette.type === 'light'
103+
? {
104+
color: theme.palette.secondary.main,
105+
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
106+
}
107+
: {
108+
color: theme.palette.text.primary,
109+
backgroundColor: theme.palette.secondary.dark,
110+
},
111+
spacer: {
112+
flex: '1 1 100%',
113+
},
114+
actions: {
115+
color: theme.palette.text.secondary,
116+
},
117+
title: {
118+
flex: '0 0 auto',
119+
},
120+
});
121+
122+
let EnhancedTableToolbar = props => {
123+
const { numSelected, classes } = props;
124+
125+
return (
126+
<Toolbar
127+
className={classNames(classes.root, {
128+
[classes.highlight]: numSelected > 0,
129+
})}
130+
>
131+
<div className={classes.title}>
132+
{numSelected > 0 ? (
133+
<Typography color="inherit" variant="subheading">
134+
{numSelected} selected
135+
</Typography>
136+
) : (
137+
<Typography variant="title" id="tableTitle">
138+
All
139+
</Typography>
140+
)}
141+
</div>
142+
<div className={classes.spacer} />
143+
<div className={classes.actions}>
144+
{numSelected > 0 ? (
145+
< Tooltip title = "CheckCircle" >
146+
<IconButton aria-label="CheckCircle">
147+
<CheckCircleIcon />
148+
</IconButton>
149+
</Tooltip>
150+
) : (
151+
<Tooltip title="Filter list">
152+
<IconButton aria-label="Filter list">
153+
<FilterListIcon />
154+
</IconButton>
155+
</Tooltip>
156+
)}
157+
</div>
158+
</Toolbar>
159+
);
160+
};
161+
162+
EnhancedTableToolbar.propTypes = {
163+
classes: PropTypes.object.isRequired,
164+
numSelected: PropTypes.number.isRequired,
165+
};
166+
167+
EnhancedTableToolbar = withStyles(toolbarStyles)(EnhancedTableToolbar);
168+
169+
const styles = theme => ({
170+
root: {
171+
width: '100%',
172+
marginTop: theme.spacing.unit * 3,
173+
},
174+
table: {
175+
minWidth: 1020,
176+
},
177+
tableWrapper: {
178+
overflowX: 'auto',
179+
},
180+
});
181+
182+
183+
class ExamsManager extends Component{
184+
constructor(props){
185+
super(props);
186+
this.course = props.source;
187+
this.state = {
188+
// all exams
189+
exams: [],
190+
//default order
191+
order: 'asc',
192+
//key for order
193+
orderBy: 'id',
194+
//page number
195+
page: 0,
196+
//rows number per page
197+
rowsPerPage: 5,
198+
// exams that already choose this course
199+
selected: [],
200+
};
201+
}
202+
203+
componentDidMount(){
204+
getList("exams")
205+
.then(exams => this.setState({exams: exams}));
206+
// get exam that already choose this course
207+
getManyReference("courses", "exams", this.course)
208+
.then(selected => this.setState({selected: selected[0]}));
209+
}
210+
211+
212+
handleRequestSort = (event, property) => {
213+
const orderBy = property;
214+
let order = 'desc';
215+
216+
if (this.state.orderBy === property && this.state.order === 'desc') {
217+
order = 'asc';
218+
}
219+
220+
this.setState({ order, orderBy });
221+
};
222+
223+
handleSelectAllClick = (event, checked) => {
224+
if (checked) {
225+
let selected = this.state.selected;
226+
for(var i = 0; i < this.state.exams.length; ++i) {
227+
let index = isIdExists(this.state.selected, this.state.exams[i].id);
228+
if (index === -1) {
229+
selected.push(this.state.exams[i]);
230+
}
231+
}
232+
this.setState({ selected: selected }, this.submit );
233+
return;
234+
}
235+
this.setState({ selected: [] }, this.submit);
236+
};
237+
238+
handleChangePage = (event, page) => {
239+
this.setState({ page: page });
240+
};
241+
242+
handleChangeRowsPerPage = event => {
243+
this.setState({ rowsPerPage: event.target.value });
244+
};
245+
246+
render(){
247+
const classes = this.props;
248+
const { exams, order, orderBy, page, rowsPerPage, selected } = this.state;
249+
console.log(exams);
250+
const emptyRows = rowsPerPage - Math.min(rowsPerPage, exams.length - page * rowsPerPage);
251+
return (
252+
<Paper className={classes.root}>
253+
<EnhancedTableToolbar numSelected={selected.length} />
254+
<div className={classes.tableWrapper}>
255+
<Table className={classes.table} aria-labelledby="tableTitle">
256+
<EnhancedTableHead
257+
numSelected={selected.length}
258+
order={order}
259+
orderBy={orderBy}
260+
onSelectAllClick={this.handleSelectAllClick}
261+
onRequestSort={this.handleRequestSort}
262+
rowCount={exams.length}
263+
/>
264+
<TableBody>
265+
{exams
266+
.sort(getSorting(order, orderBy))
267+
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
268+
.map( exam => {
269+
const isSelected = isIdExists(exams, exam.id) !== -1 ? true : false;
270+
return (
271+
<TableRow
272+
hover
273+
role="checkbox"
274+
aria-checked={isSelected}
275+
tabIndex={-1}
276+
key={exam.id}
277+
selected={isSelected}
278+
>
279+
<TableCell padding="checkbox">
280+
<Checkbox
281+
onChange={
282+
() => {
283+
let index = isIdExists(selected, exam.id);
284+
if (index === -1) {
285+
selected.push(exam);
286+
this.setState({selected: selected}, this.submit);
287+
}
288+
else if (index !== -1) {
289+
selected.splice(index, 1);
290+
this.setState({selected: selected}, this.submit);
291+
}
292+
}
293+
}
294+
checked={isIdExists(this.state.selected, exam.id) !== -1 ? true : false}
295+
/>
296+
</TableCell>
297+
<TableCell numeric={false}>{exam.id}</TableCell>
298+
<TableCell numeric={false}>{exam.examName}</TableCell>
299+
<TableCell numeric={false}>{exam.date}</TableCell>
300+
</TableRow>
301+
);
302+
})}
303+
{emptyRows > 0 && (
304+
<TableRow style={{ height: 49 * emptyRows }}>
305+
<TableCell colSpan={6} />
306+
</TableRow>
307+
)}
308+
</TableBody>
309+
</Table>
310+
</div>
311+
<TablePagination
312+
component="div"
313+
count={exams.length}
314+
rowsPerPage={rowsPerPage}
315+
page={page}
316+
backIconButtonProps={{
317+
'aria-label': 'Previous Page',
318+
}}
319+
nextIconButtonProps={{
320+
'aria-label': 'Next Page',
321+
}}
322+
onChangePage={this.handleChangePage}
323+
onChangeRowsPerPage={this.handleChangeRowsPerPage}
324+
/>
325+
</Paper>
326+
);
327+
}
328+
329+
submit(){
330+
setManyReference("exams", this.course, this.state.selected);
331+
}
332+
}
333+
334+
ExamsManager.propTypes = {
335+
classes: PropTypes.object.isRequired,
336+
};
337+
338+
339+
export default withStyles(styles)(ExamsManager);

0 commit comments

Comments
 (0)