diff --git a/src/models/studydocs.js b/src/models/studydocs.js index 7869724f91b60b9dddd318a732c4a88bd0846729..48b8cc2928e26802e6b5d3b7747e60482bbbb015 100644 --- a/src/models/studydocs.js +++ b/src/models/studydocs.js @@ -27,6 +27,25 @@ export function load(query = {}) { }); } +export function getInputSuggestions(field, input) { + const query = {}; + query[field] = { $regex: `^(?i).*${input}.*` }; + // TODO: debug Error 502 Bad Gateway returned by API + // const projection = {}; + // projection[field] = 1; + const queryEncoded = m.buildQueryString({ + where: JSON.stringify(query), + // projection: JSON.stringify(projection), + }); + return m.request({ + method: 'GET', + url: `${apiUrl}/studydocuments?${queryEncoded}`, + headers: { + Authorization: `Token ${getToken()}`, + }, + }); +} + export function reload() { return load(querySaved); } diff --git a/src/views/eventDetails.js b/src/views/eventDetails.js index 27cacf5e137fd46f6a2d067da2f6709e5298c2d5..3408450e7c0a1cb44da80cb7414b0ba0b9a3e4e0 100644 --- a/src/views/eventDetails.js +++ b/src/views/eventDetails.js @@ -4,7 +4,7 @@ import * as events from '../models/events'; import { log } from '../models/log'; import { isLoggedIn } from '../models/auth'; import inputGroup from './form/inputGroup'; -import submitButton from './form/submitButton'; +import button from './form/button'; import JSONSchemaForm from './form/jsonSchemaForm'; class EventSignupForm extends JSONSchemaForm { @@ -34,7 +34,7 @@ class EventSignupForm extends JSONSchemaForm { if (!events.currentSignupHasLoaded()) return m('span', 'Loading...'); if (typeof events.getCurrentSignup() === 'undefined') { const elements = this.renderFormElements(); - elements.push(m(submitButton, { + elements.push(m(button, { active: super.isValid(), args: { onclick: () => this.submit(), @@ -68,7 +68,7 @@ class EventSignupForm extends JSONSchemaForm { getErrors: () => this.emailErrors, value: this.email, })); - elements.push(m(submitButton, { + elements.push(m(button, { active: this.emailValid && super.isValid(), args: { onclick: () => this.submit(), diff --git a/src/views/form/button.js b/src/views/form/button.js new file mode 100644 index 0000000000000000000000000000000000000000..20e6c78e9edb7aa44b2708e2ccee041bef24ad67 --- /dev/null +++ b/src/views/form/button.js @@ -0,0 +1,17 @@ +import m from 'mithril'; + +export default class Button { + static view(vnode) { + let { args } = vnode.attrs; + if (args === undefined) { + args = {}; + } + if (args.type === undefined) { + args.type = 'button'; + } + if (!vnode.attrs.active) { + args.disabled = 'disabled'; + } + return m('button', args, vnode.attrs.title); + } +} diff --git a/src/views/form/inputGroup.js b/src/views/form/inputGroup.js index b795b2ee263e38b6c74427c83671437a0fc34f67..1890b2b23067c222135d11b22748f78501034a4f 100644 --- a/src/views/form/inputGroup.js +++ b/src/views/form/inputGroup.js @@ -1,5 +1,4 @@ import m from 'mithril'; -import { log } from '../../models/log'; export default class InputGroup { constructor(vnode) { @@ -37,18 +36,16 @@ export default class InputGroup { errorField, ]); } - args.list = `${args.id}-datalist`; - log(args); - if (args.getSuggestions) { - log('test'); + args.list = `${vnode.attrs.name}-datalist`; + if (typeof args.getSuggestions === 'function') { args.oninput_original = args.oninput; args.oninput = (e) => { - log(`get suggestions for '${e.target.value}'`); - args.getSuggestions(e.target.value, (result) => { - log('callback called!'); - this.suggestions = result; - }); - if (args.oninput_original) { + if (typeof args.getSuggestions === 'function') { + args.getSuggestions(e.target.value, (result) => { + this.suggestions = result; + }); + } + if (typeof args.oninput_original === 'function') { args.oninput_original(e); } }; diff --git a/src/views/form/jsonSchemaForm.js b/src/views/form/jsonSchemaForm.js index d8a731cdabc7315cbed45110b08028895d867307..8dc30b9317b2e1955050e5810c31a8ec9aa52c94 100644 --- a/src/views/form/jsonSchemaForm.js +++ b/src/views/form/jsonSchemaForm.js @@ -5,7 +5,6 @@ import { isNullOrUndefined } from 'util'; import { log } from '../../models/log'; import inputGroup from './inputGroup'; import selectGroup from './selectGroup'; -import mediaGroup from './mediaGroup'; export default class JSONSchemaForm { constructor() { @@ -111,14 +110,6 @@ export default class JSONSchemaForm { multipleSelect: true, }, })); - } else if (item.type === 'media') { - return m(mediaGroup, this.bind({ - name: key, - title: item.description, - args: { - multiple: 'multiple', - }, - })); } log('Unknown array property type'); return m(''); diff --git a/src/views/form/mediaGroup.js b/src/views/form/mediaGroup.js deleted file mode 100644 index f6cced54744f09681dd515fe3ebb6d088a823068..0000000000000000000000000000000000000000 --- a/src/views/form/mediaGroup.js +++ /dev/null @@ -1,38 +0,0 @@ -import m from 'mithril'; - -export default class MediaGroup { - constructor(vnode) { - this.suggestions = []; - // Link the error-getting function from the binding - this.getErrors = () => []; - if (vnode.attrs.getErrors) { - this.getErrors = vnode.attrs.getErrors; - } - } - - view(vnode) { - // set display-settings accoridng to error-state - let errorField = null; - let groupClasses = vnode.attrs.classes ? vnode.attrs.classes : ''; - const errors = this.getErrors(); - if (errors.length > 0) { - errorField = m('span', `Error: ${errors.join(', ')}`); - groupClasses += ' has-error'; - } - - let { args } = vnode.attrs; - if (args === undefined) { - args = {}; - } - args.value = vnode.attrs.value; - args.onchange = vnode.attrs.onchange; - args.oninput = vnode.attrs.oninput; - args.type = 'file'; - - return m('div', { class: groupClasses }, [ - m(`label[for=${vnode.attrs.name}]`, vnode.attrs.title), - m(`input[name=${vnode.attrs.name}][id=${vnode.attrs.name}]`, args), - errorField, - ]); - } -} diff --git a/src/views/form/selectGroup.js b/src/views/form/selectGroup.js index a834826b266b08130ca66bc27c833dab360d3d8a..e28bf2596880f88dd1236ecf58a4a9e7d689b706 100644 --- a/src/views/form/selectGroup.js +++ b/src/views/form/selectGroup.js @@ -8,16 +8,24 @@ export default class SelectGroup { } view(vnode) { - switch (vnode.attrs.args.type) { + let { args } = vnode.attrs; + if (args === undefined) { + args = {}; + } + args.value = vnode.attrs.value; + args.onchange = vnode.attrs.onchange; + args.oninput = vnode.attrs.oninput; + + switch (vnode.attrs.type) { case 'buttons': { - if (vnode.attrs.args.multipleSelect) { + if (args.multipleSelect) { return m('div', { class: vnode.attrs.classes }, [ m(`label[for=${vnode.attrs.name}]`, vnode.attrs.title), - m('div', vnode.attrs.args.options.map(option => + m('div', vnode.attrs.options.map(option => m(inputGroup, { name: vnode.attrs.name, - title: option, - value: option, + title: option.text, + value: option.value, onchange: (e) => { if (e.target.checked) { this.value.push(e.target.value); @@ -51,12 +59,13 @@ export default class SelectGroup { } case 'select': default: { - if (vnode.attrs.args.multipleSelect) { + if (args.multipleSelect) { return m('div', { class: vnode.attrs.classes }, [ m(`label[for=${vnode.attrs.name}]`, vnode.attrs.title), m( `select[name=${vnode.attrs.name}][id=${vnode.attrs.name}]`, { + args, onchange: (e) => { const value = []; let opt; @@ -79,9 +88,8 @@ export default class SelectGroup { } vnode.attrs.oninput({ target: { name: e.target.name, value } }); }, - multiple: true, }, - vnode.attrs.options.map(option => m('option', option)), + vnode.attrs.options.map(option => m('option', { value: option.value }, option.text)), ), ]); } @@ -89,13 +97,8 @@ export default class SelectGroup { m(`label[for=${vnode.attrs.name}]`, vnode.attrs.title), m( `select[name=${vnode.attrs.name}][id=${vnode.attrs.name}]`, - { - value: vnode.attrs.value, - onchange: vnode.attrs.onchange, - oninput: vnode.attrs.oninput, - multiple: false, - }, - vnode.attrs.args.options.map(option => m('option', option)), + args, + vnode.attrs.options.map(option => m('option', { value: option.value }, option.text)), ), ]); } diff --git a/src/views/form/submitButton.js b/src/views/form/submitButton.js deleted file mode 100644 index d6cd4062be6fb1ead93520e1c5ca4486ff62b066..0000000000000000000000000000000000000000 --- a/src/views/form/submitButton.js +++ /dev/null @@ -1,11 +0,0 @@ -import m from 'mithril'; - -export default class SubmitButton { - static view(vnode) { - const { args } = vnode.attrs; - if (!vnode.attrs.active) { - args.disabled = 'disabled'; - } - return m('button[type=button]', args, vnode.attrs.text); - } -} diff --git a/src/views/studydocNew.js b/src/views/studydocNew.js index ab41b23f228d11780d496543c69d116015dd8364..14bad3c41d4713e10d9312b0ff977d4adf7c9e0e 100644 --- a/src/views/studydocNew.js +++ b/src/views/studydocNew.js @@ -1,116 +1,148 @@ import m from 'mithril'; -// import Ajv from 'ajv'; import * as studydocs from '../models/studydocs'; -// import { apiUrl } from '../models/config'; import { isLoggedIn } from '../models/auth'; -import { log } from '../models/log'; import { Error401 } from './errors'; - - -// TODO: add validate +import inputGroup from './form/inputGroup'; +import selectGroup from './form/selectGroup'; +import button from './form/button'; export default class studydocNew { oninit() { - // this.ajv = new Ajv({ - // missingRefs: 'ignore', - // errorDataPath: 'property', - // allErrors: true, - // }); - // // load schema - // m.request(`${apiUrl}/docs/api-docs`).then((schema) => { - // const objectSchema = schema.definitions.Studydocument; - // this.ajv.addSchema(objectSchema, 'schema'); - // this.validate = this.ajv.getSchema('schema'); - // }); + this.doc = { type: 'exams' }; + this.isValid = false; + } - this.doc = { semester: '1', department: 'itet', type: 'exams' }; + static _getInputSuggestions(field, input, callback) { + if (input.length > 2) { + studydocs.getInputSuggestions(field, input).then((result) => { + const suggestions = new Set(); + result._items.forEach((item) => { + suggestions.add(item[field]); + }); + callback(Array.from(suggestions)); + }); + } + } + + submit() { + if (this.isValid) { + studydocs.addNew(this.doc); + m.route.set('/studydocuments'); + } } view() { if (!isLoggedIn()) return m(Error401); - return m('div', [ - m('form', { - onsubmit: (e) => { - e.preventDefault(); - log(this.doc); - studydocs.addNew(this.doc); - m.route.set('/studydocuments'); + return m('form', [ + m(inputGroup, { + name: 'title', + title: 'Title', + oninput: (e) => { + this.doc.title = e.target.value; + }, + getSuggestions: (input, callback) => + studydocNew._getInputSuggestions('title', input, callback), + }), + m(inputGroup, { + name: 'professor', + title: 'Professor', + oninput: (e) => { + this.doc.professor = e.target.value; + }, + getSuggestions: (input, callback) => + studydocNew._getInputSuggestions('professor', input, callback), + }), + m(inputGroup, { + name: 'author', + title: 'Author', + oninput: (e) => { + this.doc.author = e.target.value; + }, + getSuggestions: (input, callback) => + studydocNew._getInputSuggestions('author', input, callback), + }), + m(selectGroup, { + name: 'semester', + title: 'Semester', + type: 'select', + onchange: (e) => { + this.doc.semester = e.target.value; + }, + options: [ + { value: '1', text: '1' }, + { value: '2', text: '2' }, + { value: '3', text: '3' }, + { value: '4', text: '4' }, + { value: '5+', text: '5+' }, + ], + }), + m(selectGroup, { + name: 'department', + title: 'Department', + type: 'select', + onchange: (e) => { + this.doc.department = e.target.value; + }, + options: [ + { value: 'itet', text: 'itet' }, + { value: 'mavt', text: 'mavt' }, + ], + }), + m(inputGroup, { + name: 'lecture', + title: 'Lecture', + oninput: (e) => { + this.doc.lecture = e.target.value; + }, + getSuggestions: (input, callback) => + studydocNew._getInputSuggestions('lecture', input, callback), + }), + m(inputGroup, { + name: 'course_year', + title: 'Course Year', + type: 'number', + args: { + placeholder: (new Date()).getFullYear(), + }, + oninput: (e) => { + this.doc.course_year = e.target.value; + }, + }), + m(selectGroup, { + name: 'type', + title: 'Type', + type: 'select', + onchange: (e) => { + this.doc.type = e.target.value; + }, + options: [ + { value: 'exams', text: 'Exam' }, + { value: 'cheat sheets', text: 'Cheat sheet' }, + { value: 'lecture documents', text: 'Lecture Document' }, + { value: 'exercises', text: 'Exercise' }, + ], + }), + m(inputGroup, { + name: 'files', + title: 'Files', + args: { + type: 'file', + multiple: 1, }, - }, [ - m('input[name=title]', { - placeholder: 'title', - onchange: (e) => { - this.doc.title = e.target.value; - // this.validate(this.doc); - }, - }), - m('input[name=professor]', { - placeholder: 'professor', - onchange: (e) => { - this.doc.professor = e.target.value; - // this.validate(this.doc); - }, - }), - m('input[name=author]', { - placeholder: 'author', - onchange: (e) => { - this.doc.author = e.target.value; - // this.validate(this.doc); - }, - }), - m('select[name=semester]', { - onchange: (e) => { - this.doc.semester = e.target.value; - // this.validate(this.doc); - }, - }, [ - m('option', { value: '1' }, '1'), - m('option', { value: '2' }, '2'), - m('option', { value: '3' }, '3'), - m('option', { value: '4' }, '4'), - m('option', { value: '5+' }, '5+'), - ]), - m('select[name=department]', { - onchange: (e) => { - this.doc.department = e.target.value; - // this.validate(this.doc); - }, - }, [ - m('option', { value: 'itet' }, 'itet'), - m('option', { value: 'mavt' }, 'mavt'), - ]), - m('input[name=lecture]', { - placeholder: 'lecture', - onchange: (e) => { - this.doc.lecture = e.target.value; - // this.validate(this.doc); - }, - }), - m('input[name=course_year]', { - type: 'number', - placeholder: 2018, - onchange: (e) => { - this.doc.course_year = e.target.value; - // this.validate(this.doc); - }, - }), - m('select[name=type]', { - onchange: (e) => { - this.doc.type = e.target.value; - // this.validate(this.doc); - }, - }, [ - m('option', { value: 'exams' }, 'exams'), - m('option', { value: 'cheat sheets' }, 'cheat sheets'), - m('option', { value: 'lecture documents' }, 'lecture documents'), - m('option', { value: 'exercises' }, 'exercises'), - ]), - m('button.button[type=submit]', 'Submit'), - ]), - m('input[type=file][multiple]', { onchange: (e) => { this.doc.files = e.target.files; + if (this.doc.files.length > 0) { + this.isValid = true; + } + }, + }), + m(button, { + name: 'submit', + title: 'Submit', + active: this.isValid, + args: { + onclick: () => this.submit(), }, }), ]);