Commit 1e2deb32 authored by adietmue's avatar adietmue
Browse files

Frontend: Talk to backend and display courses in sidebar

parent a830b71e
...@@ -6,13 +6,13 @@ ...@@ -6,13 +6,13 @@
main { main {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
width: 70%; width: 60%;
} }
aside { aside {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
width: 25%; width: 35%;
margin-right: 5%; margin-right: 5%;
} }
</style> </style>
......
import PvkList from './PvkList'; import PvkList from './PvkList';
const m = require('mithril'); const m = require('mithril');
const { Courses, UserCourses } = require('./api.js');
module.exports = { module.exports = {
oninit() { Courses.load(); },
view() { view() {
<<<<<<< HEAD
return m('div', [m(PvkList)]); return m('div', [m(PvkList)]);
=======
// return m('div', 'Hellooo!');
return m('ul', [
Courses.list.map(course =>
m('li', [
m('p', `${course.lecture.title}, ${course.assistant}`),
m(
'button',
{ onclick() { UserCourses.select(course); } },
'Add Course',
),
])),
]);
// m('button', { onclick() { Courses.select(); } }, 'Add Course');
>>>>>>> Frontend: Talk to backend and display courses in sidebar
}, },
}; };
...@@ -80,7 +80,8 @@ const LoginPage = { ...@@ -80,7 +80,8 @@ const LoginPage = {
const SidebarHeader = { const SidebarHeader = {
view() { view() {
return [ return [
m('h1', 'PVK Tool Demo'), m('header', m('img', { src: '/home/alex/contractor/contractor/static/logo.svg' })),
// m('h1', 'PVK Tool Demo'),
m('p', `Hello, ${Session.user.name}`), m('p', `Hello, ${Session.user.name}`),
m('button', { onclick: logout }, 'Logout'), m('button', { onclick: logout }, 'Logout'),
Session.admin ? [ Session.admin ? [
......
import m from 'mithril'; import m from 'mithril';
import { apiUrl } from './api';
class Pvk { class Pvk {
constructor() { constructor() {
...@@ -11,7 +10,7 @@ class Pvk { ...@@ -11,7 +10,7 @@ class Pvk {
// request is needed to force mithrijs to update shared objects // request is needed to force mithrijs to update shared objects
m.request({ m.request({
method: 'GET', method: 'GET',
url: `${apiUrl}`, // url: `${apiUrl}`,
}).then(() => { }).then(() => {
this.list = [{ this.list = [{
lecture: { lecture: {
......
// User Sidebar // User Sidebar
import Pvk from './Pvk';
const m = require('mithril'); const m = require('mithril');
const { UserCourses } = require('./api.js');
function courseView(course) {
return m('li', `${course.lecture.title}, ${course.assistant}`);
}
function asList(courses) { return courses.map(courseView); }
module.exports = { module.exports = {
oninit() { UserCourses.load(); },
view() { view() {
return [ return [
m('h2', 'Your Courses'), m('h1', 'Selected Courses'),
m('p', 'You are not signed up for anything yet.'), UserCourses.selected.length ? [
m('p', `Amnt of Pvks: ${Pvk.list.length}`), asList(UserCourses.selected),
m('button', { onclick() { UserCourses.reserve(); } }, 'reserve'),
] : m('p', 'No courses selected.'),
m('h1', 'Reserved Courses'),
UserCourses.reserved.length ? [
asList(UserCourses.reserved),
m('button', { onclick() { UserCourses.pay(); } }, 'Pay'),
] : m('p', 'No courses reserved.'),
m('h1', 'Accepted Courses'),
UserCourses.accepted.length ? [
asList(UserCourses.accepted),
] : m('p', 'No courses accepted.'),
]; ];
}, },
}; };
...@@ -3,11 +3,15 @@ ...@@ -3,11 +3,15 @@
const m = require('mithril'); const m = require('mithril');
const ls = require('local-storage'); const ls = require('local-storage');
const apiUrl = 'https://amiv-api.ethz.ch'; const amivApiUrl = 'https://amiv-api.ethz.ch';
const pvkApiUrl = `//${window.location.hostname}/api`;
const adminGroupName = 'PVK Admins'; const adminGroupName = 'PVK Admins';
const storedSession = ls('session'); const storedSession = ls('session');
// Session with AMIVAPI
const Session = { const Session = {
// Quick check if session exists // Quick check if session exists
active() { return this.data.token; }, active() { return this.data.token; },
...@@ -43,7 +47,7 @@ const Session = { ...@@ -43,7 +47,7 @@ const Session = {
// Login and request user data along with the session // Login and request user data along with the session
return m.request({ return m.request({
method: 'POST', method: 'POST',
url: `${apiUrl}/sessions?embedded={"user": 1}`, url: `${amivApiUrl}/sessions?embedded={"user": 1}`,
data: { username, password }, data: { username, password },
}).then((data) => { }).then((data) => {
// Session data // Session data
...@@ -71,7 +75,7 @@ const Session = { ...@@ -71,7 +75,7 @@ const Session = {
if (this.data.token) { if (this.data.token) {
return m.request({ return m.request({
method: 'DELETE', method: 'DELETE',
url: `${apiUrl}/sessions/${this.data._id}`, url: `${amivApiUrl}/sessions/${this.data._id}`,
headers: { headers: {
Authorization: `Token ${this.data.token}`, Authorization: `Token ${this.data.token}`,
'If-Match': this.data._etag, 'If-Match': this.data._etag,
...@@ -83,7 +87,7 @@ const Session = { ...@@ -83,7 +87,7 @@ const Session = {
// Token already no valid anymore, clear session // Token already no valid anymore, clear session
this.clear(); this.clear();
} else { } else {
throw err; throw err._error;
} }
}); });
} }
...@@ -99,7 +103,7 @@ const Session = { ...@@ -99,7 +103,7 @@ const Session = {
}); });
m.request({ m.request({
method: 'GET', method: 'GET',
url: `${apiUrl}/groups?${query}`, url: `${amivApiUrl}/groups?${query}`,
headers: { Authorization: `Token ${token}` }, headers: { Authorization: `Token ${token}` },
}).then((data) => { }).then((data) => {
if (data._meta.total !== 0) { if (data._meta.total !== 0) {
...@@ -112,7 +116,7 @@ const Session = { ...@@ -112,7 +116,7 @@ const Session = {
}); });
return m.request({ return m.request({
method: 'GET', method: 'GET',
url: `${apiUrl}/groupmemberships?${query}`, url: `${amivApiUrl}/groupmemberships?${query}`,
headers: { Authorization: `Token ${token}` }, headers: { Authorization: `Token ${token}` },
}); });
} }
...@@ -132,7 +136,95 @@ const Session = { ...@@ -132,7 +136,95 @@ const Session = {
}, },
}; };
// Request to the PVK backend
function request({
resource, method = 'GET', id = '', data = {}, query = {},
}) {
// Parse query such that the backend understands it
const parsedQuery = {};
Object.keys(query).forEach((key) => {
parsedQuery[key] = JSON.stringify(query[key]);
});
const queryString = m.buildQueryString(parsedQuery);
// Send the request
return m.request({
method,
data,
url: `${pvkApiUrl}/${resource}/${id}?${queryString}`,
headers: { Authorization: `Token ${Session.data.token}` },
}).catch((err) => {
// If the error is 401, the token is invalid -> auto log out
if (err._error.code === 401) { Session.clear(); }
// Actual error information is in the '_error' field, pass this on
throw err._error;
});
}
// Generic PVK resource
class Resource {
constructor(name, query = {}) {
this.name = name;
this.list = [];
this.query = query;
}
load() {
return request({ resource: this.name, query: this.query }).then((data) => {
// Fill own list
this.list = data._items;
// Pass on data
return data;
});
}
}
const UserCourses = {
resources: {
selections: new Resource(
'selections',
{ where: { nethz: Session.user.nethz } },
),
signups: new Resource(
'signups',
{ where: { nethz: Session.user.nethz } },
),
},
load() {
this.resources.selections.load().then(() => {
// We are only interested in one the first (and single) element
this.resources.selections.list = this.resources.selections.list[0] || [];
});
this.resources.signups.load();
},
get selected() { return this.resources.selections.list; },
get reserved() {
return this.resources.signups.list.filter(({ status }) =>
status === 'reserved');
},
get accepted() {
return this.resources.signups.list.filter(({ status }) =>
status === 'accepted');
},
// TODO(Alex)
select() {},
reserve() {},
pay() {},
};
const Courses = new Resource('courses', { embedded: { lecture: 1 } });
module.exports = { module.exports = {
Session, Session,
apiUrl, request,
UserCourses,
Courses,
}; };
...@@ -41,6 +41,7 @@ frontend development. Here's how: ...@@ -41,6 +41,7 @@ frontend development. Here's how:
-e MONGO_HOST="mongodb" \ -e MONGO_HOST="mongodb" \
pvk pvk
``` ```
The `--link` and `-e` make sure the backend can access the db container. The `--link` and `-e` make sure the backend can access the db container.
And now the backend is available at port 80, ready to use! And now the backend is available at port 80, ready to use!
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment