diff --git a/src/groups/groupModal.js b/src/groups/groupTool.js similarity index 68% rename from src/groups/groupModal.js rename to src/groups/groupTool.js index c63b205401cec57492558f3e570704bc1f60ab15..47fb9253ab06b7ef24f2be94e4bf29d05e47be16 100644 --- a/src/groups/groupModal.js +++ b/src/groups/groupTool.js @@ -2,14 +2,14 @@ import m from 'mithril'; import viewGroup from './viewGroup'; import newGroup from './newGroup'; -export default class EventModal { +export default class GroupView { constructor() { this.edit = false; } view() { if (this.edit) { - return m(newGroup); + return m(newGroup, { onfinish: () => { this.edit = false; m.redraw(); } }); } return m(viewGroup, { onEdit: () => { this.edit = true; } }); } diff --git a/src/groups/newGroup.js b/src/groups/newGroup.js index 8a959d5db381818d420cfff156c00ea179a449ba..5b9c01985d6df230b925af7c550cfbc037df080a 100644 --- a/src/groups/newGroup.js +++ b/src/groups/newGroup.js @@ -1,19 +1,33 @@ import m from 'mithril'; -import { RaisedButton } from 'polythene-mithril'; +import { RaisedButton, TextField } from 'polythene-mithril'; +import SelectList from '../views/selectList'; +import DatalistController from '../listcontroller'; import EditView from '../views/editView'; export default class NewGroup extends EditView { constructor(vnode) { - super(vnode, 'groups', {}); - this.callback = () => { m.route.set('/groups'); }; + super(vnode, 'groups', { moderator: 1 }); + this.userController = new DatalistController( + 'users', {}, + ['firstname', 'lastname', 'email', 'nethz'], + ); } view() { + if (!this.data) return ''; + const submitButton = m(RaisedButton, { disabled: !this.valid, label: 'Submit', - events: { onclick: () => { this.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('div.mywrapper', [ @@ -25,6 +39,22 @@ export default class NewGroup extends EditView { label: 'the group can be seen by all users and they can subscribe themselves', }, }), + m('div', { style: { display: 'flex' } }, [ + m(TextField, { label: 'Group Moderator: ', disabled: true, style: { width: '160px' } }), + m('div', { style: { 'flex-grow': 1 } }, m(SelectList, { + controller: this.userController, + selection: this.data.moderator, + listTileAttrs: user => Object.assign({}, { title: `${user.firstname} ${user.lastname}` }), + selectedText: user => `${user.firstname} ${user.lastname}`, + onSelect: (data) => { + if (data) { + this.data.moderator = data; + } else if (this.data.moderator) { + delete this.data.moderator; + } + }, + })), + ]), m('br'), submitButton, ]); diff --git a/src/groups/viewGroup.js b/src/groups/viewGroup.js index 0ed9a3242b29d1e43e686126a59d5a4485504bc1..6214b0f25d63be03bb0f7b9e9189d9716f70b75e 100644 --- a/src/groups/viewGroup.js +++ b/src/groups/viewGroup.js @@ -1,6 +1,6 @@ import m from 'mithril'; -import { Button, Card, Toolbar, ToolbarTitle, TextField, Icon } from 'polythene-mithril'; -import { icons } from '../views/elements'; +import { Button, RaisedButton, Card, Toolbar, ToolbarTitle, TextField, Icon } from 'polythene-mithril'; +import { icons, Property } from '../views/elements'; import ItemView from '../views/itemView'; import TableView from '../views/tableView'; import DatalistController from '../listcontroller'; @@ -165,7 +165,7 @@ class EmailTable { export default class viewGroup extends ItemView { constructor() { - super('groups'); + super('groups', { moderator: 1 }); } oninit() { @@ -181,9 +181,20 @@ export default class viewGroup extends ItemView { 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 }, + }), // this div is the title line - m('div', [ + m('div.maincontainer', [ m('h1', { style: { 'margin-top': '0px', 'margin-bottom': '0px' } }, this.data.name), + this.data.moderator ? m(Property, { + title: 'Moderator', + onclick: () => { m.route.set(`/users/${this.data.moderator._id}`); }, + }, `${this.data.moderator.firstname} ${this.data.moderator.lastname}`) : '', ]), m('div.viewcontainer', [ // now-column layout: This first column are the members diff --git a/src/index.js b/src/index.js index 7ff710ab076c7fc322422d5360f3346b712bba1f..38ef293872e4f759078bd07b1037ab3139e8b3a5 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ import m from 'mithril'; import { OauthRedirect } from './auth'; import GroupList from './groups/overview'; -import viewGroup from './groups/viewGroup'; +import GroupView from './groups/groupTool'; import { UserModal, UserTable, NewUser } from './users/userTool'; import { MembershipView } from './membershipTool'; import NewGroup from './groups/newGroup'; @@ -35,7 +35,7 @@ m.route(root, '/users', { '/draftevent': layoutWith(eventDraft), '/eventwithexport': layoutWith(eventWithExport), '/groups': layoutWith(GroupList), - '/groups/:id': layoutWith(viewGroup), + '/groups/:id': layoutWith(GroupView), '/newgroup': layoutWith(NewGroup), '/oauthcallback': OauthRedirect, // '/announce': layoutWith(AnnounceTool), diff --git a/src/views/editView.js b/src/views/editView.js index ec72d74f1e70ce6afc51616fcbf78f69d3f29a35..c1c6dcc46a6ecb61e73cc4a5446dbfc2c1512676 100644 --- a/src/views/editView.js +++ b/src/views/editView.js @@ -43,7 +43,14 @@ export default class EditView extends ItemView { this.data = {}; // callback when edit is finished - this.callback = vnode.attrs.onfinish; + if (vnode.attrs.onfinish) this.callback = vnode.attrs.onfinish; + else { + this.callback = (item) => { + console.log(item); + if (item) m.route.set(`/${resource}/${item._id}`); + else m.route.set(`/${resource}`); + }; + } } oninit() { @@ -60,13 +67,15 @@ export default class EditView extends ItemView { const objectSchema = schema.definitions[ objectNameForResource[this.resource]]; // console.log(objectSchema); - // filter out any field that is of type media and replace with type - // object + // filter out any field that is not understood by the validator tool Object.keys(objectSchema.properties).forEach((property) => { if (objectSchema.properties[property].type === 'media' || objectSchema.properties[property].type === 'json_schema_object') { objectSchema.properties[property].type = 'object'; } + if (objectSchema.properties[property].format === 'objectid') { + delete objectSchema.properties[property]; + } }); // delete objectSchema.properties['_id']; console.log(this.ajv.addSchema(objectSchema, 'schema')); diff --git a/src/views/elements.js b/src/views/elements.js index 1cc18d815fc61e4589e6847cf2bb69e3bbbd0387..df18ce35ba188121af5e2ea375a43edff07f0388 100644 --- a/src/views/elements.js +++ b/src/views/elements.js @@ -225,8 +225,8 @@ export class DropdownCard { // attrs is the title, children the text // therefore, you can call it with m(Property, title, text) export class Property { - view({ attrs: { title, style }, children }) { - return m('div', { style }, [ + view({ attrs: { title, ...restAttrs }, children }) { + return m('div', restAttrs, [ m('span', { style: { 'margin-top': '10px', diff --git a/src/views/selectList.js b/src/views/selectList.js index ed2f98df709637d55e3cc9263a386cf19a68eb8c..548ac710c3973f9edec0fae09beb2d5377ae0831 100644 --- a/src/views/selectList.js +++ b/src/views/selectList.js @@ -22,17 +22,16 @@ class SearchField { view({ state, attrs }) { // incoming value and focus added for result list example: const value = attrs.value !== undefined ? attrs.value : state.value(); - const onCancel = attrs.onCancel !== undefined ? attrs.onCancel : () => {}; - const ExitButton = { + const ExitButton = attrs.onCancel ? { view() { return m(Button, { label: 'Cancel', className: 'blue-button', - events: { onclick: onCancel }, + events: { onclick: attrs.onCancel }, }); }, - }; + } : 'div'; return m(Search, Object.assign( {}, @@ -50,7 +49,6 @@ class SearchField { }, buttons: { none: { - before: m(SearchIcon), after: m(ExitButton), }, focus: { @@ -74,11 +72,18 @@ class SearchField { } export default class SelectList { - constructor({ attrs: { listTileAttrs } }) { - this.selected = null; + constructor({ attrs: { listTileAttrs, onSelect = false } }) { this.showList = false; this.searchValue = ''; this.listTileAttrs = listTileAttrs; + this.onSelect = onSelect; + // initialize the Selection + this.selected = null; + } + + onupdate({ attrs: { selection = null } }) { + console.log(selection); + if (selection) this.selected = selection; } item() { @@ -88,7 +93,11 @@ export default class SelectList { hoverable: true, className: 'themed-list-tile', events: { - onclick: () => { this.selected = data; this.showList = false; }, + onclick: () => { + if (this.onSelect) { this.onSelect(data); } + this.selected = data; + this.showList = false; + }, }, }; // Overwrite default attrs @@ -100,8 +109,8 @@ export default class SelectList { view({ attrs: { controller, - onSubmit = () => {}, - onCancel = () => {}, + onSubmit = false, + onCancel = false, selectedText, }, }) { @@ -110,10 +119,15 @@ export default class SelectList { m(IconButton, { icon: { svg: m.trust(icons.clear) }, ink: false, - events: { onclick: () => { this.selected = null; } }, + events: { + onclick: () => { + if (this.onSelect) { this.onSelect(null); } + this.selected = null; + }, + }, }), m(ToolbarTitle, { text: selectedText(this.selected) }), - m(Button, { + onSubmit ? m(Button, { label: 'Submit', className: 'blue-button', events: { @@ -124,12 +138,16 @@ export default class SelectList { controller.refresh(); }, }, - }), + }) : '', ]) : m(SearchField, Object.assign({}, { style: { background: 'rgb(78, 242, 167)' }, onChange: ({ value, focus }) => { if (focus) { this.showList = true; + } else if (!focus) { + // don't close the list immidiately, as 'out of focus' could + // also mean that the user is clicking on a list item + setTimeout(() => { this.showList = false; m.redraw(); }, 50); } if (value !== this.searchValue) { // if we always update the search value, this would also happen @@ -139,16 +157,15 @@ export default class SelectList { // not due to focus change. this.searchValue = value; controller.setSearch(value); - debounce(() => { controller.refresh(); }, 500); + setTimeout(() => { controller.refresh(); }, 500); } }, onCancel, - defaultValue: '', })), (this.showList && !this.selected) ? m(List, { - style: { height: '400px' }, + style: { height: '400px', 'background-color': 'white' }, tiles: m(infinite, controller.infiniteScrollParams(this.item())), - }) : null, + }) : '', ]); } }