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/eventTool.js b/src/eventTool.js index 2cdf9fa59ef35f55b9cf7a5e64c1ba79d6b3205b..92d9af899a075f1dc27822e2377ff4380d5b7028 100644 --- a/src/eventTool.js +++ b/src/eventTool.js @@ -164,14 +164,3 @@ export class EventModal { ]); } } - -export class EventTable { - view() { - return m(TableView, { - resource: 'events', - keys: config.tableKeys, - titles: config.tableKeys.map(key => config.keyDescriptors[key] || key), - onAdd: () => { m.route.set('/newevent'); }, - }); - } -} diff --git a/src/events/eventModal.js b/src/events/eventModal.js new file mode 100644 index 0000000000000000000000000000000000000000..057151ec8328677b4929ba437f33a0173b09f669 --- /dev/null +++ b/src/events/eventModal.js @@ -0,0 +1,16 @@ +import m from 'mithril'; +import viewEvent from './viewEvent'; +import newEvent from './newEvent'; + +export default class EventModal { + constructor() { + this.edit = false; + } + + view() { + if (this.edit) { + return m(newEvent); + } + return m(viewEvent, { onEdit: () => { this.edit = true; } }); + } +} diff --git a/src/events/eventWithExport.js b/src/events/eventWithExport.js index 0c9ac713803e1907df7b386af96b809e7853d5bd..2d7171a66ec085c5c7d5b775f9a91336a5e4b6af 100644 --- a/src/events/eventWithExport.js +++ b/src/events/eventWithExport.js @@ -35,7 +35,7 @@ export default class eventWithExport extends EditView { required: true, floatingLabel: true, dense: true, - onChange : (newState) => {this.title_en = this.title_en;}, + onChange : (newState) => {this.title_en = newState.value; console.log(this.title_en);}, value: this.title_en, }); const fieldDescriptionEn = m(TextField, { @@ -45,23 +45,30 @@ export default class eventWithExport extends EditView { dense: true, multiLine: true, rows: 6, - onChange : (newState) => {this.fieldDescriptionEn = this.fieldDescriptionEn;}, + onChange : (newState) => {this.fieldDescriptionEn = newState.value; console.log(this.fieldDescriptionEn);}, value: this.fieldDescriptionEn, }); + // Needs administrator (Kulturi). const fieldLocation = m(TextField, { label: 'Location:', floatingLabel: true, required: true, - onChange : (newState) => {this.fieldLocation = this.fieldLocation;}, + onChange : (newState) => {this.fieldLocation = newState.value; console.log(this.fieldLocation);}, value: this.fieldLocation, }); + + // Bottom. const buttonMaker = m(Button, { - label: "Submit Request!", - onClick: () => alert("You did not finish the editing of the fields.") + // console.log(JSON.stringify(this.keyDescriptors)), + label: "Submit Request!", + onClick: () => alert("You did not finish the editing of the fields.") }); - return m('div', [ + // Return! + return m('div', { + style: { height: '100%', 'overflow-y': 'scroll'} + }, [ m('h1', 'For the event creator:', fieldTitleEn , fieldDescriptionEn, 'For the AMIV administrator:', fieldLocation), m('div.footer', buttonMaker), ]); diff --git a/src/events/newEvent.js b/src/events/newEvent.js index 3cff68aa1d0a4391bd6e61ca577ec273ace06f9c..1aef5b23b2097be1ce8b3d2e2e1ab920737d2105 100644 --- a/src/events/newEvent.js +++ b/src/events/newEvent.js @@ -1,18 +1,28 @@ import m from 'mithril'; -import { TextField, Button, Checkbox, RadioGroup, IconButton, SVG } from 'polythene-mithril'; +import { Button, Checkbox, RadioGroup, IconButton, SVG, TextField } from 'polythene-mithril'; +import { styler } from 'polythene-core-css'; import EditView from '../views/editView'; -import { icons } from '../views/elements'; +import { icons, textInput, datetimeInput } from '../views/elements'; + +const style = [ + { + '.mywrapper': { + padding: '10px', + }, + }, +]; +styler.add('event-add', style); export default class newEvent extends EditView { constructor(vnode) { - super(vnode, 'events'); + super(vnode, 'events', {}); this.currentpage = 1; + this.data = {}; } - addOne() { this.currentpage = this.currentpage + 1; - if (this.currentpage === 4) { - this.currentpage = 3; + if (this.currentpage === 5) { + this.currentpage = 4; } } @@ -23,167 +33,75 @@ export default class newEvent extends EditView { } } - view(vnode) { + view() { if (!this.currentpage) return ''; - // German and English Information - const fieldTitleEn = m(TextField, { - label: 'Event Title [EN]', - required: true, - floatingLabel: true, - dense: true, - onChange: (newState) => { this.title_en = newState.value; console.log(this.title_en); }, - value: vnode.state.title_en, - }); - - const fieldCatchphraseEn = m(TextField, { - label: 'Catchphrase [EN]', - floatingLabel: true, - dense: true, - help: 'Fun description to make your event look more interesting than it is', - focusHelp: true, - }); - - const fieldDescriptionEn = m(TextField, { - label: 'Description [EN]', - required: true, - floatingLabel: true, - dense: true, - multiLine: true, - rows: 6, - }); - - const fieldTitleDe = m(TextField, { - label: 'Event Title [DE]', - floatingLabel: true, - dense: true, - }); - - const fieldCatchphraseDe = m(TextField, { - label: 'Catchphrase [DE]', - floatingLabel: true, - dense: true, - help: 'Fun description to make your event look more interesting than it is', - focusHelp: true, - }); - - const fieldDescriptionDe = m(TextField, { - label: 'Description [DE]', - floatingLabel: true, - dense: true, - multiLine: true, - rows: 6, - }); - - // Start of relevant data - - const fieldPrice = m(TextField, { - label: 'Price:', - type: 'number', - help: 'In Rappen/Cents', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const fieldStartDate = m(TextField, { - label: 'Event Start[Date and Time]:', - help: 'Format: 01.01.1970-18:00', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const fieldEndDate = m(TextField, { - label: 'Event End[Date and Time]:', - help: 'Format: 01.01.1970-1800', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const fieldStartRegDate = m(TextField, { - label: 'Registration Start[Date and Time]:', - help: 'Format: 01.01.1970-18:00', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const fieldEndRegDate = m(TextField, { - label: 'Registration End[Date and Time]:', - help: 'Format: 01.01.1970-1800', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const fieldLocation = m(TextField, { - label: 'Location:', - floatingLabel: true, - required: true, - }); - const fieldNumberOfParticipants = m(TextField, { - label: 'Number of open spots:', - type: 'number', - floatingLabel: true, - required: true, - }); - - // Everything above is working fine atm. (13:35) - - const fieldAdvStart = m(TextField, { - label: 'Registration Start[Date and Time]:', - type: 'datetime', - help: 'Format: 01.01.1970-18:00', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const fieldAdvEnd = m(TextField, { - label: 'Registration End[Date and Time]:', - help: 'Format: 01.01.1970-1800', - focusHelp: true, - floatingLabel: true, - required: true, - }); - const buttonBannerUp = m(Button, { - label: 'Select Banner File', - events: { - onclick: () => console.log('click'), + const firstTableInputs = { + title_en: { + label: 'English Event Title', }, - }); - - const buttonInfoUp = m(Button, { - label: 'Select Infoscreen File', - events: { - onclick: () => console.log('click'), + catchphrase_en: { + label: 'English Catchphrase', }, - }); - - const buttonPosterUp = m(Button, { - label: 'Select Poster File', - events: { - onclick: () => console.log('click'), + description_en: { + label: 'English Description', + multiLine: true, + rows: 5, }, - }); + title_de: { + label: 'German Event Title', + }, + catchphrase_de: { + label: 'German Catchphrase', + }, + description_de: { + label: 'German Description', + multiLine: true, + rows: 5, + }, + }; - const buttonThumbUp = m(Button, { - label: 'Select Thumbnail File', - events: { - onclick: () => console.log('click'), + const thirdTableInputs = { + spots: { + label: 'Number of Spots', + help: '0 for open event', + focusHelp: true, }, - }); + price: { + label: 'Price', + }, + time_register_start: { + label: 'Start of Registration', + }, + time_register_end: { + label: 'End of Registration', + }, + }; - const buttonUploadAll = m(Button, { - label: 'Upload', - events: { - onclick: () => console.log('click'), + const forthTableInputs = { + time_advertising_start: { + label: 'Start of Advertisement', + type: 'datetime', + required: true, }, - }); + time_advertising_end: { + label: 'End of Advertisement', + required: true, + }, + priority: { + label: 'Priority', + }, + }; 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)), ); @@ -209,6 +127,12 @@ export default class newEvent extends EditView { defaultChecked: false, label: 'Allow non AMIV Members?', value: '100', + onChange: (state) => { + this.allow_email_signup = state.checked; + this.data.allow_email_signup = state.checked; + console.log(this.data); // Temp proof of concept. + }, + checked: this.allow_email_signup, }); const radioButtonSelectionMode = m(RadioGroup, { @@ -223,26 +147,78 @@ export default class newEvent extends EditView { 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, }); - function layoutWith(page) { - return m('div', page); - } + const title = [ + 'Create an Event', 'When and Where?', 'Signups', 'Advertisement', + ][this.currentpage - 1]; + // checks currentPage and selects the fitting page - if (this.currentpage === 1) { - return layoutWith(m('h1', 'Event description:', iconLeft, iconRight, m('br'), fieldTitleEn, fieldCatchphraseEn, fieldDescriptionEn, fieldTitleDe, fieldCatchphraseDe, fieldDescriptionDe)); - } else if (this.currentpage === 2) { - return layoutWith(m( - 'h1', 'Critical Information:', iconLeft, iconRight, m('br'), fieldStartDate, fieldEndDate, fieldStartRegDate, - fieldEndRegDate, fieldLocation, fieldPrice, fieldNumberOfParticipants, - )); - } else if (this.currentpage === 3) { - return layoutWith(m( - 'h1', 'Advertise Information', iconLeft, iconRight, m('br'), fieldAdvStart, fieldAdvEnd, checkboxWebsite, - checkboxAnnounce, checkboxInfoScreen, buttonBannerUp, buttonInfoUp, buttonPosterUp, - buttonThumbUp, m('br'), buttonUploadAll, m('br'), checkboxAllowMail, radioButtonSelectionMode, - )); - } - return layoutWith(m('')); + return m('div.mywrapper', [ + m('h1', title), + m('br'), + iconLeft, + iconRight, + m('br'), + m('div', { + style: { + display: (this.currentpage === 1) ? 'block' : 'none', + }, + }, Object.keys(firstTableInputs).map((key) => { + const attrs = firstTableInputs[key]; + const attributes = Object.assign({}, attrs); + attributes.name = key; + attributes.floatingLabel = true; + return m(textInput, this.bind(attributes)); + })), + m('div', { + style: { + display: (this.currentpage === 2) ? 'block' : 'none', + }, + }, [ + m(datetimeInput, this.bind({ + name: 'time_start', + label: 'Event Start Time', + })), + m(datetimeInput, this.bind({ + name: 'time_end', + label: 'Event End Time', + })), + m(textInput, this.bind({ + name: 'location', + label: 'Location', + floatingLabel: true, + })), + radioButtonSelectionMode, checkboxAllowMail, + ]), + m('div', { + style: { + display: (this.currentpage === 3) ? 'block' : 'none', + }, + }, Object.keys(thirdTableInputs).map((key) => { + const attrs = thirdTableInputs[key]; + const attributes = Object.assign({}, attrs); + attributes.name = key; + attributes.floatingLabel = true; + return m(textInput, this.bind(attributes)); + })), + m('div', { + style: { + display: (this.currentpage === 4) ? 'block' : 'none', + }, + }, Object.keys(forthTableInputs).map((key) => { + const attrs = forthTableInputs[key]; + const attributes = Object.assign({}, attrs); + attributes.name = key; + attributes.floatingLabel = true; + return m(textInput, this.bind(attributes)); + })), + ]); } } diff --git a/src/events/viewEvent.js b/src/events/viewEvent.js index fece3982ed3b26f8aa780e9ad93dbe527a30b2a1..8022c9d0d6afccedab4c2c861cda579110784604 100644 --- a/src/events/viewEvent.js +++ b/src/events/viewEvent.js @@ -1,40 +1,116 @@ import m from 'mithril'; -import { Checkbox, Button, Card, TextField, IconButton, Toolbar, ToolbarTitle } from 'polythene-mithril'; +import { + Switch, + Button, + Card, + TextField, + IconButton, + Icon, + Toolbar, + ToolbarTitle, +} from 'polythene-mithril'; +import { styler } from 'polythene-core-css'; import ItemView from '../views/itemView'; -import { events as config, eventsignups as signupConfig } from '../config.json'; +import { eventsignups as signupConfig } from '../config.json'; import TableView from '../views/tableView'; import DatalistController from '../listcontroller'; import { dateFormatter } from '../utils'; import { icons } from '../views/elements'; -import { styler } from 'polythene-core-css'; +import { ResourceHandler } from '../auth'; +import { apiUrl } from '../config'; const viewLayout = [ { - '.eventViewContainer': { - display: 'grid', - 'grid-template-columns': '50% 50%', - 'grid-gap': '50px', - }, - '.eventViewLeft': { - 'grid-column': 1, - }, - '.eventViewRight': { - 'grid-column': 2, - }, - '.eventViewRight h4': { - 'margin-top': '0px', - } - } + '.eventViewContainer': { + 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, + }, + '.eventViewRight': { + 'grid-column': 2, + }, + '.eventViewRight h4': { + '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) { @@ -48,6 +124,7 @@ class ParticipantsTable { view() { return m(Card, { + style: { 'height': '300px' }, content: m(TableView, { controller: this.ctrl, keys: signupConfig.tableKeys, @@ -63,58 +140,65 @@ class ParticipantsTable { } } +class EmailList { + view({ attrs: { list } }) { + const emails = list.toString().replace(/,/g, '; '); + return m(Card, { + content: m(TextField, { value: emails, label: '', multiLine: true }, ''), + }); + } +} + export default class viewEvent extends ItemView { - constructor() { - super('events'); - this.details = false; - this.waitlist = false; - this.emailAdresses = false; + constructor() { + super('events'); + this.signupHandler = new ResourceHandler('eventsignups'); + this.description = false; + this.advertisement = false; + this.registration = false; + this.emailAdresses = false; + this.emaillist = ['']; + this.showAllEmails = false; + } + + oninit() { + this.handler.getItem(this.id, this.embedded).then((item) => { + this.data = item; + m.redraw(); + }); + this.setUpEmailList(this.showAllEmails); + } + + setUpEmailList(showAll) { + if (!showAll) { + this.signupHandler.get({ where: { accepted: true } }).then((data) => { + this.emaillist = (data._items.map(item => item.email)); + m.redraw(); + }); + } else { + this.signupHandler.get({}).then((data) => { + this.emaillist = (data._items.map(item => item.email)); + m.redraw(); + }); } + } - view() { + view({ attrs: { onEdit } }) { 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) { - displayCatchphraseDe = m("t3", {class: "text"}, "de: " + this.data.catchphrase_de); - } - - if(this.data.catchphrase_en) { - displayCatchphraseEn = m("t3", {class: "text"}, "en: " + this.data.catchphrase_en); - }*/ - - 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: "details" }), + m(ToolbarTitle, { text: "description" }), ]); - let displayWaitlistButton = m(Toolbar, { compact: true, events: { onclick: () => this.waitlist = !this.waitlist } }, [ + let displayAdvertisementButton = m(Toolbar, { compact: true, events: { onclick: () => this.advertisement = !this.advertisement } }, [ m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }), - m(ToolbarTitle, { text: "waitlist" }), + 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) } }), @@ -123,100 +207,132 @@ 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: "details" }), + m(ToolbarTitle, { text: "description" }), ]); - displayDetails = m(Card, { + displayDescription = m(Card, { + className: 'eventInfoCard', content: [ - { - primary: { - title: "Catchphrase", - subtitle: displayCatchphrase, - } - }, { any: { - content: this.data.time_start ? m('p', m('strong', `when: from ${dateFormatter(this.data.time_start)} to ${dateFormatter(this.data.time_end)}`)) : '', - }, + content: [ + m(PropertyInfo, { + title: 'Catchphrase', + de: this.data.catchphrase_de, + en: this.data.catchphrase_en, + }), + ] + } }, { any: { - content: this.data.location ? m('p', m('strong', `where: ${this.data.location}`)) : '', + 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: "advertisement" }), + ]); + displayAdvertisement = m(Card, { + className: 'eventInfoCard', + + content: [ { - primary: { - title: "Description", - subtitle: displayDescription + 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) } ) + ]), + ] } }, { - primary: { - title: "Priority", - subtitle: displayPriority + any: { + 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)}`)) : '', + ] } }, { - actions: { + any: { content: [ - m(Button, { - label: "Action 1" - }), - m(Button, { - label: "Action 2" - }) + 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}`)) : '', ] } }, - { - text: { - content: "More text" - } - } ] }) } - if (this.waitlist) { - displayWaitlistButton = m(Toolbar, { compact: true, events: { onclick: () => this.waitlist = !this.waitlist } }, [ + 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: "waitlist" }), + m(ToolbarTitle, { text: "registration" }), ]); - displayWaitlist = m(Card, { + displayRegistration = m(Card, { + className: 'eventInfoCard', content: [ { - primary: { - title: "Primary title", - subtitle: "Subtitle" + 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}`)) : '', + ] } }, { - actions: { + any: { content: [ - m(Button, { - label: "Action 1" - }), - m(Button, { - label: "Action 2" - }) + 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)}`)) : '', ] } }, + { - text: { - content: "More text" + any: { + content: [ + 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}`)) : '', + ] } - } - ] + }, + { + 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) { @@ -225,59 +341,91 @@ export default class viewEvent extends ItemView { m(ToolbarTitle, { text: "email adresses" }), ]); displayEmailAdresses = m(Card, { + className: 'eventInfoCard', content: [ { - primary: { - title: "Primary title", - subtitle: "Subtitle" - } - }, - { - actions: { - content: [ - m(Button, { - label: "Action 1" + any: + { + content: m(Switch, { + defaultChecked: false, + label: 'show unaccepted', + onChange: () => { + this.showAllEmails = !this.showAllEmails; + this.setUpEmailList(this.showAllEmails); + + }, }), - m(Button, { - label: "Action 2" - }) - ] - } + }, }, { - text: { - content: "More text" - } - } - ] + any: + { + content: m(EmailList, { list: this.emaillist }), + } + }, + ], - }) + }); + } + + let displaySpots = '-'; + + if(this.data.spots !== 0) { + displaySpots = this.data.spots; } return m("div", { - style: { height: '100%', 'overflow-y': 'scroll'} + style: { height: '100%', 'overflow-y': 'scroll', padding: '10px'} },[ - m("h1", {class: "title"}, this.data.title_de), - m(Button, {element: 'div', label: "Update Event"}), + m(Button, { + element: 'div', + label: 'Update Event', + events: { onclick: onEdit }, + }), + m('div', [ + this.data.img_thumbnail ? m('img', { + src: `${apiUrl.slice(0, -1)}${this.data.img_thumbnail.file}`, + height: '50px', + style: { float: 'left' }, + }) : '', + 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, - displayWaitlistButton, - displayWaitlist, + displayDescriptionButton, + displayDescription, + + displayAdvertisementButton, + displayAdvertisement, + + displayRegistrationButton, + displayRegistration, displayEmailAdressesButton, displayEmailAdresses, ]), m('div.eventViewRight', [ m('h4', 'Accepted Participants'), - m(ParticipantsTable, { where: { accepted: true } }), + m(ParticipantsTable, { where: { accepted: true, event: this.data['_id'] } }), m('p', ''), m('h4', 'Participants on Waiting List'), - m(ParticipantsTable, { where: { accepted: false } }), + m(ParticipantsTable, { where: { accepted: false, event: this.data['_id'] } }), ]) ]), diff --git a/src/index.js b/src/index.js index fee7d341d902bf5f349f7014387583a0e887ff09..75ea31cb6a7311ed13668df0e5d908650239d367 100644 --- a/src/index.js +++ b/src/index.js @@ -5,16 +5,13 @@ import { UserModal, UserTable, NewUser } from './userTool'; import { MembershipView } from './membershipTool'; import EventTable from './events/table'; import newEvent from './events/newEvent'; -import viewEvent from './events/viewEvent'; +import EventModal from './events/eventModal'; import eventDraft from './events/eventDraft'; import eventWithExport from './events/eventWithExport'; import Layout from './layout'; -// import AnnounceTool from './announceTool'; import './style'; -const main = document.createElement('div'); -document.body.appendChild(main); -const root = main; +const root = document.body; function layoutWith(view) { @@ -31,7 +28,7 @@ m.route(root, '/users', { '/newuser': layoutWith(NewUser), '/groupmemberships/:id': layoutWith(MembershipView), '/events': layoutWith(EventTable), - '/events/:id': layoutWith(viewEvent), + '/events/:id': layoutWith(EventModal), '/newevent': layoutWith(newEvent), '/draftevent': layoutWith(eventDraft), '/eventwithexport': layoutWith(eventWithExport), 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/editView.js b/src/views/editView.js index 91db119c24d2b021f4d0a3111e0625cd4e43b6d4..4b2c4b0badfccf4ae7f8afb03279c977c30e3823 100644 --- a/src/views/editView.js +++ b/src/views/editView.js @@ -29,6 +29,7 @@ export default class EditView extends ItemView { constructor(vnode, resource, embedded, valid = true) { super(resource, embedded); this.changed = false; + this.resource = resource; // state for validation this.valid = valid; @@ -38,6 +39,7 @@ export default class EditView extends ItemView { allErrors: true, }); this.errors = {}; + this.data = {}; // callback when edit is finished this.callback = vnode.attrs.onfinish; @@ -55,6 +57,15 @@ export default class EditView extends ItemView { m.request(`${apiUrl}docs/api-docs`).then((schema) => { const objectSchema = schema.definitions[ objectNameForResource[this.resource]]; + console.log(objectSchema); + // filter out any field that is of type media and replace with type + // object + Object.keys(objectSchema.properties).forEach((property) => { + if (objectSchema.properties[property].type === 'media' || + objectSchema.properties[property].type === 'json_schema_object') { + objectSchema.properties[property].type = 'object'; + } + }); this.ajv.addSchema(objectSchema, 'schema'); }); } @@ -65,10 +76,12 @@ export default class EditView extends ItemView { if (!this.errors[attrs.name]) this.errors[attrs.name] = []; const boundFormelement = { - onchange: (e) => { + onChange: (name, value) => { this.changed = true; // bind changed data - this.data[e.target.name] = e.target.value; + this.data[name] = value; + + console.log(this.data); // validate against schema const validate = this.ajv.getSchema('schema'); diff --git a/src/views/elements.js b/src/views/elements.js index 98cd7d9e67eca4461b7f2cf728dd2bd0b9df6343..52c0624383402870cb3b6185cc8629239f0c552f 100644 --- a/src/views/elements.js +++ b/src/views/elements.js @@ -2,12 +2,14 @@ import m from 'mithril'; import { IconButton, TextField } from 'polythene-mithril'; export class textInput { - constructor({ attrs: { getErrors } }) { + constructor({ attrs: { getErrors, name, label } }) { // Link the error-getting function from the binding this.getErrors = () => []; + this.name = name; if (getErrors) { this.getErrors = getErrors; } + this.value = ''; } view({ attrs }) { @@ -15,12 +17,92 @@ export class textInput { const errors = this.getErrors(); const attributes = Object.assign({}, attrs); - attributes.valid = errors.length > 0; + attributes.valid = errors.length === 0; attributes.error = errors.join(', '); + attributes.onChange = ({ value }) => { + if (value !== this.value) { + this.value = value; + attrs.onChange(this.name, value); + } + }; return m(TextField, attributes); } } +export class datetimeInput { + constructor({ attrs: { getErrors, name, onChange } }) { + // Link the error-getting function from the binding + this.getErrors = () => []; + this.name = name; + if (getErrors) { + this.getErrors = getErrors; + } + this.value = ''; + this.date = null; + this.time = null; + this.onChangeCallback = onChange; + } + + onChange() { + if (this.date && this.time) { + const date = new Date(this.date); + const h_m = this.time.split(':'); + date.setHours(h_m[0]); + date.setMinutes(h_m[1]); + if (this.onChangeCallback) { + this.onChangeCallback(this.name, date.toISOString()); + } + } + } + + view({ attrs: { label } }) { + // set display-settings accoridng to error-state + const errors = this.getErrors(); + + const date = { + type: 'date', + style: { + width: '150px', + float: 'left', + }, + onChange: ({ value }) => { + if (value !== this.date) { + this.date = value; + this.onChange(); + } + }, + valid: errors.length === 0, + error: errors.join(', '), + }; + + const time = { + type: 'time', + style: { + width: '100px', + }, + onChange: ({ value }) => { + if (value !== this.time) { + this.time = value; + this.onChange(); + } + }, + valid: errors.length === 0, + }; + return m('div', [ + m(TextField, { + label, + disabled: true, + style: { + width: '200px', + float: 'left', + }, + }), + m(TextField, date), + m(TextField, time), + ]); + } +} + export class selectGroup { view(vnode) { return m('div.form-group', { class: vnode.attrs.classes }, [ @@ -53,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 = {