Skip to content
Snippets Groups Projects
Commit 635b1a72 authored by Kai Ott's avatar Kai Ott
Browse files

Merge branch 'codingweekendfs18' of https://gitlab.ethz.ch/amiv/amiv-admintool...

Merge branch 'codingweekendfs18' of https://gitlab.ethz.ch/amiv/amiv-admintool into codingweekendfs18
parents 8bea1055 f1dd8cc7
No related branches found
No related tags found
No related merge requests found
......@@ -11,5 +11,6 @@ module.exports = {
"class-methods-use-this": 0,
"prefer-destructuring": 1,
"no-underscore-dangle": 0,
"linebreak-style": 0,
}
};
.ftpconfig
node_modules/
package-lock.json
......@@ -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>
......
/*
IMPORTS
*/
/*
GENERAL SETUP
*/
html, body {
width: 100%;
height: 100%;
}
body {
overflow: hidden;
}
/*
UTILITY CLASSES
*/
.smooth {
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
/*
LOGIN PANEL
*/
.loginPanel {
width: 100%;
height: 100%;
top: 0%;
left: 0%;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
z-index: 20;
/*background: rgba(30,30,30,.9);*/
background: rgba(31, 45, 84, 1);
}
.loginPanel>div {
box-shadow: 0 0 2em #000;
}
.loginPanel .login-logo {
width: 70%;
}
/*
MAIN FRAMEWORK
*/
.wrapper-main {
height: 100%;
width: 100%;
display: grid;
grid-template-columns: 280px auto;
}
.wrapper-sidebar {
z-index: 10;
grid-column: 1;
height: 100%;
overflow-y: auto;
position: fixed;
background: #222;
color: #fff;
box-shadow: 0 0 1em rgba(0, 0, 0, 0.5);
}
.wrapper-sidebar .navbar li {
float: none;
display: block;
}
.wrapper-sidebar ul li a {
color: inherit;
}
.wrapper-sidebar ul li a:hover, .wrapper-sidebar ul li a:active, .wrapper-sidebar ul li a:focus {
color: #000;
}
.wrapper-sidebar>div {
padding: 1em;
}
.wrapper-sidebar .sidebar-logo {
width: 100%;
}
.wrapper-content {
height: 100vh;
grid-column: 2;
background: #eee;
overflow: hidden;
}
/*
ANNOUNCE TOOL
*/
.selected{
background: #98FB98;
}
.featured{
background: salmon;
}
.selectedcolor{
background: #98FB98;
}
.featuredcolor{
background: salmon;
}
......@@ -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",
......
......@@ -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 {
......
import m from 'mithril';
import { Button, Card, TextField } from "polythene-mithril"
import EditView from '../views/editView';
import { styler } from 'polythene-core-css';
const draftStyle = [
{
'.footer': {
position: 'fixed',
left: 0,
bottom: 0,
width: '100%',
'background-color': 'red',
color: 'white',
'text-align': 'center',
}
}
]
styler.add('eventDraft', draftStyle);
export default class eventDraft extends EditView {
......@@ -8,6 +26,26 @@ export default class eventDraft extends EditView {
}
view() {
return m('h1', 'Hello World');
const fieldDescriptionEn = m(TextField, {
label: 'Description [EN]',
required: true,
floatingLabel: true,
dense: true,
multiLine: true,
rows: 6,
});
const buttonMaker = m(Button, {
label: "Submit Request",
});
// ])
return m('div',[
m('h1', {class: "title"}, "Request a new event"),
m('div.footer', buttonMaker),
]);
}
}
import m from 'mithril';
import {
TextField,
Button,
Card
} from 'polythene-mithril';
import EditView from '../views/editView';
export default class eventWithExport extends EditView {
constructor(vnode) {
super(vnode, 'events');
}
view() {
// Editable by event creator.
const fieldTitleEn = m(TextField, {
label: 'Event Title [EN]',
required: true,
floatingLabel: true,
dense: true,
});
const fieldDescriptionEn = m(TextField, {
label: 'Description [EN]',
required: true,
floatingLabel: true,
dense: true,
multiLine: true,
rows: 6,
});
const creatorButton = m(Button, {
label: 'EXPORT'
// label: 'Create a hyperlink string describing your project.'
});
// Needs administrator (Kulturi).
const fieldLocation = m(TextField, {
label: 'Location:',
floatingLabel: true,
required: true,
});
return m('div', [
m('h1', 'For the event creator:', fieldTitleEn, fieldDescriptionEn, 'For the AMIV administrator:', fieldLocation, creatorButton),
]);
}
}
\ No newline at end of file
import m from 'mithril';
import { Checkbox, Button, Card, TextField } from 'polythene-mithril';
import { Checkbox, Button, Card, TextField, IconButton, Toolbar, ToolbarTitle } from 'polythene-mithril';
import ItemView from '../views/itemView';
import { events as config, eventsignups as signupConfig } from '../config.json';
import TableView from '../views/tableView';
import DatalistController from '../listcontroller';
import { dateFormatter } from '../utils';
import { icons } from '../views/elements';
import { styler } from 'polythene-core-css';
const viewLayout = [
{
'.eventViewContainer': {
display: 'grid',
'grid-template-columns': '50% 50%',
'grid-gap': '50px',
},
'.eventViewLeft': {
'grid-column': 1,
},
'.eventViewRight': {
'grid-column': 2,
}
}
];
styler.add('eventView', viewLayout);
class ParticipantsTable {
......@@ -42,50 +61,230 @@ class ParticipantsTable {
}
export default class viewEvent extends ItemView {
constructor() {
super('events');
this.showParticipants = false;
}
constructor() {
super('events');
this.details = false;
this.waitlist = false;
this.emailAdresses = false;
}
view() {
if (!this.data) return '';
console.log(Object.keys(this));
console.log(this['data']);
let displayCatchphraseDe = null;
let displayCatchphraseEn = null;
let displayDescriptionDe = null;
let displayDescriptionEn = null;
let displayPriority = null;
if(this.data.catchphrase_de) {
displayCatchphraseDe = m("t3", {class: "text"}, this.data.catchphrase_de);
}
if(this.data.catchphrase_en) {
displayCatchphraseEn = m("t3", {class: "text"}, this.data.catchphrase_en);
}
if(this.data.description_de) {
displayDescriptionDe = m("t3", {class: "text"}, this.data.description_de);
}
if(this.data.description_en) {
displayDescriptionEn = m("t3", {class: "text"}, this.data.description_en);
}
if(this.data.priority) {
displayPriority = m("t3", {class: "text"}, this.data.priority);
}
let displayDetailsButton = m(Toolbar, { compact: true, events: { onclick: () => this.details = !this.details } }, [
m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }),
m(ToolbarTitle, { text: "details" }),
]);
let displayWaitlistButton = m(Toolbar, { compact: true, events: { onclick: () => this.waitlist = !this.waitlist } }, [
m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }),
m(ToolbarTitle, { text: "waitlist" }),
]);
let displayEmailAdressesButton = m(Toolbar, { compact: true, events: { onclick: () => this.emailAdresses = !this.emailAdresses } }, [
m(IconButton, { icon: { svg: m.trust(icons.ArrowRight) } }),
m(ToolbarTitle, { text: "email adresses" }),
]);
view() {
if (!this.data) return '';
console.log(this.data);
const navigation = m('nav', m('ul', [m('li', 'event overview'), m('li', 'participants'), m('li', 'something')]));
let displayDetails = null;
let displayWaitlist = null;
let displayEmailAdresses = null;
function edit() {
alert('edit event...');
if (this.details) {
displayDetailsButton = m(Toolbar, { compact: true, events: { onclick: () => this.details = !this.details } }, [
m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }),
m(ToolbarTitle, { text: "details" }),
]);
displayDetails = m(Card, {
content: [
{
primary: {
title: "Catchphrase DE",
subtitle: displayCatchphraseDe
}
},
{
primary: {
title: "Catchphrase EN",
subtitle: displayCatchphraseEn
}
},
{
any: {
content: this.data.time_start ? m('p', m('strong', `when: from ${dateFormatter(this.data.time_start)} to ${dateFormatter(this.data.time_end)}`)) : '',
},
},
{
any: {
content: this.data.location ? m('p', m('strong', `where: ${this.data.location}`)) : '',
}
},
{
primary: {
title: "Description DE",
subtitle: displayDescriptionDe
}
},
{
primary: {
title: "Description EN",
subtitle: displayDescriptionEn
}
},
{
primary: {
title: "Priority",
subtitle: displayPriority
}
},
{
actions: {
content: [
m(Button, {
label: "Action 1"
}),
m(Button, {
label: "Action 2"
})
]
}
},
{
text: {
content: "More text"
}
}
]
})
}
if (this.waitlist) {
displayWaitlistButton = m(Toolbar, { compact: true, events: { onclick: () => this.waitlist = !this.waitlist } }, [
m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }),
m(ToolbarTitle, { text: "waitlist" }),
]);
displayWaitlist = m(Card, {
content: [
{
primary: {
title: "Primary title",
subtitle: "Subtitle"
}
},
{
actions: {
content: [
m(Button, {
label: "Action 1"
}),
m(Button, {
label: "Action 2"
})
]
}
},
{
text: {
content: "More text"
}
}
]
})
}
if (this.emailAdresses) {
displayEmailAdressesButton = m(Toolbar, { compact: true, events: { onclick: () => this.emailAdresses = !this.emailAdresses } }, [
m(IconButton, { icon: { svg: m.trust(icons.ArrowDown) } }),
m(ToolbarTitle, { text: "email adresses" }),
]);
displayEmailAdresses = m(Card, {
content: [
{
primary: {
title: "Primary title",
subtitle: "Subtitle"
}
},
{
actions: {
content: [
m(Button, {
label: "Action 1"
}),
m(Button, {
label: "Action 2"
})
]
}
},
{
text: {
content: "More text"
}
}
]
})
}
return m("div", {
style: { height: '100%', 'overflow-y': 'scroll'}
},[
m("h1", {class: "title"}, this.data.title_de),
m(Button, {element: 'div', label: "Update Event"}),
m('div.eventViewContainer', [
m('div.eventViewLeft', [
displayDetailsButton,
displayDetails,
displayWaitlistButton,
displayWaitlist,
displayEmailAdressesButton,
displayEmailAdresses,
]),
m('div.eventViewRight', [
m('h4', 'Accepted Participants'),
m(ParticipantsTable, { where: { accepted: true } }),
m('p', ''),
m('h4', 'Participants on Waiting List'),
m(ParticipantsTable, { where: { accepted: false } }),
])
]),
])
}
const editEventButton = m('div', m(Button, {
label: 'edit event',
events: {
onclick: () => edit(),
},
}));
const showParticipantsButton = m('div', m(Button, {
label: `display participants ${this.showParticipants ? '>' : '<'}`,
events: {
onclick: () => {
this.showParticipants = !this.showParticipants;
},
},
}));
return m('main', { style: { height: '100%', overflow: 'scroll' } }, [navigation,
m('h1', { class: 'title' }, 'Event overview'),
m('h3', m('em', this.data.title_en)),
this.data.time_start ? m('p', m('strong', `when: from ${dateFormatter(this.data.time_start)} to ${dateFormatter(this.data.time_end)}`)) : '',
this.data.location ? m('p', m('strong', `where: ${this.data.location}`)) : '',
editEventButton,
m('h1', { class: 'title' }, 'Participants'),
showParticipantsButton,
this.showParticipants ? m('h4', 'Accepted Participants') : '',
this.showParticipants ? m(ParticipantsTable, { where: { accepted: true } }) : '',
this.showParticipants ? m('p', '') : '',
this.showParticipants ? m('h4', 'Participants on Waiting List') : '',
this.showParticipants ? m(ParticipantsTable, { where: { accepted: false } }) : '',
]);
}
}
......@@ -7,7 +7,8 @@ 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 eventWithExport from './events/eventWithExport';
import Layout from './layout';
// import AnnounceTool from './announceTool';
import './style';
......@@ -16,15 +17,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() {
......@@ -42,6 +34,7 @@ m.route(root, '/users', {
'/events/:id': layoutWith(viewEvent),
'/newevent': layoutWith(newEvent),
'/draftevent': layoutWith(eventDraft),
'/eventwithexport': layoutWith(eventWithExport),
'/groups': layoutWith({
view() {
return m(TableView, {
......
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,
},
'.main-toolbar': {
backgroundColor: '#1f2d54',
color: '#fff',
height: '72px',
'grid-column': '1 / span 2',
'grid-row': 1,
},
'.wrapper-main': {
height: '100%',
width: '100%',
display: 'grid',
'grid-template-columns': '200px auto',
'grid-template-rows': '72px auto',
},
'.wrapper-sidebar': {
'grid-column': 1,
'grid-row': 2,
height: '100%',
'overflow-y': 'auto',
background: '#cccccc',
color: 'white',
},
'.wrapper-content': {
height: 'calc(100vh - 72px)',
'grid-column': 2,
'grid-row': 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('div.wrapper-main.smooth', [
m(Toolbar, { className: 'main-toolbar' }, [
m(ToolbarTitle, { text: 'AMIV Admintools' }),
]),
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', [
]),
]));*/
]);
}
}
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' }),
]),
]));
}
}
......@@ -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();
......
import m from 'mithril';
import { IconButton } from 'polythene-mithril';
import { IconButton, TextField } from 'polythene-mithril';
export class inputGroup {
constructor(vnode) {
export class textInput {
constructor({ attrs: { getErrors } }) {
// Link the error-getting function from the binding
this.getErrors = () => [];
if (vnode.attrs.getErrors) {
this.getErrors = vnode.attrs.getErrors;
if (getErrors) {
this.getErrors = getErrors;
}
}
view(vnode) {
view({ attrs }) {
// set display-settings accoridng to error-state
let errorField = null;
let groupClasses = vnode.attrs.classes ? vnode.attrs.classes : '';
const errors = this.getErrors();
if (errors.length > 0) {
errorField = m('span.help-block', `Error: ${errors.join(', ')}`);
groupClasses += ' has-error';
}
return m('div.form-group', { class: groupClasses }, [
m(`label[for=${vnode.attrs.name}]`, vnode.attrs.title),
m(`input[name=${vnode.attrs.name}][id=${vnode.attrs.name}].form-control`, {
value: vnode.attrs.value, onchange: vnode.attrs.onchange,
}),
errorField,
]);
const attributes = Object.assign({}, attrs);
attributes.valid = errors.length > 0;
attributes.error = errors.join(', ');
return m(TextField, attributes);
}
}
......@@ -57,6 +48,10 @@ export const icons = {
iconSearchSVG: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>',
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>',
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 = {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment