diff --git a/index.html b/index.html index 2346026a7488d41fdb3a0fb9f5684826e6d0ef7f..8e349c093abbd60149bff293f0dda356162b03db 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,7 @@ <!--link href="//cdn.rawgit.com/noelboss/featherlight/1.7.10/release/featherlight.min.css" type="text/css" rel="stylesheet" /--> - <link href="lib/cust/main.css" rel="stylesheet"> + <!--link href="lib/cust/main.css" rel="stylesheet"--> </head> <body> diff --git a/package.json b/package.json index e415967d0ca6ae4ee3a41f400c584f12f1655390..632e715d13c07f4c3b6a9b0787e4e670a546e5f0 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "author": "Hermann Blum et al", "dependencies": { + "@material/drawer": "^0.30.0", "ajv": "^5.5.0", "axios": "^0.17.1", "mithril": "^1.1.6", diff --git a/src/auth.js b/src/auth.js index 67c2e8de33d136ac65fa4a402bff495b8c1a523f..8222c1c845ecfd9a8270220e2e0f8758847a029f 100644 --- a/src/auth.js +++ b/src/auth.js @@ -186,7 +186,9 @@ export class ResourceHandler { return new Promise((resolve, reject) => { getSession().then((api) => { api.post(this.resource, item).then((response) => { - if (response.status >= 400) { + if (response.status === 422) { + reject(response.data); + } else if (response.status >= 400) { resetSession(); reject(); } else { @@ -212,7 +214,9 @@ export class ResourceHandler { headers: { 'If-Match': item._etag }, data: submitData, }).then((response) => { - if (response.status >= 400) { + if (response.status === 422) { + reject(response.data); + } else if (response.status >= 400) { resetSession(); reject(); } else { diff --git a/src/index.js b/src/index.js index 94446b62b68d7acc36f3e914b06e51a437522ea5..61412e9857a85caba3be3c83e391cc2eebf08030 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,7 @@ import EventTable from './events/table'; import newEvent from './events/newEvent'; import viewEvent from './events/viewEvent'; import eventDraft from './events/eventDraft'; -import Sidebar from './sidebar'; +import Layout from './layout'; // import AnnounceTool from './announceTool'; import './style'; @@ -16,15 +16,6 @@ document.body.appendChild(main); const root = main; -class Layout { - view(vnode) { - return m('div.wrapper-main.smooth', [ - m(Sidebar), - m('div.wrapper-content', vnode.children), - ]); - } -} - function layoutWith(view) { return { view() { diff --git a/src/layout.js b/src/layout.js new file mode 100644 index 0000000000000000000000000000000000000000..eaa0fb04d5dfcc5ccb3c45e4bfa861b095cf3275 --- /dev/null +++ b/src/layout.js @@ -0,0 +1,108 @@ +import m from 'mithril'; +import '@material/drawer'; +import { List, ListTile, Icon, Toolbar, ToolbarTitle } from 'polythene-mithril'; +import { styler } from 'polythene-core-css'; +import { icons } from './views/elements'; + +const layoutStyle = [ + { + body: { + padding: 0, + margin: 0, + }, + '.wrapper-main': { + height: '100%', + width: '100%', + display: 'grid', + 'grid-template-columns': '200px auto', + }, + '.wrapper-sidebar': { + 'grid-column': 1, + height: '100%', + 'overflow-y': 'auto', + position: 'fixed', + background: '#cccccc', + color: 'white', + }, + '.wrapper-content': { + height: '100vh', + 'grid-column': 2, + background: '#eee', + overflow: 'hidden', + }, + }, +]; +styler.add('layout', layoutStyle); + +class Menupoint { + view({ attrs: { title, href, icon = null } }) { + return m(ListTile, { + url: { + href, + oncreate: m.route.link, + }, + front: icon ? m(Icon, { + avatar: true, + svg: m.trust(icon), + }) : '', + title, + }); + } +} + +export default class Layout { + view({ children }) { + return m('div', [ + m(Toolbar, { + style: { + backgroundColor: '#1f2d54', + color: '#fff', + height: '72px', + }, + }, [ + m(ToolbarTitle, { text: 'AMIV Admintools' }), + ]), + m('div.wrapper-main.smooth', [ + m( + 'nav.mdc-drawer.mdc-drawer--permanent.mdc-typography.wrapper-sidebar', + { style: { width: '200px' } }, + m(List, { + className: 'drawer-menu', + header: { + title: 'Menu', + }, + tiles: [ + m(Menupoint, { + href: '/users', + icon: icons.iconUsersSVG, + title: 'Users', + }), + m(Menupoint, { + href: '/events', + icon: icons.iconEventSVG, + title: 'Events', + }), + m(Menupoint, { + href: '/groups', + title: 'Groups', + }), + m(Menupoint, { + href: '/announce', + title: 'Announce', + }), + ], + }), + ), + m('div.wrapper-content', children), + ]), + + /*return m('div.wrapper-sidebar.smooth', m('div.container-fluid', [ + m('a[href=/]', { oncreate: m.route.link }, [ + m('img.sidebar-logo[src="res/logo/main.svg"]'), + ]), + m('ul.nav.nav-pills.nav-stacked.nav-sidebar', [ + ]), + ]));*/ + ]); + } +} diff --git a/src/sidebar.js b/src/sidebar.js deleted file mode 100644 index e113e4e772f77f46313134f997d7c52be23e268c..0000000000000000000000000000000000000000 --- a/src/sidebar.js +++ /dev/null @@ -1,26 +0,0 @@ -const m = require('mithril'); - -class Button { - view(vnode) { - return m('li', m('a', { href: vnode.attrs.href, oncreate: m.route.link }, [ - m('span.glyphicon', { class: `glyphicon-${vnode.attrs.glyph}` }), - m.trust(` ${vnode.attrs.title}`), - ])); - } -} - -export default class Sidebar { - view() { - return m('div.wrapper-sidebar.smooth', m('div.container-fluid', [ - m('a[href=/]', { oncreate: m.route.link }, [ - m('img.sidebar-logo[src="res/logo/main.svg"]'), - ]), - m('ul.nav.nav-pills.nav-stacked.nav-sidebar', [ - m(Button, { href: '/users', glyph: 'list-alt', title: 'Users' }), - m(Button, { href: '/events', glyph: 'calendar', title: 'Events' }), - m(Button, { href: '/groups', glyph: 'blackboard', title: 'Groups' }), - m(Button, { href: '/announce', glyph: 'bullhorn', title: 'Announce' }), - ]), - ])); - } -} diff --git a/src/views/editView.js b/src/views/editView.js index 8f1de7f5d2d2f930c8b826ca2e26fa897ba00c05..91db119c24d2b021f4d0a3111e0625cd4e43b6d4 100644 --- a/src/views/editView.js +++ b/src/views/editView.js @@ -46,10 +46,9 @@ export default class EditView extends ItemView { oninit() { if (this.id) { // load data for item - getSession().then((apiSession) => { - this.loadItemData(apiSession); - }).catch(() => { - m.route.set('/login'); + this.handler.getItem(this.id, this.embedded).then((item) => { + this.data = item; + m.redraw(); }); } // load schema @@ -99,48 +98,33 @@ export default class EditView extends ItemView { return boundFormelement; } - submit(method, fields) { + submit(method) { return () => { if (this.changed) { - getSession().then((apiSession) => { - // build request - const request = { method }; - if (method === 'POST' || method === 'PATCH') { - // fields like `_id` are not post/patchable - // We therefore only send patchable fields - const submitData = {}; - fields.forEach((key) => { - submitData[key] = this.data[key]; + let request; + if (method === 'POST') { + request = this.handler.post(this.data); + } else if (method === 'PATCH') { + request = this.handler.patch(this.data); + } + request.then((response) => { + this.callback(response); + }).catch((error) => { + console.log(error); + // Process the API error + const { response } = error; + if (response.status === 422) { + // there are problems with some fields, display them + Object.keys(response.data._issues).forEach((field) => { + this.errors[field] = [response.data._issues[field]]; }); - request.data = submitData; - } - - // if request is PATCH or DELETE, add If-Match header and set url - if (method === 'PATCH' || method === 'DELETE') { - request.headers = { 'If-Match': this.data._etag }; - request.url = `${this.resource}/${this.id}`; + m.redraw(); + } else if (response.status === 403) { + // Unauthorized + m.route.set('/login'); } else { - request.url = this.resource; + console.log(error); } - - apiSession(request).then((response) => { - this.callback(response); - }).catch((error) => { - // Process the API error - const { response } = error; - if (response.status === 422) { - // there are problems with some fields, display them - Object.keys(response.data._issues).forEach((field) => { - this.errors[field] = [response.data._issues[field]]; - }); - m.redraw(); - } else if (response.status === 403) { - // Unauthorized - m.route.set('/login'); - } else { - console.log(error); - } - }); }); } else { this.callback(); diff --git a/src/views/elements.js b/src/views/elements.js index 6d37c2fdde33ba0db75d7f296c29e457f4751773..2981ae4ab65960bb7ad8392ad186d146e98465f9 100644 --- a/src/views/elements.js +++ b/src/views/elements.js @@ -58,7 +58,9 @@ export const icons = { iconBackSVG: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>', iconClearSVG: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>', ArrowRight: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/><path d="M0-.25h24v24H0z" fill="none"/></svg>', - ArrowDown: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z"/><path d="M0-.75h24v24H0z" fill="none"/></svg>' + ArrowDown: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z"/><path d="M0-.75h24v24H0z" fill="none"/></svg>', + 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>', }; export const BackButton = {