diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2ca11654e067c21016e92a6c20fb140739e7ecdc..115e98509b03a255e2bdf485dda387361bcb65c7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,16 @@ stages: + - test - build - deploy +eslint: + stage: test + image: node:latest + before_script: + - npm install + script: + - npm run lint + build_master_dev: stage: build image: docker:latest diff --git a/package.json b/package.json index cec5bc8ef281702b9580a78c36c3f0ad27d4ff2b..38f9e51b114d6a2cf4a64ee3f7dfddac3152da92 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "start": "webpack-dev-server --hot --inline", "build": "webpack -p --config webpack.config.prod.js", "build-dev": "webpack -p --config webpack.config.dev.js", - "lint": "eslint src/**" + "lint": "eslint src/**.js" }, "repository": { "type": "git", diff --git a/src/announceTool.js b/src/announceTool.js deleted file mode 100644 index 736287a6042f20fb93eb217793863def22117443..0000000000000000000000000000000000000000 --- a/src/announceTool.js +++ /dev/null @@ -1,29 +0,0 @@ -import tool from 'announcetool'; - -const m = require('mithril'); - -export default class AnnounceTool { - oncreate() { - if (tool.wasRenderedOnce()) { - // jQuery catches the first document.ready, but afterwards we have to - // trigger a render - tool.render(); - } - } - - view() { - return m('div', [ - m('div#tableset', [ - m('p#events'), - m('div#buttonrow', [ - m('button#preview.btn.btn-default', 'Preview'), - m('button#reset.btn.btn-default', 'Reset'), - m('button#send.btn.btn-default', 'Send'), - ]), - ]), - m('br'), - m('hr'), - m('textarea#target'), - ]); - } -} diff --git a/src/auth.js b/src/auth.js index 8fff89bc8a8e30d642efeb443bc6a4518286532a..5d097d4f29a617ba07fe713f9a209f883cbe2dd7 100644 --- a/src/auth.js +++ b/src/auth.js @@ -2,6 +2,7 @@ import m from 'mithril'; import axios from 'axios'; import ClientOAuth2 from 'client-oauth2'; import { Snackbar } from 'polythene-mithril'; +// eslint-disable-next-line import/extensions import { apiUrl, ownUrl, oAuthID } from 'networkConfig'; import * as localStorage from './localStorage'; import config from './resourceConfig.json'; @@ -185,6 +186,9 @@ export class ResourceHandler { Snackbar.show({ title: 'Network error, try again.', style: { color: 'red' } }); } + // in future, we may communicate based on the data available + // therefore, require data already here + // eslint-disable-next-line no-unused-vars error422(data) { Snackbar.show({ title: 'Errors in object, please fix.' }); } @@ -331,11 +335,11 @@ export class ResourceHandler { export class OauthRedirect { view() { - oauth.token.getToken(m.route.get()).then((response) => { + oauth.token.getToken(m.route.get()).then((auth) => { APISession.authenticated = true; - APISession.token = response.accessToken; - localStorage.set('token', response.accessToken); - amivapi.get(`sessions/${response.accessToken}`, { + APISession.token = auth.accessToken; + localStorage.set('token', auth.accessToken); + amivapi.get(`sessions/${auth.accessToken}`, { headers: { 'Content-Type': 'application/json', Authorization: APISession.token }, }).then((response) => { console.log(response); diff --git a/src/eventTool.js b/src/eventTool.js deleted file mode 100644 index 92d9af899a075f1dc27822e2377ff4380d5b7028..0000000000000000000000000000000000000000 --- a/src/eventTool.js +++ /dev/null @@ -1,166 +0,0 @@ -import ItemView from './views/itemView'; -import EditView from './views/editView'; -import { inputGroup, selectGroup, submitButton } from './views/elements'; -import TableView from './views/tableView'; -import { events as config } from './config.json'; - -const m = require('mithril'); - -export class EventView extends ItemView { - constructor() { - super('events'); - this.memberships = []; - } - - view() { - // do not render anything if there is no data yet - if (!this.data) return m.trust(''); - - let comissionBadge = m('span.label.label-important', 'Who is resp of this event?'); - if (this.data.membership === 'kultur') { - comissionBadge = m('span.label.label-success', 'Kulturi event'); - } else if (this.data.membership === 'eestec') { - comissionBadge = m('span.label.label-important', 'EESTEC event'); - } else if (this.data.membership === 'limes') { - comissionBadge = m('span.label.label-warning', 'LIMES event'); - } - - // TODO Question Lio171201:are we missing a "responsible" key? - const detailKeys = [ - 'title_de', - 'rfid', - 'location', 'time_start', 'time_end', - 'show_website', 'catchphrase', - 'time_register_start', 'price', 'allow_email_signup']; - - return m('div', [ - m('h1', `${this.data.title_de}`), - comissionBadge, - m('table', detailKeys.map(key => m('tr', [ - m('td.detail-descriptor', config.keyDescriptors[key]), - m('td', this.data[key] ? this.data[key] : ''), - ]))), - m('h2', 'Location'), m('br'), - m(TableView, { - resource: 'events', - keys: ['event.location'], - query: { - where: { user: this.id }, - embedded: { group: 1 }, - }, - }), - m('h2', 'Signups'), m('br'), - m(TableView, { - resource: 'events', - keys: ['event.title_de'], - query: { - where: { user: this.id }, - embedded: { event: 1 }, - }, - }), - ]); - } -} - -class EventEdit extends EditView { - constructor(vnode) { - super(vnode, 'events'); - } - - getForm() { - return m('form', [ - m('div.row', [ - m(inputGroup, this.bind({ title: 'Deutscher Titel', name: 'title_de' })), - m(inputGroup, this.bind({ title: 'English Title', name: 'title_en' })), - m(inputGroup, this.bind({ title: 'Location', name: 'location' })), - // m(inputGroup, this.bind({ title: 'Date-start', name: 'datetimepicker1' })), - // $('#datetimepicker1').datetimepicker(); - m(selectGroup, this.bind({ - classes: 'col-xs-6', - title: 'May non-AMIV members register?', - name: 'allow_email_signup', - options: [true, false], - })), - m(selectGroup, this.bind({ - classes: 'col-xs-6', - title: 'Show on the website?', - name: 'show_website', - options: [true, false], - })), - m(selectGroup, this.bind({ - classes: 'col-xs-6', - title: 'Piority from 1 to 10?', - name: 'priority', - // could be done with array.apply: - options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - })), - ]), - m('span', JSON.stringify(this.data)), - m('span', JSON.stringify(this.errors)), - ]); - } - - view() { - // do not render anything if there is no data yet - if (!this.data) return m.trust(''); - - return m('form', [ - this.getForm(), - m(submitButton, { - active: this.valid, - args: { - onclick: this.submit('PATCH', config.patchableKeys), - class: 'btn-warning', - }, - text: 'Update', - }), - ]); - } -} - -export class NewEvent extends EventEdit { - constructor(vnode) { - super(vnode); - this.data = { - title_de: 'Unvollstaendiges Event', - priority: 7, - show_website: false, - }; - this.valid = false; - - // if the creation is finished, UI should switch to new Event - this.callback = (response) => { m.route.set(`/events/${response.data._id}`); }; - } - - view() { - return m('form', [ - this.getForm(), - m(submitButton, { - active: this.valid, - args: { - onclick: this.submit('POST', config.patchableKeys), - class: 'btn-warning', - }, - text: 'Create', - }), - ]); - } -} - -export class EventModal { - constructor() { - this.edit = false; - } - - view() { - if (this.edit) { - return m(EventEdit, { onfinish: () => { this.edit = false; m.redraw(); } }); - } - // else - return m('div', [ - m('div.btn.btn-default', { onclick: () => { this.edit = true; } }, 'Edit'), - m('br'), - m(EventView), - ]); - } -} diff --git a/src/groups/list.js b/src/groups/list.js index 2683122d04233eec891d729f83147de6eb5d403b..3641a860d09cf77d15a694d8fb510ead6dd9c4ce 100644 --- a/src/groups/list.js +++ b/src/groups/list.js @@ -3,8 +3,6 @@ import { Card } from 'polythene-mithril'; import { DatalistController } from 'amiv-web-ui-components'; import { loadingScreen } from '../layout'; import { ResourceHandler, getCurrentUser } from '../auth'; -import { colors } from '../style'; - class GroupListItem { view({ attrs: { name, _id, color = '#ffffff' } }) { @@ -36,7 +34,7 @@ export default class GroupList { this.ctrl.getFullList().then((moderatedList) => { this.moderatedGroups = moderatedList; m.redraw(); - }) + }); }); } @@ -54,13 +52,13 @@ export default class GroupList { m('div', { style: { 'font-size': '20px', - 'margin': '10px 5px' + margin: '10px 5px', }, }, 'moderated by you'), m('div', { - style: { display: 'flex', 'flex-wrap': 'wrap'Â } + style: { display: 'flex', 'flex-wrap': 'wrap' }, }, this.moderatedGroups.map(item => - m(GroupListItem, { ...item } ))), + m(GroupListItem, { ...item }))), ]), m('div.maincontainer', { style: { display: 'flex', 'flex-wrap': 'wrap', 'margin-top': '5px' }, @@ -68,18 +66,18 @@ export default class GroupList { this.moderatedGroups.length > 0 && m('div', { style: { 'font-size': '20px', - 'margin': '10px 5px' + margin: '10px 5px', }, }, 'all groups'), m('div', { - style: { display: 'flex', 'flex-wrap': 'wrap'Â } - }, - this.groups.map(item => m(GroupListItem, item)), - m('div', { - style: { 'max-width': '500px', margin: '5px' }, - onclick: () => { m.route.set('/newgroup'); }, - }, m(Card, { content: [{ primary: { title: '+ add' } }] })), - ), + style: { display: 'flex', 'flex-wrap': 'wrap' }, + }, [ + this.groups.map(item => m(GroupListItem, item)), + m('div', { + style: { 'max-width': '500px', margin: '5px' }, + onclick: () => { m.route.set('/newgroup'); }, + }, m(Card, { content: [{ primary: { title: '+ add' } }] })), + ]), ]), ]); } diff --git a/src/index.js b/src/index.js index 4162b8392f5b337cbcec217e798a70801c5bd4e0..814f273b5f595a7c81a37995ca295a3a4966e626 100644 --- a/src/index.js +++ b/src/index.js @@ -3,11 +3,13 @@ import { OauthRedirect } from './auth'; import GroupList from './groups/list'; import GroupItem from './groups/item'; import { UserItem, UserTable } from './users/userTool'; -import { MembershipView } from './membershipTool'; +import MembershipView from './membershipTool'; import EventTable from './events/table'; import EventItem from './events/item'; import JobTable from './jobs/table'; import JobItem from './jobs/item'; +import StudydocTable from './studydocs/list'; +import studydocItem from './studydocs/item'; import { Layout } from './layout'; import './style'; @@ -38,4 +40,7 @@ m.route(root, '/events', { '/joboffers': layoutWith(JobTable), '/newjoboffer': layoutWith(JobItem), '/joboffers/:id': layoutWith(JobItem), + '/studydocuments': layoutWith(StudydocTable), + '/studydocuments/:id': layoutWith(studydocItem), + '/newstudydocument': layoutWith(studydocItem), }); diff --git a/src/itemcontroller.js b/src/itemcontroller.js index c05a36737bb2b822dd33e25bc3d40d33caadf3bf..5b429fcf384ecaaa9939826dfbfe8a4a8aca2e84 100644 --- a/src/itemcontroller.js +++ b/src/itemcontroller.js @@ -39,7 +39,7 @@ export default class ItemController { cancel() { if (this.modus === 'edit') this.changeModus('view'); - if (this.modus === 'new') m.route.set(`/${this.resource}`); + else m.route.set(`/${this.resource}`); } changeModus(newModus) { diff --git a/src/layout.js b/src/layout.js index f067434c5b1985867968554a674c4c18cca412cb..8f78e29701cfe2393177d2e9b1840a43f18d1edf 100644 --- a/src/layout.js +++ b/src/layout.js @@ -143,8 +143,9 @@ export class Layout { title: 'Job offers', }), m(Menupoint, { - href: '/announce', - title: 'Announce', + href: '/studydocuments', + icon: icons.studydoc, + title: 'Studydocs', }), ], }), diff --git a/src/membershipTool.js b/src/membershipTool.js index e662be97fd7650851467fd08dd3c9ac8808cce97..d7c6818b59b0a513ce71266bb2d588e0e6e7abef 100644 --- a/src/membershipTool.js +++ b/src/membershipTool.js @@ -1,9 +1,7 @@ +import m from 'mithril'; import EditView from './views/editView'; -import SelectList from './views/selectList'; -const m = require('mithril'); - -export class MembershipView extends EditView { +export default class MembershipView extends EditView { constructor(vnode) { super(vnode, 'groupmemberships', { user: 1, group: 1 }); } @@ -23,17 +21,3 @@ export class MembershipView extends EditView { ]); } } - -export class NewMembership { - constructor() { - this.selectUser = new SelectList('users', ['firstname', 'lastname'], { - view(vnode) { - return m('span', `${vnode.attrs.firstname} ${vnode.attrs.lastname}`); - }, - }); - } - - view() { - return m(this.selectUser); - } -} diff --git a/src/resourceConfig.json b/src/resourceConfig.json index 3d58b7cd7ef6960db36c9998da39b752ea4af22f..34314bd3ee98421c664730f4a6faa017a1fbe0e3 100644 --- a/src/resourceConfig.json +++ b/src/resourceConfig.json @@ -117,5 +117,14 @@ }, "sessions": { "searchKeys": [] + }, + "studydocuments": { + "searchKeys": [ + "title", + "lecture", + "professor", + "author", + "uploader" + ] } } diff --git a/src/studydocs/editDoc.js b/src/studydocs/editDoc.js new file mode 100644 index 0000000000000000000000000000000000000000..b5ae296803211c4b72ed030cc01bb25448aca201 --- /dev/null +++ b/src/studydocs/editDoc.js @@ -0,0 +1,56 @@ +import m from 'mithril'; +import { RadioGroup } from 'polythene-mithril'; +import EditView from '../views/editView'; + + +export default class editDoc extends EditView { + view() { + return this.layout([ + m('h3', 'Add a New Studydocument'), + ...this.form.renderPage({ + // uploader + author: { type: 'text', label: 'Author' }, + files: { type: 'text', label: 'File' }, // buggy only singel file possible + lecture: { type: 'text', label: 'Lecture' }, + title: { type: 'text', label: 'Title' }, + professor: { type: 'text', label: 'Professor' }, + course_year: { type: 'number', lable: 'Year' }, // semester unterscheidung, plausibility + }), + // department //drop-down-list + m('div', 'Semester'), // formatieren + m(RadioGroup, { + name: 'semester', + buttons: [ + { value: '1', label: '1.', defaultChecked: this.form.data.gender === '1' }, + { value: '2', label: '2', defaultChecked: this.form.data.gender === '2' }, + { value: '3', label: '3', defaultChecked: this.form.data.gender === '3' }, + { value: '4', label: '4', defaultChecked: this.form.data.gender === '4' }, + { value: '5', label: '5+', defaultChecked: this.form.data.gender === '5' }, + ], + onChange: ({ value }) => { console.log(value); this.form.data.gender = value; }, + }), + m(RadioGroup, { + name: 'type', + buttons: [{ + value: 'exames', + label: 'exames', + defaultChecked: this.form.data.gender === 'exames', + }, { + value: 'cheat_sheet', + label: 'cheat sheet', + defaultChecked: this.form.data.gender === 'cheat_sheet', + }, { + value: 'lecture_documents', + label: 'lecture documents', + defaultChecked: this.form.data.gender === 'lecture_documents', + }, { + value: 'exercise', + label: 'exercise', + defaultChecked: this.form.data.gender === 'exercise', + }], + onChange: ({ value }) => { console.log(value); this.form.data.gender = value; }, + }), + + ]); + } +} diff --git a/src/studydocs/item.js b/src/studydocs/item.js new file mode 100644 index 0000000000000000000000000000000000000000..d84067ad8ac25311c308ca50cb054f16c2245622 --- /dev/null +++ b/src/studydocs/item.js @@ -0,0 +1,17 @@ +import m from 'mithril'; +import viewDoc from './viewDoc'; +import editDoc from './editDoc'; +import ItemController from '../itemcontroller'; +import { loadingScreen } from '../layout'; + +export default class studydocItem { + constructor() { + this.controller = new ItemController('studydocuments'); + } + + view() { + if (!this.controller || !this.controller.data) return m(loadingScreen); + if (this.controller.modus !== 'view') return m(editDoc, { controller: this.controller }); + return m(viewDoc, { controller: this.controller }); + } +} diff --git a/src/studydocs/list.js b/src/studydocs/list.js new file mode 100644 index 0000000000000000000000000000000000000000..35c84d53fd55e6b0622648b6219bd763214d18f9 --- /dev/null +++ b/src/studydocs/list.js @@ -0,0 +1,39 @@ +import m from 'mithril'; +import { DatalistController } from 'amiv-web-ui-components'; +import { studydocuments as config } from '../resourceConfig.json'; +import TableView from '../views/tableView'; +import { dateFormatter } from '../utils'; +import { ResourceHandler } from '../auth'; + + +/* Table of all studydocuments */ +export default class StudydocTable { + constructor() { + this.handler = new ResourceHandler('studydocuments', config.tableKeys); + this.ctrl = new DatalistController((query, search) => this.handler.get({ search, ...query })); + } + + getItemData(data) { + return [ + m('div', { style: { width: 'calc(100% - 30em)' } }, data.title), + m('div', { style: { width: '6em' } }, data.department.toUpperCase()), + m('div', { style: { width: '6em' } }, data.semester), + m('div', { style: { width: '18em' } }, data.lecture), + ]; + } + + view() { + return m(TableView, { + controller: this.ctrl, + keys: config.tableKeys, + tileContent: this.getItemData, + titles: [ + { text: 'Titel', width: 'calc(100% - 30em)' }, + { text: 'Department', width: '6em' }, + { text: 'Semester', width: '6em' }, + { text: 'Lecture', width: '18em' }, + ], + onAdd: () => { m.route.set('/newstudydocument'); }, + }); + } +} diff --git a/src/studydocs/viewDoc.js b/src/studydocs/viewDoc.js new file mode 100644 index 0000000000000000000000000000000000000000..f9de58fde7168cfebdb188e3ec997ad787fdb493 --- /dev/null +++ b/src/studydocs/viewDoc.js @@ -0,0 +1,46 @@ +import m from 'mithril'; +// eslint-disable-next-line import/extensions +import { apiUrl } from 'networkConfig'; +import ItemView from '../views/itemView'; +import { Property } from '../views/elements'; + + +export default class viewDoc extends ItemView { + view() { + const stdMarg = { margin: '5px' }; + + return this.layout(m('div.maincontainer', [ + m('h3', { + style: { 'margin-top': '0px', 'margin-bottom': '0px' }, + }, this.data.title), + // below the title, most important details are listed + m('div', { style: { display: 'flex' } }, [ + this.data.lecture && m(Property, { + title: 'Lecture', + style: stdMarg, + }, `${this.data.lecture} ${this.data.department.toUpperCase()}`), + this.data.semster && m(Property, { + title: 'Semester', + style: stdMarg, + }, this.data.semester), + this.data.department && !this.data.lecture && m(Property, { + title: 'Department', + style: stdMarg, + }, this.data.department.toUpperCase()), + this.data.professor && m(Property, { + title: 'Professor', + style: stdMarg, + }, this.data.professor), + this.data.author && m(Property, { + title: 'Author', + style: stdMarg, + }, this.data.author), + this.data.uploader && m(Property, { + title: 'Uploader', + style: stdMarg, + }, this.data.uploader), + ]), + ])); + } +} + diff --git a/src/style.js b/src/style.js index a1d6c33c515e7ec9da4f861080925e893cf7dcaf..7bbf9cb829f1948e311fc23a7d566f5efa126579 100644 --- a/src/style.js +++ b/src/style.js @@ -5,12 +5,13 @@ addTypography(); // https://material.io/tools/color/#!/?view.left=0&view.right=1 // &secondary.color=e8462b&primary.color=274284 +// eslint-disable-next-line import/prefer-default-export export const colors = { amiv_blue: '#1F2D54', amiv_red: '#e8462b', green: '#4ef599', blue: '#274284', - //light_blue: '#5378E1', + // light_blue: '#5378E1', light_blue: '#5a6db4', orange: 'orange', }; diff --git a/src/users/viewUser.js b/src/users/viewUser.js index f3be0984f54cf79106d37692401efa3409749fbe..4a926b6130c24dea22dd855051db3a13b58928d2 100644 --- a/src/users/viewUser.js +++ b/src/users/viewUser.js @@ -56,17 +56,17 @@ export default class UserView extends ItemView { ...stdMargin, }, 'Regular Member'); } else if (this.data.membership === 'extraordinary') { - membership = m( - chip, - { svg: icons.checked, svgBackground: colors.green, ...stdMargin }, - 'Extraordinary Member', - ); + membership = m(chip, { + svg: icons.checked, + svgBackground: colors.green, + ...stdMargin, + }, 'Extraordinary Member'); } else if (this.data.membership === 'honorary') { - membership = m( - chip, - { svg: icons.star, svgBackground: colors.orange, ...stdMargin }, - 'Honorary Member', - ); + membership = m(chip, { + svg: icons.star, + svgBackground: colors.orange, + ...stdMargin, + }, 'Honorary Member'); } // Selector that is only displayed if "new" is clicked in the diff --git a/src/views/elements.js b/src/views/elements.js index 1c04568172c8dc464a93e6dd361068b53f1c7b3c..c693854c2515eb1d7699db773751c981540d6519 100644 --- a/src/views/elements.js +++ b/src/views/elements.js @@ -20,6 +20,7 @@ export const icons = { department: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M0 0h48v48H0z" fill="none"/><path d="M24 14V6H4v36h40V14H24zM12 38H8v-4h4v4zm0-8H8v-4h4v4zm0-8H8v-4h4v4zm0-8H8v-4h4v4zm8 24h-4v-4h4v4zm0-8h-4v-4h4v4zm0-8h-4v-4h4v4zm0-8h-4v-4h4v4zm20 24H24v-4h4v-4h-4v-4h4v-4h-4v-4h16v20zm-4-16h-4v4h4v-4zm0 8h-4v4h4v-4z"/></svg>', amivWheel: '<svg width="81.059502" height="80.056625" viewBox="0 0 82 82" id="svg2"><metadata id="metadata8"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="defs6"><clipPath id="clipPath18"><path d="m 0,849.563 1960.52,0 L 1960.52,0 0,0 0,849.563 z" id="path20" /></clipPath></defs><g transform="matrix(1.25,0,0,-1.25,-16.34525,92.96925)" id="g10"><g transform="scale(0.1,0.1)" id="g12"><g clip-path="url(#clipPath18)" id="g16"><path d="m 566.012,342.883 c -44.453,-61.184 -130.383,-74.797 -191.563,-30.344 -3.969,2.891 -7.719,5.957 -11.289,9.18 l 41.192,29.922 40.945,-56.375 51.351,117.707 37.684,-51.848 44.727,32.5 -40.387,55.598 41.469,30.132 c 19.257,-43.32 15.679,-95.437 -14.129,-136.472 m -235.504,23.465 c -19.887,43.554 -16.5,96.32 13.601,137.75 44.45,61.179 130.383,74.789 191.559,30.336 4.352,-3.161 8.391,-6.579 12.254,-10.125 l -41.762,-30.344 -40.558,55.82 -44.735,-32.5 40.563,-55.828 -0.067,-0.051 -127.726,-12.449 38.203,-52.578 -41.332,-30.031 z m 366.523,24.668 c 1.41,10.644 2.207,21.48 2.207,32.511 0,11.028 -0.797,21.86 -2.207,32.508 l -57.468,8.922 c -2.571,11.469 -6.196,22.711 -10.864,33.57 l 41.211,40.961 c -5.109,9.438 -10.828,18.676 -17.312,27.598 -6.481,8.922 -13.496,17.223 -20.899,25 l -51.679,-26.52 c -4.372,3.84 -8.93,7.532 -13.731,11.02 -4.84,3.512 -9.801,6.73 -14.84,9.719 l 9.258,57.351 c -9.676,4.641 -19.734,8.75 -30.238,12.16 -10.481,3.407 -21.039,5.993 -31.586,7.938 l -26.262,-51.918 c -11.742,1.07 -23.519,1.031 -35.199,-0.066 l -26.293,51.984 c -10.559,-1.945 -21.109,-4.531 -31.598,-7.938 -10.492,-3.41 -20.551,-7.519 -30.23,-12.148 l 9.269,-57.434 c -10.039,-5.925 -19.582,-12.859 -28.511,-20.707 l -51.746,26.559 c -7.407,-7.777 -14.422,-16.07 -20.903,-25 -6.492,-8.922 -12.211,-18.16 -17.32,-27.598 l 41.258,-41.011 c -4.715,-10.922 -8.36,-22.137 -10.887,-33.512 l -57.481,-8.93 c -1.421,-10.64 -2.218,-21.48 -2.218,-32.508 0,-11.031 0.797,-21.855 2.218,-32.511 l 57.563,-8.934 c 2.559,-11.445 6.168,-22.668 10.82,-33.496 L 240.09,307.57 c 5.109,-9.445 10.828,-18.683 17.32,-27.597 6.481,-8.926 13.488,-17.227 20.903,-25 l 51.675,26.523 c 4.41,-3.867 9,-7.59 13.84,-11.105 4.801,-3.485 9.723,-6.688 14.723,-9.657 l -9.258,-57.336 c 9.687,-4.636 19.746,-8.75 30.238,-12.156 10.489,-3.418 21.039,-5.996 31.598,-7.929 l 26.219,51.843 c 11.773,-1.093 23.57,-1.062 35.285,0.039 l 26.238,-51.894 c 10.559,1.945 21.117,4.523 31.598,7.941 10.504,3.406 20.551,7.52 30.238,12.149 l -9.246,57.285 c 10.078,5.957 19.648,12.898 28.617,20.789 l 51.621,-26.492 c 7.403,7.773 14.41,16.074 20.899,25 6.484,8.914 12.203,18.152 17.312,27.597 l -41.148,40.907 c 4.73,10.957 8.379,22.207 10.929,33.644 l 57.34,8.895" id="path30" style="fill:#f03d30;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g></g></g></svg>', menu: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>', + studydoc: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></svg>', }; // Property as specified by material design: small, grey title and larger diff --git a/src/views/itemView.js b/src/views/itemView.js index 00edcfcb52a08f7ba0656ec45da5664191f51cd2..a5072aeb4673a9c373c162ca63811447f899e9f5 100644 --- a/src/views/itemView.js +++ b/src/views/itemView.js @@ -1,8 +1,9 @@ import m from 'mithril'; -import { Toolbar, Dialog, Button } from 'polythene-mithril'; +import { IconButton, Toolbar, ToolbarTitle, Dialog, Button } from 'polythene-mithril'; import { ButtonCSS } from 'polythene-css'; import { colors } from '../style'; import { loadingScreen } from '../layout'; +import { icons } from './elements'; ButtonCSS.addStyle('.itemView-edit-button', { color_light_background: colors.light_blue, @@ -53,21 +54,29 @@ export default class ItemView { layout(children, buttons = []) { if (!this.controller || !this.controller.data) return m(loadingScreen); return m('div', [ - m(Toolbar, m('div.pe-button-row', [ - m(Button, { - element: 'div', - className: 'itemView-edit-button', - label: `Edit ${this.resource.charAt(0).toUpperCase()}${this.resource.slice(1, -1)}`, - events: { onclick: () => { this.controller.changeModus('edit'); } }, - }), - m(Button, { - label: `Delete ${this.resource.charAt(0).toUpperCase()}${this.resource.slice(1, -1)}`, - className: 'itemView-delete-button', - border: true, - events: { onclick: () => this.delete() }, + m(Toolbar, [ + m('div', { style: { width: 'calc(100% - 48px)' } }, m('div.pe-button-row', [ + m(Button, { + element: 'div', + className: 'itemView-edit-button', + label: `Edit ${this.resource.charAt(0).toUpperCase()}${this.resource.slice(1, -1)}`, + events: { onclick: () => { this.controller.changeModus('edit'); } }, + }), + m(Button, { + label: `Delete ${this.resource.charAt(0).toUpperCase()}${this.resource.slice(1, -1)}`, + className: 'itemView-delete-button', + border: true, + events: { onclick: () => this.delete() }, + }), + ...buttons, + ])), + m(IconButton, { + style: { 'margin-left': 'auto', 'margin-right': '0px' }, + icon: { svg: { content: m.trust(icons.clear) } }, + events: { onclick: () => { this.controller.cancel(); } }, }), - ...buttons, - ])), + + ]), m('div', { style: { height: 'calc(100vh - 130px)', 'overflow-y': 'scroll' }, }, children), diff --git a/src/views/selectList.js b/src/views/selectList.js deleted file mode 100644 index 6bd86623a4c5a56d152ff1a4c8c2b04509cc38e8..0000000000000000000000000000000000000000 --- a/src/views/selectList.js +++ /dev/null @@ -1,168 +0,0 @@ -import m from 'mithril'; -import Stream from 'mithril/stream'; -import { - List, ListTile, Search, IconButton, Button, Toolbar, - ToolbarTitle, -} from 'polythene-mithril'; -import infinite from 'mithril-infinite'; -import { icons, BackButton, ClearButton, SearchIcon } from './elements'; - - -class SearchField { - oninit() { - this.value = Stream(''); - this.setInputState = Stream(); - - // const clear = () => setInputState()({ value: '', focus: false}); - this.clear = () => this.value(''); - this.leave = () => this.value(''); - } - - view({ state, attrs }) { - // incoming value and focus added for result list example: - const value = attrs.value !== undefined ? attrs.value : state.value(); - return m(Search, Object.assign( - {}, - { - textfield: { - label: 'type here', - onChange: (newState) => { - state.value(newState.value); - state.setInputState(newState.setInputState); - // onChange callback added for result list example: - if (attrs.onChange) attrs.onChange(newState, state.setInputState); - }, - value, - defaultValue: attrs.defaultValue, - }, - buttons: { - focus: { - before: m(BackButton, { leave: state.leave }), - }, - focus_dirty: { - before: m(BackButton, { leave: state.leave }), - after: m(ClearButton, { clear: state.clear }), - }, - dirty: { - before: m(BackButton, { leave: state.leave }), - after: m(ClearButton, { clear: state.clear }), - }, - }, - }, - attrs, - )); - } -} - -export default class SelectList { - 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 } }) { - // make it possible to change the selection from outside, e.g. to set the field to an - // existing group moderator - if (selection) this.selected = selection; - } - - item() { - return (data) => { - const attrs = { - compactFront: true, - hoverable: true, - className: 'themed-list-tile', - events: { - onclick: () => { - if (this.onSelect) { this.onSelect(data); } - this.selected = data; - this.showList = false; - }, - }, - }; - // Overwrite default attrs - Object.assign(attrs, this.listTileAttrs(data)); - return m(ListTile, attrs); - }; - } - - view({ - attrs: { - controller, - onSubmit = false, - onCancel = false, - selectedText, - }, - }) { - return m('div', [ - m(Toolbar, { compact: true, style: { background: 'rgb(78, 242, 167)' } }, this.selected ? [ - m(IconButton, { - icon: { svg: m.trust(icons.clear) }, - ink: false, - events: { - onclick: () => { - if (this.onSelect) { this.onSelect(null); } - this.selected = null; - }, - }, - }), - m(ToolbarTitle, { text: selectedText(this.selected) }), - onSubmit ? m(Button, { - label: 'Submit', - className: 'blue-button', - events: { - onclick: () => { - onSubmit(this.selected); - this.selected = null; - controller.setSearch(''); - controller.refresh(); - }, - }, - }) : '', - ] : [ - m(SearchField, Object.assign({}, { - style: { background: 'rgb(78, 242, 167)' }, - onChange: ({ value, focus }) => { - // onChange is called either if the value or focus fo the SearchField - // changes. - // At value change we want to update the search - // at focus changt we hie the list of results. As focus change also - // happens while clicking on an item in the list of results, the list - // is hidden after a short Timeout that has to be sufficiently long - // to register the onclick of the listitem. Can be a problem for different - // OS and browsers. - 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(); }, 500); - } - if (value !== this.searchValue) { - // if we always update the search value, this would also happen - // immidiately in the moment where we click on the listitem. - // Then, the list get's updated before the click is registered. - // So, we make sure this state change is due to value change and - // not due to focus change. - this.searchValue = value; - controller.debouncedSearch(value); - } - }, - })), - onCancel ? m(Button, { - label: 'cancel', - className: 'blue-button', - events: { onclick: onCancel }, - }) : '', - ]), - (this.showList && !this.selected) ? m(List, { - style: { height: '400px', 'background-color': 'white' }, - tiles: m(infinite, controller.infiniteScrollParams(this.item())), - }) : '', - ]); - } -}