diff --git a/src/config.json b/src/config.json index 511510749233da02e7452d8e86f14f17544617da..0c6dc1dfe4b5839abe92f447489133637279cd4f 100644 --- a/src/config.json +++ b/src/config.json @@ -90,8 +90,6 @@ "user.firstname", "email" ], - "searchKeys": [ - "email" - ] + "searchKeys": [] } } diff --git a/src/events/newEvent.js b/src/events/newEvent.js index b30f519e9e35bbcbf23761d03dcd3c2c1c72c0b7..ff5bccb9d831ee6011c12ff4265f9a3e0f0d53d0 100644 --- a/src/events/newEvent.js +++ b/src/events/newEvent.js @@ -13,15 +13,14 @@ const style = [ ]; styler.add('event-add', style); - export default class newEvent extends EditView { constructor(vnode) { super(vnode, 'events', {}); this.currentpage = 1; this.food = false; this.sbbAbo = false; + this.data = {}; } - addOne() { this.currentpage = this.currentpage + 1; if (this.currentpage === 5) { @@ -103,12 +102,14 @@ export default class newEvent extends EditView { }; const iconRight = m( - IconButton, { events: { onclick: () => { this.addOne(); } } }, + IconButton, + { events: { onclick: () => { this.addOne(); } } }, m(SVG, m.trust(icons.ArrowRight)), ); const iconLeft = m( - IconButton, { events: { onclick: () => { this.subOne(); } } }, + IconButton, + { events: { onclick: () => { this.subOne(); } } }, m(SVG, m.trust(icons.ArrowLeft)), ); @@ -179,13 +180,18 @@ export default class newEvent extends EditView { { value: 'fcfs', label: 'First come, first serve', - defaultChecked: true, }, { value: 'manual', label: 'Selection made by organizer', }, ], + onChange: (state) => { + this.selection_strategy = state.value; + this.data.selection_strategy = state.value; + console.log(this.data); // Temp proof of concept. + }, + value: this.selection_strategy, }); const buttonFinish = m(Button, { diff --git a/src/events/viewEvent.js b/src/events/viewEvent.js index a7758e91d99cedf8265a30e35859ea1d78069a70..433bdfd5c8ef13716580539d080304ba59fc4dfc 100644 --- a/src/events/viewEvent.js +++ b/src/events/viewEvent.js @@ -5,6 +5,7 @@ import { Card, TextField, IconButton, + Icon, Toolbar, ToolbarTitle, } from 'polythene-mithril'; @@ -18,32 +19,97 @@ import { icons } from '../views/elements'; import { ResourceHandler } from '../auth'; const viewLayout = [ - { + { '.eventViewContainer': { - display: 'grid', - 'grid-template-columns': '40% 60%', - 'grid-gap': '50px', + display: 'grid', + 'grid-template-columns': '40% 55%', + 'grid-gap': '50px', + }, + '.propertyTitle': { + color: 'rgba(0, 0, 0, 0.54)', + }, + '.propertyText': { + color: 'rgba(0, 0, 0, 0.87)', + }, + '.propertyLangIndicator': { + width: '30px', + height: '20px', + float: 'left', + 'background-color': 'rgb(031,045,084)', + 'border-radius': '10px', + 'text-align': 'center', + 'line-height': '20px', + color: 'rgb(255,255,255)', + 'margin-right': '10px', + 'font-size' : '11px', + }, + '.eventInfoCard': { + padding: '10px', + 'font-size': '15sp', }, '.eventViewLeft': { - 'grid-column': 1, + 'grid-column': 1, }, '.eventViewRight': { - 'grid-column': 2, + 'grid-column': 2, }, '.eventViewRight h4': { - 'margin-top': '2px', + 'margin-top': '0px', }, }, ]; styler.add('eventView', viewLayout); +class PropertyInfo { + view({ attrs: { title, de, en } }) { + //const text = ''; + + if(de && en) { + return m('div', + m('p.propertyTitle', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [title]), + m('div', [ + m('div', { className: 'propertyLangIndicator' }, 'DE'), + m('p.propertyText', de), + ]), + m('div', [ + m('div', { className: 'propertyLangIndicator' }, 'EN'), + m('p.propertyText', en ), + ]), + ) + } else if(de) { + return m('div', + m('p.propertyTitle', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [title]), + m('div', [ + m('div', { className: 'propertyLangIndicator' }, 'DE'), + m('p.propertyText', de), + ]), + ) + } else if(en) { + return m('div', + m('p.propertyTitle', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [title]), + m('div', [ + m('div', { className: 'propertyLangIndicator' }, 'EN'), + m('p.propertyText', en), + ]), + ) + } + } +} class ParticipantsTable { constructor({ attrs: { where } }) { - this.ctrl = new DatalistController('eventsignups', { - embedded: { user: 1 }, - where, - }, signupConfig.tableKeys); + this.ctrl = new DatalistController( + 'eventsignups', { + embedded: { user: 1 }, + where, + }, + [ + 'email', + 'user.firstname', + 'user.lastname', + ], + false, + ); } getItemData(data) { @@ -85,7 +151,9 @@ export default class viewEvent extends ItemView { constructor() { super('events'); this.signupHandler = new ResourceHandler('eventsignups'); - this.details = false; + this.description = false; + this.advertisement = false; + this.registration = false; this.emailAdresses = false; this.emaillist = ['']; this.showAllEmails = false; @@ -115,34 +183,20 @@ export default class viewEvent extends ItemView { view() { if (!this.data) return ''; + console.log(Object.keys(this)); + console.log(this['data']); - let displayCatchphrase = null; - let displayDescription = null; - let displayPriority = null; - - if(this.data.catchphrase_de && this.data.catchphrase_en) { - displayCatchphrase = m("t3", {class: "text"}, "de: " + this.data.catchphrase_de + " / en: " + this.data.catchphrase_en); - } else if(this.data.catchphrase_de) { - displayCatchphrase = m("t3", {class: "text"}, "de: " + this.data.catchphrase_de); - } else if(this.data.catchphrase_en) { - displayCatchphrase = m("t3", {class: "text"}, "en: " + this.data.catchphrase_en); - } - - if(this.data.description_de && this.data.description_en) { - displayDescription = m("t3", {class: "text"}, "de: " + this.data.description_de + " / en: " + this.data.description_en); - } else if(this.data.catchphrase_de) { - displayDescription = m("t3", {class: "text"}, "de: " + this.data.description_de); - } else if(this.data.catchphrase_en) { - displayDescription = m("t3", {class: "text"}, "en: " + this.data.description_en); - } - - if(this.data.priority) { - displayPriority = m("t3", {class: "text"}, this.data.priority); - } - - let displayDetailsButton = m(Toolbar, { compact: true, events: { onclick: () => this.details = !this.details } }, [ + let displayDescriptionButton = m(Toolbar, { compact: true, events: { onclick: () => this.description = !this.description } }, [ + m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }), + m(ToolbarTitle, { text: "description" }), + ]); + let displayAdvertisementButton = m(Toolbar, { compact: true, events: { onclick: () => this.advertisement = !this.advertisement } }, [ m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }), - m(ToolbarTitle, { text: "details" }), + m(ToolbarTitle, { text: "advertisement" }), + ]); + let displayRegistrationButton = m(Toolbar, { compact: true, events: { onclick: () => this.registration = !this.registration } }, [ + m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }), + m(ToolbarTitle, { text: "registration" }), ]); let displayEmailAdressesButton = m(Toolbar, { compact: true, events: { onclick: () => this.emailAdresses = !this.emailAdresses } }, [ m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }), @@ -151,74 +205,141 @@ export default class viewEvent extends ItemView { - let displayDetails = null; - let displayWaitlist = null; + let displayDescription = null; + let displayAdvertisement = null; + let displayRegistration = null; let displayEmailAdresses = null; - if (this.details) { - displayDetailsButton = m(Toolbar, { compact: true, events: { onclick: () => this.details = !this.details } }, [ + if (this.description) { + displayDescriptionButton = m(Toolbar, { compact: true, events: { onclick: () => this.description = !this.description } }, [ + m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }), + m(ToolbarTitle, { text: "description" }), + ]); + displayDescription = m(Card, { + className: 'eventInfoCard', + content: [ + { + any: { + content: [ + m(PropertyInfo, { + title: 'Catchphrase', + de: this.data.catchphrase_de, + en: this.data.catchphrase_en, + }), + ] + } + }, + { + any: { + content: [ + m(PropertyInfo, { + title: 'Description', + de: this.data.description_de, + en: this.data.description_en, + }), + ] + } + }, + ] + + }) + } + + if (this.advertisement) { + displayAdvertisementButton = m(Toolbar, { compact: true, events: { onclick: () => this.advertisement = !this.advertisement } }, [ m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }), - m(ToolbarTitle, { text: "details" }), + m(ToolbarTitle, { text: "advertisement" }), ]); - displayDetails = m(Card, { + displayAdvertisement = m(Card, { + className: 'eventInfoCard', + content: [ { - primary: { - title: "Catchphrase", - subtitle: displayCatchphrase, + any: { + content: [ + + m('p', + [ + m('span', { style: { float: 'left'} }, 'annonce:'), this.data.show_annonce ? m(Icon, { style: { float: 'left'}, svg: m.trust(icons.iconCheckedSVG) }): m(Icon, { style: { float: 'left'}, svg: m.trust(icons.iconClearSVG) } ), + m('span', { style: { float: 'left'} }, ' infoscreen:'), this.data.show_infoscreen ? m(Icon, { style: { float: 'left'}, svg: m.trust(icons.iconCheckedSVG) }): m(Icon, { style: { float: 'left'}, svg: m.trust(icons.iconClearSVG) } ), + m('span', { style: { float: 'left'} }, ' website:'), this.data.show_website ? m(Icon, { style: { float: 'left'}, svg: m.trust(icons.iconCheckedSVG) }): m(Icon, { style: { float: 'left'}, svg: m.trust(icons.iconClearSVG) } ) + ]), + ] } }, { any: { - content: this.data.time_start ? m('p', m('strong', `when: from ${dateFormatter(this.data.time_start)} to ${dateFormatter(this.data.time_end)}`)) : '', - }, + content: [ + this.data.time_advertising_start ? m('div', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [m('span.propertyTitle', 'Advertising Time')]) : '', + this.data.time_advertising_start ? m('div', m('p.propertyText', ` ${dateFormatter(this.data.time_advertising_start)} - ${dateFormatter(this.data.time_advertising_end)}`)) : '', + ] + } }, { any: { - content: this.data.location ? m('p', m('strong', `where: ${this.data.location}`)) : '', + content: [ + this.data.priority ? m('div', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [m('span.propertyTitle', 'Priority')]) : '', + this.data.priority ? m('div', m('p.propertyText', ` ${this.data.priority}`)) : '', + ] } }, + ] + + }) + } + + if (this.registration) { + displayEmailAdressesButton = m(Toolbar, { compact: true, events: { onclick: () => this.registration = !this.registration } }, [ + m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }), + m(ToolbarTitle, { text: "registration" }), + ]); + displayRegistration = m(Card, { + className: 'eventInfoCard', + content: [ { - primary: { - title: "Description", - subtitle: displayDescription + any: { + content: [ + this.data.price ? m('div', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [m('span.propertyTitle', 'Price')]) : '', + this.data.price ? m('div', m('p.propertyText', ` ${this.data.price}`)) : '', + ] } }, { - primary: { - title: "Priority", - subtitle: displayPriority + any: { + content: [ + this.data.time_register_start ? m('div', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [m('span.propertyTitle', 'Registration Time')]) : '', + this.data.time_register_start ? m('div', m('p.propertyText', ` ${dateFormatter(this.data.time_register_start)} - ${dateFormatter(this.data.time_register_end)}`)) : '', + ] } }, + { - actions: { + any: { content: [ - m(Button, { - label: "Action 1" - }), - m(Button, { - label: "Action 2" - }) + this.data.selection_strategy ? m('div', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [m('span.propertyTitle', 'Selection strategy')]) : '', + this.data.selection_strategy ? m('div', m('p.propertyText', ` ${this.data.selection_strategy}`)) : '', ] } }, { - text: { - content: "More text" + any: { + content: [ + this.data.allow_email_signup ? m('div', {style: { 'margin-top': '10px', 'margin-bottom': '3px' } }, [m('span.propertyTitle', 'non AMIV-Members allowed')]) : '', + ] } - } - ] + }, + ], - }) + }); } - if (this.emailAdresses) { displayEmailAdressesButton = m(Toolbar, { compact: true, events: { onclick: () => this.emailAdresses = !this.emailAdresses } }, [ m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }), m(ToolbarTitle, { text: "email adresses" }), ]); displayEmailAdresses = m(Card, { + className: 'eventInfoCard', content: [ { any: @@ -245,17 +366,43 @@ export default class viewEvent extends ItemView { }); } + let displaySpots = '-'; + + if(this.data.spots !== 0) { + displaySpots = this.data.spots; + } + - return m('div', { - style: { height: '100%', 'overflow-y': 'scroll'}, - }, [ - m("h1", this.data.title_de || this.data.title_en), + return m("div", { + style: { height: '100%', 'overflow-y': 'scroll', padding: '10px'} + },[ m(Button, {element: 'div', label: "Update Event"}), + m("h1", {style: { 'margin-top': '0px', 'margin-bottom': '0px' } }, [this.data.title_de]), + m('div', { style: { float: 'left', 'margin-right': '20px'} }, [ + m('div', this.data.signup_count ? m('span.propertyTitle', `Signups`) : m.trust(' ')), + m('div', this.data.signup_count ? m('p.propertyText', ` ${this.data.signup_count} / ${displaySpots}`) : m.trust(' ')), + ]), + m('div', { style: { float: 'left', 'margin-right': '20px'} }, [ + m('div', this.data.location ? m('span.propertyTitle', `Location`) : m.trust(' ') ), + m('div', this.data.location ? m('p.propertyText', ` ${this.data.location}`) : m.trust(' ')), + ]), + m('div', [ + m('div', this.data.time_start ? m('span.propertyTitle', `Time`): m.trust(' ')), + m('div', this.data.time_start ? m('p.propertyText', ` ${dateFormatter(this.data.time_start)} - ${dateFormatter(this.data.time_end)}`): m.trust(' ')), + ]), + - m('div.eventViewContainer', [ + m('div.eventViewContainer', { style: { 'margin-top': '50px' } }, [ m('div.eventViewLeft', [ - displayDetailsButton, - displayDetails, + + displayDescriptionButton, + displayDescription, + + displayAdvertisementButton, + displayAdvertisement, + + displayRegistrationButton, + displayRegistration, displayEmailAdressesButton, displayEmailAdresses, diff --git a/src/listcontroller.js b/src/listcontroller.js index bc759b307e92a02d5522e68b65014274b5bb75de..1d871958e6b962ce77e83a49e3d59d2bfb16c5f2 100644 --- a/src/listcontroller.js +++ b/src/listcontroller.js @@ -2,10 +2,16 @@ import Stream from 'mithril/stream'; import { ResourceHandler } from './auth'; export default class DatalistController { - constructor(resource, query = {}, searchKeys = false) { - this.handler = new ResourceHandler(resource, searchKeys); + constructor(resource, query = {}, searchKeys = false, onlineSearch = true) { + this.onlineSearch = onlineSearch; + if (onlineSearch) { + this.handler = new ResourceHandler(resource, searchKeys); + } else { + this.handler = new ResourceHandler(resource, false); + this.clientSearchKeys = searchKeys ||Â []; + } this.query = query || {}; - this.items = []; + this.search = null; // state pointer that is counted up every time the table is refreshed so // we can tell infinite scroll that the data-version has changed. this.stateCounter = Stream(0); @@ -27,25 +33,66 @@ export default class DatalistController { getPageData(pageNum) { // for some reason this is called before the object is instantiated. // check this and return nothing - const query = {}; - Object.keys(this.query).forEach((key) => { query[key] = this.query[key]; }); + const query = Object.assign({}, this.query); query.max_results = 10; query.page = pageNum; return new Promise((resolve, reject) => { this.handler.get(query).then((data) => { - resolve(data._items); + // If onlineSearch is false, we filter the page-results at the client + // because the API would not understand the search pattern, e.g. for + // embedded keys like user.firstname + if (!this.onlineSearch && this.clientSearchKeys.length > 0 && this.search) { + const response = []; + // We go through all response items and will add them to response if + // they match the query. + data._items.forEach((item) => { + // Try every search Key seperately, such that any match with any + // key is sufficient + this.clientSearchKeys.some((key) => { + if (key.match(/.*\..*/)) { + // traverse the key, this is a key pointing to a sub-object + let intermediateObject = Object.assign({}, item); + key.split('.').forEach((subKey) => { + intermediateObject = intermediateObject[subKey]; + }); + if (intermediateObject.includes(this.search)) { + response.push(item); + // return true to end the search of this object, it is already + // matched + return true; + } + } else if (item[key] && item[key].includes(this.search)) { + response.push(item); + // return true to end the search of this object, it is already + // matched + return true; + } + return false; + }); + }); + resolve(response); + } else { + resolve(data._items); + } }); }); } setSearch(search) { - this.query.search = search; - this.refresh(); + if (this.onlineSearch) { + this.search = search; + this.query.search = search; + this.refresh(); + } else if (this.clientSearchKeys.length > 0) { + this.search = search; + this.refresh(); + } } setQuery(query) { this.query = query; + this.query.search = this.search; this.refresh(); } } diff --git a/src/views/elements.js b/src/views/elements.js index 875001950f82a8a3d671ea1c4dc9ba5384afffe2..52c0624383402870cb3b6185cc8629239f0c552f 100644 --- a/src/views/elements.js +++ b/src/views/elements.js @@ -135,6 +135,7 @@ export const icons = { iconUsersSVG: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M3 5v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2H5c-1.11 0-2 .9-2 2zm12 4c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3zm-9 8c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6v-1z"/><path d="M0 0h24v24H0z" fill="none"/></svg>', iconEventSVG: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z"/><path d="M0 0h24v24H0z" fill="none"/></svg>', ArrowLeft: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/><path d="M0-.5h24v24H0z" fill="none"/></svg>', + iconCheckedSVG: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>', }; export const BackButton = {