diff --git a/src/auth.js b/src/auth.js index 048051bb9d375333ed4d0cfe072fd0e9b3dd4578..e62afe0e1838851034890fbea4afa1af4d756843 100644 --- a/src/auth.js +++ b/src/auth.js @@ -86,9 +86,20 @@ export function getSession() { } export class ResourceHandler { - constructor(resource, searchKeys = false) { + /* Handler to get and manipulate resource items + * + * resource: String of the resource to accessm e,g, 'events' + * searchKeys: keys in the resource item on which to perform search, i.e. + * when search is set, any of these keys may match the search pattern + * E.g. for an event, this may be ['title_de', 'title_en', 'location'] + * updateCallback: method with argument of newData + * is called additionally to the promise resolve at any time that data of an item is + * changed + */ + constructor(resource, searchKeys = false, updateCallback = () => {}) { this.resource = resource; this.searchKeys = searchKeys || config[resource].searchKeys; + this.updateCallback = updateCallback; this.noPatchKeys = [ '_etag', '_id', '_created', '_links', '_updated', ...(config[resource].notPatchableKeys || [])]; @@ -188,6 +199,7 @@ export class ResourceHandler { resetSession(); reject(); } else { + this.updateCallback(response.data); resolve(response.data); } }).catch((e) => { @@ -225,6 +237,7 @@ export class ResourceHandler { resetSession(); reject(); } else { + this.updateCallback(response.data); resolve(response.data); } }).catch((e) => { diff --git a/src/events/newEvent.js b/src/events/newEvent.js index 859a29b6fbe85972b9df87fa9ed0122aad277a79..55f902fa7bf904fcde68a64358163eb15bb8514d 100644 --- a/src/events/newEvent.js +++ b/src/events/newEvent.js @@ -2,7 +2,7 @@ import m from 'mithril'; import { RaisedButton, RadioGroup, Slider } from 'polythene-mithril'; import { styler } from 'polythene-core-css'; import { apiUrl } from 'networkConfig'; -import EditView from '../views/editView'; +import {Â EditView } from '../views/editView'; import { fileInput } from '../views/elements'; const style = [ diff --git a/src/groups/groupTool.js b/src/groups/groupTool.js index 47fb9253ab06b7ef24f2be94e4bf29d05e47be16..3d7364272696979f306e632c71cd52aa2fa41177 100644 --- a/src/groups/groupTool.js +++ b/src/groups/groupTool.js @@ -1,16 +1,18 @@ import m from 'mithril'; import viewGroup from './viewGroup'; import newGroup from './newGroup'; +import ItemTool from '../views/itemTool'; -export default class GroupView { +export default class GroupTool extends ItemTool { constructor() { - this.edit = false; + super('groups', { moderator: 1 }); } - view() { - if (this.edit) { - return m(newGroup, { onfinish: () => { this.edit = false; m.redraw(); } }); - } - return m(viewGroup, { onEdit: () => { this.edit = true; } }); + detailView() { + return m(viewGroup, { handler: this.handler, data: this.data }); + } + + editView() { + return m(newGroup, { onCancel: () => { this.modus = 'view'; } }); } } diff --git a/src/groups/newGroup.js b/src/groups/newGroup.js index 5b9c01985d6df230b925af7c550cfbc037df080a..7fab50512fd120bb3ae3d898c584fbbdbcc61138 100644 --- a/src/groups/newGroup.js +++ b/src/groups/newGroup.js @@ -1,8 +1,8 @@ import m from 'mithril'; -import { RaisedButton, TextField } from 'polythene-mithril'; +import { TextField } from 'polythene-mithril'; import SelectList from '../views/selectList'; import DatalistController from '../listcontroller'; -import EditView from '../views/editView'; +import { EditView, EditLayout } from '../views/editView'; export default class NewGroup extends EditView { @@ -14,23 +14,18 @@ export default class NewGroup extends EditView { ); } - view() { + view({ attrs: { onCancel = () => {} }}) { if (!this.data) return ''; - - const submitButton = m(RaisedButton, { - disabled: !this.valid, - label: 'Submit', - events: { - onclick: () => { - // excahgne moderator object with string of id - const { moderator } = this.data; - if (moderator) { this.data.moderator = `${moderator._id}`; } - this.submit(); - }, + return m(EditLayout, { + title: 'Edit Group', + onSubmit: () => { + // excahgne moderator object with string of id + const { moderator } = this.data; + if (moderator) { this.data.moderator = `${moderator._id}`; } + this.submit(); }, - }); - - return m('div.mywrapper', [ + onCancel, + }, m('div.mywrapper', [ m('h3', 'Add a New Group'), ...this.renderPage({ name: { type: 'text', label: 'Group Name' }, @@ -55,8 +50,6 @@ export default class NewGroup extends EditView { }, })), ]), - m('br'), - submitButton, - ]); + ])); } } diff --git a/src/groups/viewGroup.js b/src/groups/viewGroup.js index 6214b0f25d63be03bb0f7b9e9189d9716f70b75e..cb9cd74527d55c781d5d9da79b5b1646a7c1cbd6 100644 --- a/src/groups/viewGroup.js +++ b/src/groups/viewGroup.js @@ -163,68 +163,46 @@ class EmailTable { } } -export default class viewGroup extends ItemView { - constructor() { - super('groups', { moderator: 1 }); - } - - oninit() { - this.handler.getItem(this.id, this.embedded).then((item) => { - this.data = item; - m.redraw(); - }); - } - - view({ attrs: { onEdit } }) { - if (!this.data) return ''; - - return m('div.maincontainer', { - style: { height: '100%', 'overflow-y': 'scroll' }, - }, [ - // Button to edit the Group - m(RaisedButton, { - element: 'div', - label: 'Edit Group', - border: true, - events: { onclick: onEdit }, - }), +export default class viewGroup { + view({ attrs: { handler, data } }) { + return m('div.maincontainer', [ // this div is the title line m('div.maincontainer', [ - m('h1', { style: { 'margin-top': '0px', 'margin-bottom': '0px' } }, this.data.name), - this.data.moderator ? m(Property, { + m('h1', { style: { 'margin-top': '0px', 'margin-bottom': '0px' } }, data.name), + data.moderator ? m(Property, { title: 'Moderator', - onclick: () => { m.route.set(`/users/${this.data.moderator._id}`); }, - }, `${this.data.moderator.firstname} ${this.data.moderator.lastname}`) : '', + onclick: () => { m.route.set(`/users/${data.moderator._id}`); }, + }, `${data.moderator.firstname} ${data.moderator.lastname}`) : '', ]), m('div.viewcontainer', [ // now-column layout: This first column are the members m('div.viewcontainercolumn', [ m('h4', 'Members'), - m(MembersTable, { group: this.id }), + m(MembersTable, { group: data._id }), ]), // the second column contains receive_from and forward_to emails m('div.viewcontainercolumn', [ m(EmailTable, { - list: this.data.receive_from || [], + list: data.receive_from || [], onSubmit: (newItem) => { - const oldList = this.data.receive_from || []; - this.handler.patch({ - _id: this.data._id, - _etag: this.data._etag, + const oldList = data.receive_from || []; + handler.patch({ + _id: data._id, + _etag: data._etag, receive_from: [...oldList, newItem], - }).then((newData) => { this.data = newData; }); + }); }, onRemove: (item) => { - const oldList = this.data.receive_from; + const oldList = data.receive_from; // remove the first occurence of the given item-string const index = oldList.indexOf(item); if (index !== -1) { oldList.splice(index, 1); - this.handler.patch({ - _id: this.data._id, - _etag: this.data._etag, + handler.patch({ + _id: data._id, + _etag: data._etag, receive_from: oldList, - }).then((newData) => { this.data = newData; }); + }).then(() => m.redraw()); } }, }), diff --git a/src/jobs/newJob.js b/src/jobs/newJob.js index 37c95210aaca0a3a95f5f6f4368f82b12cf400d8..fad8748ac9e9809c3ca5b7e74427d74e35a26a5b 100644 --- a/src/jobs/newJob.js +++ b/src/jobs/newJob.js @@ -1,6 +1,6 @@ import m from 'mithril'; import { RaisedButton } from 'polythene-mithril'; -import EditView from '../views/editView'; +import { EditView } from '../views/editView'; export default class newJob extends EditView { diff --git a/src/layout.js b/src/layout.js index 77f34a0a3962e0cac4927cbafe6f0e2d036fb9b8..40c9b7f0f97c4803875eae8ac178e2a4ed08c91d 100644 --- a/src/layout.js +++ b/src/layout.js @@ -1,6 +1,6 @@ import m from 'mithril'; import '@material/drawer'; -import { List, ListTile, Icon, Toolbar, ToolbarTitle } from 'polythene-mithril'; +import { List, ListTile, Icon, Toolbar, ToolbarTitle, Dialog } from 'polythene-mithril'; import { styler } from 'polythene-core-css'; import { icons } from './views/elements'; import { resetSession } from './auth'; @@ -106,6 +106,8 @@ export default class Layout { ), m('div.wrapper-content', children), ]), + // dialog element will show when Dialog.show() is called, this is only a placeholder + m(Dialog), ]); } } diff --git a/src/users/editUser.js b/src/users/editUser.js index 8efb2210326ed9655e0b92dceeeaec7b0ba42178..46e1bc1588709b741f613b10feaef56b4de03308 100644 --- a/src/users/editUser.js +++ b/src/users/editUser.js @@ -1,6 +1,6 @@ import m from 'mithril'; import { RaisedButton, RadioGroup } from 'polythene-mithril'; -import EditView from '../views/editView'; +import { EditView } from '../views/editView'; export default class UserEdit extends EditView { diff --git a/src/views/editView.js b/src/views/editView.js index c1c6dcc46a6ecb61e73cc4a5446dbfc2c1512676..2021f5d8caa8c9bdc67bcca08aff89b9ffcb0ae6 100644 --- a/src/views/editView.js +++ b/src/views/editView.js @@ -1,8 +1,8 @@ import Ajv from 'ajv'; -import { Checkbox } from 'polythene-mithril'; +import { Checkbox, IconButton, Toolbar, ToolbarTitle, Button } from 'polythene-mithril'; import { apiUrl } from 'networkConfig'; import ItemView from './itemView'; -import { textInput, datetimeInput, numInput } from './elements'; +import { textInput, datetimeInput, numInput, icons } from './elements'; const m = require('mithril'); @@ -15,7 +15,23 @@ const objectNameForResource = { events: 'Event', }; -export default class EditView extends ItemView { +export class EditLayout { + view({ attrs: { title, onSubmit = () => {}, onCancel = () => {} }, children }) { + return m('div', { style: { 'background-color': 'white' } }, [ + m(Toolbar, [ + m(IconButton, { + icon: { svg: { content: m.trust(icons.clear) } }, + events: { onclick: onCancel }, + }), + m(ToolbarTitle, title), + m(Button, { label: 'submit', events: { onclick: onSubmit } }), + ]), + children, + ]); + } +} + +export class EditView extends ItemView { /* Extension of ItemView to edit a data item * * Requires: diff --git a/src/views/itemTool.js b/src/views/itemTool.js new file mode 100644 index 0000000000000000000000000000000000000000..7b04c6d5fe28975f12feeca86ad4e909296ba381 --- /dev/null +++ b/src/views/itemTool.js @@ -0,0 +1,105 @@ +import m from 'mithril'; +import { + RaisedButton, + Dialog, + Button, + Toolbar, + IconButton, + ToolbarTitle, +} from 'polythene-mithril'; +import { ResourceHandler } from '../auth'; +import { icons } from './elements'; + + +export default class ItemTool { + constructor(resource, embedded) { + this.id = m.route.param('id'); + if (this.id) { + this.modus = 'view'; + } else { + this.modus = 'new'; + } + this.handler = new ResourceHandler(resource, false, (newData) => { this.data = newData; }); + this.embedded = embedded || {}; + // by default, we go to the resource list after deletion of an item + // this may be overwritten with other behaviour + this.onDelete = () => { console.log('on delete'); m.route.set(`/${resource}`); }; + } + + oninit() { + if (this.id) { + this.handler.getItem(this.id, this.embedded).then((item) => { + this.data = item; + m.redraw(); + }); + } + } + + creationView() { + console.log(`creation for ${this.resource} not found`); + return null; + } + + /* + * Should display this.data + */ + detailView() { + console.log(`detail view for ${this.resource} not found`); + return null; + } + + /* + * To edit an existing item. Has to implement this.onSubmit, which is called + */ + editView() { + console.log(`edit view for ${this.resource} not found`); + return null; + } + + delete() { + Dialog.show({ + body: 'Are you sure you want to delete this item?', + backdrop: true, + footerButtons: [ + m(Button, { + label: 'Cancel', + events: { onclick: () => Dialog.hide() }, + }), + m(Button, { + label: 'Delete', + events: { + onclick: () => { + Dialog.hide(); + this.handler.delete(this.data).then(this.onDelete); + }, + }, + })], + }); + } + + view() { + if (this.modus === 'new') { + return this.creationView(); + } else if (this.modus === 'edit') { + return this.editView(); + } + if (!this.data) return ''; + return m('div', { style: { height: '100%', 'overflow-y': 'scroll' } }, [ + m('div', { style: { display: 'flex' } }, [ + m(RaisedButton, { + element: 'div', + label: 'Edit', + border: true, + events: { onclick: () => { this.modus = 'edit'; } }, + }), + m(RaisedButton, { + className: 'red-row-button', + label: 'Delete', + border: true, + events: { onclick: () => this.delete() }, + }), + ]), + this.detailView(), + ]); + } +} diff --git a/src/views/itemView.js b/src/views/itemView.js index bed55516635cb9327af137a005e1a894f9aa1d9b..6fda6b46716f20aa2903ff27b8b437c1083411bc 100644 --- a/src/views/itemView.js +++ b/src/views/itemView.js @@ -1,9 +1,9 @@ +import m from 'mithril'; import { ResourceHandler } from '../auth'; - -const m = require('mithril'); +import { Dialog, Button } from 'polythene-mithril'; export default class ItemView { - /* Basic class show a data item + /* Basic class to show a data item * * Required: * - call constructor with 'resource'