From b0487a0f36c8ed1c5b3c65de6078c9124d31f826 Mon Sep 17 00:00:00 2001 From: Sandro Lutz <code@temparus.ch> Date: Sun, 4 Mar 2018 12:22:13 +0100 Subject: [PATCH] Merge remote-tracking branch 'origin/master' into studydocs-autocomplete --- src/models/events.js | 115 +++++++++++++-------- src/views/eventDetails.js | 167 ++++++++++++++++++------------- src/views/form/jsonSchemaForm.js | 14 ++- src/views/form/selectGroup.js | 18 +++- 4 files changed, 197 insertions(+), 117 deletions(-) diff --git a/src/models/events.js b/src/models/events.js index 5c4fadbe..c779be86 100644 --- a/src/models/events.js +++ b/src/models/events.js @@ -1,7 +1,6 @@ import m from 'mithril'; import { apiUrl } from './config'; import { getToken, getUserId, isLoggedIn } from './auth'; -import { log } from './log'; const lang = 'de'; const date = `${new Date().toISOString().split('.')[0]}Z`; @@ -15,23 +14,23 @@ export function getList() { return this.list; } -export function getCurrent() { - return this.current; +export function getSelectedEvent() { + return this.selectedEvent; } -export function getCurrentSignup() { - return this.currentSignup; +export function getSignupForSelectedEvent() { + return this.selectedEventSignup; } -export function currentSignupHasLoaded() { - return this.currentSignupLoaded; +export function signupForSelectedEventHasLoaded() { + return this.selectedEventSignupLoaded; } -export function checkCurrentSignup() { +export function loadSignupForSelectedEvent() { const queryString = m.buildQueryString({ where: JSON.stringify({ user: getUserId(), - event: this.getCurrent()._id, + event: this.getSelectedEvent()._id, }), }); @@ -42,14 +41,58 @@ export function checkCurrentSignup() { Authorization: `Token ${getToken()}`, } : {}, }).then((result) => { - [this.currentSignup] = result._items; - this.currentSignupLoaded = true; + [this.selectedEventSignup] = result._items; + this.selectedEventSignupLoaded = true; }); } -export function signupCurrent(additionalFields, email = '') { +export function _signupUserForSelectedEvent(additionalFieldsString) { + if (typeof this.selectedEventSignup !== 'undefined') { + return m.request({ + method: 'PATCH', + url: `${apiUrl}/eventsignups/${this.selectedEventSignup._id}`, + data: { + additional_fields: additionalFieldsString, + }, + headers: getToken() ? { + Authorization: `Token ${getToken()}`, + 'If-Match': this.selectedEventSignup._etag, + } : { 'If-Match': this.selectedEventSignup._etag }, + }).then(() => { this.loadSignupForSelectedEvent(); }); + } + + return m.request({ + method: 'POST', + url: `${apiUrl}/eventsignups`, + data: { + event: this.selectedEvent._id, + additional_fields: additionalFieldsString, + user: getUserId(), + }, + headers: getToken() ? { + Authorization: `Token ${getToken()}`, + } : {}, + }).then(() => { this.loadSignupForSelectedEvent(); }); +} + +export function _signupEmailForSelectedEvent(additionalFieldsString, email) { + return m.request({ + method: 'POST', + url: `${apiUrl}/eventsignups`, + data: { + event: this.selectedEvent._id, + additional_fields: additionalFieldsString, + email, + }, + headers: getToken() ? { + Authorization: `Token ${getToken()}`, + } : {}, + }).then(() => { this.loadSignupForSelectedEvent(); }); +} + +export function signupForSelectedEvent(additionalFields, email = '') { let additionalFieldsString; - if (this.current.additional_fields === undefined || + if (this.selectedEvent.additional_fields === undefined || additionalFields === null || typeof additionalFields !== 'object') { additionalFieldsString = undefined; } else { @@ -57,33 +100,23 @@ export function signupCurrent(additionalFields, email = '') { } if (isLoggedIn()) { - log(`UserId: ${getUserId()}`); - m.request({ - method: 'POST', - url: `${apiUrl}/eventsignups`, - data: { - event: this.current._id, - additional_fields: additionalFieldsString, - user: getUserId(), - }, - headers: getToken() ? { - Authorization: `Token ${getToken()}`, - } : {}, - }).then(() => { this.checkCurrentSignup(); }); - } else if (this.current.allow_email_signup) { - log(`Email: ${email}`); + return this._signupUserForSelectedEvent(additionalFieldsString); + } else if (this.selectedEvent.allow_email_signup) { + return this._signupEmailForSelectedEvent(additionalFieldsString, email); + } + return Promise.reject(new Error('Signup not allowed')); +} + +export function signoffForSelectedEvent() { + if (isLoggedIn() && typeof this.selectedEventSignup !== 'undefined') { m.request({ - method: 'POST', - url: `${apiUrl}/eventsignups`, - data: { - event: this.current._id, - additional_fields: additionalFieldsString, - email, - }, + method: 'DELETE', + url: `${apiUrl}/eventsignups/${this.selectedEventSignup._id}`, headers: getToken() ? { Authorization: `Token ${getToken()}`, - } : {}, - }).then(() => { this.checkCurrentSignup(); }); + 'If-Match': this.selectedEventSignup._etag, + } : { 'If-Match': this.selectedEventSignup._etag }, + }).then(() => { this.loadSignupForSelectedEvent(); }); } } @@ -113,9 +146,9 @@ export function load(query = {}) { }); } -export function loadCurrent(eventId) { - this.current = this.getList().find(item => item._id === eventId); - if (typeof this.current === 'undefined') { +export function selectEvent(eventId) { + this.selectedEvent = this.getList().find(item => item._id === eventId); + if (typeof this.selectedEvent === 'undefined') { this.load({ where: { time_advertising_start: { $lte: date }, @@ -124,7 +157,7 @@ export function loadCurrent(eventId) { }, sort: ['-priority', 'time_advertising_start'], }).then(() => { - this.current = this.getList().find(item => item._id === eventId); + this.selectedEvent = this.getList().find(item => item._id === eventId); }); } } diff --git a/src/views/eventDetails.js b/src/views/eventDetails.js index 3408450e..82bddd7d 100644 --- a/src/views/eventDetails.js +++ b/src/views/eventDetails.js @@ -10,112 +10,145 @@ import JSONSchemaForm from './form/jsonSchemaForm'; class EventSignupForm extends JSONSchemaForm { oninit(vnode) { super.oninit(vnode); + this.email = ''; this.emailErrors = []; this.emailValid = false; if (isLoggedIn()) { - events.checkCurrentSignup(); + events.loadSignupForSelectedEvent() + .then(() => { + if (typeof events.getSignupForSelectedEvent() !== 'undefined') { + this.data = JSON.parse(events.getSignupForSelectedEvent().additional_fields) || {}; + } + }); } } - submit() { - if (isLoggedIn()) { - events.signupCurrent(super.getValue()); - } else { - events.signupCurrent(super.getValue(), this.email); - } + signup() { + events.signupForSelectedEvent(super.getValue(), this.email) + .then(() => log('Successfully signed up for the event!')) + .catch(() => log('Could not sign up of the event!')); + } + + signoff() { + events.signoffForSelectedEvent(); + this.validate(); } view() { // do not render anything if there is no data yet - if (typeof events.getCurrent() === 'undefined') return m(); + if (typeof events.getSelectedEvent() === 'undefined') return m(''); if (isLoggedIn()) { // do not render form if there is no signup data of the current user - if (!events.currentSignupHasLoaded()) return m('span', 'Loading...'); - if (typeof events.getCurrentSignup() === 'undefined') { - const elements = this.renderFormElements(); - elements.push(m(button, { - active: super.isValid(), - args: { - onclick: () => this.submit(), - }, - text: 'Signup', - })); - return m('form', elements); + if (!events.signupForSelectedEventHasLoaded()) return m('span', 'Loading...'); + + const elements = this.renderFormElements(); + elements.push(this._renderSignupButton()); + if (typeof events.getSignupForSelectedEvent() !== 'undefined') { + elements.unshift(m('div', 'You have already signed up. Update your data below.')); + elements.push(this._renderSignoffButton()); } - return m('div', 'You have already signed up for this event.'); - } else if (events.getCurrent().allow_email_signup) { + return m('form', elements); + } else if (events.getSelectedEvent().allow_email_signup) { const elements = this.renderFormElements(); - elements.push(m(inputGroup, { - name: 'email', - title: 'Email', - args: { - type: 'text', - }, - oninput: (e) => { - // bind changed data - this.email = e.target.value; - - // validate if email address has the right structure - if (EmailValidator.validate(this.email)) { - this.emailValid = true; - this.emailErrors = []; - } else { - this.emailValid = false; - this.emailErrors = ['Not a valid email address']; - } - }, - getErrors: () => this.emailErrors, - value: this.email, - })); - elements.push(m(button, { - active: this.emailValid && super.isValid(), - args: { - onclick: () => this.submit(), - }, - text: 'Signup', - })); + elements.push(this._renderEmailField()); + elements.push(this._renderSignupButton()); return m('form', elements); } return m('div', 'This event is for AMIV members only.'); } + + isValid() { + if (!isLoggedIn()) { + return super.isValid() && this.emailValid; + } + return super.isValid(); + } + + _renderEmailField() { + return m(inputGroup, { + name: 'email', + title: 'Email', + args: { + type: 'text', + }, + oninput: (e) => { + // bind changed data + this.email = e.target.value; + + // validate if email address has the right structure + if (EmailValidator.validate(this.email)) { + this.emailValid = true; + this.emailErrors = []; + } else { + this.emailValid = false; + this.emailErrors = ['Not a valid email address']; + } + }, + getErrors: () => this.emailErrors, + value: this.email, + }); + } + + _renderSignupButton() { + return m(button, { + name: 'signup', + title: 'Signup', + active: super.isValid(), + args: { + onclick: () => this.signup(), + }, + }); + } + + _renderSignoffButton() { + return m(button, { + name: 'signoff', + title: 'Delete signup', + active: true, + args: { + onclick: () => this.signoff(), + }, + }); + } } export default class EventDetails { static oninit(vnode) { - events.loadCurrent(vnode.attrs.eventId); + events.selectEvent(vnode.attrs.eventId); } static view() { - if (typeof events.getCurrent() === 'undefined') { - return m(); + if (typeof events.getSelectedEvent() === 'undefined') { + return m(''); } - log(events.getCurrent()); + let eventSignupForm; const now = new Date(); - const registerStart = new Date(events.getCurrent().time_register_start); - const registerEnd = new Date(events.getCurrent().time_register_end); - log(`Now: ${now}`); - log(`Start: ${registerStart}`); - log(`End: ${registerEnd}`); + const registerStart = new Date(events.getSelectedEvent().time_register_start); + const registerEnd = new Date(events.getSelectedEvent().time_register_end); if (registerStart <= now) { if (registerEnd >= now) { eventSignupForm = m(EventSignupForm, { - schema: events.getCurrent().additional_fields === undefined ? - undefined : JSON.parse(events.getCurrent().additional_fields), + schema: events.getSelectedEvent().additional_fields === undefined ? + undefined : JSON.parse(events.getSelectedEvent().additional_fields), }); } else { - eventSignupForm = m('div', 'The registration period is over.'); + let participantNotice = ''; + if (events.getSignupForSelectedEvent() !== 'undefined') { + participantNotice = m('You signed up for this event.'); + } + eventSignupForm = m('div', ['The registration period is over.', participantNotice]); } } else { eventSignupForm = m('div', `The registration starts at ${registerStart}`); } return m('div', [ - m('h1', events.getCurrent().title_de), - m('span', events.getCurrent().time_start), - m('span', events.getCurrent().signup_count), - m('span', events.getCurrent().spots), - m('p', events.getCurrent().description_de), + m('h1', events.getSelectedEvent().title_de), + m('span', events.getSelectedEvent().time_start), + m('span', events.getSelectedEvent().signup_count), + m('span', events.getSelectedEvent().spots), + m('p', events.getSelectedEvent().description_de), eventSignupForm, ]); } diff --git a/src/views/form/jsonSchemaForm.js b/src/views/form/jsonSchemaForm.js index 8dc30b93..a83c8900 100644 --- a/src/views/form/jsonSchemaForm.js +++ b/src/views/form/jsonSchemaForm.js @@ -77,6 +77,12 @@ export default class JSONSchemaForm { return this.schema === undefined || this.valid; } + validate() { + // validate the new data against the schema + const validate = this.ajv.getSchema('schema'); + this.valid = validate(this.data); + } + getValue() { return this.data; } @@ -104,9 +110,9 @@ export default class JSONSchemaForm { return m(selectGroup, this.bind({ name: key, title: item.description, + type: item.items.enum.length > 8 ? 'select' : 'buttons', + options: item.items.enum, args: { - options: item.items.enum, - type: item.items.enum.length > 8 ? 'select' : 'buttons', multipleSelect: true, }, })); @@ -123,9 +129,9 @@ export default class JSONSchemaForm { return m(selectGroup, this.bind({ name: key, title: item.description, + options: item.enum, + type: 'select', args: { - options: item.enum, - type: 'select', multipleSelect: false, }, })); diff --git a/src/views/form/selectGroup.js b/src/views/form/selectGroup.js index e28bf259..45a8c8f5 100644 --- a/src/views/form/selectGroup.js +++ b/src/views/form/selectGroup.js @@ -16,12 +16,19 @@ export default class SelectGroup { args.onchange = vnode.attrs.onchange; args.oninput = vnode.attrs.oninput; + const options = vnode.attrs.options.map((option) => { + if (typeof option === 'object') { + return option; + } + return { value: option, text: option }; + }); + switch (vnode.attrs.type) { case 'buttons': { if (args.multipleSelect) { return m('div', { class: vnode.attrs.classes }, [ m(`label[for=${vnode.attrs.name}]`, vnode.attrs.title), - m('div', vnode.attrs.options.map(option => + m('div', options.map(option => m(inputGroup, { name: vnode.attrs.name, title: option.text, @@ -47,10 +54,11 @@ export default class SelectGroup { ]); } return m('div', { class: vnode.attrs.classes }, [ - m('div', vnode.attrs.options.map(option => + m('div', options.map(option => m(inputGroup, { name: vnode.attrs.name, - title: option, + title: option.text, + value: option.value, onchange: vnode.attrs.onchange, args: { type: 'radio' }, }))), @@ -89,7 +97,7 @@ export default class SelectGroup { vnode.attrs.oninput({ target: { name: e.target.name, value } }); }, }, - vnode.attrs.options.map(option => m('option', { value: option.value }, option.text)), + options.map(option => m('option', { value: option.value }, option.text)), ), ]); } @@ -98,7 +106,7 @@ export default class SelectGroup { m( `select[name=${vnode.attrs.name}][id=${vnode.attrs.name}]`, args, - vnode.attrs.options.map(option => m('option', { value: option.value }, option.text)), + options.map(option => m('option', { value: option.value }, option.text)), ), ]); } -- GitLab