Newer
Older
import { RadioGroup, Switch, Dialog, Button, Tabs, Icon } from 'polythene-mithril';
import { FileInput } from 'amiv-web-ui-components';
import { TabsCSS, ButtonCSS } from 'polythene-css';
// eslint-disable-next-line import/extensions
import { apiUrl, ownUrl } from 'networkConfig';
import { loadingScreen } from '../layout';
ButtonCSS.addStyle('.nav-button', {
color_light_border: 'rgba(0, 0, 0, 0.09)',
color_light_disabled_background: 'rgba(0, 0, 0, 0.09)',
color_light_disabled_border: 'transparent',
});
TabsCSS.addStyle('.edit-tabs', {
color_light: '#555555',
// no hover effect
color_light_hover: '#555555',
color_light_selected: colors.amiv_blue,
color_light_tab_indicator: colors.amiv_blue,
});
export default class newEvent extends EditView {
constructor(vnode) {
// check whether the user has the right to create events or can only propose
this.rightSubmit = !m.route.get().startsWith('/proposeevent');
// proposition URL-link decoder
if (this.rightSubmit && m.route.param('proposition')) {
const data = JSON.parse(window.atob(m.route.param('proposition')));
console.log(data);
this.form.data = data;
}
if (!this.form.data.priority) this.form.data.priority = 1;
// read additional_fields to make it editable
if (this.form.data.additional_fields) {
const copy = JSON.parse(this.form.data.additional_fields);
this.form.data.add_fields_sbb = 'sbb_abo' in copy.properties;
this.form.data.add_fields_food = 'food' in copy.properties;
this.form.data.additional_fields = null;
let i = 0;
while (`text${i}` in copy.properties) {
this.form.data[`add_fields_text${i}`] = copy.properties[`text${i}`].title;
i += 1;
}
// TODO: find a better solution to keep track of the additional textfields
this.add_fields_text_index = i;
} else {
this.add_fields_text_index = 0;
// price can either not be set or set to null
// if it is 0 however, that would mean that there actually is a price that
// you can edit
this.hasprice = 'price' in this.form.data && this.form.data.price !== null;
this.hasregistration = 'spots' in this.form.data || 'time_registration_start' in this.form.data;
// Collect images seperate from everything else
const images = {};
['thumbnail', 'banner', 'infoscreen', 'poster'].forEach((key) => {
if (this.form.data[`new_${key}`]) {
images[`img_${key}`] = this.form.data[`new_${key}`];
delete this.form.data[`new_${key}`];
if (this.form.data[`img_${key}`] !== undefined) delete this.form.data[`img_${key}`];
});
// Merge Options for additional fields
const additionalFields = {
$schema: 'http://json-schema.org/draft-04/schema#',
additionalProperties: false,
title: 'Additional Fields',
type: 'object',
properties: {},
required: [],
};
if (this.form.data.add_fields_sbb) {
additionalFields.properties.sbb_abo = {
additionalFields.required.push('sbb_abo');
if (this.form.data.add_fields_food) {
additionalFields.properties.food = {
enum: ['Omnivor', 'Vegi', 'Vegan', 'Other'],
};
additionalFields.properties.food_special = {
title: 'Special Food Requirements',
additionalFields.required.push('food');
let i = 0;
while (`add_fields_text${i}` in this.form.data) {
const fieldName = `text${i}`;
additionalFields.properties[fieldName] = {
type: 'string',
minLength: 1,
title: this.form.data[`add_fields_text${i}`],
};
additionalFields.required.push(fieldName);
delete this.form.data[`add_fields_text${i}`];
i += 1;
}
if ('add_fields_sbb' in this.form.data) delete this.form.data.add_fields_sbb;
if ('add_fields_food' in this.form.data) delete this.form.data.add_fields_food;
// if the properties are empty, we null the whole field, otherwise we send a json string
// of the additional fields object
if (Object.keys(additionalFields.properties).length > 0) {
this.form.data.additional_fields = JSON.stringify(additionalFields);
this.form.data.additional_fields = null;
// if spots is not set, also remove 'allow_email_signup'
if (!('spots' in this.form.data) && 'allow_email_signup' in this.form.data
&& !this.form.data.allow_email_signup) {
delete this.form.data.allow_email_signup;
// Propose <=> Submit desicion due to rights
// Submition tool
if (Object.keys(images).length > 0) {
const imageForm = new FormData();
Object.keys(images).forEach(key => imageForm.append(key, images[key]));
imageForm.append('_id', this.form.data._id);
imageForm.append('_etag', this.form.data._etag);
// first upload the images as formData, then the rest as JSON
this.controller.handler.patch(imageForm).then(({ _etag }) => {
this.submit({ ...this.form.data, _etag });
// Propose tool
Dialog.show({
title: 'Congratulations!',
body: [
m(
'div',
'You sucessfuly setup an event.',
'Please send this link to the respectiv board member for validation.',
),
m('input', {
type: 'text',
style: { width: '335px' },
value: `${ownUrl}/newevent?${m.buildQueryString({
proposition: window.btoa(JSON.stringify(this.form.data)),
})}`,
id: 'textId',
}),
],
backdrop: true,
footerButtons: [
m(Button, {
label: 'Copy',
events: {
onclick: () => {
const copyText = document.getElementById('textId');
copyText.select();
document.execCommand('copy');
},
},
}),
],
});
if (!this.form.schema) return m(loadingScreen);
const titles = ['Event Description', 'When and Where?', 'Signups', 'Advertisement'];
if (this.rightSubmit) titles.push('Images');
const buttonRight = m(Button, {
label: m('div.pe-button__label', m(Icon, {
svg: { content: m.trust(icons.ArrowRight) },
style: { top: '-5px', float: 'right' },
}), 'next'),
disabled: this.currentpage === titles.length,
border: true,
className: 'nav-button',
events: { onclick: () => { this.currentpage = Math.min(this.currentpage + 1, 5); } },
const buttonLeft = m(Button, {
label: m('div.pe-button__label', m(Icon, {
svg: { content: m.trust(icons.ArrowLeft) },
style: { top: '-5px', float: 'left' },
}), 'previous'),
disabled: this.currentpage === 1,
border: true,
className: 'nav-button',
events: { onclick: () => { this.currentpage = Math.max(1, this.currentpage - 1); } },
const radioButtonSelectionMode = m(RadioGroup, {
name: 'Selection Mode',
buttons: [
{
value: 'fcfs',
label: 'First come, first serve',
},
{
value: 'manual',
label: 'Selection made by organizer',
},
],

Lionel Trebuchon
committed
onChange: (state) => {
this.selection_strategy = state.value;
this.form.data.selection_strategy = state.value;

Lionel Trebuchon
committed
},
value: this.selection_strategy,
const keysPages = [[
'title_en',
'catchphrase_en',
'description_en',
'title_de',
'catchphrase_de',
'description_de',
],
['time_start', 'time_end', 'location'],
['price', 'spots', 'time_register_start', 'time_register_end'],
['time_advertising_start', 'time_advertising_end'],
[],
];
const errorPages = keysPages.map(keysOfOnePage => keysOfOnePage.map((key) => {
if (this.form.errors && key in this.form.errors) return this.form.errors[key].length > 0;
return false;
}).includes(true));
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
const addFieldsText = [];
let i = 0;
while (`add_fields_text${i}` in this.form.data) {
addFieldsText.push(this.form._renderField(`add_fields_text${i}`, {
type: 'string',
label: `Label for Textfield ${i}`,
}));
const fieldIndex = i;
addFieldsText.push(m(Button, {
label: `Remove Textfield ${i}`,
className: 'red-row-button',
events: {
onclick: () => {
let index = fieldIndex;
while (`add_fields_text${index + 1}` in this.form.data) {
this.form.data[`add_fields_text${index}`] =
this.form.data[`add_fields_text${index + 1}`];
index += 1;
}
delete this.form.data[`add_fields_text${index}`];
this.add_fields_text_index = index;
},
},
}));
i += 1;
}
// checks currentPage and selects the fitting page
// navigation bar
// all pages are displayed, current is highlighted,
// validation errors are shown per page by red icon-background
m(Tabs, {
className: 'edit-tabs',
// it would be too easy if we could set the background color in the theme class
style: { backgroundColor: colors.orange },
onChange: ({ index }) => { this.currentpage = index + 1; },
centered: true,
selectedTabIndex: this.currentpage - 1,
}, [...titles.entries()].map((numAndTitle) => {
const buttonAttrs = { label: numAndTitle[1] };
if (errorPages[numAndTitle[0]]) {
// in case of an error, put an error icon before the tab label
buttonAttrs.label = m('div', m(Icon, {
svg: { content: m.trust(icons.error) },
style: { top: '-2px', 'margin-right': '4px' },
}), numAndTitle[1]);
}
return buttonAttrs;
m('div.maincontainer', { style: { height: 'calc(100vh - 180px)', 'overflow-y': 'auto' } }, [
// page 1: title & description
m('div', {
style: { display: (this.currentpage === 1) ? 'block' : 'none' },
}, [
...this.form.renderSchema(['title_en', 'catchphrase_en']),
this.form._renderField('description_en', {
type: 'string',
label: 'English Description',
multiLine: true,
rows: 5,
}),
...this.form.renderSchema(['title_de', 'catchphrase_de']),
this.form._renderField('description_de', {
type: 'string',
label: 'German Description',
multiLine: true,
rows: 5,
// page 2: when & where
m('div', {
style: { display: (this.currentpage === 2) ? 'block' : 'none' },
}, this.form.renderSchema(['time_start', 'time_end', 'location'])),
// page 3: registration
m('div', {
style: { display: (this.currentpage === 3) ? 'block' : 'none' },
}, [
m(Switch, {
label: 'people have to pay something to attend this event',
style: { 'margin-bottom': '5px' },
checked: this.hasprice,
onChange: ({ checked }) => {
this.hasprice = checked;
if (!checked) {
// if it originally had a price, set to null, otherwise delete
if (this.controller.data.price) this.form.data.price = null;
else delete this.form.data.price;
}
},
}),
this.hasprice && this.form._renderField('price', { label: 'Price', type: 'number' }),
m('br'),
m(Switch, {
label: 'people have to register to attend this event',
checked: this.hasregistration,
onChange: ({ checked }) => {
this.hasregistration = checked;
if (!checked) {
delete this.form.data.spots;
delete this.form.data.time_register_start;
delete this.form.data.time_register_end;
delete this.form.data.add_fields_sbb;
delete this.form.data.add_fields_food;
delete this.form.data.allow_email_signup;
delete this.form.data.selection_strategy;
}
},
}),
...this.hasregistration && this.form.renderSchema([
'spots', 'time_register_start', 'time_register_end']),
this.hasregistration && this.form._renderField('add_fields_food', {
type: 'boolean',
this.hasregistration && this.form._renderField('add_fields_sbb', {
type: 'boolean',
label: 'SBB Abonnement',
}),
m('br'),
...this.hasregistration && addFieldsText,
m('br'),
this.hasregistration && m(Button, {
label: 'Additional Textfield',
className: 'blue-button',
border: true,
events: {
onclick: () => {
this.form.data[`add_fields_text${this.add_fields_text_index}`] = '';
this.add_fields_text_index += 1;
},
},
m('br'),
...this.hasregistration && this.form.renderSchema(['allow_email_signup']),
this.hasregistration && radioButtonSelectionMode,
]),
// page 4: advertisement
m('div', {
style: { display: (this.currentpage === 4) ? 'block' : 'none' },
}, [
...this.form.renderSchema(['time_advertising_start', 'time_advertising_end']),
// TODO is deactivated now
/*
m.trust('Priority<br>'),
m(Slider, {
min: 1,
max: 10,
stepSize: 1,
// value: this.data.priority || 1,
// onChange: ({ value }) => { this.data.priority = value; },
}),
*/
...this.form.renderSchema(['show_website', 'show_announce', 'show_infoscreen']),
]),
// page 5: images
m('div', {
style: { display: (this.currentpage === 5) ? 'block' : 'none' },
}, [
['thumbnail', 'banner', 'poster', 'infoscreen'].map(key => [
this.form.data[`img_${key}`] ? m('img', {
src: `${apiUrl}${this.form.data[`img_${key}`].file}`,
style: { 'max-height': '50px', 'max-width': '100px' },
}) : m('div', `currently no ${key} image set`),
m(FileInput, this.form.bind({
name: `new_${key}`,
label: `New ${key} Image`,
accept: 'image/png, image/jpeg',
})),
]),
// bottom back & forth
m('div', {
style: {
display: 'flex',
'justify-content': 'space-between',
padding: '35px',
'padding-top': '20px',
},
}, [buttonLeft, buttonRight]),
], this.rightSubmit ? 'submit' : 'propose', false);