Commit db87d94e authored by fanconic's avatar fanconic
Browse files

Merge branch 'master' into frontpage

parents 93fef96c 9ded61ef
......@@ -7,6 +7,11 @@ ButtonCSS.addStyle('.blue-button', {
color_light_text: 'white',
});
ButtonCSS.addStyle('.flat-button', {
color_light_background: 'white',
color_light_text: 'gray',
});
/**
* Generic button component
*
......
import m from 'mithril';
import { Checkbox } from 'polythene-mithril';
// import { CheckboxCSS } from 'polythene-css';
// CheckboxCSS.addStyle('.my-checkbox', {});
export default class CheckboxComponent {
constructor() {
this.defaultProps = {
// className: 'my-checkbox',
// label: 'Unnamed checkbox',
};
this.defaultProps = {};
}
view(vnode) {
......
import m from 'mithril';
export default class DropdownComponent {
constructor() {
this.selectedId = 0;
}
view(vnode) {
return m(
'select',
{ onchange: m.withAttr('value', vnode.selectedId), ...this.defaultProps, ...vnode.attrs },
[vnode.attrs.data.map(label => m('option', { value: label.id }, label.name))]
);
}
}
......@@ -2,4 +2,5 @@ export { default as Button } from './Button';
export { default as Checkbox } from './Checkbox';
export { default as RadioGroup } from './RadioGroup';
export { default as Tabs } from './Tabs';
export { default as Dropdown } from './Dropdown';
export { default as TextField } from './TextField';
......@@ -25,6 +25,7 @@ export default class InputGroup {
args = {};
}
args.value = vnode.attrs.value;
args.type = vnode.attrs.type;
args.onchange = vnode.attrs.onchange;
args.oninput = vnode.attrs.oninput;
args.getSuggestions = vnode.attrs.getSuggestions;
......
import m from 'mithril';
import { isLoggedIn } from '../models/auth';
import { isLoggedIn, getUsername, login } from '../models/auth';
import * as user from '../models/user';
import * as groups from '../models/groups';
import inputGroup from './form/inputGroup';
import { Button } from '../components';
// shows all relevant user information
......@@ -26,16 +27,30 @@ class showUserInfo {
// provides a form to change the users password (if not authenticated by LDAP)
class changePasswordForm {
oninit() {
this.password_old = '';
this.password1 = '';
this.password2 = '';
}
submit() {
const password = this.password1;
this.busy = true;
// renew authentication token, so we are allowed to change the password
login(getUsername(), this.password_old);
user
.update({ password })
.then(() => {
this.busy = false;
this.password_old = '';
this.password1 = '';
this.password2 = '';
})
.catch(() => {
this.busy = false;
this.password_old = '';
this.password2 = '';
});
}
......@@ -47,7 +62,8 @@ class changePasswordForm {
// * Does not contain any whitespace characters
validate() {
this.valid =
this.password1.length > 8 &&
this.password_old.length > 0 &&
this.password1.length >= 8 &&
this.password1.length <= 100 &&
!this.password1.match(/\s/g) &&
this.password1.match(/[A-Z]/g) &&
......@@ -60,36 +76,73 @@ class changePasswordForm {
view() {
if (user.get() === undefined) return m();
const buttonArgs = { events: { onclick: () => this.submit() } };
const buttonArgs = {};
let buttons;
if (!this.valid || !this.equal || this.busy) {
buttonArgs.disabled = true;
}
if (user.get().password_set) {
buttons = [
m(Button, {
...buttonArgs,
label: 'change password',
events: { onclick: () => this.submit() },
}),
m(Button, {
disabled: this.password_old.length === 0,
label: 'Revert to LDAP',
events: {
onclick: () => {
this.password1 = '';
this.password2 = '';
this.submit();
},
},
}),
];
} else {
buttons = m(Button, {
...buttonArgs,
label: 'set password',
events: { onclick: () => this.submit() },
});
}
return m('div', [
m('input', {
m('div', 'Requirements: min 8 characters, upper and lower case letters and numbers'),
m(inputGroup, {
name: 'password_old',
title: 'Old Password',
type: 'password',
value: this.password_old,
oninput: e => {
this.password_old = e.target.value;
this.validate();
},
}),
m(inputGroup, {
name: 'password1',
id: 'password1',
title: 'New Password',
type: 'password',
placeholder: 'Password',
value: this.password1,
oninput: e => {
this.password1 = e.target.value;
this.validate();
},
}),
m('input', {
m(inputGroup, {
name: 'password2',
id: 'password2',
title: 'Repeat',
type: 'password',
placeholder: 'Repeat',
value: this.password2,
oninput: e => {
this.password2 = e.target.value;
this.validate();
},
}),
m(Button, { ...buttonArgs, label: 'change password' }),
buttons,
]);
}
}
......@@ -120,11 +173,9 @@ class rfidForm {
}
return m('div', [
m('input', {
m(inputGroup, {
name: 'rfid',
id: 'rfid',
type: 'text',
placeholder: 'RFID',
title: 'RFID',
value: this.rfid,
oninput: e => {
this.rfid = e.target.value;
......@@ -166,43 +217,97 @@ class announceSubscriptionForm {
// shows group memberships and allows to withdraw and enroll for selected groups.
class groupMemberships {
static oninit() {
oninit() {
groups.load({ allow_self_enrollment: true });
groups.loadMemberships();
this.busy = [];
this.confirm = [];
this.query = '';
}
static view() {
view() {
// Searchbar for groups
const filterForm = m('div', [
m(inputGroup, {
name: 'group_search',
title: 'Search groups',
oninput: e => {
this.query = e.target.value;
if (this.query.length > 0) {
this.isValid = true;
}
},
}),
]);
return m('div', [
filterForm,
m(
'div',
groups.getMemberships().map(membership => {
const buttonArgs = {
events: {
onclick: () => {
this.busy[membership.group._id] = true;
groups
.withdraw(membership._id, membership._etag)
.then(() => {
this.busy[membership.group._id] = false;
})
.catch(() => {
this.busy[membership.group._id] = false;
});
},
},
};
const buttonArgs = {};
let buttons;
if (this.query.length > 0 && !new RegExp(this.query, 'gi').test(membership.group.name)) {
return m('');
}
if (this.busy[membership.group._id]) {
buttonArgs.disabled = true;
}
if (this.confirm[membership.group._id]) {
buttons = [
m(Button, {
...buttonArgs,
label: 'cancel',
className: 'flat-button',
events: {
onclick: () => {
this.confirm[membership.group._id] = false;
this.busy[membership.group._id] = false;
},
},
}),
m('span', ' '),
m(Button, {
...buttonArgs,
label: 'confirm',
events: {
onclick: () => {
this.busy[membership.group._id] = true;
groups
.withdraw(membership._id, membership._etag)
.then(() => {
this.busy[membership.group._id] = false;
this.confirm[membership.group._id] = false;
})
.catch(() => {
this.busy[membership.group._id] = false;
this.confirm[membership.group._id] = false;
});
},
},
}),
];
} else {
buttons = m(Button, {
...buttonArgs,
label: 'withdraw',
events: {
onclick: () => {
this.confirm[membership.group._id] = true;
},
},
});
}
return m('div', [
m('span', membership.group.name),
membership.expiry === undefined
? undefined
: m('span', `(expires on ${membership.expiry})`),
m(Button, { ...buttonArgs, label: 'withdraw' }),
buttons,
]);
})
),
......@@ -210,8 +315,13 @@ class groupMemberships {
'div',
groups.getList().map(group => {
if (groups.getMemberships().some(element => element.group._id === group._id)) {
return m.trust('');
return m('');
}
if (this.query.length > 0 && !new RegExp(this.query, 'gi').test(group.name)) {
return m('');
}
const buttonArgs = {
events: {
onclick: () => {
......
......@@ -3,9 +3,43 @@ import * as studydocs from '../../models/studydocs';
import { apiUrl } from '../../models/config';
import { isLoggedIn } from '../../models/auth';
import { Error401 } from '../errors';
import { Button, Checkbox, TextField } from '../../components';
import { Button, Checkbox, TextField, Dropdown } from '../../components';
const tableHeadings = ['title', 'type'];
const filterNames = {
department: { itet: 'D-ITET', mavt: 'D-MAVT' },
type: {
'cheat sheet': 'Zusammenfassung',
exams: 'Alte Prüfungen',
'lecture documents': 'Unterichts Unterlagen',
exercies: 'Übungsserien',
},
};
const subjects = {
itet: [
['Digitaltechnik', 'Analysis 1', 'Netzwerke und Schaltungen 1', 'Informatik 1'],
['Koma'],
['Physics 2'],
[],
[],
[],
],
mavt: [
[
'Analysis 1',
'Werkstoffe und Fertigung 1',
'Lineare Algebra 1',
'Chemie',
'Maschinenelemente',
],
['Innovationsprozess'],
['Dynamics', 'Thermodynamik 1', 'Philosophie'],
['Fluiddynamik1', 'Thermodynamik 2'],
[],
[],
],
};
export default class studydocList {
constructor(vnode) {
......@@ -15,20 +49,49 @@ export default class studydocList {
static oninit() {
studydocs.load();
this.semester = 1;
this.lecture = 'Fach';
this.search = '';
this.filter = {
department: {},
type: {},
semester: {},
};
this.filter = {};
Object.keys(filterNames).forEach(key => {
const filterValue = {};
Object.keys(filterNames[key]).forEach(subKey => {
filterValue[subKey] = false;
});
this.filter[key] = filterValue;
});
}
static selectDocument(doc) {
this.doc = doc;
}
static lectureData() {
const data = [];
if (this.filter.department.itet || !this.filter.department.mavt) {
for (let i = 0; i < subjects.itet[this.semester - 1].length; i += 1) {
data.push({
id: subjects.itet[this.semester - 1][i],
name: subjects.itet[this.semester - 1][i],
});
}
}
if (this.filter.department.mavt || !this.filter.department.itet) {
for (let i = 0; i < subjects.mavt[this.semester - 1].length; i += 1) {
data.push({
id: subjects.mavt[this.semester - 1][i],
name: subjects.mavt[this.semester - 1][i],
});
}
}
return data;
}
static changeFilter(filterKey, filterValue, checked) {
this.filter[filterKey][filterValue] = checked;
this.updateFilter();
}
static updateFilter() {
const query = {};
Object.keys(this.filter).forEach(key => {
let queryValue = '';
......@@ -43,6 +106,8 @@ export default class studydocList {
query[key] = { $regex: `^(?i).*${queryValue}.*` };
}
});
query.semester = { $regex: `^(?i).*${String(this.semester)}.*` };
query.lecture = { $regex: `^(?i).*${this.lecture}.*` };
studydocs.load(query);
}
......@@ -81,35 +146,40 @@ export default class studydocList {
m(Button, { label: 'Search' }),
]
),
m('div.department-check', [
m(Checkbox, {
label: 'D-ITET',
onChange: state => this.changeFilter('department', 'itet', state.checked),
}),
m(Checkbox, {
label: 'D-MAVT',
onChange: state => this.changeFilter('department', 'mavt', state.checked),
}),
]),
m('div.type-check', [
m(Checkbox, {
label: 'Zusammenfassung',
onChange: state => this.changeFilter('type', 'cheat sheets', state.checked),
}),
m(Checkbox, {
label: 'Alte Prüfungen',
onChange: state => this.changeFilter('type', 'exams', state.checked),
}),
m(Checkbox, {
label: 'Unterichts Unterlagen',
onChange: state => this.changeFilter('type', 'lecture documents', state.checked),
Object.keys(filterNames).map(key =>
m('div.check', [
Object.keys(filterNames[key]).map(subKey =>
m(Checkbox, {
label: filterNames[key][subKey],
onChange: state => this.changeFilter(key, subKey, state.checked),
})
),
])
),
m('div.drop', [
m(Dropdown, {
data: [
{ id: 1, name: '1. Semester' },
{ id: 2, name: '2. Semester' },
{ id: 3, name: '3. Semester' },
{ id: 4, name: '4. Semester' },
{ id: 5, name: '5. Semester' },
{ id: 6, name: '6. Semester' },
],
onchange: event => {
this.semester = event.target.value;
this.updateFilter();
},
}),
m(Checkbox, {
label: 'Übungsserien',
onChange: state => this.changeFilter('type', 'exercises', state.checked),
m(Dropdown, {
data: this.lectureData(),
onchange: event => {
this.lecture = event.target.value;
this.updateFilter();
},
}),
]),
m(Button, {
label: 'Add new',
events: { onclick: () => m.route.set('/studydocuments/new') },
......
......@@ -11,17 +11,18 @@
display: grid;
grid-template-rows: auto auto auto auto;
align-content: start;
grid-gap: 10px;
grid-gap: 20px;
form{
display: grid;
grid-row: auto auto;
border-bottom: solid 1px black;
//border-bottom: solid 1px black;
}
.department-check, .type-check{
.check, .drop{
display: grid;
grid-row: auto auto;
border-bottom: solid 1px black;
border-top: solid 1px black;
grid-gap: 10px;
//border-bottom: solid 1px black;
//border-top: solid 1px black;
}
}
.content{
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment