From bd5953017307e21e777eea7d4efac6f51f5856dd Mon Sep 17 00:00:00 2001
From: clemaire <c.lemaire@student.tudelft.nl>
Date: Mon, 26 Aug 2019 16:37:10 +0200
Subject: [PATCH] Add react frontend

---
 backend/LICENSE                               |   661 +
 backend/LICENSE.header                        |    17 +
 backend/build.gradle                          |   198 +
 backend/gradle/wrapper/gradle-wrapper.jar     |   Bin 0 -> 56177 bytes
 .../gradle/wrapper/gradle-wrapper.properties  |     5 +
 backend/gradlew                               |   172 +
 backend/gradlew.bat                           |    84 +
 backend/pom.xml                               |   375 +
 .../nl/tudelft/ewi/queue/DatabaseLoader.java  |   312 +
 .../ewi/queue/GlobalMethodSecurityConfig.java |    44 +
 .../tudelft/ewi/queue/HttpSessionConfig.java  |    27 +
 .../tudelft/ewi/queue/QueueApplication.java   |   152 +
 .../ewi/queue/RoleHierarchyConfig.java        |    34 +
 .../ewi/queue/SamlWebSecurityConfig.java      |   570 +
 .../tudelft/ewi/queue/WebSecurityConfig.java  |   125 +
 .../nl/tudelft/ewi/queue/WebSocketConfig.java |    41 +
 .../queue/annotation/AuthenticatedUser.java   |    28 +
 .../ewi/queue/controller/AdminController.java |   123 +
 .../ewi/queue/controller/AuthController.java  |    52 +
 .../queue/controller/CourseController.java    |   607 +
 .../controller/ErrorControllerAdvice.java     |    84 +
 .../ewi/queue/controller/GroupController.java |    88 +
 .../queue/controller/HistoryController.java   |   126 +
 .../ewi/queue/controller/HomeController.java  |    87 +
 .../ewi/queue/controller/LabController.java   |   453 +
 .../controller/NotificationController.java    |   118 +
 .../queue/controller/RequestController.java   |   499 +
 .../ewi/queue/controller/RoomController.java  |    46 +
 .../ewi/queue/controller/SSOController.java   |    71 +
 .../java/nl/tudelft/ewi/queue/cqsr/Event.java |    30 +
 .../tudelft/ewi/queue/cqsr/RecordsEvents.java |    37 +
 .../queue/dialect/AuthenticatedDialect.java   |    40 +
 .../AuthenticatedExpressionObjectFactory.java |    73 +
 .../ewi/queue/dialect/FilterDialect.java      |    34 +
 .../FilterExpressionObjectFactory.java        |   160 +
 .../ewi/queue/dialect/PrettyTimeDialect.java  |    35 +
 .../PrettyTimeExpressionObjectFactory.java    |   107 +
 .../ewi/queue/dialect/RequestDialect.java     |    33 +
 .../RequestExpressionObjectFactory.java       |    68 +
 .../exception/AlreadyEnqueuedException.java   |    29 +
 .../queue/exception/InvalidSlotException.java |    27 +
 .../queue/factory/NotificationFactory.java    |    51 +
 .../forms/AssignmentListWrapperForm.java      |    44 +
 .../ewi/queue/forms/ParticipantForm.java      |    54 +
 .../queue/helper/BrightspaceCsvHelper.java    |   149 +
 .../tudelft/ewi/queue/helper/EmailHelper.java |   115 +
 .../ewi/queue/helper/MarkDownParser.java      |    33 +
 .../tudelft/ewi/queue/helper/TimeHelper.java  |    31 +
 .../tudelft/ewi/queue/model/Assignment.java   |   128 +
 .../nl/tudelft/ewi/queue/model/Assistant.java |    41 +
 .../nl/tudelft/ewi/queue/model/Course.java    |   408 +
 .../tudelft/ewi/queue/model/DefaultRole.java  |    41 +
 .../nl/tudelft/ewi/queue/model/Direction.java |    33 +
 .../ewi/queue/model/FirstYearMentorGroup.java |    89 +
 .../ewi/queue/model/FirstYearStudent.java     |   117 +
 .../nl/tudelft/ewi/queue/model/Group.java     |    82 +
 .../java/nl/tudelft/ewi/queue/model/Lab.java  |   774 ++
 .../nl/tudelft/ewi/queue/model/LabSlot.java   |    61 +
 .../nl/tudelft/ewi/queue/model/Manager.java   |    41 +
 .../tudelft/ewi/queue/model/Notification.java |   160 +
 .../ewi/queue/model/NotificationList.java     |    56 +
 .../nl/tudelft/ewi/queue/model/Request.java   |   684 ++
 .../ewi/queue/model/RequestApprovedEvent.java |    76 +
 .../ewi/queue/model/RequestAssignedEvent.java |    70 +
 .../ewi/queue/model/RequestCreatedEvent.java  |    63 +
 .../ewi/queue/model/RequestEntity.java        |    91 +
 .../tudelft/ewi/queue/model/RequestEvent.java |    71 +
 .../queue/model/RequestForwardToAnyEvent.java |    70 +
 .../queue/model/RequestForwardedEvent.java    |    76 +
 .../queue/model/RequestProcessingEvent.java   |    62 +
 .../ewi/queue/model/RequestRejectedEvent.java |    91 +
 .../ewi/queue/model/RequestRevokedEvent.java  |    63 +
 .../tudelft/ewi/queue/model/RequestSlot.java  |    63 +
 .../tudelft/ewi/queue/model/RequestType.java  |    81 +
 .../java/nl/tudelft/ewi/queue/model/Role.java |    70 +
 .../java/nl/tudelft/ewi/queue/model/Room.java |   100 +
 .../java/nl/tudelft/ewi/queue/model/Slot.java |   140 +
 .../nl/tudelft/ewi/queue/model/Student.java   |    53 +
 .../ewi/queue/model/StudentNotFoundEvent.java |    84 +
 .../tudelft/ewi/queue/model/Subscription.java |   125 +
 .../nl/tudelft/ewi/queue/model/Teacher.java   |    41 +
 .../java/nl/tudelft/ewi/queue/model/User.java |   319 +
 .../ewi/queue/model/UserPrincipal.java        |    38 +
 .../repository/AssignmentRepository.java      |    25 +
 .../queue/repository/CourseRepository.java    |    52 +
 .../FirstYearMentorGroupRepository.java       |    36 +
 .../FirstYearStudentRepository.java           |    35 +
 .../ewi/queue/repository/GroupRepository.java |    29 +
 .../ewi/queue/repository/LabRepository.java   |    30 +
 .../repository/NotificationRepository.java    |    31 +
 .../queue/repository/RequestRepository.java   |    41 +
 .../repository/RequestTypeRepository.java     |    25 +
 .../ewi/queue/repository/RoleRepository.java  |    28 +
 .../ewi/queue/repository/RoomRepository.java  |    33 +
 .../ewi/queue/repository/UserRepository.java  |    34 +
 .../queue/resolver/UserArgumentResolver.java  |    74 +
 .../ewi/queue/service/AdminService.java       |   127 +
 .../queue/service/AuthenticationService.java  |    61 +
 .../ewi/queue/service/CourseService.java      |    70 +
 .../ewi/queue/service/GroupService.java       |   120 +
 .../tudelft/ewi/queue/service/LabService.java |   388 +
 .../queue/service/NotificationService.java    |    83 +
 .../ewi/queue/service/PermissionService.java  |   533 +
 .../ewi/queue/service/RequestService.java     |   322 +
 .../service/SAMLUserDetailsServiceImpl.java   |   172 +
 .../tudelft/ewi/queue/tasks/RequestTasks.java |    64 +
 .../ewi/queue/validator/LabValidator.java     |    58 +
 .../queue/validator/ParticipantValidator.java |    68 +
 .../ewi/queue/validator/SlotValidator.java    |    42 +
 .../queue/viewmodel/AssignmentViewModel.java  |    70 +
 .../java/nl/tudelft/ewi/queue/views/View.java |    22 +
 .../resources/application.properties.template |    66 +
 backend/src/main/resources/changeLog-h2.yaml  |  1537 +++
 .../src/main/resources/changelog-master.yaml  |    24 +
 backend/src/main/resources/changelog.yaml     |  1510 +++
 .../main/resources/log4j.properties.template  |    40 +
 backend/src/main/resources/logback.xml        |    65 +
 .../src/main/resources/messages.properties    |    22 +
 backend/src/main/resources/migrations.yaml    |    80 +
 backend/src/main/resources/rebel.xml          |    30 +
 .../main/resources/security/samlKeystore.jks  |   Bin 0 -> 4237 bytes
 .../main/resources/static/CHOSEN_LICENSE.md   |    23 +
 .../static/css/bootstrap-multiselect.css      |    18 +
 .../main/resources/static/css/chosen.min.css  |    28 +
 .../src/main/resources/static/css/global.css  |   656 +
 .../src/main/resources/static/css/labview.css |    21 +
 .../css/tempusdominus-bootstrap-4.min.css     |   204 +
 backend/src/main/resources/static/favicon.ico |   Bin 0 -> 15086 bytes
 .../src/main/resources/static/img/icon.png    |   Bin 0 -> 1463 bytes
 .../resources/static/img/tudelft-logo.png     |   Bin 0 -> 4627 bytes
 .../static/js/bootstrap-multiselect.js        |  1416 +++
 .../resources/static/js/chosen.jquery.min.js  |    20 +
 .../resources/static/js/ctrl_enter_submit.js  |    23 +
 .../src/main/resources/static/js/global.js    |    98 +
 .../src/main/resources/static/js/labstatus.js |    58 +
 .../main/resources/static/js/map_loader.js    |    44 +
 backend/src/main/resources/static/js/push.js  |    91 +
 .../main/resources/static/js/request_table.js |   177 +
 .../js/tempusdominus-bootstrap-4.min.js       |     7 +
 .../src/main/resources/static/manifest.json   |    20 +
 backend/src/main/resources/static/sw.js       |    75 +
 .../templates/admin/mentor/groups.html        |    68 +
 .../main/resources/templates/admin/rooms.html |    76 +
 .../resources/templates/admin/rooms/edit.html |    72 +
 .../main/resources/templates/admin/view.html  |    66 +
 .../main/resources/templates/container.html   |    33 +
 .../resources/templates/course/create.html    |    98 +
 .../templates/course/create/participant.html  |    64 +
 .../main/resources/templates/course/edit.html |    79 +
 .../resources/templates/course/enroll.html    |    51 +
 .../resources/templates/course/index.html     |   108 +
 .../templates/course/remove/participant.html  |    39 +
 .../main/resources/templates/course/view.html |    98 +
 .../templates/course/view/assignments.html    |   118 +
 .../templates/course/view/delete.html         |    46 +
 .../templates/course/view/feedback.html       |    75 +
 .../templates/course/view/group.html          |    43 +
 .../templates/course/view/groups.html         |    68 +
 .../resources/templates/course/view/info.html |   123 +
 .../resources/templates/course/view/labs.html |    66 +
 .../templates/course/view/leave.html          |    41 +
 .../templates/course/view/participants.html   |   204 +
 .../templates/course/view/settings.html       |    85 +
 .../templates/course/view/status.html         |   188 +
 .../main/resources/templates/error/403.html   |    37 +
 .../main/resources/templates/error/404.html   |    36 +
 .../templates/error/alreadyEnqueued.html      |    36 +
 .../templates/error/entityNotFound.html       |    36 +
 .../main/resources/templates/error/error.html |    40 +
 .../templates/error/thouShaltNotPass.html     |    39 +
 .../resources/templates/history/index.html    |    52 +
 .../resources/templates/history/student.html  |    47 +
 .../resources/templates/home/dashboard.html   |    80 +
 .../main/resources/templates/home/index.html  |    47 +
 .../main/resources/templates/lab/create.html  |   340 +
 .../main/resources/templates/lab/edit.html    |   255 +
 .../main/resources/templates/lab/enqueue.html |   190 +
 .../main/resources/templates/lab/remove.html  |    42 +
 .../main/resources/templates/lab/view.html    |   382 +
 .../src/main/resources/templates/layout.html  |   129 +
 .../src/main/resources/templates/login.html   |    62 +
 .../templates/notification/index.html         |    84 +
 .../main/resources/templates/pagination.html  |    47 +
 .../resources/templates/request/approve.html  |    63 +
 .../resources/templates/request/forward.html  |    81 +
 .../resources/templates/request/list.html     |   115 +
 .../templates/request/list/filters.html       |   156 +
 .../templates/request/list/pagination.html    |    76 +
 .../templates/request/list/requesttable.html  |    68 +
 .../resources/templates/request/reject.html   |    71 +
 .../resources/templates/request/status.html   |    35 +
 .../resources/templates/request/view.html     |   198 +
 .../templates/saml/idpselection.html          |    38 +
 .../nl/tudelft/ewi/QueueApplicationTests.java |    57 +
 .../controller/CourseControllerTest.java      |   111 +
 .../controller/RequestControllerTest.java     |   257 +
 .../FilterExpressionObjectFactoryTest.java    |   113 +
 .../nl/tudelft/ewi/queue/model/GroupTest.java |   116 +
 .../nl/tudelft/ewi/queue/model/LabTest.java   |   252 +
 .../tudelft/ewi/queue/model/RequestTest.java  |    83 +
 frontend/frontend/.gitignore                  |    23 +
 frontend/frontend/README.md                   |    44 +
 frontend/frontend/package.json                |    36 +
 frontend/frontend/public/favicon.ico          |   Bin 0 -> 3870 bytes
 frontend/frontend/public/index.html           |    43 +
 frontend/frontend/public/logo192.png          |   Bin 0 -> 5347 bytes
 frontend/frontend/public/logo512.png          |   Bin 0 -> 9664 bytes
 frontend/frontend/public/manifest.json        |    25 +
 frontend/frontend/public/robots.txt           |     2 +
 frontend/frontend/src/App.css                 |    33 +
 frontend/frontend/src/App.test.tsx            |     9 +
 frontend/frontend/src/App.tsx                 |    27 +
 frontend/frontend/src/index.css               |    13 +
 frontend/frontend/src/index.tsx               |    12 +
 frontend/frontend/src/logo.svg                |     7 +
 frontend/frontend/src/react-app-env.d.ts      |     1 +
 frontend/frontend/src/serviceWorker.ts        |   143 +
 frontend/frontend/tsconfig.json               |    25 +
 frontend/frontend/yarn.lock                   | 10050 ++++++++++++++++
 gradle/wrapper/gradle-wrapper.jar             |   Bin 56177 -> 56177 bytes
 gradle/wrapper/gradle-wrapper.properties      |     2 +-
 gradlew.bat                                   |   168 +-
 222 files changed, 36295 insertions(+), 85 deletions(-)
 create mode 100644 backend/LICENSE
 create mode 100644 backend/LICENSE.header
 create mode 100644 backend/build.gradle
 create mode 100644 backend/gradle/wrapper/gradle-wrapper.jar
 create mode 100644 backend/gradle/wrapper/gradle-wrapper.properties
 create mode 100644 backend/gradlew
 create mode 100644 backend/gradlew.bat
 create mode 100644 backend/pom.xml
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/DatabaseLoader.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/GlobalMethodSecurityConfig.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/HttpSessionConfig.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/QueueApplication.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/RoleHierarchyConfig.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/SamlWebSecurityConfig.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/WebSecurityConfig.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/WebSocketConfig.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/annotation/AuthenticatedUser.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/AdminController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/AuthController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/CourseController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/ErrorControllerAdvice.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/GroupController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/HistoryController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/HomeController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/LabController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/NotificationController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/RequestController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/RoomController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/controller/SSOController.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/cqsr/Event.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/cqsr/RecordsEvents.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedDialect.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedExpressionObjectFactory.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterDialect.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactory.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeDialect.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeExpressionObjectFactory.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestDialect.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestExpressionObjectFactory.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/exception/AlreadyEnqueuedException.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/exception/InvalidSlotException.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/factory/NotificationFactory.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/forms/AssignmentListWrapperForm.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/forms/ParticipantForm.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/helper/BrightspaceCsvHelper.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/helper/EmailHelper.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/helper/MarkDownParser.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/helper/TimeHelper.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Assignment.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Assistant.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Course.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/DefaultRole.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Direction.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearMentorGroup.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearStudent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Group.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Lab.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/LabSlot.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Manager.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Notification.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/NotificationList.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Request.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestApprovedEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestAssignedEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestCreatedEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEntity.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardToAnyEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardedEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestProcessingEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRejectedEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRevokedEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestSlot.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/RequestType.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Role.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Room.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Slot.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Student.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/StudentNotFoundEvent.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Subscription.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/Teacher.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/User.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/model/UserPrincipal.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/AssignmentRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/CourseRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearMentorGroupRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearStudentRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/GroupRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/LabRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/NotificationRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestTypeRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/RoleRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/RoomRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/repository/UserRepository.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/resolver/UserArgumentResolver.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/AdminService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/AuthenticationService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/CourseService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/GroupService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/LabService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/NotificationService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/PermissionService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/RequestService.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/service/SAMLUserDetailsServiceImpl.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/tasks/RequestTasks.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/validator/LabValidator.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/validator/ParticipantValidator.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/validator/SlotValidator.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/viewmodel/AssignmentViewModel.java
 create mode 100644 backend/src/main/java/nl/tudelft/ewi/queue/views/View.java
 create mode 100644 backend/src/main/resources/application.properties.template
 create mode 100644 backend/src/main/resources/changeLog-h2.yaml
 create mode 100644 backend/src/main/resources/changelog-master.yaml
 create mode 100644 backend/src/main/resources/changelog.yaml
 create mode 100644 backend/src/main/resources/log4j.properties.template
 create mode 100644 backend/src/main/resources/logback.xml
 create mode 100644 backend/src/main/resources/messages.properties
 create mode 100644 backend/src/main/resources/migrations.yaml
 create mode 100644 backend/src/main/resources/rebel.xml
 create mode 100644 backend/src/main/resources/security/samlKeystore.jks
 create mode 100644 backend/src/main/resources/static/CHOSEN_LICENSE.md
 create mode 100644 backend/src/main/resources/static/css/bootstrap-multiselect.css
 create mode 100644 backend/src/main/resources/static/css/chosen.min.css
 create mode 100644 backend/src/main/resources/static/css/global.css
 create mode 100644 backend/src/main/resources/static/css/labview.css
 create mode 100644 backend/src/main/resources/static/css/tempusdominus-bootstrap-4.min.css
 create mode 100644 backend/src/main/resources/static/favicon.ico
 create mode 100644 backend/src/main/resources/static/img/icon.png
 create mode 100644 backend/src/main/resources/static/img/tudelft-logo.png
 create mode 100644 backend/src/main/resources/static/js/bootstrap-multiselect.js
 create mode 100644 backend/src/main/resources/static/js/chosen.jquery.min.js
 create mode 100644 backend/src/main/resources/static/js/ctrl_enter_submit.js
 create mode 100644 backend/src/main/resources/static/js/global.js
 create mode 100644 backend/src/main/resources/static/js/labstatus.js
 create mode 100644 backend/src/main/resources/static/js/map_loader.js
 create mode 100644 backend/src/main/resources/static/js/push.js
 create mode 100644 backend/src/main/resources/static/js/request_table.js
 create mode 100644 backend/src/main/resources/static/js/tempusdominus-bootstrap-4.min.js
 create mode 100644 backend/src/main/resources/static/manifest.json
 create mode 100644 backend/src/main/resources/static/sw.js
 create mode 100644 backend/src/main/resources/templates/admin/mentor/groups.html
 create mode 100644 backend/src/main/resources/templates/admin/rooms.html
 create mode 100644 backend/src/main/resources/templates/admin/rooms/edit.html
 create mode 100644 backend/src/main/resources/templates/admin/view.html
 create mode 100644 backend/src/main/resources/templates/container.html
 create mode 100644 backend/src/main/resources/templates/course/create.html
 create mode 100644 backend/src/main/resources/templates/course/create/participant.html
 create mode 100644 backend/src/main/resources/templates/course/edit.html
 create mode 100644 backend/src/main/resources/templates/course/enroll.html
 create mode 100644 backend/src/main/resources/templates/course/index.html
 create mode 100644 backend/src/main/resources/templates/course/remove/participant.html
 create mode 100644 backend/src/main/resources/templates/course/view.html
 create mode 100644 backend/src/main/resources/templates/course/view/assignments.html
 create mode 100644 backend/src/main/resources/templates/course/view/delete.html
 create mode 100644 backend/src/main/resources/templates/course/view/feedback.html
 create mode 100644 backend/src/main/resources/templates/course/view/group.html
 create mode 100644 backend/src/main/resources/templates/course/view/groups.html
 create mode 100644 backend/src/main/resources/templates/course/view/info.html
 create mode 100644 backend/src/main/resources/templates/course/view/labs.html
 create mode 100644 backend/src/main/resources/templates/course/view/leave.html
 create mode 100644 backend/src/main/resources/templates/course/view/participants.html
 create mode 100644 backend/src/main/resources/templates/course/view/settings.html
 create mode 100644 backend/src/main/resources/templates/course/view/status.html
 create mode 100644 backend/src/main/resources/templates/error/403.html
 create mode 100644 backend/src/main/resources/templates/error/404.html
 create mode 100644 backend/src/main/resources/templates/error/alreadyEnqueued.html
 create mode 100644 backend/src/main/resources/templates/error/entityNotFound.html
 create mode 100644 backend/src/main/resources/templates/error/error.html
 create mode 100644 backend/src/main/resources/templates/error/thouShaltNotPass.html
 create mode 100644 backend/src/main/resources/templates/history/index.html
 create mode 100644 backend/src/main/resources/templates/history/student.html
 create mode 100644 backend/src/main/resources/templates/home/dashboard.html
 create mode 100644 backend/src/main/resources/templates/home/index.html
 create mode 100644 backend/src/main/resources/templates/lab/create.html
 create mode 100644 backend/src/main/resources/templates/lab/edit.html
 create mode 100644 backend/src/main/resources/templates/lab/enqueue.html
 create mode 100644 backend/src/main/resources/templates/lab/remove.html
 create mode 100644 backend/src/main/resources/templates/lab/view.html
 create mode 100644 backend/src/main/resources/templates/layout.html
 create mode 100644 backend/src/main/resources/templates/login.html
 create mode 100644 backend/src/main/resources/templates/notification/index.html
 create mode 100644 backend/src/main/resources/templates/pagination.html
 create mode 100644 backend/src/main/resources/templates/request/approve.html
 create mode 100644 backend/src/main/resources/templates/request/forward.html
 create mode 100644 backend/src/main/resources/templates/request/list.html
 create mode 100644 backend/src/main/resources/templates/request/list/filters.html
 create mode 100644 backend/src/main/resources/templates/request/list/pagination.html
 create mode 100644 backend/src/main/resources/templates/request/list/requesttable.html
 create mode 100644 backend/src/main/resources/templates/request/reject.html
 create mode 100644 backend/src/main/resources/templates/request/status.html
 create mode 100644 backend/src/main/resources/templates/request/view.html
 create mode 100644 backend/src/main/resources/templates/saml/idpselection.html
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/QueueApplicationTests.java
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/queue/controller/CourseControllerTest.java
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/queue/controller/RequestControllerTest.java
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactoryTest.java
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/queue/model/GroupTest.java
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/queue/model/LabTest.java
 create mode 100644 backend/src/test/java/nl/tudelft/ewi/queue/model/RequestTest.java
 create mode 100644 frontend/frontend/.gitignore
 create mode 100644 frontend/frontend/README.md
 create mode 100644 frontend/frontend/package.json
 create mode 100644 frontend/frontend/public/favicon.ico
 create mode 100644 frontend/frontend/public/index.html
 create mode 100644 frontend/frontend/public/logo192.png
 create mode 100644 frontend/frontend/public/logo512.png
 create mode 100644 frontend/frontend/public/manifest.json
 create mode 100644 frontend/frontend/public/robots.txt
 create mode 100644 frontend/frontend/src/App.css
 create mode 100644 frontend/frontend/src/App.test.tsx
 create mode 100644 frontend/frontend/src/App.tsx
 create mode 100644 frontend/frontend/src/index.css
 create mode 100644 frontend/frontend/src/index.tsx
 create mode 100644 frontend/frontend/src/logo.svg
 create mode 100644 frontend/frontend/src/react-app-env.d.ts
 create mode 100644 frontend/frontend/src/serviceWorker.ts
 create mode 100644 frontend/frontend/tsconfig.json
 create mode 100644 frontend/frontend/yarn.lock

diff --git a/backend/LICENSE b/backend/LICENSE
new file mode 100644
index 000000000..9d96f4a29
--- /dev/null
+++ b/backend/LICENSE
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    Queue
+    Copyright (C) 2019  EIP
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published
+    by the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/backend/LICENSE.header b/backend/LICENSE.header
new file mode 100644
index 000000000..4438687c9
--- /dev/null
+++ b/backend/LICENSE.header
@@ -0,0 +1,17 @@
+Queue - A Queueing system that can be used to handle labs in higher education
+Copyright (C) 2016-2019  Delft University of Technology
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+
diff --git a/backend/build.gradle b/backend/build.gradle
new file mode 100644
index 000000000..a4e7b2282
--- /dev/null
+++ b/backend/build.gradle
@@ -0,0 +1,198 @@
+buildscript {
+	ext {
+		springBootVersion = '1.5.22.RELEASE'
+		//springBootVersion = '2.0.9.RELEASE'
+		queryDslVersion = '4.2.1'
+	}
+	repositories {
+		mavenCentral()
+		jcenter()
+	}
+	dependencies {
+		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
+		classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0'
+//		classpath 'org.owasp:dependency-check-gradle:3.3.1'
+		classpath 'org.springframework:springloaded:1.2.8.RELEASE'
+	}
+}
+
+plugins {
+//	id 'java-library'
+	id "org.owasp.dependencycheck" version "5.0.0-M3.1"
+	id "com.github.hierynomus.license" version "0.15.0"
+	id "com.github.hierynomus.license-report" version "0.15.0"
+}
+
+license {
+	header = file("LICENSE.header")
+	skipExistingHeaders true
+}
+
+downloadLicenses {
+	includeProjectDependencies true
+}
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+apply plugin: 'org.springframework.boot'
+//apply plugin: 'io.spring.dependency-management'
+apply plugin: 'com.github.ben-manes.versions'
+//apply plugin: 'org.owasp.dependencycheck'
+apply plugin: 'jacoco'
+
+jar {
+	baseName = 'queue'
+	version = '0.0.1-SNAPSHOT'
+}x
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+repositories {
+	mavenLocal()
+	mavenCentral()
+}
+
+ext['thymeleaf.version'] = '3.0.9.RELEASE'
+ext['thymeleaf-layout-dialect.version'] = '2.3.0'
+
+dependencies {
+	compile 'org.hibernate:hibernate-core:5.2.17.Final'
+	compile 'org.hibernate:hibernate-entitymanager:5.2.17.Final'
+	compile 'org.hibernate:hibernate-java8:5.2.17.Final'
+	//compile("org.springframework.boot:spring-boot-properties-migrator")
+	compile("org.springframework.boot:spring-boot-starter-data-jpa")
+	compile("org.springframework.boot:spring-boot-starter-thymeleaf")
+	compile("org.springframework.boot:spring-boot-starter-web")
+	compile("org.springframework.boot:spring-boot-starter-security")
+	compile("org.springframework.boot:spring-boot-devtools")
+	compile("org.springframework.boot:spring-boot-starter-websocket")
+	compile("org.springframework.boot:spring-boot-starter-actuator")
+	compile("org.springframework:spring-messaging")
+	compile group: "org.springframework.security.extensions", name: "spring-security-saml2-core", version: "1.0.3.RELEASE"
+	compile group: 'org.springframework.boot', name: 'spring-boot-starter-parent', version: "${springBootVersion}", ext: 'pom'
+	compile 'org.springframework.session:spring-session:1.3.3.RELEASE'
+	compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: "${springBootVersion}"
+	compile("org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.2.RELEASE")
+	compile("org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.1.RELEASE")
+	compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
+	compile("org.apache.httpcomponents:fluent-hc:4.5.5")
+	compile("org.bouncycastle:bcprov-jdk15on:1.59")
+	compile("org.bouncycastle:bcpkix-jdk15on:1.59")
+//	compile("org.json:json")
+	compile group: 'org.json', name: 'json', version: '20180130'
+	compile("nl.martijndwars:web-push:3.1.1")
+	compile group: 'com.fasterxml.jackson.module', name: 'jackson-modules-java8', version: '2.9.4', ext: 'pom'
+	compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.12'
+	compile("org.jooq:jool:0.9.12")
+	compile("com.querydsl:querydsl-jpa:$queryDslVersion")
+//	compile("com.querydsl:querydsl-apt:$queryDslVersion:jpa")
+	compile group: 'net.anthavio', name: 'airbrake-logback', version: '1.0.3'
+	
+	compile('javax.xml.bind:jaxb-api:2.3.0')
+	compile('javax.activation:activation:1.1')
+	compile('org.glassfish.jaxb:jaxb-runtime:2.3.0')
+
+	// Assets
+	compile 'org.webjars:jquery:3.4.1'
+	compile group: 'org.webjars.npm', name: 'popper.js', version: '1.13.0'
+	compile 'org.webjars.npm:ramda:0.25.0'
+	compile 'org.webjars:bootstrap:4.0.0'
+	compile 'org.webjars.bower:fontawesome:4.7.0'
+	compile 'org.webjars.bower:momentjs:2.20.1'
+	compile 'org.webjars:stomp-websocket:2.3.3-1'
+	compile 'org.webjars.bower:sockjs-client:1.1.4'
+	compile 'org.webjars:handlebars:4.0.6'
+	compile group: 'org.webjars', name: 'chartjs', version: '2.7.0'
+	compile "com.sun.mail:javax.mail:1.6.2"
+	compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
+
+	compile("com.h2database:h2")
+	compile("org.liquibase:liquibase-core")
+	testCompile group: "com.github.fppt", name: "jedis-mock", version: "0.1.8"
+	compile 'com.vladsch.flexmark:flexmark-all:0.22.4'
+	
+	testCompile('org.springframework.boot:spring-boot-starter-test')
+	testCompile("org.postgresql:postgresql:9.4.1212")
+	testCompile group: 'org.springframework.security', name: 'spring-security-test', version: '4.2.7.RELEASE'
+
+	annotationProcessor (
+		"com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
+		"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
+		"javax.annotation:javax.annotation-api:1.3.2",
+	)
+
+
+//	annotationProcessor("javax.persistence:javax.persistence-api")
+//	annotationProcessor("com.querydsl:querydsl-apt:$queryDslVersion:jpa")
+
+//	api(
+//			"com.querydsl:querydsl-jpa:$queryDslVersion"
+//	)
+
+//	implementation(
+//		platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"),
+//		'org.springframework.boot:spring-boot-starter-validation',
+//		'org.springframework.boot:spring-boot-starter-data-jpa'
+//	)
+
+//	annotationProcessor(
+//		platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"),
+//		'jakarta.persistence:jakarta.persistence-api',
+//		'jakarta.annotation:jakarta.annotation-api',
+//		"com.querydsl:querydsl-apt:$queryDslVersion:jpa"
+//	)
+
+}
+
+compileJava {
+	options.compilerArgs << "-s"
+	options.compilerArgs << "$projectDir/generated"
+	options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+
+	doFirst {
+		file(new File(projectDir, "/generated")).mkdirs()
+	}
+}
+
+eclipse {
+	classpath {
+		 containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
+		 containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
+	}
+}
+
+bootRun {
+	addResources = true
+}
+
+idea {
+	module {
+		sourceDirs += file('generated/')
+		generatedSourceDirs += file('generated/')
+		inheritOutputDirs = false
+		outputDir = file("$buildDir/classes/main/")
+	}
+}
+
+jacocoTestReport {
+	group = "Reporting"
+	reports {
+		xml.enabled = true
+		csv.enabled = false
+		html.destination file("${buildDir}/reports/coverage")
+	}
+}
+
+// to get it to work again with JDK 10
+
+//tasks.withType(AbstractCompile) {
+//  options.compilerArgs += ["--add-modules", "java.xml.bind"]
+//}
+
+//tasks.withType(Test) {
+//  jvmArgs += ["--add-modules", "java.xml.bind"]
+//}
+
diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..29953ea141f55e3b8fc691d31b5ca8816d89fa87
GIT binary patch
literal 56177
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
UvVl|@F&Ht#3o<bLISJwc0Qv(lCjbBd

literal 0
HcmV?d00001

diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..f4d7b2bf6
--- /dev/null
+++ b/backend/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/backend/gradlew b/backend/gradlew
new file mode 100644
index 000000000..cccdd3d51
--- /dev/null
+++ b/backend/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/backend/gradlew.bat b/backend/gradlew.bat
new file mode 100644
index 000000000..f9553162f
--- /dev/null
+++ b/backend/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/backend/pom.xml b/backend/pom.xml
new file mode 100644
index 000000000..569aaf12a
--- /dev/null
+++ b/backend/pom.xml
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>nl.tudelft.ewi</groupId>
+  <artifactId>queue</artifactId>
+  <version>0.9</version>
+
+  <properties>
+    <maven.compiler.source>8</maven.compiler.source>
+    <maven.compiler.target>8</maven.compiler.target>
+    <querydsl.version>4.2.1</querydsl.version>
+  </properties>
+
+  <parent>
+	  <groupId>org.springframework.boot</groupId>
+	  <artifactId>spring-boot-starter-parent</artifactId>
+	  <!-- version>2.0.5.RELEASE</version -->
+	  <version>1.5.15.RELEASE</version>
+  </parent>
+
+  <build>
+	  <plugins>
+		  <plugin>
+			  <artifactId>maven-compiler-plugin</artifactId>
+
+			  <configuration>
+				  <useIncrementalCompilation>false</useIncrementalCompilation>
+			  </configuration>
+		  </plugin>
+		  <plugin>
+			  <groupId>org.springframework.boot</groupId>
+			  <artifactId>spring-boot-maven-plugin</artifactId>
+		  </plugin>
+          <plugin>
+            <groupId>com.mysema.maven</groupId>
+            <artifactId>apt-maven-plugin</artifactId>
+            <version>1.1.3</version>
+            <executions>
+              <execution>
+                <goals>
+                  <goal>process</goal>
+                </goals>
+                <configuration>
+					<outputDirectory>target/generated-sources/annotations</outputDirectory>
+					<!-- processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor -->
+					<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+		  <plugin>
+			  <groupId>org.jacoco</groupId>
+			  <artifactId>jacoco-maven-plugin</artifactId>
+			  <version>0.8.4</version>
+			  <executions>
+				  <execution>
+					  <goals>
+						  <goal>prepare-agent</goal>
+					  </goals>
+				  </execution>
+				  <!-- attached to Maven test phase -->
+				  <execution>
+					  <id>report</id>
+					  <phase>test</phase>
+					  <goals>
+						  <goal>report</goal>
+					  </goals>
+				  </execution>
+			  </executions>
+		  </plugin>
+	  </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.hibernate</groupId>
+      <artifactId>hibernate-core</artifactId>
+      <version>5.2.17.Final</version>
+    </dependency>
+    <dependency>
+      <groupId>org.hibernate</groupId>
+      <artifactId>hibernate-entitymanager</artifactId>
+      <version>5.2.17.Final</version>
+    </dependency>
+    <dependency>
+      <groupId>org.hibernate</groupId>
+      <artifactId>hibernate-java8</artifactId>
+      <version>5.2.17.Final</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-jpa</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-thymeleaf</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-websocket</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-messaging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.security.extensions</groupId>
+      <artifactId>spring-security-saml2-core</artifactId>
+      <version>1.0.3.RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-parent</artifactId>
+      <version>1.5.15.RELEASE</version>
+      <type>pom</type>
+      <exclusions>
+        <exclusion>
+          <artifactId>*</artifactId>
+          <groupId>*</groupId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.session</groupId>
+      <artifactId>spring-session</artifactId>
+      <version>1.3.3.RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-data-redis</artifactId>
+      <version>1.5.15.RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>org.thymeleaf.extras</groupId>
+      <artifactId>thymeleaf-extras-springsecurity4</artifactId>
+      <version>3.0.2.RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>org.thymeleaf.extras</groupId>
+      <artifactId>thymeleaf-extras-java8time</artifactId>
+      <version>3.0.1.RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>nz.net.ultraq.thymeleaf</groupId>
+      <artifactId>thymeleaf-layout-dialect</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>fluent-hc</artifactId>
+      <version>4.5.5</version>
+    </dependency>
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcprov-jdk15on</artifactId>
+      <version>1.59</version>
+    </dependency>
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcpkix-jdk15on</artifactId>
+      <version>1.59</version>
+    </dependency>
+    <dependency>
+      <groupId>org.json</groupId>
+      <artifactId>json</artifactId>
+      <version>20180130</version>
+    </dependency>
+    <dependency>
+      <groupId>nl.martijndwars</groupId>
+      <artifactId>web-push</artifactId>
+      <version>3.1.1</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-modules-java8</artifactId>
+      <version>2.9.4</version>
+      <type>pom</type>
+      <exclusions>
+        <exclusion>
+          <artifactId>*</artifactId>
+          <groupId>*</groupId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <version>8.0.12</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jooq</groupId>
+      <artifactId>jool</artifactId>
+      <version>0.9.12</version>
+    </dependency>
+    <dependency>
+      <groupId>com.querydsl</groupId>
+      <artifactId>querydsl-jpa</artifactId>
+      <version>${querydsl.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.querydsl</groupId>
+      <artifactId>querydsl-core</artifactId>
+      <version>${querydsl.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.querydsl</groupId>
+      <artifactId>querydsl-apt</artifactId>
+      <version>${querydsl.version}</version>
+      <classifier>jpa</classifier>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.anthavio</groupId>
+      <artifactId>airbrake-logback</artifactId>
+      <version>1.0.3</version>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
+      <artifactId>jquery</artifactId>
+      <version>3.4.1</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars.npm</groupId>
+      <artifactId>popper.js</artifactId>
+      <version>1.13.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars.npm</groupId>
+      <artifactId>ramda</artifactId>
+      <version>0.25.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
+      <artifactId>bootstrap</artifactId>
+      <version>4.0.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars.bower</groupId>
+      <artifactId>fontawesome</artifactId>
+      <version>4.7.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars.bower</groupId>
+      <artifactId>momentjs</artifactId>
+      <version>2.20.1</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
+      <artifactId>stomp-websocket</artifactId>
+      <version>2.3.3-1</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars.bower</groupId>
+      <artifactId>sockjs-client</artifactId>
+      <version>1.1.4</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
+      <artifactId>handlebars</artifactId>
+      <version>4.0.6</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.webjars</groupId>
+      <artifactId>chartjs</artifactId>
+      <version>2.7.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.sun.mail</groupId>
+      <artifactId>javax.mail</artifactId>
+      <version>1.6.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.vladsch.flexmark</groupId>
+      <artifactId>flexmark-all</artifactId>
+      <version>0.22.4</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.github.fppt</groupId>
+      <artifactId>jedis-mock</artifactId>
+      <version>0.1.8</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <version>9.4.1212</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-test</artifactId>
+      <version>4.2.7.RELEASE</version>
+      <scope>test</scope>
+    </dependency>
+	<dependency>
+		<groupId>javax.xml.bind</groupId>
+		<artifactId>jaxb-api</artifactId>
+		<version>2.3.0</version>
+	</dependency>
+	<dependency>
+		<groupId>org.springframework.data</groupId>
+		<artifactId>spring-data-commons-core</artifactId>
+		<version>1.1.0.RELEASE</version>
+	</dependency>
+
+  </dependencies>
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.thymeleaf</groupId>
+        <artifactId>thymeleaf</artifactId>
+        <version>3.0.9.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.thymeleaf</groupId>
+        <artifactId>thymeleaf-spring4</artifactId>
+        <version>3.0.9.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>nz.net.ultraq.thymeleaf</groupId>
+        <artifactId>thymeleaf-layout-dialect</artifactId>
+        <version>2.3.0</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+</project>
+
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/DatabaseLoader.java b/backend/src/main/java/nl/tudelft/ewi/queue/DatabaseLoader.java
new file mode 100644
index 000000000..9ba5f2589
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/DatabaseLoader.java
@@ -0,0 +1,312 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.AssignmentRepository;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.RequestTypeRepository;
+import nl.tudelft.ewi.queue.repository.RoomRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@Profile("!production")
+public class DatabaseLoader {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private LabRepository labRepository;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
+    private RequestRepository requestRepository;
+
+    @Autowired
+    private RequestTypeRepository requestTypeRepository;
+
+    @Autowired
+    private RoomRepository roomRepository;
+
+    private static final Logger logger = LoggerFactory.getLogger(DatabaseLoader.class);
+
+    @Autowired
+    private AssignmentRepository assignmentRepository;
+
+    public DatabaseLoader() {
+    }
+
+    /**
+     * Create a new user with the given name as username, password and displayname
+     *
+     * @param name
+     *
+     * @return
+     */
+    private static User dummyUser(String name, DefaultRole defaultRole) {
+        return new User(name + "@tudelft.nl", name, name, "a@b.c", defaultRole, -2);
+    }
+
+    private void saveUserWithCheck(User user) {
+        String newName = StringUtils.stripAccents(user.getDisplayName());
+        logger.info("inserted: {}", newName);
+        user.setDisplayName(newName);
+        userRepository.save(user);
+
+    }
+
+    @PostConstruct
+    private void initDatabase() {
+        // Normal students
+        List<User> students = new ArrayList<>();
+
+        for (int i = 1; i <= 100; i++) {
+            User student = dummyUser("student" + i, DefaultRole.ROLE_STUDENT);
+            students.add(student);
+            userRepository.save(student);
+        }
+
+    	// Special users
+        User student12 = userRepository.save(new User("studentCedille@tudelft.nl", "studentCedille", "??????tudent12", "a@b" +
+                ".c", DefaultRole.ROLE_STUDENT, -2));
+        saveUserWithCheck(student12);
+
+        User student13 = new User("utf8@tudelft.nl", "", "UTF8 Mu??????i??????",
+                "utf8@tudelft.net", DefaultRole.ROLE_STUDENT, -2);
+        saveUserWithCheck(student13);
+
+
+        User assistant1 = userRepository.save(dummyUser("assistant1", DefaultRole.ROLE_STUDENT));
+        User assistant2 = userRepository.save(dummyUser("assistant2", DefaultRole.ROLE_STUDENT));
+        User assistant3 = userRepository.save(dummyUser("assistant3", DefaultRole.ROLE_STUDENT));
+        User assistant4 = userRepository.save(dummyUser("assistant4", DefaultRole.ROLE_STUDENT));
+
+        User manager1 = userRepository.save(dummyUser("manager1", DefaultRole.ROLE_STUDENT));
+        User manager2 = userRepository.save(dummyUser("manager2", DefaultRole.ROLE_STUDENT));
+        User manager3 = userRepository.save(dummyUser("manager3", DefaultRole.ROLE_STUDENT));
+
+        User teacher1 = userRepository.save(dummyUser("teacher1", DefaultRole.ROLE_TEACHER));
+        User teacher2 = userRepository.save(dummyUser("teacher2", DefaultRole.ROLE_TEACHER));
+        User teacher3 = userRepository.save(dummyUser("teacher3", DefaultRole.ROLE_TEACHER));
+        User teacher4 = userRepository.save(dummyUser("teacher4", DefaultRole.ROLE_TEACHER));
+
+        User admin1 = userRepository.save(dummyUser("admin", DefaultRole.ROLE_ADMIN));
+        User admin2 = userRepository.save(dummyUser("admin2", DefaultRole.ROLE_ADMIN));
+
+        // Course
+        Course course1 = new Course("Algorithms & Datastructures", "TI1316");
+        Assignment assignment1 = new Assignment(course1, "Assembly programming");
+
+        course1.addAssignment(assignment1);
+
+
+        courseRepository.save(course1);
+        assignmentRepository.save(assignment1);
+
+        Course course2 = new Course("Concepts of Programming Languages", "TI2606");
+        Assignment assignment2 = new Assignment(course2, "Interpreter implementation");
+
+        course2.addAssignment(assignment2);
+
+        courseRepository.save(course2);
+        assignmentRepository.save(assignment2);
+
+        Course course3 = new Course("OOP", "TI1206");
+        course3.addAssignment(new Assignment(course3, "Week 1"));
+        course3.addAssignment(new Assignment(course3, "Week 2"));
+        course3.addAssignment(new Assignment(course3, "Week 3"));
+        course3.addAssignment(new Assignment(course3, "Week 4"));
+        course3.addAssignment(new Assignment(course3, "Week 5"));
+        course3.addAssignment(new Assignment(course3, "Week 6"));
+        course3.addAssignment(new Assignment(course3, "Week 7"));
+        course3.addAssignment(new Assignment(course3, "Week 8"));
+        course3.addAssignment(new Assignment(course3, "Week 9"));
+
+        courseRepository.save(course3);
+
+        Course course4 = new Course("Computerorganisation", "TI1406");
+        course4.addAssignment(new Assignment(course4, "Assembly factorial"));
+        course4.addAssignment(new Assignment(course4, "Assembly ping-pong"));
+
+        courseRepository.save(course4);
+
+        for (int i = 0; i < 25; i++) {
+            User student1 = students.get(i);
+            User student2 = students.get(i + 25);
+            User student3 = students.get(i + 50);
+            User student4 = students.get(i + 75);
+            student1.addRole(new Student(student1, course1, LocalDateTime.now()));
+            student2.addRole(new Student(student2, course2, LocalDateTime.now()));
+            student3.addRole(new Student(student3, course3, LocalDateTime.now()));
+            student4.addRole(new Student(student4, course4, LocalDateTime.now()));
+            userRepository.save(student1);
+            userRepository.save(student2);
+            userRepository.save(student3);
+            userRepository.save(student4);
+        }
+
+        assistant1.addRole(new Assistant(assistant1, course1));
+        assistant2.addRole(new Assistant(assistant2, course1));
+		assistant3.addRole(new Assistant(assistant3, course1));
+		assistant4.addRole(new Assistant(assistant4, course1));
+
+		assistant1.addRole(new Student(assistant1, course2, LocalDateTime.now()));
+        assistant2.addRole(new Assistant(assistant2, course2));
+        assistant3.addRole(new Assistant(assistant3, course2));
+		assistant4.addRole(new Assistant(assistant4, course2));
+
+		assistant1.addRole(new Student(assistant1, course3, LocalDateTime.now()));
+		assistant2.addRole(new Student(assistant2, course3, LocalDateTime.now()));
+        assistant3.addRole(new Assistant(assistant3, course3));
+        assistant4.addRole(new Assistant(assistant4, course3));
+
+		assistant1.addRole(new Student(assistant1, course4, LocalDateTime.now()));
+		assistant2.addRole(new Student(assistant2, course4, LocalDateTime.now()));
+		assistant3.addRole(new Student(assistant3, course4, LocalDateTime.now()));
+        assistant4.addRole(new Assistant(assistant4, course4));
+
+        manager1.addRole(new Manager(manager1, course1));
+        manager2.addRole(new Manager(manager2, course2));
+        manager3.addRole(new Manager(manager3, course2));
+
+        teacher1.addRole(new Teacher(teacher1, course1));
+        teacher2.addRole(new Teacher(teacher2, course1));
+        teacher3.addRole(new Teacher(teacher3, course2));
+        teacher4.addRole(new Teacher(teacher4, course2));
+
+        userRepository.save(assistant1);
+        userRepository.save(assistant2);
+        userRepository.save(assistant3);
+        userRepository.save(assistant4);
+
+        userRepository.save(manager1);
+        userRepository.save(manager2);
+        userRepository.save(manager3);
+
+        userRepository.save(teacher1);
+        userRepository.save(teacher2);
+        userRepository.save(teacher3);
+        userRepository.save(teacher4);
+
+        userRepository.save(admin1);
+        userRepository.save(admin2);
+
+        RequestType requestType = new RequestType();
+        requestType.setName("Submission");
+        requestTypeRepository.save(requestType);
+        RequestType questionType = new RequestType();
+        questionType.setName("Question");
+        requestTypeRepository.save(questionType);
+
+        // Labs
+        Lab lab1 = new Lab();
+        lab1.setCourse(course1);
+        lab1.setSlot(new LabSlot(LocalDateTime.now(), LocalDateTime.now().plusHours(4)));
+        lab1.setSignOffIntervals(true);
+        lab1.setIntervalTime(15L);
+        lab1.setCapacity(3L);
+        lab1.addAssignment(assignment1);
+        ArrayList<RequestType> requestTypes = new ArrayList<>();
+        requestTypes.add(requestType);
+        requestTypes.add(questionType);
+        lab1.setAllowedRequestTypes(requestTypes);
+        Room room1 = new Room("DW-PC 4 (060 1verd)");
+        Room room2 = new Room("DW-PC 3 (010 1verd)");
+        Room room3 = new Room("DW-PC 2 (200BG)");
+        roomRepository.save(room1);
+        roomRepository.save(room2);
+        roomRepository.save(room3);
+
+        lab1.addRoom(room1);
+        lab1.addRoom(room2);
+        lab1.addRoom(room3);
+        lab1.setDirection(Direction.STUDENT_VISIT_TA);
+
+        roomRepository.save(room1);
+        roomRepository.save(room2);
+        roomRepository.save(room3);
+        labRepository.save(lab1);
+
+        Lab lab2 = new Lab();
+        lab2.setCourse(course1);
+        lab2.setSlot(new LabSlot(LocalDateTime.now(), LocalDateTime.now().plusHours(4)));
+        lab2.setSignOffIntervals(true);
+        lab2.setIntervalTime(15L);
+        lab2.setCapacity(3L);
+        lab2.addAssignment(assignment1);
+        lab2.setAllowedRequestTypes(requestTypes);
+        Room room4 = new Room("DW-PC 3 (010 1verd)2");
+        Room room5 = new Room("DW-PC 2 (200BG)2");
+        lab2.addRoom(room4);
+        lab2.addRoom(room5);
+        lab2.setDirection(Direction.STUDENT_VISIT_TA);
+
+        assignment1.addLab(lab1);
+        assignment1.addLab(lab2);
+
+
+        roomRepository.save(room4);
+        roomRepository.save(room5);
+        labRepository.save(lab2);
+        assignmentRepository.save(assignment1);
+
+        Lab lab3 = new Lab();
+        lab3.setCourse(course2);
+        lab3.setSlot(new LabSlot(LocalDateTime.now(), LocalDateTime.now().plusHours(4)));
+        lab3.setSignOffIntervals(true);
+        lab3.setIntervalTime(15L);
+        lab3.setCapacity(3L);
+        lab3.addAssignment(assignment2);
+        lab3.setAllowedRequestTypes(requestTypes);
+        Room room6 = new Room("DW-PC 3 (010 1verd)1");
+        Room room7 = new Room("DW-PC 2 (200BG)1");
+        lab3.addRoom(room6);
+        lab3.addRoom(room7);
+        lab3.setDirection(Direction.STUDENT_VISIT_TA);
+
+        assignment2.addLab(lab3);
+
+        roomRepository.save(room6);
+        roomRepository.save(room7);
+        labRepository.save(lab3);
+        assignmentRepository.save(assignment2);
+
+        for (int i = 0; i < 25; i++) {
+            Request request1 = new Request(students.get(i), assignment1, room1, requestType, "", lab1);
+            Request request2 = new Request(students.get(i), assignment2, room1, requestType, "", lab2);
+            requestRepository.save(request1);
+            requestRepository.save(request2);
+        }
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/GlobalMethodSecurityConfig.java b/backend/src/main/java/nl/tudelft/ewi/queue/GlobalMethodSecurityConfig.java
new file mode 100644
index 000000000..f6c3906ab
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/GlobalMethodSecurityConfig.java
@@ -0,0 +1,44 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
+import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
+
+import javax.annotation.Resource;
+
+@Configuration
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
+    @Resource
+    private RoleHierarchy roleHierarchy;
+
+    @Override
+    protected MethodSecurityExpressionHandler createExpressionHandler() {
+        DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler =
+            (DefaultMethodSecurityExpressionHandler) super.createExpressionHandler();
+
+        methodSecurityExpressionHandler.setRoleHierarchy(roleHierarchy);
+
+        return methodSecurityExpressionHandler;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/HttpSessionConfig.java b/backend/src/main/java/nl/tudelft/ewi/queue/HttpSessionConfig.java
new file mode 100644
index 000000000..725d6c311
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/HttpSessionConfig.java
@@ -0,0 +1,27 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+
+@Profile("production")
+@EnableRedisHttpSession
+public class HttpSessionConfig {
+}
+
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/QueueApplication.java b/backend/src/main/java/nl/tudelft/ewi/queue/QueueApplication.java
new file mode 100644
index 000000000..2495aab99
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/QueueApplication.java
@@ -0,0 +1,152 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package nl.tudelft.ewi.queue;
+
+import nl.martijndwars.webpush.PushService;
+import nl.martijndwars.webpush.Utils;
+import nl.tudelft.ewi.queue.dialect.AuthenticatedDialect;
+import nl.tudelft.ewi.queue.dialect.FilterDialect;
+import nl.tudelft.ewi.queue.dialect.PrettyTimeDialect;
+import nl.tudelft.ewi.queue.dialect.RequestDialect;
+import nl.tudelft.ewi.queue.helper.MarkDownParser;
+import nl.tudelft.ewi.queue.resolver.UserArgumentResolver;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.i18n.FixedLocaleResolver;
+import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
+import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
+
+import javax.annotation.PostConstruct;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.spec.InvalidKeySpecException;
+import java.util.List;
+import java.util.Locale;
+
+@EntityScan(basePackageClasses = {QueueApplication.class, Jsr310JpaConverters.class})
+@SpringBootApplication
+@EnableScheduling
+@EnableCaching
+public class QueueApplication extends
+		WebMvcConfigurerAdapter {
+	private static final Logger logger = LoggerFactory.getLogger(QueueApplication.class);
+	private static final LocaleResolver localeResolver = new FixedLocaleResolver(Locale.ENGLISH);
+	private static final Java8TimeDialect java8TimeDialect = new Java8TimeDialect();
+	private static final AuthenticatedDialect authenticatedDialect = new AuthenticatedDialect();
+	private static final PrettyTimeDialect prettyTimeDialect = new PrettyTimeDialect();
+	private static final RequestDialect requestDialect = new RequestDialect();
+	private static final FilterDialect filterDialect = new FilterDialect();
+	private static final SpringSecurityDialect springSecurityDialect = new SpringSecurityDialect();
+	private static final MarkDownParser markdownParser = new MarkDownParser();
+
+	@Autowired
+	private ApplicationContext context;
+
+	@Value("${queue.gcmApiKey}")
+	private String gcmApiKey;
+
+	@Value("${queue.pushPublicKey}")
+	private String pushPublicKey;
+
+	@Value("${queue.pushPrivateKey}")
+	private String pushPrivateKey;
+
+	public static void main(String[] args) {
+		SpringApplication.run(QueueApplication.class, args);
+	}
+
+	@PostConstruct
+	public static void registerSecurityProvider() {
+		Security.addProvider(new BouncyCastleProvider());
+	}
+
+	@Override
+	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+		argumentResolvers.add(context.getBean(UserArgumentResolver.class));
+	}
+
+	@Bean
+	public static LocaleResolver localeResolver() {
+		return localeResolver;
+	}
+
+	@Bean
+	public PushService pushService() {
+		PushService pushService = new PushService(gcmApiKey);
+		try {
+			pushService.setPublicKey(Utils.loadPublicKey(pushPublicKey));
+			pushService.setPrivateKey(Utils.loadPrivateKey(pushPrivateKey));
+		} catch (IllegalArgumentException | NoSuchProviderException | NoSuchAlgorithmException | InvalidKeySpecException | NoSuchMethodError e) {
+			// TODO FIXME Catching NoSuchMethodError here because maven has trouble loading Bouncy Castle
+			logger.error("Could not load keys for push API");
+		}
+
+		return pushService;
+	}
+
+	@Bean
+	public static Java8TimeDialect java8TimeDialect() {
+		return java8TimeDialect;
+	}
+
+	@Bean
+	public static AuthenticatedDialect authenticatedDialect() {
+		return authenticatedDialect;
+	}
+
+	@Bean
+	public static PrettyTimeDialect prettyTimeDialect() {
+		return prettyTimeDialect;
+	}
+
+	@Bean
+	public static RequestDialect requestDialect() {
+		return requestDialect;
+	}
+
+	@Bean
+	public static FilterDialect filterDialect() {
+		return filterDialect;
+	}
+
+	@Bean
+	public static SpringSecurityDialect securityDialect() {
+		return springSecurityDialect;
+	}
+
+	@Bean
+	public static MarkDownParser markDownParser() {
+		return markdownParser;
+	}
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/RoleHierarchyConfig.java b/backend/src/main/java/nl/tudelft/ewi/queue/RoleHierarchyConfig.java
new file mode 100644
index 000000000..fdd668b73
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/RoleHierarchyConfig.java
@@ -0,0 +1,34 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
+
+@Configuration
+public class RoleHierarchyConfig {
+    @Bean
+    public RoleHierarchy roleHierarchy() {
+        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
+        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_TEACHER > ROLE_STUDENT");
+
+        return roleHierarchy;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/SamlWebSecurityConfig.java b/backend/src/main/java/nl/tudelft/ewi/queue/SamlWebSecurityConfig.java
new file mode 100644
index 000000000..6c20c4349
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/SamlWebSecurityConfig.java
@@ -0,0 +1,570 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import nl.tudelft.ewi.queue.service.SAMLUserDetailsServiceImpl;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.velocity.app.VelocityEngine;
+import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
+import org.opensaml.saml2.metadata.provider.MetadataProvider;
+import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.xml.parse.ParserPool;
+import org.opensaml.xml.parse.StaticBasicParserPool;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.saml.*;
+import org.springframework.security.saml.context.SAMLContextProviderImpl;
+import org.springframework.security.saml.context.SAMLContextProviderLB;
+import org.springframework.security.saml.key.JKSKeyManager;
+import org.springframework.security.saml.key.KeyManager;
+import org.springframework.security.saml.log.SAMLDefaultLogger;
+import org.springframework.security.saml.metadata.*;
+import org.springframework.security.saml.parser.ParserPoolHolder;
+import org.springframework.security.saml.processor.*;
+import org.springframework.security.saml.util.VelocityFactory;
+import org.springframework.security.saml.websso.*;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.FilterChainProxy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.access.channel.ChannelProcessingFilter;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
+import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Timer;
+
+@Profile("production")
+@Configuration
+@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
+@EnableWebSecurity
+public class SamlWebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Autowired
+    private ResourceLoader resourceLoader;
+
+    @Autowired
+    private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;
+
+    @Value("${saml.keystore.path}")
+    private String keystorePath;
+
+    @Value("${saml.keystore.alias}")
+    private String keystoreAlias;
+
+    @Value("${saml.keystore.password}")
+    private String keystorePassword;
+
+    @Value("${saml.entityId}")
+    private String entityId;
+
+    @Value("${saml.entityBaseURL}")
+    private String entityBaseURL;
+
+    @Value("${saml.contextProvider.scheme}")
+    private String contextProviderScheme;
+
+    @Value("${saml.contextProvider.serverName}")
+    private String contextProviderServerName;
+
+    @Value("${saml.contextProvider.port}")
+    private int contextProviderPort;
+
+    protected HttpSecurity samlizedConfig(HttpSecurity http) throws Exception {
+        //@formatter:off
+        http
+            .httpBasic()
+                .authenticationEntryPoint(samlEntryPoint())
+                .and()
+            .csrf()
+                .ignoringAntMatchers("/saml/**")
+                .and()
+            .authorizeRequests()
+                .antMatchers("/saml/**")
+                .permitAll()
+                .and()
+            .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
+            .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class)
+            .exceptionHandling().accessDeniedPage("/error.html");
+        //@formatter:on
+
+        return http;
+    }
+    
+    /**
+     * Defines the web based security configuration.
+     *
+     * @param http It allows configuring web based security for specific http requests.
+     * @throws Exception
+     */
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http = samlizedConfig(http);
+
+        http.authorizeRequests()
+            .antMatchers("/").permitAll()
+            .antMatchers("/manifest.json").permitAll()
+            .antMatchers("/favicon.ico").permitAll()
+            .antMatchers("/sw.js").permitAll()
+            .antMatchers("/css/**").permitAll()
+            .antMatchers("/img/**").permitAll()
+            .antMatchers("/js/**").permitAll()
+            .antMatchers("/webjars/**").permitAll()
+            .antMatchers("/stomp/**").permitAll()
+            .antMatchers("/saml/**").permitAll()
+            .antMatchers("/lab/submit*").permitAll()
+            .anyRequest()
+            .authenticated();
+
+        http
+            .logout()
+            .logoutSuccessUrl("/");
+    }
+
+    @Bean
+    public static SAMLBootstrap SAMLBootstrap() {
+        return new SAMLBootstrap();
+    }
+
+    // Initialization of the velocity engine
+    @Bean
+    public VelocityEngine velocityEngine() {
+        return VelocityFactory.getEngine();
+    }
+
+    // XML parser pool needed for OpenSAML parsing
+    @Bean(initMethod = "initialize")
+    public StaticBasicParserPool parserPool() {
+        return new StaticBasicParserPool();
+    }
+
+    @Bean(name = "parserPoolHolder")
+    public ParserPoolHolder parserPoolHolder() {
+        return new ParserPoolHolder();
+    }
+
+    // Bindings, encoders and decoders used for creating and parsing messages
+    @Bean
+    public MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager() {
+        return new MultiThreadedHttpConnectionManager();
+    }
+
+    @Bean
+    public HttpClient httpClient() {
+        return new HttpClient(multiThreadedHttpConnectionManager());
+    }
+
+    // SAML Authentication Provider responsible for validating of received SAML messages
+    @Bean
+    public SAMLAuthenticationProvider samlAuthenticationProvider() {
+        SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
+        samlAuthenticationProvider.setUserDetails(samlUserDetailsServiceImpl);
+        samlAuthenticationProvider.setForcePrincipalAsString(false);
+
+        return samlAuthenticationProvider;
+    }
+
+    /**
+     * Provider of default SAML Context.
+     *
+     * Normally, we would return an instance of SAMLContextProviderImpl. For
+     * queue, we return an instance of SAMLContextProviderLB. This provider
+     * deals with reverse proxies, where SSL is terminated before the request
+     * hits the application. See also https://goo.gl/GXi3Q1.
+     *
+     * @return
+     */
+    @Bean
+    public SAMLContextProviderImpl contextProvider() {
+        SAMLContextProviderLB samlContextProvider = new SAMLContextProviderLB();
+        samlContextProvider.setScheme(contextProviderScheme);
+        samlContextProvider.setServerName(contextProviderServerName);
+        samlContextProvider.setServerPort(contextProviderPort);
+        samlContextProvider.setIncludeServerPortInRequestURL(false);
+        samlContextProvider.setContextPath("/");
+
+        return samlContextProvider;
+    }
+
+    // Logger for SAML messages and events
+    @Bean
+    public SAMLDefaultLogger samlLogger() {
+        return new SAMLDefaultLogger();
+    }
+
+    // SAML 2.0 WebSSO Assertion Consumer
+    @Bean
+    public WebSSOProfileConsumer webSSOprofileConsumer() {
+        return new WebSSOProfileConsumerImpl();
+    }
+
+    // SAML 2.0 Holder-of-Key WebSSO Assertion Consumer
+    @Bean
+    public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer() {
+        return new WebSSOProfileConsumerHoKImpl();
+    }
+
+    // SAML 2.0 Web SSO profile
+    @Bean
+    public WebSSOProfile webSSOprofile() {
+        return new WebSSOProfileImpl();
+
+        // WebSSOProfileImpl defines maxAuthenticationAge as 7200 seconds. Increase if necessary
+    }
+
+    // SAML 2.0 Holder-of-Key Web SSO profile
+    @Bean
+    public WebSSOProfileConsumerHoKImpl hokWebSSOProfile() {
+        return new WebSSOProfileConsumerHoKImpl();
+    }
+
+    // SAML 2.0 ECP profile
+    @Bean
+    public WebSSOProfileECPImpl ecpprofile() {
+        return new WebSSOProfileECPImpl();
+    }
+
+    @Bean
+    public SingleLogoutProfile logoutprofile() {
+        return new SingleLogoutProfileImpl();
+    }
+
+    // Central storage of cryptographic keys
+    @Bean
+    public KeyManager keyManager() {
+        return new JKSKeyManager(
+            // File pointing to the keystore
+            resourceLoader.getResource(keystorePath),
+            // Password to access the keystore (null = no password)
+            keystorePassword,
+            // Passwords used to access private keys
+            ImmutableMap.of(keystoreAlias, keystorePassword),
+            // Default key
+            keystoreAlias
+        );
+    }
+
+    @Bean
+    public WebSSOProfileOptions defaultWebSSOProfileOptions() {
+        WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
+        webSSOProfileOptions.setIncludeScoping(false);
+
+        // Force IdP to re-authenticate user if issued token is too old to prevent
+        // "Authentication statement is too old to be used with value" exception
+        // See: http://stackoverflow.com/questions/30528636/saml-login-errors
+        webSSOProfileOptions.setForceAuthN(true);
+
+        return webSSOProfileOptions;
+    }
+
+    // Entry point to initialize authentication, default values taken from
+    // properties file
+    @Bean
+    public SAMLEntryPoint samlEntryPoint() {
+        SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint();
+        samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
+        return samlEntryPoint;
+    }
+
+    // Setup advanced info about metadata
+    @Bean
+    public ExtendedMetadata extendedMetadata() {
+        ExtendedMetadata extendedMetadata = new ExtendedMetadata();
+        extendedMetadata.setIdpDiscoveryEnabled(false);
+        extendedMetadata.setSignMetadata(false);
+
+        return extendedMetadata;
+    }
+
+    @Bean
+    @Qualifier("production-TUD")
+    public ExtendedMetadataDelegate loginProductionTudelftMetadataProvider() throws MetadataProviderException {
+        String metadataUrl = "https://gatekeeper2.tudelft.nl/openaselect/profiles/saml2";
+        Timer backgroundTaskTimer = new Timer(true);
+        
+        HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(backgroundTaskTimer, httpClient(), metadataUrl);
+        httpMetadataProvider.setParserPool(parserPool());
+
+        ExtendedMetadataDelegate extendedMetadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
+        extendedMetadataDelegate.setMetadataTrustCheck(true);
+        extendedMetadataDelegate.setMetadataRequireSignature(false);
+
+        return extendedMetadataDelegate;
+    }
+
+    @Bean
+    @Qualifier("test-TUD")
+    public ExtendedMetadataDelegate loginTestTudelftMetadataProvider() throws MetadataProviderException {
+        String metadataUrl = "https://gatekeepert.tudelft.nl/openaselect/profiles/saml2";
+        Timer backgroundTaskTimer = new Timer(true);
+        
+        HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(backgroundTaskTimer, httpClient(), metadataUrl);
+        httpMetadataProvider.setParserPool(parserPool());
+
+        ExtendedMetadataDelegate extendedMetadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
+        extendedMetadataDelegate.setMetadataTrustCheck(false);
+        extendedMetadataDelegate.setMetadataRequireSignature(false);
+
+        return extendedMetadataDelegate;
+    }
+    
+    @Bean
+    @Qualifier("test-testSHIB")
+    public ExtendedMetadataDelegate loginTestSHIBMetadataProvider() throws MetadataProviderException {
+        String metadataUrl = "https://www.testshib.org/metadata/testshib-providers.xml";
+        Timer backgroundTaskTimer = new Timer(true);
+        
+        HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(backgroundTaskTimer, httpClient(), metadataUrl);
+        httpMetadataProvider.setParserPool(parserPool());
+
+        ExtendedMetadataDelegate extendedMetadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
+        extendedMetadataDelegate.setMetadataTrustCheck(true);
+        extendedMetadataDelegate.setMetadataRequireSignature(false);
+
+        return extendedMetadataDelegate;
+    }
+
+    // IDP Metadata configuration - paths to metadata of IDPs in circle of trust
+    // is here
+    // Do no forget to call initialize method on providers
+    @Bean
+    @Qualifier("metadata")
+    public CachingMetadataManager metadata() throws MetadataProviderException {
+        List<MetadataProvider> providers = new ArrayList<>();
+        providers.add(loginProductionTudelftMetadataProvider());
+//        providers.add(loginTestTudelftMetadataProvider());
+//        providers.add(loginTestSHIBMetadataProvider());
+
+        return new CachingMetadataManager(providers);
+    }
+
+    // Filter automatically generates default SP metadata
+    @Bean
+    public MetadataGenerator metadataGenerator() {
+        MetadataGenerator metadataGenerator = new MetadataGenerator();
+        metadataGenerator.setEntityId(entityId);
+        metadataGenerator.setEntityBaseURL(entityBaseURL);
+        metadataGenerator.setExtendedMetadata(extendedMetadata());
+        metadataGenerator.setIncludeDiscoveryExtension(false);
+        metadataGenerator.setKeyManager(keyManager());
+
+        return metadataGenerator;
+    }
+
+    // The filter is waiting for connections on URL suffixed with filterSuffix
+    // and presents SP metadata there
+    @Bean
+    public MetadataDisplayFilter metadataDisplayFilter() {
+        return new MetadataDisplayFilter();
+    }
+
+    // Handler deciding where to redirect user after successful login
+    @Bean
+    public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
+        SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler = new SavedRequestAwareAuthenticationSuccessHandler();
+        successRedirectHandler.setDefaultTargetUrl("/");
+
+        return successRedirectHandler;
+    }
+
+    // Handler deciding where to redirect user after failed login
+    @Bean
+    public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() {
+        SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
+        failureHandler.setUseForward(true);
+        failureHandler.setDefaultFailureUrl("/error");
+        return failureHandler;
+    }
+
+    @Bean
+    public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception {
+        SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
+        samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
+        samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager());
+        samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
+        return samlWebSSOHoKProcessingFilter;
+    }
+
+    // Processing filter for WebSSO profile messages
+    @Bean
+    public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception {
+        SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
+        samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
+        samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
+        samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
+        return samlWebSSOProcessingFilter;
+    }
+
+    @Bean
+    public MetadataGeneratorFilter metadataGeneratorFilter() {
+        return new MetadataGeneratorFilter(metadataGenerator());
+    }
+
+    // Handler for successful logout
+    @Bean
+    public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
+        SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
+        successLogoutHandler.setDefaultTargetUrl("/");
+        return successLogoutHandler;
+    }
+
+    // Logout handler terminating local session
+    @Bean
+    public SecurityContextLogoutHandler logoutHandler() {
+        SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
+        logoutHandler.setInvalidateHttpSession(true);
+        logoutHandler.setClearAuthentication(true);
+        return logoutHandler;
+    }
+
+    // Filter processing incoming logout messages
+    // First argument determines URL user will be redirected to after successful
+    // global logout
+    @Bean
+    public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
+        return new SAMLLogoutProcessingFilter(successLogoutHandler(), logoutHandler());
+    }
+
+    // Overrides default logout processing filter with the one processing SAML
+    // messages
+    @Bean
+    public SAMLLogoutFilter samlLogoutFilter() {
+        return new SAMLLogoutFilter(successLogoutHandler(),
+            new LogoutHandler[]{logoutHandler()},
+            new LogoutHandler[]{logoutHandler()});
+    }
+
+    // IDP Discovery service
+    @Bean
+    public SAMLDiscovery samlIDPDiscovery() {
+        SAMLDiscovery samlDiscovery = new SAMLDiscovery();
+        samlDiscovery.setIdpSelectionPath("/saml/idpSelection");
+
+        return samlDiscovery;
+    }
+
+    // Bindings
+    private ArtifactResolutionProfile artifactResolutionProfile() {
+        final ArtifactResolutionProfileImpl artifactResolutionProfile = new ArtifactResolutionProfileImpl(httpClient());
+        artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding()));
+        return artifactResolutionProfile;
+    }
+
+    @Bean
+    public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) {
+        return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile());
+    }
+
+    @Bean
+    public HTTPSOAP11Binding soapBinding() {
+        return new HTTPSOAP11Binding(parserPool());
+    }
+
+    @Bean
+    public HTTPPostBinding httpPostBinding() {
+        return new HTTPPostBinding(parserPool(), velocityEngine());
+    }
+
+    @Bean
+    public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() {
+        return new HTTPRedirectDeflateBinding(parserPool());
+    }
+
+    @Bean
+    public HTTPSOAP11Binding httpSOAP11Binding() {
+        return new HTTPSOAP11Binding(parserPool());
+    }
+
+    @Bean
+    public HTTPPAOS11Binding httpPAOS11Binding() {
+        return new HTTPPAOS11Binding(parserPool());
+    }
+
+    // Processor
+    @Bean
+    public SAMLProcessorImpl processor() {
+        Collection<SAMLBinding> bindings = new ArrayList<>();
+        bindings.add(httpRedirectDeflateBinding());
+        bindings.add(httpPostBinding());
+        bindings.add(artifactBinding(parserPool(), velocityEngine()));
+        bindings.add(httpSOAP11Binding());
+        bindings.add(httpPAOS11Binding());
+
+        return new SAMLProcessorImpl(bindings);
+    }
+
+    /**
+     * Define the security filter chain in order to support SSO Auth by using SAML 2.0
+     *
+     * @return Filter chain proxy
+     * @throws Exception
+     */
+    @Bean
+    public FilterChainProxy samlFilter() throws Exception {
+        ImmutableList<SecurityFilterChain> chain = ImmutableList.of(
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), samlEntryPoint()),
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"), samlLogoutFilter()),
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"), metadataDisplayFilter()),
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"), samlWebSSOProcessingFilter()),
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"), samlWebSSOHoKProcessingFilter()),
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"), samlLogoutProcessingFilter()),
+            new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"), samlIDPDiscovery())
+        );
+
+        return new FilterChainProxy(chain);
+    }
+
+    /**
+     * Returns the authentication manager currently used by Spring.
+     * It represents a bean definition with the aim allow wiring from
+     * other classes performing the Inversion of Control (IoC).
+     *
+     * @throws Exception
+     */
+    @Bean
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    // Register authentication manager for SAML provider
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) {
+        auth.authenticationProvider(samlAuthenticationProvider());
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/WebSecurityConfig.java b/backend/src/main/java/nl/tudelft/ewi/queue/WebSecurityConfig.java
new file mode 100644
index 000000000..795d1ffbc
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/WebSecurityConfig.java
@@ -0,0 +1,125 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import nl.tudelft.ewi.queue.service.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.access.AccessDecisionVoter;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.access.vote.AffirmativeBased;
+import org.springframework.security.access.vote.RoleHierarchyVoter;
+import org.springframework.security.access.vote.RoleVoter;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+import org.springframework.security.web.access.expression.WebExpressionVoter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+@Profile("!production")
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+	private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
+
+	@Autowired
+	private AuthenticationService authenticationService;
+
+	/**
+     * Defines the web based security configuration.
+     *
+     * @param http It allows configuring web based security for specific http requests.
+     * @throws Exception
+     */
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        //http = samlizedConfig(http);
+
+        http.authorizeRequests()
+            .antMatchers("/").permitAll()
+//            .antMatchers("/login").permitAll()
+            .antMatchers("/logout").permitAll()
+            .antMatchers("/manifest.json").permitAll()
+            .antMatchers("/favicon.ico").permitAll()
+            .antMatchers("/sw.js").permitAll()
+            .antMatchers("/css/**").permitAll()
+            .antMatchers("/img/**").permitAll()
+            .antMatchers("/js/**").permitAll()
+            .antMatchers("/webjars/**").permitAll()
+            .antMatchers("/stomp/**").permitAll()
+            .antMatchers("/saml/**").permitAll()
+            .antMatchers("/lab/submit").permitAll()
+            .anyRequest()
+            .authenticated()
+            .and()
+            .formLogin().loginPage("/login").permitAll();
+
+
+            http.csrf().ignoringAntMatchers("/lab/submit");
+//            http.csrf().disable();
+
+        http
+            .logout()
+            .logoutSuccessUrl("/");
+    }
+
+    @Bean
+    public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
+        DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
+        defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy);
+
+        return defaultWebSecurityExpressionHandler;
+    }
+
+    @Bean
+    public RoleVoter roleVoter(RoleHierarchy roleHierarchy) {
+        return new RoleHierarchyVoter(roleHierarchy);
+    }
+
+    @Bean
+    public AffirmativeBased getAccessDecisionManager(RoleHierarchy roleHierarchy) {
+        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
+        expressionHandler.setRoleHierarchy(roleHierarchy);
+
+        WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
+        webExpressionVoter.setExpressionHandler(expressionHandler);
+
+        List<AccessDecisionVoter<?>> voters = new ArrayList<>();
+        voters.add(webExpressionVoter);
+
+        return new AffirmativeBased(voters);
+    }
+
+	@Autowired
+	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
+    	logger.info("Set up authservice");
+		auth.userDetailsService(authenticationService);
+	}
+
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/WebSocketConfig.java b/backend/src/main/java/nl/tudelft/ewi/queue/WebSocketConfig.java
new file mode 100644
index 000000000..a7f2893ff
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/WebSocketConfig.java
@@ -0,0 +1,41 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.messaging.simp.config.MessageBrokerRegistry;
+import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
+
+@Configuration
+@EnableWebSocketMessageBroker
+public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
+
+    @Override
+    public void configureMessageBroker(MessageBrokerRegistry registry) {
+        registry.enableSimpleBroker("/queue/");
+        registry.setApplicationDestinationPrefixes("/app");
+    }
+
+    @Override
+    public void registerStompEndpoints(StompEndpointRegistry registry) {
+        registry.addEndpoint("/stomp").withSockJS();
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/annotation/AuthenticatedUser.java b/backend/src/main/java/nl/tudelft/ewi/queue/annotation/AuthenticatedUser.java
new file mode 100644
index 000000000..252e6d56c
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/annotation/AuthenticatedUser.java
@@ -0,0 +1,28 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AuthenticatedUser {
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/AdminController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/AdminController.java
new file mode 100644
index 000000000..594811f96
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/AdminController.java
@@ -0,0 +1,123 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import nl.tudelft.ewi.queue.model.Room;
+import nl.tudelft.ewi.queue.repository.FirstYearMentorGroupRepository;
+import nl.tudelft.ewi.queue.repository.RoomRepository;
+import nl.tudelft.ewi.queue.service.AdminService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.validation.Valid;
+import java.io.IOException;
+
+@Controller
+@PreAuthorize("@permissionService.isAdmin(principal)")
+public class AdminController {
+
+    @Autowired
+    private FirstYearMentorGroupRepository firstYearMentorGroupRepository;
+
+    @Autowired
+    private RoomRepository roomRepository;
+
+    @Autowired
+    private AdminService adminService;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "admin";
+    }
+
+    @RequestMapping(value = "/admin", method = RequestMethod.GET)
+    public String index(Model model) {
+        return "redirect:/admin/mentor";
+    }
+
+    @RequestMapping(value = "/admin/mentor", method = RequestMethod.GET)
+    public String mentorGroups(Model model) {
+        model.addAttribute("groups", firstYearMentorGroupRepository.findAllByActiveIsTrue());
+
+        return "admin/mentor/groups.html";
+    }
+
+    @RequestMapping(value = "/admin/mentor/upload", method = RequestMethod.POST)
+    public String uploadMentorCsv(Model model,
+                                  @RequestParam("file") MultipartFile csv,
+                                  RedirectAttributes redirectAttributes) throws IOException {
+        int created = adminService.importCsv(csv);
+        String successMessage = String.format("Imported %d first year students.", created);
+        redirectAttributes.addFlashAttribute("message", successMessage);
+
+        return "redirect:/admin/mentor";
+    }
+
+    @RequestMapping(value = "/admin/rooms", method = RequestMethod.GET)
+    public String rooms(Model model) {
+        model.addAttribute("rooms", roomRepository.findAllByOrderByNameAsc());
+
+        return "admin/rooms.html";
+    }
+
+    @RequestMapping(value = "/admin/rooms", method = RequestMethod.POST)
+    public String createRoom(Model model, @Valid Room room, RedirectAttributes redirectAttributes) {
+        roomRepository.save(room);
+        redirectAttributes.addFlashAttribute("message", "Room has been created.");
+        return "redirect:/admin/rooms";
+    }
+
+    @RequestMapping(value = "/admin/room/edit/{id}", method = RequestMethod.GET)
+    public String editRoom(Model model, @PathVariable("id") Long id) {
+        Room room = roomRepository.findOne(id);
+        model.addAttribute("room", room);
+
+        return "admin/rooms/edit.html";
+    }
+
+    @RequestMapping(value = "/admin/room/edit/{id}", method = RequestMethod.POST)
+    public String saveRoom(@PathVariable("id") Long id,
+                           @RequestParam("map") MultipartFile map,
+                           Room room, RedirectAttributes redirectAttributes) throws IOException {
+        Room objRoom = roomRepository.findOne(id);
+        objRoom.setName(room.getName());
+        objRoom.setPlaceholder(room.isPlaceholder());
+        if (!map.getOriginalFilename().isEmpty()) {
+            String filePath = adminService.uploadFile(map, "static/maps/");
+            objRoom.setMapFilePath(filePath);
+        }
+        roomRepository.save(objRoom);
+        redirectAttributes.addFlashAttribute("message", "Room has been saved.");
+        return "redirect:/admin/rooms";
+    }
+
+    @RequestMapping(value = "/admin/room/delete", method = RequestMethod.POST)
+    public String deleteRoom(Model model,
+                             @RequestParam(value = "id") Long id,
+                             RedirectAttributes redirectAttributes) {
+        Room objRoom = roomRepository.findOne(id);
+        roomRepository.delete(objRoom);
+        redirectAttributes.addFlashAttribute("message", "Room has been deleted.");
+        return "redirect:/admin/rooms";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/AuthController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/AuthController.java
new file mode 100644
index 000000000..4774aa2ad
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/AuthController.java
@@ -0,0 +1,52 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package nl.tudelft.ewi.queue.controller;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@Controller
+@Profile("development")
+public class AuthController {
+	private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
+
+	@ModelAttribute("page")
+	public static String page() {
+		return "authentication";
+	}
+
+	@RequestMapping(value = "/login", method = RequestMethod.GET)
+	public static String login(Model model) {
+		logger.info("User logged in");
+		return "login";
+	}
+
+	@RequestMapping(value = "/logout", method = RequestMethod.GET)
+	public static String logout() {
+		logger.info("User logged out");
+		return "login";
+	}
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/CourseController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/CourseController.java
new file mode 100644
index 000000000..b778418e8
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/CourseController.java
@@ -0,0 +1,607 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import com.querydsl.core.types.dsl.BooleanExpression;
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.forms.AssignmentListWrapperForm;
+import nl.tudelft.ewi.queue.forms.ParticipantForm;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.AssignmentRepository;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.FirstYearStudentRepository;
+import nl.tudelft.ewi.queue.repository.RoleRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import nl.tudelft.ewi.queue.service.CourseService;
+import nl.tudelft.ewi.queue.service.GroupService;
+import nl.tudelft.ewi.queue.service.LabService;
+import nl.tudelft.ewi.queue.service.RequestService;
+import nl.tudelft.ewi.queue.validator.ParticipantValidator;
+import nl.tudelft.ewi.queue.viewmodel.AssignmentViewModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.support.PagedListHolder;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.persistence.EntityNotFoundException;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Controller
+public class CourseController {
+
+    @Autowired
+    private RequestService requestService;
+
+    private static final Logger logger = LoggerFactory.getLogger(CourseController.class);
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
+    private RoleRepository roleRepository;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private CourseService courseService;
+
+    @Autowired
+    private GroupService groupService;
+
+    @Autowired
+    private LabService labService;
+
+    @Autowired
+    private AssignmentRepository assignmentRepository;
+
+    @Autowired
+    private ParticipantValidator participantValidator;
+
+    //@Autowired
+    //private FirstYearStudentRepository firstYearStudentRepository;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "courses";
+    }
+
+    @InitBinder("participantForm")
+    protected void initBinder(WebDataBinder binder) {
+        binder.addValidators(participantValidator);
+    }
+
+    @RequestMapping(value = "/courses", method = RequestMethod.GET)
+    public String list(@AuthenticatedUser User user, Model model, @PageableDefault(sort = {"id"}, direction = Sort.Direction.DESC) Pageable pageable) {
+        BooleanExpression archivedAndTeachingCourses = QCourse.course.isArchived.eq(false).or(QCourse.course.in(user.getTeaches()));
+
+        Page<Course> courses = courseRepository.findAll(archivedAndTeachingCourses, pageable);
+
+        model.addAttribute("courses", courses);
+
+        return "course/index";
+    }
+
+    @RequestMapping(value = "/course/{id}", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canViewCourse(principal, #id)")
+    public String view(@PathVariable("id") Long id, Model model) {
+        Course course = getCourse(id);
+
+        model.addAttribute("course", course);
+
+        return "course/view/info";
+    }
+
+    @RequestMapping(value = "/course/{id}/leave", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canLeaveCourse(principal, #id)")
+    public String leave(@AuthenticatedUser User user, @PathVariable("id") Long id, Model model) {
+        Course course = getCourse(id);
+
+        model.addAttribute("course", course);
+
+        return "course/view/leave";
+    }
+
+    @RequestMapping(value = "/course/{id}/leave", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canLeaveCourse(principal, #id)")
+    public String leave(@AuthenticatedUser User user, @PathVariable("id") Long id) {
+        Course course = getCourse(id);
+
+        Optional<Role> role = user.getRole(course);
+
+        role.ifPresent(r -> {
+            user.getRoles().remove(r);
+
+            userRepository.save(user);
+            roleRepository.delete(r);
+        });
+
+        return "redirect:/courses";
+    }
+
+    @RequestMapping(value = "/course/{id}/participants", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canManageParticipants(principal, #id)")
+    public String viewParticipants(@PathVariable("id") Long id,
+                                   @RequestParam(value = "staff", required = false) Optional<Boolean> staff,
+                                   @RequestParam(value = "filter", required = false) Optional<String> filter,
+                                   @RequestParam(value = "clean", required = false) Optional<Boolean> clean,
+                                   Model model,
+                                   Pageable pageable) {
+        Course course = getCourse(id);
+        if (staff.isPresent() && staff.get()) {
+            List<User> assistants = course.getAssistants();
+            model.addAttribute("feedbackCount", courseService.feedbackCount(assistants));
+        } else {
+            List<Role> students = course.getStudents();
+            if (filter.isPresent()) {
+                students = students.stream()
+                        .filter(student -> student.getUser().getUsername().toLowerCase().contains(filter.get().toLowerCase()))
+                        .collect(Collectors.toList());
+            }
+            students.sort(CourseController::compareLexicographically);
+            PagedListHolder<Role> pagedListHolder = new PagedListHolder<>(students);
+            pagedListHolder.setPage(pageable.getPageNumber());
+            model.addAttribute("students", pagedListHolder);
+        }
+
+        if (clean.isPresent() && clean.get()) {
+            List<User> users = course.getTeachers();
+            users.addAll(course.getAssistants());
+            users.addAll(course.getManagers());
+            int updateCounter = 0;
+            for (User user : users) {
+                if (user.getSubscription() != null) {
+                    updateCounter++;
+                    user.setSubscription(null);
+                }
+            }
+            userRepository.save(users);
+            model.addAttribute("cleaned", updateCounter);
+        }
+
+        model.addAttribute("staff", staff.orElse(false));
+        model.addAttribute("course", getCourse(id));
+
+        model.addAttribute("paginationRange", 10);
+
+        return "course/view/participants";
+    }
+
+    @RequestMapping(value = "/course/{id}/participants/create", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canManageParticipants(principal, #id)")
+    public String createParticipant(@PathVariable("id") Long id, Model model) {
+        ParticipantForm participantForm = new ParticipantForm();
+        participantForm.setCourseId(id);
+
+        model.addAttribute("course", getCourse(id));
+        model.addAttribute("participantForm", participantForm);
+
+        return "course/create/participant";
+    }
+
+    @RequestMapping(value = "/course/{id}/participants/create", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canAddParticipant(principal, #id, #participantForm.role)")
+    public String storeParticipant(@PathVariable("id") Long id,
+                                   @Valid ParticipantForm participantForm,
+                                   BindingResult bindingResult,
+                                   Model model) {
+        if (bindingResult.hasErrors()) {
+            model.addAttribute("course", getCourse(id));
+
+            return "course/create/participant";
+        }
+
+        String username = User.guarantueeValidNetId(participantForm.getUsername());
+
+        Course course = getCourse(id);
+        course.addRole(getRole(participantForm.getRole(), getUser(username), course));
+
+        courseRepository.save(course);
+
+        return "redirect:/course/" + id + "/participants";
+    }
+
+    @RequestMapping(value = "/course/{courseId}/participants/{id}/remove", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canUpdateParticipant(principal, #courseId, #id)")
+    public String deleteParticipant(@PathVariable("courseId") Long courseId, @PathVariable("id") Long id, Model model) {
+        Role role = getRole(id);
+
+        model.addAttribute("course", getCourse(courseId));
+        model.addAttribute("role", role);
+
+        return "course/remove/participant";
+    }
+
+    @RequestMapping(value = "/course/{courseId}/participants/{id}/remove", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canUpdateParticipant(principal, #courseId, #id)")
+    public String removeParticipant(@PathVariable("courseId") Long courseId, @PathVariable("id") Long id) {
+        Role role = getRole(id);
+
+        roleRepository.delete(role);
+
+        return "redirect:/course/" + courseId + "/participants";
+    }
+
+    @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
+    public String viewAssignments(@PathVariable("id") Long id,
+                                  Model model) {
+        Course course = getCourse(id);
+
+        AssignmentListWrapperForm assignmentList = new AssignmentListWrapperForm();
+
+        for (Assignment assignment : course.getAssignments())
+            assignmentList.add(AssignmentViewModel.convert(assignment));
+
+        model.addAttribute("course", course);
+        model.addAttribute("assignments", assignmentList);
+
+        return "course/view/assignments";
+    }
+
+    @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {"addAssignment"})
+    @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
+    public String addAssignment(@PathVariable("id") Long id,
+                                @ModelAttribute("addAssignment") AssignmentListWrapperForm assignmentList,
+                                Model model) {
+        Course course = getCourse(id);
+
+        if (assignmentList == null) {
+            assignmentList = new AssignmentListWrapperForm();
+        }
+
+        assignmentList.add(new AssignmentViewModel());
+
+        model.addAttribute("course", course);
+        model.addAttribute("assignments", assignmentList);
+
+        return "course/view/assignments";
+    }
+
+    @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {"removeAssignment"})
+    @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
+    public String removeAssignment(@PathVariable("id") Long id,
+                                   @RequestParam("removeAssignment") int assignmentID,
+                                   Model model,
+                                   RedirectAttributes redirectAttributes) {
+        Course course = getCourse(id);
+        Long aID = (long) assignmentID;
+        Assignment assignment = assignmentRepository.findOne(aID);
+        assert assignment.getCourse().getId().equals(course.getId());
+        assignmentRepository.delete(aID);
+
+        redirectAttributes.addFlashAttribute("message", "Assignment removed.");
+        model.addAttribute("course", course);
+
+        return "redirect:/course/" + id + "/assignments";
+    }
+
+    @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {"storeAssignments"})
+    @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
+    public String storeAssignments(@PathVariable("id") Long id,
+                                   @ModelAttribute("storeAssignments") AssignmentListWrapperForm assignmentList,
+                                   Model model,
+                                   RedirectAttributes redirectAttributes) {
+        Course course = getCourse(id);
+
+        for (AssignmentViewModel viewAssign : assignmentList.getAssignmentList()) {
+            if (!viewAssign.getAssignmentID().isEmpty()) {
+                Long assignmentId = Long.parseLong(viewAssign.getAssignmentID());
+                Assignment assignment = assignmentRepository.findOne(assignmentId);
+                assert assignment.getCourse().getId().equals(course.getId());
+                assignment.setName(viewAssign.getName());
+                assignmentRepository.save(assignment);
+            } else {
+                Assignment assignment = viewAssign.convertToNew();
+                course.addAssignment(assignment);
+            }
+        }
+
+        courseRepository.save(course);
+
+        redirectAttributes.addFlashAttribute("message", "Assignments updated.");
+
+        return "redirect:/course/" + id + "/assignments";
+    }
+
+    @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {
+            "uploadEnqueue"})
+    @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
+    public String uploadEnqueue(@PathVariable("id") Long id,
+                                @RequestParam("csv") MultipartFile csv,
+                                @RequestParam("uploadEnqueue") Long assignmentId) throws IOException {
+        Course course = getCourse(id);
+        groupService.importCsv(course, csv);
+
+        List<Group> groups = course.getGroups();
+
+		logger.error("Going to enqueue this many groups:" + groups.size());
+
+        Assignment assignment = assignmentRepository.findOne(assignmentId);
+
+        for(Lab lab : assignment.getLabs()){
+            for(Request req : lab.getPending()) {
+                requestService.revoke(req);
+            }
+        }
+
+        for(Group group : groups) {
+            labService.randomEnqueue(assignment, group);
+        }
+
+
+        return "redirect:/course/" + id + "/assignments";
+    }
+
+    @RequestMapping(value = "/course/{id}/labs", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canViewCourse(principal, #id)")
+    public String viewLabs(@PathVariable("id") Long id, Model model) {
+        Course course = getCourse(id);
+
+        model.addAttribute("course", course);
+        model.addAttribute("occupation", numberOfAvailableSlotsStrings(course, labService));
+
+        return "course/view/labs";
+    }
+
+    private List<String> numberOfAvailableSlotsStrings(Course course, LabService labService) {
+        List<String> retList = new ArrayList<>();
+        for (Lab lab: course.getLabs()) {
+            if (!lab.getSignOffIntervals()) {
+                retList.add("");
+            } else {
+                long allSlots = labService.getIntervalsForLab(lab).size() * lab.getCapacity();
+                long slotsTaken = lab.getRequests().stream().filter(request -> !(request.isRejected()
+                        || request.isRevoked())).count();
+                String retString = "(" + slotsTaken + "/" + allSlots + " slots taken)";
+                retList.add(retString);
+            }
+        }
+        System.out.println(retList);
+        return retList;
+    }
+
+    @RequestMapping(value = "/course/{id}/enroll", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canEnrollCourse(principal, #id)")
+    public String enroll(@PathVariable("id") Long id, Model model) {
+        Course course = getCourse(id);
+
+        model.addAttribute("course", course);
+
+        return "course/enroll";
+    }
+
+    @RequestMapping(value = "/course/{id}/enroll", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canEnrollCourse(principal, #id)")
+    public String enroll(@AuthenticatedUser User user, @PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+        Course course = getCourse(id);
+
+        User student = userRepository.findOne(user.getId());
+
+        courseService.enroll(student, course);
+
+        redirectAttributes.addFlashAttribute("message", "You are now enrolled.");
+
+        return "redirect:/course/" + course.getId();
+    }
+
+    @RequestMapping(value = "/course/{id}/settings", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canEditCourse(principal, #id)")
+    public String settings(@AuthenticatedUser User user, @PathVariable("id") Long id, Model model) {
+        Course course = getCourse(id);
+
+        model.addAttribute("course", course);
+
+        return "course/view/settings";
+    }
+
+    @RequestMapping(value = "/course/{id}/settings", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canEditCourse(principal, #id)")
+    public String update(@AuthenticatedUser User user, @PathVariable("id") Long id, @Valid Course course, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return "course/view/settings";
+        }
+
+        courseRepository.save(course);
+
+        return "redirect:/course/" + course.getId();
+    }
+
+    @RequestMapping(value = "/course/{id}/remove", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canRemoveCourse(principal, #id)")
+    public String remove(@AuthenticatedUser User user, @PathVariable("id") Long id,
+                         Model model) {
+        Course course = getCourse(id);
+
+        model.addAttribute("course", course);
+
+        return "course/view/delete";
+    }
+
+    @RequestMapping(value = "/course/{id}/remove", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canRemoveCourse(principal, #id)")
+    public String remove(@AuthenticatedUser User user, @PathVariable("id") Long id) {
+        Course course = getCourse(id);
+
+        courseRepository.delete(course);
+
+        return "redirect:/courses";
+    }
+
+    @RequestMapping(value = "/course/create", method = RequestMethod.GET)
+    @PreAuthorize("hasRole('TEACHER')")
+    public String create(@AuthenticatedUser User user, Model model) {
+        model.addAttribute("course", new Course());
+
+        return "course/create";
+    }
+
+    @RequestMapping(value = "/course/create", method = RequestMethod.POST)
+    @PreAuthorize("hasRole('TEACHER')")
+    public String store(@AuthenticatedUser User user,
+                        @Valid Course course,
+                        BindingResult bindingResult,
+                        @RequestParam("teacherUsername") Optional<String> teacherUsername,
+                        RedirectAttributes redirectAttributes) {
+        if (bindingResult.hasErrors()) {
+            return "course/create";
+        }
+        courseRepository.save(course);
+
+        if (user.isTeacher() && !user.teaches(course)) {
+            addTeacherToCourse(user, course);
+        } else if (teacherUsername.isPresent() && !teacherUsername.get().isEmpty()) {
+            redirectAttributes = addTeacherNetIdToCourse(teacherUsername, course, redirectAttributes);
+        }
+
+        return "redirect:/course/" + course.getId();
+    }
+
+    @RequestMapping(value = "/course/{cId}/participants/feedback/{id}", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canViewFeedback(principal, #id)")
+    public String feedback(@AuthenticatedUser User user,
+                           @PathVariable("cId") Long courseId,
+                           @PathVariable("id") Long id,
+                           Model model,
+                           Pageable page) {
+        Course course = getCourse(courseId);
+
+        User assistant = userRepository.findOne(id);
+
+        List<Request> filteredList = courseService.requestsWithFeedback(assistant);
+        PagedListHolder<Request> requestsWithFeedback = new PagedListHolder<>(filteredList);
+        requestsWithFeedback.setPage(page.getPageNumber());
+
+        model.addAttribute("course", course);
+        model.addAttribute("assistant", assistant);
+        model.addAttribute("feedback", requestsWithFeedback);
+        model.addAttribute("page", page);
+
+        return "course/view/feedback";
+    }
+
+    @RequestMapping(value = "/course/{id}/status", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
+    public String status(@AuthenticatedUser User user,
+                         @PathVariable("id") Long id,
+                         Model model) {
+        Course course = getCourse(id);
+        model.addAttribute("course", course);
+        return "course/view/status";
+    }
+
+    protected static Role getRole(String name, User user, Course course) {
+        switch (name) {
+            case "student":
+                return new Student(user, course, LocalDateTime.now());
+            case "assistant":
+                return new Assistant(user, course);
+            case "manager":
+                return new Manager(user, course);
+            case "teacher":
+                return new Teacher(user, course);
+            default:
+                throw new IllegalArgumentException("Unknown role name");
+        }
+    }
+
+    protected Role getRole(Long id) {
+        Role role = roleRepository.findOne(id);
+
+        if (null == role) {
+            throw new EntityNotFoundException("Role was not found");
+        }
+
+        return role;
+    }
+
+    protected Course getCourse(Long id) {
+        Course course = courseRepository.findOne(id);
+
+        if (null == course) {
+            throw new EntityNotFoundException("Course was not found");
+        }
+
+        return course;
+    }
+
+    protected User getUser(String username) {
+        User user = userRepository.findByUsername(username);
+
+        if (null == user) {
+            throw new EntityNotFoundException("User was not found");
+        }
+
+        return user;
+    }
+
+    private RedirectAttributes addTeacherNetIdToCourse(Optional<String> teacherUsername,
+                                                       Course course, RedirectAttributes redirectAttributes) {
+        String teacherNetId = User.guarantueeValidNetId(teacherUsername.get());
+        User teacher = userRepository.findByUsername(teacherNetId);
+        if (teacher != null) {
+            addTeacherToCourse(teacher, course);
+        } else {
+            redirectAttributes.addFlashAttribute("message",
+                    "Teacher not added. Could not find teacher with NetID: " + teacherNetId);
+        }
+        return redirectAttributes;
+    }
+
+    private void addTeacherToCourse(User teacher, Course course) {
+        Role teacherRole = new Teacher(teacher, course);
+        course.addRole(teacherRole);
+        roleRepository.save(teacherRole);
+    }
+
+    private static int compareLexicographically(Role one, Role two) {
+        byte[] left = one.getUser().getUsername().getBytes();
+        byte[] right = two.getUser().getUsername().getBytes();
+        for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
+            int a = (left[i] & 0xff);
+            int b = (right[j] & 0xff);
+            if (a != b) {
+                return a - b;
+            }
+        }
+        return left.length - right.length;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/ErrorControllerAdvice.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/ErrorControllerAdvice.java
new file mode 100644
index 000000000..f6d169172
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/ErrorControllerAdvice.java
@@ -0,0 +1,84 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package nl.tudelft.ewi.queue.controller;
+
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+
+@ControllerAdvice
+public class ErrorControllerAdvice {
+	
+	private static final Logger logger = LoggerFactory.getLogger(ErrorControllerAdvice.class);
+
+	public ErrorControllerAdvice() {
+		// TODO Auto-generated constructor stub
+	}
+
+	@ExceptionHandler(javax.persistence.EntityNotFoundException.class)
+	public static String handle404Exception(Exception e) {
+		logger.error("Not found exception: " + e);
+		e.printStackTrace();
+		return "error/entityNotFound";
+	}
+
+	@ExceptionHandler(org.springframework.security.access.AccessDeniedException.class)
+	public static String handleDeniedException(@AuthenticatedUser User user, HttpServletRequest request, Exception e) {
+		logger.error("Access Denied: (User: " + user.getDisplayName() + ", page: " + request.getRequestURI() + "): " + e);
+		e.printStackTrace();
+		return "error/thouShaltNotPass";
+	}
+	
+	@ExceptionHandler(DataIntegrityViolationException.class)
+	public static String handleSQLException(Exception e) {
+		logger.error("SQL Error" + e);
+		e.printStackTrace();
+		return "error/error";
+	}
+
+	@ExceptionHandler(value = Exception.class)
+	public static ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
+		logger.error("A Request (" + req.getRequestURI() + "):  raised " + e);
+		e.printStackTrace();
+		// If the exception is annotated with @ResponseStatus rethrow it and let
+		// the framework handle it - like the OrderNotFoundException example
+		// at the start of this post.
+		// AnnotationUtils is a Spring Framework utility class.
+		if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
+			throw e;
+
+		// Otherwise setup and send the user to a default error-view.
+		ModelAndView mav = new ModelAndView();
+    	mav.addObject("exception", e);
+    	mav.addObject("url", req.getRequestURL());
+    	mav.setViewName("error/error");
+    	return mav;
+		//return "error/error";
+	}
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/GroupController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/GroupController.java
new file mode 100644
index 000000000..f937fc651
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/GroupController.java
@@ -0,0 +1,88 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import nl.tudelft.ewi.queue.model.Course;
+import nl.tudelft.ewi.queue.model.Group;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.GroupRepository;
+import nl.tudelft.ewi.queue.service.GroupService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import java.io.IOException;
+
+@Controller
+public class GroupController {
+
+    @Autowired
+    private GroupRepository groupRepository;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
+    private GroupService groupService;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "groups";
+    }
+
+    @RequestMapping("/course/{id}/groups")
+    @PreAuthorize("@permissionService.canManageParticipants(principal, #id)")
+    public String view(@PathVariable("id") Long id,  Model model) {
+        Course course = courseRepository.findOne(id);
+
+        model.addAttribute("course", course);
+
+        return "course/view/groups";
+    }
+
+    @PostMapping("/course/{id}/groups/import")
+    @PreAuthorize("@permissionService.canManageParticipants(principal, #id)")
+    public String importGroups(@PathVariable("id") Long id, @RequestParam("file") MultipartFile csv,
+                               Model model, RedirectAttributes redirectAttributes) throws IOException {
+        Course course = courseRepository.findOne(id);
+        model.addAttribute("course", course);
+
+        groupService.importCsv(course, csv);
+        redirectAttributes.addFlashAttribute("message", "Groups imported successfully.");
+
+        return "redirect:/course/" + course.getId() + "/groups";
+    }
+
+    @RequestMapping(value = "/course/{id}/group/{groupId}")
+    @PreAuthorize("@permissionService.canManageParticipants(principal, #id)")
+    public String view(@PathVariable("id") Long id, @PathVariable("groupId") Long groupId, Model model) {
+        Course course = courseRepository.findOne(id);
+        Group group = groupRepository.findOne(groupId);
+        model.addAttribute("course", course);
+        model.addAttribute("group", group);
+        return "course/view/group";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/HistoryController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/HistoryController.java
new file mode 100644
index 000000000..95aa73186
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/HistoryController.java
@@ -0,0 +1,126 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import com.google.common.collect.Lists;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.RequestTypeRepository;
+import nl.tudelft.ewi.queue.repository.RoleRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.querydsl.binding.QuerydslPredicate;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Controller
+public class HistoryController {
+
+    @Autowired
+    private RequestRepository requestRepository;
+
+    @Autowired
+    private RoleRepository roleRepository;
+
+    @Autowired
+    private RequestTypeRepository requestTypeRepository;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "history";
+    }
+
+    @RequestMapping("/history/course/{cId}/student/{id}")
+    @PreAuthorize("@permissionService.canManageParticipants(principal, #cId)")
+    public String listForStudent(@PathVariable("id") Long id, @PathVariable("cId") Long cId,
+                                 Model model, Pageable pageable,
+                                 @QuerydslPredicate(root = Request.class) Predicate predicate,
+                                 @RequestParam MultiValueMap<String, String> parameters) {
+        Role role = roleRepository.findOne(id);
+        User user = role.getUser();
+        getOldRequests(model, user, predicate, parameters, pageable);
+        model.addAttribute("student", user);
+        return "history/student";
+    }
+
+    @RequestMapping("/history")
+    public String list(@AuthenticatedUser User user, Model model,
+                       @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable,
+                       @QuerydslPredicate(root = Request.class) Predicate predicate,
+                       @RequestParam MultiValueMap<String, String> parameters) {
+        getOldRequests(model, user, predicate, parameters, pageable);
+        return "history/index";
+    }
+
+    private void getOldRequests(Model model, User user,
+                                 Predicate predicate,
+                                 MultiValueMap<String, String> parameters,
+                                 Pageable pageable) {
+        List<Course> courses = user.getParticipates();
+
+        List<Lab> labs = courses.stream()
+                .flatMap(course -> course.getLabs().stream())
+                .collect(Collectors.toList());
+
+        List<Lab> activeLabs = courses.stream()
+                .flatMap(course -> course.getLabs().stream())
+                .collect(Collectors.toList());
+
+        List<Assignment> assignments = activeLabs.stream()
+                .flatMap(lab -> lab.getAssignments().stream())
+                .collect(Collectors.toList());
+
+        List<Group> groups = courses.stream().filter(Course::getHasGroups)
+                .flatMap(c -> c.getGroups().stream())
+                .collect(Collectors.toList());
+
+        groups = groups.stream().filter(g -> g.hasMember(user)).collect(Collectors.toList());
+
+        QRequest qRequest = QRequest.request;
+        BooleanExpression currentUser = qRequest.requestEntity.id.eq(user.getId());
+        BooleanExpression currentGroup = qRequest.requestEntity.in(groups);
+
+        Page<Request> filteredRequests = requestRepository.findAll((currentGroup.or(currentUser)).and(predicate), pageable);
+
+        ArrayList<RequestType> requestTypes = Lists.newArrayList(requestTypeRepository.findAll());
+
+        model
+                .addAttribute("requests", filteredRequests)
+                .addAttribute("courses", courses)
+                .addAttribute("labs", labs)
+                .addAttribute("assignments", assignments)
+                .addAttribute("state", parameters.toSingleValueMap())
+                .addAttribute("requestTypes", requestTypes);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/HomeController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/HomeController.java
new file mode 100644
index 000000000..f015561dc
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/HomeController.java
@@ -0,0 +1,87 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Controller
+public class HomeController {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "main";
+    }
+
+    @RequestMapping("/")
+    public String index(Model model) {
+        if (SecurityContextHolder.getContext().getAuthentication() != null) {
+            if (SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
+                if (!(SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken)) {
+                    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+                    UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
+
+                    return dashboard(userRepository.findByUsername(userPrincipal.getUsername()), model);
+                }
+            }
+        }
+
+        return "home/index";
+    }
+
+    private String dashboard(@AuthenticatedUser User user, Model model) {
+        model.addAttribute("user", user);
+        List<Role> availableCourses = user.getRoles().stream()
+                .filter(role -> !role.getCourse().getIsArchived()
+                                || role.getUser().isTeacher())
+                .collect(Collectors.toList());
+//        System.out.println(availableCourses);
+        model.addAttribute("availableCourses", availableCourses);
+
+        // TODO this is probably not efficient
+        if (user.isAdmin()) {
+            List<Course> runningCourses = courseRepository.findAll().stream()
+                    .filter(course -> !course.getTodaysLabs().isEmpty())
+                    .collect(Collectors.toList());
+            model.addAttribute("runningCourses", runningCourses);
+        }
+
+        return "home/dashboard";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/LabController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/LabController.java
new file mode 100644
index 000000000..48a5e33e5
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/LabController.java
@@ -0,0 +1,453 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.Assignment;
+import nl.tudelft.ewi.queue.model.Course;
+import nl.tudelft.ewi.queue.model.FirstYearMentorGroup;
+import nl.tudelft.ewi.queue.model.Group;
+import nl.tudelft.ewi.queue.model.Lab;
+import nl.tudelft.ewi.queue.model.Request;
+import nl.tudelft.ewi.queue.model.RequestEntity;
+import nl.tudelft.ewi.queue.model.RequestSlot;
+import nl.tudelft.ewi.queue.model.RequestType;
+import nl.tudelft.ewi.queue.model.Role;
+import nl.tudelft.ewi.queue.model.Room;
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.repository.AssignmentRepository;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.FirstYearMentorGroupRepository;
+import nl.tudelft.ewi.queue.repository.GroupRepository;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.RequestTypeRepository;
+import nl.tudelft.ewi.queue.repository.RoleRepository;
+import nl.tudelft.ewi.queue.repository.RoomRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import nl.tudelft.ewi.queue.service.LabService;
+import nl.tudelft.ewi.queue.service.RequestService;
+import nl.tudelft.ewi.queue.validator.LabValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Controller
+public class LabController {
+
+    private static final Logger logger = LoggerFactory.getLogger(LabController.class);
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
+    private RoleRepository roleRepository;
+
+    @Autowired
+    private LabRepository labRepository;
+
+    @Autowired
+    private AssignmentRepository assignmentRepository;
+
+    @Autowired
+    private RoomRepository roomRepository;
+
+    @Autowired
+    private LabService labService;
+
+    @Autowired
+    private RequestService requestService;
+
+    @Autowired
+    private LabValidator labValidator;
+
+    @Autowired
+    private RequestTypeRepository requestTypeRepository;
+
+    @Autowired
+    private FirstYearMentorGroupRepository firstYearMentorGroupRepository;
+
+    @Autowired
+    private GroupRepository groupRepository;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "labs";
+    }
+
+    @InitBinder("lab")
+    protected void initBinder(WebDataBinder binder) {
+        binder.addValidators(labValidator);
+    }
+
+    @RequestMapping(value = "/lab/{id}", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canViewLab(principal, #id)")
+    public String view(@AuthenticatedUser User user, @PathVariable("id") Long id, Model model) {
+        Lab lab = labService.getLab(id);
+
+        List<User> students =
+                lab.getCourse().getStudents().stream().map(Role::getUser).collect(Collectors.toList());
+        model.addAttribute("students", students);
+
+        if (lab.getSignOffIntervals()) {
+            model.addAttribute("timeslots", labService.getIntervalsForLab(lab));
+            model.addAttribute("requests", lab.getPending());
+        }
+
+        List<Request> requestsForLab;
+        if (user.teaches(lab.getCourse()) || user.manages(lab.getCourse()) || user.assists(lab.getCourse()) || user.isAdmin()) {
+            requestsForLab = lab.getRequests();
+        } else {
+            requestsForLab = lab.requestsBy(user);
+        }
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("requestsForLab", requestsForLab);
+        model.addAttribute("course", lab.getCourse());
+
+        return "lab/view";
+    }
+
+    @RequestMapping(value = "/lab/{id}/enqueue", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canEnqueueSelfForLab(principal, #id)")
+    public String enqueue(@PathVariable("id") Long id, Model model) {
+        Lab lab = labService.getLab(id);
+
+        if (lab.getSignOffIntervals()) {
+            model.addAttribute("timeslots", labService.getIntervalsForLab(lab));
+        }
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("course", lab.getCourse());
+        model.addAttribute("request", new Request());
+
+        return "lab/enqueue";
+    }
+
+    @RequestMapping(value = "/lab/{id}/enqueue", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canEnqueueSelfForLab(principal, #id)")
+    public String enqueue(@AuthenticatedUser User user, @PathVariable("id") Long id,
+                          @Valid Request request, BindingResult bindingResult, Model model) {
+        Lab lab = labService.getLab(id);
+
+        if (bindingResult.hasErrors() || !lab.getRooms().contains(request.getRoom())) {
+            model.addAttribute("lab", lab);
+            model.addAttribute("course", lab.getCourse());
+            if (lab.getSignOffIntervals()) {
+                model.addAttribute("timeslots", labService.getIntervalsForLab(lab));
+            }
+            return "lab/enqueue";
+        }
+
+        if (lab.getCourse().getHasGroups()) {
+            Group group = lab.getCourse().getGroup(user);
+            request.setRequestEntity(group);
+        } else {
+            request.setRequestEntity(user);
+        }
+
+        request.setLab(lab);
+        labService.enqueue(request);
+
+        return "redirect:/lab/" + id;
+    }
+
+    @RequestMapping(value = "/lab/{id}/enqueuestudent", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    @PreAuthorize("@permissionService.canEnqueueOthersForLab(principal, #id)")
+    public String enqueueOther(@PathVariable("id") Long id,
+                               @RequestParam("netid") String netid,
+                               @RequestParam("assignmentid") Long assignmentid,
+                               @RequestParam("slotStart") String slotStart,
+                               @RequestParam("slotEnd") String slotEnd) {
+        Assignment assignment = labService.getAssignment(assignmentid);
+        Lab lab = labService.getLab(id);
+        Course course = lab.getCourse();
+
+        User user = userRepository.findByUsername(netid);
+
+        RequestEntity enqueueEntity;
+        if (course.getHasGroups()) {
+            enqueueEntity = course.getGroup(user);
+        } else {
+            enqueueEntity = user;
+        }
+
+        RequestSlot slot = new RequestSlot(LocalDateTime.parse(slotStart),
+                LocalDateTime.parse(slotEnd));
+
+        Request request = new Request(enqueueEntity, assignment, lab.getRooms().get(0),
+                requestTypeRepository.findByName("Submission"), "", lab, slot);
+
+        slot.setRequest(request);
+        labService.enqueue(request);
+
+        return "redirect:/lab/" + id;
+    }
+
+    @RequestMapping(value = "/lab/{id}/unenqueue", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canUnenqueueSelfFromLab(principal, #id)")
+    public String unenqueue(@AuthenticatedUser User user, @PathVariable("id") Long id) {
+        Lab lab = labService.getLab(id);
+
+        Optional<Request> request = lab.getPendingRequest(user);
+        if (lab.getCourse().getHasGroups()) {
+            request = lab.getPendingRequest(lab.getCourse().getGroup(user));
+        }
+
+        request.ifPresent(request1 -> requestService.revoke(request1));
+
+        return "redirect:/lab/" + id;
+    }
+
+    @RequestMapping(value = "/lab/{id}/unenqueue/{netid}", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canUnenqueuOthersFromLab(principal, #id)")
+    public String unenqueueOther(@PathVariable("id") Long id,
+                                 @PathVariable("netid") String netid) {
+        Lab lab = labService.getLab(id);
+
+        User user = userRepository.findByUsername(netid);
+
+        Optional<Request> request = lab.getPendingRequest(user);
+        if (lab.getCourse().getHasGroups()) {
+            request = lab.getPendingRequest(lab.getCourse().getGroup(user));
+        }
+
+        request.ifPresent(request1 -> requestService.revoke(request1));
+
+        return "redirect:/lab/" + id;
+    }
+
+    @RequestMapping(value = "/course/{id}/lab/create", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canCreateLab(principal, #id)")
+    public String create(@PathVariable("id") Long id, Model model) {
+        Course course = labService.getCourse(id);
+
+        Lab lab = new Lab();
+        lab.setCourse(course);
+        Iterable<RequestType> requestTypes = requestTypeRepository.findAll();
+        Iterable<FirstYearMentorGroup> mentorGroups = firstYearMentorGroupRepository.findAllByActiveIsTrue();
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("rooms", roomRepository.findAllByOrderByNameAsc());
+        model.addAttribute("course", course);
+        model.addAttribute("requesttypes", requestTypes);
+        model.addAttribute("mentorgroups", mentorGroups);
+
+        return "lab/create";
+    }
+
+    @RequestMapping(value = "/course/{id}/lab/create", method = RequestMethod.POST, params = {"addRoom"})
+    public String createAddRoom(@PathVariable("id") Long id, @RequestParam("roomName") String name, Lab lab, Model model) {
+        Course course = labService.getCourse(id);
+
+        if (!name.isEmpty()) {
+            Room room = new Room(name);
+            roomRepository.save(room);
+            lab.getRooms().add(room);
+        }
+        lab.setCourse(course);
+        Iterable<RequestType> requestTypes = requestTypeRepository.findAll();
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("rooms", roomRepository.findAllByOrderByNameAsc());
+        model.addAttribute("course", course);
+        model.addAttribute("requesttypes", requestTypes);
+
+        return "lab/create";
+    }
+
+    @RequestMapping(value = "/course/{id}/lab/create", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canCreateLab(principal, #id)")
+    public String create(@PathVariable("id") Long id,
+                         @Valid Lab lab,
+                         BindingResult bindingResult,
+                         @RequestParam(value = "weekRepeat") Optional<Integer> weekRepeat,
+                         Model model, RedirectAttributes redirectAttributes) {
+
+        Course course = labService.getCourse(id);
+
+        if (bindingResult.hasErrors()) {
+            model.addAttribute("lab", lab);
+            model.addAttribute("rooms", roomRepository.findAllByOrderByNameAsc());
+            model.addAttribute("course", course);
+            model.addAttribute("requesttypes", requestTypeRepository.findAll());
+            model.addAttribute("mentorgroups", firstYearMentorGroupRepository.findAllByActiveIsTrue());
+
+            return "lab/create";
+        }
+
+        labService.saveLab(course, lab, weekRepeat);
+
+        redirectAttributes.addFlashAttribute("message", "Lab created.");
+
+        return "redirect:/lab/" + lab.getId();
+    }
+
+    @RequestMapping(value = "/lab/{id}/edit", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canEditLab(principal, #id)")
+    public String edit(@PathVariable("id") Long id, Model model) {
+        Lab lab = labService.getLab(id);
+        List<RequestType> requestTypes = new ArrayList<>();
+        requestTypeRepository.findAll().iterator().forEachRemaining(requestTypes::add);
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("course", lab.getCourse());
+        model.addAttribute("rooms", roomRepository.findAllByOrderByNameAsc());
+        model.addAttribute("requesttypes", requestTypes);
+        model.addAttribute("mentorgroups", firstYearMentorGroupRepository.findAllByActiveIsTrue());
+
+        return "lab/edit";
+    }
+
+    @RequestMapping(value = "/lab/{id}", method = RequestMethod.POST, params = {"addRoom"})
+    public String addRoom(@PathVariable("id") Long id, @RequestParam("roomName") String name, Lab labData, Model model) {
+        Lab lab = labService.getLab(id);
+
+        lab.setDirection(labData.getDirection());
+        lab.setSlot(labData.getSlot());
+        lab.setRooms(labData.getRooms());
+        lab.setAssignments(labData.getAssignments());
+        lab.setSlotSelectionOpensAt(labData.getSlotSelectionOpensAt());
+        if (!name.isEmpty()) {
+            Room room = new Room(name);
+            roomRepository.save(room);
+            lab.getRooms().add(room);
+        }
+        Iterable<RequestType> requestTypes = requestTypeRepository.findAll();
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("course", lab.getCourse());
+        model.addAttribute("requesttypes", requestTypes);
+        model.addAttribute("mentorgroups", firstYearMentorGroupRepository.findAllByActiveIsTrue());
+
+        return "lab/edit";
+    }
+
+    @RequestMapping(value = "/lab/{id}", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canEditLab(principal, #id)")
+    public String store(@PathVariable("id") Long id, @Valid Lab labData, BindingResult bindingResult, Model model, RedirectAttributes redirectAttributes) {
+        if (bindingResult.hasErrors()) {
+            model.addAttribute("lab", labData);
+            model.addAttribute("course", labData.getCourse());
+            model.addAttribute("requesttypes", requestTypeRepository.findAll());
+            model.addAttribute("mentorgroups", firstYearMentorGroupRepository.findAllByActiveIsTrue());
+
+            return "lab/edit";
+        }
+
+        labService.updateLab(id, labData);
+
+        redirectAttributes.addFlashAttribute("message", "Lab updated.");
+
+        return "redirect:/lab/" + id;
+    }
+
+    // --- Remove
+
+    @RequestMapping(value = "/lab/{id}/remove", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canRemoveLab(principal, #id)")
+    public String remove(@PathVariable("id") Long id, Model model) {
+        Lab lab = labService.getLab(id);
+
+        model.addAttribute("lab", lab);
+        model.addAttribute("course", lab.getCourse());
+
+        return "lab/remove";
+    }
+
+    @RequestMapping(value = "/lab/{id}/remove", method = RequestMethod.POST)
+    @PreAuthorize("@permissionService.canRemoveLab(principal, #id)")
+    public String destroy(@PathVariable("id") Long id, Model model, RedirectAttributes redirectAttributes) {
+        Lab lab = labService.getLab(id);
+
+        labRepository.delete(lab);
+
+        redirectAttributes.addFlashAttribute("message", "Lab removed");
+
+        return "redirect:/course/" + lab.getCourse().getId() + "/labs";
+    }
+
+    /**
+     * Takes courseid, assignmentid, and a netid and enqueues the student for a
+     * lab in which the assignment can be signed off.
+     *
+     * @param assignmentid The assignment to sign off.
+     * @param netid        The student who wants to sign off.
+     * @return A redirect to the front page.
+     */
+    @RequestMapping(value = "/lab/submit", method = RequestMethod.POST)
+    // , consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
+    public String submit(@RequestParam String assignmentid,
+                         @RequestParam String netid,
+                         @RequestParam String verification) {
+
+        //todo: add some kind of authentication here :)
+
+        Assignment assignment = labService.getAssignment(Long.parseLong(assignmentid));
+
+        if (!assignment.getVerification().equals(verification)) {
+            logger.error("%s tried to enrol with the wrong verification" +
+                    " code.", netid);
+            return "redirect:/";
+        }
+
+        Course course = assignment.getCourse();
+
+        User user = userRepository.findByUsername(netid);
+
+        RequestEntity enqueueEntity;
+        if (course.getHasGroups()) {
+            enqueueEntity = course.getGroup(user);
+        } else {
+            enqueueEntity = user;
+        }
+
+        Optional<Lab> enqueuedLab = labService.randomEnqueue(assignment, enqueueEntity);
+
+        if (enqueuedLab.isPresent()) {
+        	logger.info("Successful auto enqueue for "  + enqueueEntity);
+        	return "redirect:/lab/" + enqueuedLab.get().getId();
+        }
+
+        //todo: some kind of error reply to the caller of this method
+        logger.error("No lab exists for this student/assignment combination.");
+        return "redirect:/";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/NotificationController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/NotificationController.java
new file mode 100644
index 000000000..45e74c7f2
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/NotificationController.java
@@ -0,0 +1,118 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import javax.persistence.EntityNotFoundException;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.NotificationRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import java.util.List;
+
+@Controller
+public class NotificationController {
+
+    @Autowired
+    NotificationRepository notificationRepository;
+    
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    CourseRepository courseRepository;
+
+    @ModelAttribute("page")
+    public static String page() {
+        return "notifications";
+    }
+
+    @RequestMapping(value = "/notifications", method = RequestMethod.GET)
+    public static String list(@AuthenticatedUser User user, Model model) {
+        model.addAttribute("notifications", user.getNotifications());
+
+        return "notification/index";
+    }
+
+    @RequestMapping(value = "/notifications/course/{id}", method = RequestMethod.GET)
+    public String filter(@AuthenticatedUser User user, Model model, @PathVariable("id") Long id) {
+        model.addAttribute("notifications", user.getNotifications().forCourse(getCourse(id)));
+        model.addAttribute("active", id);
+
+        return "notification/index";
+    }
+
+    @RequestMapping(value = "/notification/{id}", method = RequestMethod.GET)
+    @PreAuthorize("@permissionService.canViewNotification(principal, #id)")
+    public String view(@AuthenticatedUser User user, @PathVariable("id") Long id) {
+        Notification notification = getNotification(id);
+        notification.setRead(true);
+
+        notificationRepository.save(notification);
+
+        return "redirect:/notifications";
+    }
+    @RequestMapping(value = "/notifications/clear", method = RequestMethod.GET)
+    public String clearAll(@AuthenticatedUser User user, RedirectAttributes redirectAttributes) {
+        List<Notification> notificationList = notificationRepository.findByUser(user);
+        notificationRepository.delete(notificationList);
+        redirectAttributes.addFlashAttribute("message", "Notifications cleared.");
+
+        return "redirect:/notifications";
+    }
+
+
+    protected Notification getNotification(Long id) {
+        Notification notification = notificationRepository.findOne(id);
+
+        if (null == notification) {
+            throw new EntityNotFoundException("Notification was not found");
+        }
+
+        return notification;
+    }
+
+    protected Course getCourse(Long id) {
+        Course course = courseRepository.findOne(id);
+
+        if (null == course) {
+            throw new EntityNotFoundException("Course was not found");
+        }
+
+        return course;
+    }
+    
+    @RequestMapping(value = "/notifications/subscription", method = RequestMethod.POST)
+    @ResponseStatus(value = HttpStatus.NO_CONTENT)
+    public void subscription(@AuthenticatedUser User user, @RequestBody Subscription subscription) {
+
+        user.setSubscription(subscription);
+
+        userRepository.save(user);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/RequestController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/RequestController.java
new file mode 100644
index 000000000..30d15ef6a
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/RequestController.java
@@ -0,0 +1,499 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import com.google.common.collect.Lists;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.RequestTypeRepository;
+import nl.tudelft.ewi.queue.repository.RoomRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import nl.tudelft.ewi.queue.service.RequestService;
+import nl.tudelft.ewi.queue.views.View;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.querydsl.binding.QuerydslPredicate;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.data.web.config.EnableSpringDataWebSupport;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.persistence.EntityNotFoundException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.groupingBy;
+
+@Controller
+@EnableSpringDataWebSupport
+public class RequestController {
+
+	@Autowired
+	private CourseRepository courseRepository;
+
+	@Autowired
+	private RequestRepository requestRepository;
+
+	@Autowired
+	private UserRepository userRepository;
+
+	@Autowired
+	private LabRepository labRepository;
+
+	@Autowired
+	private RequestService requestService;
+
+	@Autowired
+	private RequestTypeRepository requestTypeRepository;
+
+	@Autowired
+	private RoomRepository roomRepository;
+
+	@ModelAttribute("page")
+	public static String page() {
+		return "requests";
+	}
+
+	// This sorts the latest request first
+	private static int compareDates(Request a, Request b) {
+		if (a.getCreatedAt().isEqual(b.getCreatedAt()))
+			return 0;
+		else if (a.getCreatedAt().isAfter(b.getCreatedAt()))
+			return -1;
+		else return 1;
+	}
+
+	@RequestMapping(value = "/requests", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canViewRequests(principal)")
+	public String list(HttpServletRequest httpRequest,
+					   @AuthenticatedUser User user, Model model,
+					   @PageableDefault(sort = "id", direction = Sort.Direction.DESC, size = 25) Pageable pageable,
+					   @QuerydslPredicate(root = Request.class) Predicate predicate,
+					   @RequestParam(value = "clear", required = false) Optional<Boolean> clearFilters,
+					   @RequestParam MultiValueMap<String, String> parameters,
+					   RedirectAttributes redirectAttributes) {
+		@SuppressWarnings("unchecked")
+		MultiValueMap<String, String> sessionParams = (MultiValueMap<String, String>) httpRequest.getSession()
+				.getAttribute("filters");
+
+		if (clearFilters.isPresent()) {
+			requestService.clearFilters(httpRequest, parameters);
+		} else if (sessionParams != null && !sessionParams.isEmpty() && parameters.containsKey(
+				"size") && parameters.size() == 1) {
+			sessionParams.remove("size");
+			for (String key : sessionParams.keySet()) {
+				for (String val : sessionParams.get(key)) {
+					parameters.add(key, val);
+				}
+			}
+			requestService.storeParamsInSession(httpRequest, parameters, "filters");
+		} else if (sessionParams != null && !sessionParams.isEmpty() && sessionParams.containsKey(
+				"size")) {
+			boolean equalToPrev = sessionParams.get("size").equals(parameters.get("size"));
+
+			parameters.remove("size");
+			for (String val : sessionParams.get("size")) {
+				parameters.add("size", val);
+			}
+			requestService.storeParamsInSession(httpRequest, parameters, "filters");
+			if(!equalToPrev) {
+				return requestService.redirectToRequestWithParams(redirectAttributes, parameters);
+			}
+		} else {
+			requestService.storeParamsInSession(httpRequest, parameters, "filters");
+		}
+
+		Stream<Course> teaches = user.getTeaches().stream();
+		Stream<Course> manages = user.getManages().stream();
+		Stream<Course> assists = user.getAssists().stream();
+
+		List<Course> courses = Stream
+				.of(teaches, manages, assists)
+				.flatMap(Function.identity())
+				.filter(course -> !course.getIsArchived())
+				.collect(Collectors.toList());
+
+		List<Lab> labs = courses.stream()
+				.flatMap(course -> course.getLabs().stream())
+				.collect(Collectors.toList());
+
+		List<Lab> activeLabs = courses.stream()
+				.flatMap(course -> course.getLabs().stream())
+				.filter(Lab::display)
+				.distinct()
+				.collect(Collectors.toList());
+
+		List<Long> activeLabIds = activeLabs.stream()
+				.map(Lab::getId)
+				.collect(Collectors.toList());
+
+		List<Assignment> assignments = activeLabs.stream()
+				.flatMap(lab -> lab.getAssignments().stream())
+				.distinct()
+				.collect(Collectors.toList());
+
+		BooleanExpression filterOnActiveLabs = QRequest.request.lab.id.in(activeLabIds);
+
+		List<Room> rooms = roomRepository.findAllByOrderByNameAsc();
+
+		Page<Request> filteredRequests = requestRepository.findAll(filterOnActiveLabs.and(predicate), pageable);
+
+		Map<Long, Long> queueCount = new HashMap<>();
+		for (Lab lab : activeLabs) {
+			BooleanExpression filterOnCurrentLab = QRequest.request.lab.id.eq(lab.getId());
+			Iterable<Request> allLabRequests = requestRepository.findAll(filterOnCurrentLab.and(predicate));
+			queueCount.put(lab.getId(), lab.getQueuedCount(Lists.newArrayList(allLabRequests), user));
+		}
+
+		ArrayList<RequestType> requestTypes = Lists.newArrayList(requestTypeRepository.findAll());
+
+		model
+				.addAttribute("requests", filteredRequests)
+				.addAttribute("state", parameters)
+				.addAttribute("courses", courses)
+				.addAttribute("rooms", rooms)
+				.addAttribute("assignments", assignments)
+				.addAttribute("activeLabs", activeLabs)
+				.addAttribute("queued", queueCount)
+				.addAttribute("requestTypes", requestTypes)
+		;
+
+		return "request/list";
+	}
+
+	@RequestMapping(value = "/requests/next/{id}", method = RequestMethod.POST)
+	@PreAuthorize("@permissionService.canNextRequest(principal)")
+	public String next(@AuthenticatedUser User user,
+					   @PathVariable("id") Long id,
+					   @QuerydslPredicate(root = Request.class) Predicate predicate,
+					   @RequestParam MultiValueMap<String, String> parameters) {
+		Lab lab = labRepository.findOne(id);
+		Optional<Request> request = requestService.next(user, lab, predicate);
+
+		return request.map(request1 -> "redirect:/request/" + request1.getId()).orElse("redirect:/requests");
+
+	}
+
+	@RequestMapping(value = "/request/{id}", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canViewRequest(principal, #id)")
+	public String view(@PathVariable("id") Long id, Model model) {
+		Request request = getRequest(id);
+
+		model
+				.addAttribute("request", request)
+				.addAttribute("submissionUrl", requestService.setSubmissionUrl(request))
+				.addAttribute("course", request.getLab().getCourse());
+
+		return "request/view";
+	}
+
+	@RequestMapping(value = "/request/{id}/approve", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canFinishRequest(principal, #id)")
+	public String approve(@PathVariable("id") Long id, Model model) {
+		Request request = getRequest(id);
+
+		model.addAttribute("request", request);
+
+		return "request/approve";
+	}
+
+	@RequestMapping(value = "/request/{id}", method = RequestMethod.POST, params = {"approve"})
+	@PreAuthorize("@permissionService.canFinishRequest(principal, #id)")
+	public String approve(@AuthenticatedUser User user,
+						  @PathVariable("id") Long id,
+						  @RequestParam(value = "comment") String comment,
+						  RedirectAttributes redirectAttributes) {
+		Request request = getRequest(id);
+
+		if (request.isArchived()) {
+			return "redirect:/requests";
+		}
+		requestService.approve(user, request, comment);
+
+		redirectAttributes.addFlashAttribute("message", "Request approved.");
+
+		return "redirect:/requests";
+	}
+
+	@RequestMapping(value = "/request/{id}/reject", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canFinishRequest(principal, #id)")
+	public String reject(@PathVariable("id") Long id, Model model) {
+		Request request = getRequest(id);
+
+		model.addAttribute("request", request);
+
+		return "request/reject";
+	}
+
+	@RequestMapping(value = "/request/{id}", method = RequestMethod.POST, params = {"reject"})
+	@PreAuthorize("@permissionService.canFinishRequest(principal, #id)")
+	public String reject(@AuthenticatedUser User user, @PathVariable("id") Long id,
+						 @RequestParam(value = "comment") String comment,
+						 @RequestParam(value = "commentForStudent") String commentForStudent,
+						 RedirectAttributes redirectAttributes) {
+		Request request = getRequest(id);
+
+		if (request.isArchived()) {
+			return "redirect:/requests";
+		}
+
+		if (comment.trim().isEmpty() || commentForStudent.trim().isEmpty()) {
+			redirectAttributes.addFlashAttribute("message", "You need to provide a reason to the student and the TAs.");
+			return "redirect:request/reject";
+		}
+
+		requestService.reject(user, request, comment, commentForStudent);
+
+		redirectAttributes.addFlashAttribute("message", "Request rejected.");
+
+		return "redirect:/requests";
+	}
+
+	@RequestMapping(value = "/request/{id}/forward", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canFinishRequest(principal, #id)")
+	public String forward(@AuthenticatedUser User user, @PathVariable("id") Long id, Model model) {
+		Request request = getRequest(id);
+		Course course = request.getLab().getCourse();
+
+		List<User> teachers = course.getTeachers();
+		List<User> managers = course.getManagers();
+		List<User> assistants = course.getAssistants();
+
+		List<User> others = teachers;
+		others.addAll(managers);
+		others.addAll(assistants);
+
+		// Do not forward to yourself
+		others = others.stream()
+				.filter(u -> !u.equals(user))
+				.sorted(Comparator.comparing(User::getDisplayName))
+				.collect(Collectors.toList());
+
+		model
+				.addAttribute("request", request)
+				.addAttribute("assistants", others)
+		;
+
+		return "request/forward";
+	}
+
+	@RequestMapping(value = "/request/{id}/notfound", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canFinishRequest(principal,#id)")
+	public String notFound(@AuthenticatedUser User assistant, @PathVariable("id") Long id, Model model) {
+		Request request = getRequest(id);
+		requestService.notFound(request, assistant);
+		return "redirect:/requests";
+	}
+
+	@RequestMapping(value = "/request/{id}", method = RequestMethod.POST, params = {"forward"})
+	@PreAuthorize("@permissionService.canFinishRequest(principal, #id)")
+	public String forward(@PathVariable("id") Long id,
+						  @RequestParam(value = "assistant") Long assistantId,
+						  @RequestParam(value = "comment") String comment,
+						  RedirectAttributes redirectAttributes) {
+		Request request = getRequest(id);
+
+		if (request.isArchived()) {
+			return "redirect:/requests";
+		}
+
+		if (assistantId == -1)
+			requestService.forwardToAny(request, comment);
+		else
+			requestService.forward(request, getAssistant(assistantId), comment);
+
+		redirectAttributes.addFlashAttribute("message", "Request forwarded.");
+
+		return "redirect:/requests";
+	}
+
+	@RequestMapping(value = "/course/{id}/requests/export", method = RequestMethod.GET, produces = "application/json")
+	@PreAuthorize("@permissionService.canEditCourse(principal, #id)")
+	@JsonView(View.Summary.class)
+	@ResponseBody
+	public List<Request> export(@PathVariable("id") Long id) {
+		Course course = getCourse(id);
+
+		Stream<Request> requests = course.getLabs().stream().flatMap(lab ->
+				lab.getRequests().stream()
+		);
+
+		return requests.collect(Collectors.toList());
+	}
+
+	@RequestMapping(value = "/course/{id}/requests/signofflist.csv", method = RequestMethod.GET)
+	@PreAuthorize("@permissionService.canEditCourse(principal, #id)")
+	@ResponseBody
+	public void completionExport(@PathVariable("id") Long id, HttpServletResponse response) throws IOException {
+		Course course = getCourse(id);
+
+		RequestType submissionRequestType = getSubmissionRequestType();
+
+		Stream<Request> requests = course.getLabs().stream()
+				.flatMap(lab -> lab.getRequests().stream())
+				.filter(request -> request.getRequestType().equals(submissionRequestType));
+
+		List<Assignment> assignments = course.getAssignments();
+
+		Map<RequestEntity, Map<Assignment, List<Request>>> requestsPerUser =
+				requests.collect(
+						groupingBy(Request::getRequestEntity,
+								groupingBy(Request::getAssignment)));
+
+		List<List<String>> rows = new ArrayList<>();
+		List<String> header = new ArrayList<>();
+		header.add("netid");
+		header.addAll(assignments.stream().map(Assignment::getName).collect(Collectors.toList()));
+		rows.add(header);
+		requestsPerUser.forEach((requestEntity, assignmentListMap) -> {
+			List<String> row = new ArrayList<>();
+			if (requestEntity.isUser()) {
+				User student = (User)requestEntity;
+				row.add(student.getUsername()); // NetID
+
+				// We loop over the known list of assignments so we're sure to preserve order.
+				assignments.forEach(assignment -> {
+					// Retrieve the requests for this specific assignment
+					List<Request> requestsForAssignment = assignmentListMap.getOrDefault(assignment, Lists.newArrayList());
+					if (requestsForAssignment.isEmpty()) {
+						// Default to NO_REQUEST if there were no requests for this assignment by this user.
+						row.add("NO_REQUEST");
+					} else {
+						// only show result for last entry as this should be the pass/fail
+						requestsForAssignment.sort(RequestController::compareDates);
+						row.add(String.valueOf(requestsForAssignment.get(0).getStatus()));
+					}
+				});
+			} else {
+				// entity is not a user but a group; we need to iterate over the members here; not supported yet
+				row.add("Group result not supported yet");
+			}
+			rows.add(row);
+		});
+		response.setContentType("text/plain; charset=utf-8");
+		response.setHeader("Content-Disposition", "attachment; filename=\"signofflist.csv\"");
+		StringBuilder sb = new StringBuilder();
+		rows.forEach(row -> sb.append(row.stream().map(Object::toString).collect(Collectors.joining(","))).append("\n"));
+		response.getWriter().print(sb.toString());
+		response.getWriter().close();
+	}
+
+	private RequestType getSubmissionRequestType() {
+		RequestType submissionRequestType = null;
+
+		for (RequestType r : requestTypeRepository.findAll()) {
+			if (r.getName().equals("Submission")) {
+				submissionRequestType = r;
+				break;
+			}
+		}
+		return submissionRequestType;
+	}
+
+	@RequestMapping(value = "/request/{id}/feedback", method = RequestMethod.POST)
+	public String feedback(@AuthenticatedUser User user,
+						   @PathVariable("id") Long id,
+						   @RequestParam(value = "feedback") String feedback,
+						   RedirectAttributes redirectAttributes) {
+		Request request = getRequest(id);
+		assert request.getRequestEntity().getId().equals(user.getId());
+		request.setFeedback(feedback);
+		requestRepository.save(request);
+		redirectAttributes.addFlashAttribute("message", "Feedback successfully saved.");
+		return "redirect:/request/" + id;
+	}
+
+
+	@RequestMapping(value = "/request/{id}/update-room/", method = RequestMethod.POST)
+	public String updateRoom(@AuthenticatedUser User user,
+							 @PathVariable("id") Long id,
+							 @RequestParam(value = "room") Long roomId,
+							 @RequestParam(value = "comment", required = false) String comment,
+							 RedirectAttributes redirectAttributes) {
+		Request request = getRequest(id);
+		assert request.getRequestEntity().getId().equals(user.getId());
+		Room room = roomRepository.findOne(roomId);
+		if (comment != null) {
+			request.setComment(comment);
+		} else {
+			request.setComment("");
+		}
+		request.setRoom(room);
+		requestRepository.save(request);
+		redirectAttributes.addFlashAttribute("message", "Your room has been updated.");
+		return "redirect:/lab/" + request.getLab().getId();
+	}
+
+	private User getAssistant(Long id) {
+		User user = userRepository.findOne(id);
+
+		if (null == user) {
+			throw new EntityNotFoundException("Entity was not found");
+		}
+
+		return user;
+	}
+
+	private Request getRequest(Long id) {
+		Request request = requestRepository.findOne(id);
+
+		if (null == request) {
+			throw new EntityNotFoundException("Entity was not found");
+		}
+
+		return request;
+	}
+
+	protected Course getCourse(Long id) {
+		Course course = courseRepository.findOne(id);
+
+		if (null == course) {
+			throw new EntityNotFoundException("Entity was not found");
+		}
+
+		return course;
+	}
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/RoomController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/RoomController.java
new file mode 100644
index 000000000..4792f305e
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/RoomController.java
@@ -0,0 +1,46 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import nl.tudelft.ewi.queue.model.Room;
+import nl.tudelft.ewi.queue.repository.RoomRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class RoomController {
+
+    @Autowired
+    private RoomRepository roomRepository;
+
+    @RequestMapping(value = "/room/{id}", method = RequestMethod.GET, produces = "application/json")
+    @PreAuthorize("@permissionService.canViewRoomLayout(principal, #id)")
+    @ResponseBody
+    public String getRoom(@PathVariable("id") Long id) {
+        Room room = roomRepository.findOne(id);
+        if (room != null && room.getMapFilePath() != null) {
+            return "{\"path\": \"" + room.getMapFilePath() + "\"}";
+        }
+        return "{}";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/controller/SSOController.java b/backend/src/main/java/nl/tudelft/ewi/queue/controller/SSOController.java
new file mode 100644
index 000000000..7aa5ff892
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/controller/SSOController.java
@@ -0,0 +1,71 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.saml.metadata.MetadataManager;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Set;
+
+@Controller
+@RequestMapping("/saml")
+@Profile("production")
+public class SSOController {
+
+    private static final Logger logger = LoggerFactory.getLogger(SSOController.class);
+
+    @Autowired
+    private MetadataManager metadata;
+
+    @RequestMapping(value = "/idpSelection", method = RequestMethod.GET)
+    public String idpSelection(HttpServletRequest request, Model model) {
+        if (!(SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken)) {
+            logger.warn("The current user is already logged.");
+            return "redirect:/";
+        }
+        
+		if (isForwarded(request)) {
+		    Set<String> idps = metadata.getIDPEntityNames();
+		    for (String idp : idps) {
+		        logger.info("Configured Identity Provider for SSO: " + idp);
+		    }
+		    model.addAttribute("idps", idps);
+		    return "saml/idpselection";
+		}
+		
+		logger.warn("Direct accesses to '/idpSelection' route are not allowed");
+		return "redirect:/";
+    }
+
+    /*
+     * Checks if an HTTP request is forwarded from servlet.
+     */
+    private boolean isForwarded(HttpServletRequest request) {
+        return request.getAttribute("javax.servlet.forward.request_uri") != null;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/cqsr/Event.java b/backend/src/main/java/nl/tudelft/ewi/queue/cqsr/Event.java
new file mode 100644
index 000000000..dda80814a
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/cqsr/Event.java
@@ -0,0 +1,30 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.cqsr;
+
+public interface Event<T> {
+    /**
+     * Every event belongs to an aggregate
+     */
+    public T getAggregate();
+
+    /**
+     * Apply the event to the given aggregate.
+     */
+    public T apply(T aggregate);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/cqsr/RecordsEvents.java b/backend/src/main/java/nl/tudelft/ewi/queue/cqsr/RecordsEvents.java
new file mode 100644
index 000000000..9737ba112
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/cqsr/RecordsEvents.java
@@ -0,0 +1,37 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.cqsr;
+
+import java.util.List;
+
+public interface RecordsEvents<T> {
+    /**
+     * Get all events in chronological order
+     */
+    List<? extends Event<T>> getEvents();
+
+    /**
+     * Add an event
+     */
+    void addEvent(Event<T> event);
+
+    /**
+     * Apply all events to the current object
+     */
+    T apply();
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedDialect.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedDialect.java
new file mode 100644
index 000000000..97482083c
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedDialect.java
@@ -0,0 +1,40 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.thymeleaf.dialect.IDialect;
+import org.thymeleaf.dialect.IExpressionObjectDialect;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+public class AuthenticatedDialect implements IExpressionObjectDialect, IDialect {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Override
+    public IExpressionObjectFactory getExpressionObjectFactory() {
+        return new AuthenticatedExpressionObjectFactory(userRepository);
+    }
+
+    @Override
+    public String getName() {
+        return "AuthenticatedDialect";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedExpressionObjectFactory.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedExpressionObjectFactory.java
new file mode 100644
index 000000000..0c90cb0ea
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/AuthenticatedExpressionObjectFactory.java
@@ -0,0 +1,73 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import com.google.common.collect.Sets;
+import nl.tudelft.ewi.queue.model.UserPrincipal;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.thymeleaf.context.IExpressionContext;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+import java.util.Set;
+
+public class AuthenticatedExpressionObjectFactory implements IExpressionObjectFactory {
+
+    private UserRepository userRepository;
+
+    private static final String AUTHENTICATED_OBJECT_NAME = "authenticated";
+
+    AuthenticatedExpressionObjectFactory(UserRepository userRepository) {
+        this.userRepository = userRepository;
+    }
+
+    @Override
+    public Set<String> getAllExpressionObjectNames() {
+        return Sets.newHashSet(AUTHENTICATED_OBJECT_NAME);
+    }
+
+    @Override
+    public Object buildObject(IExpressionContext context, String expressionObjectName) {
+        if (AUTHENTICATED_OBJECT_NAME.equals(expressionObjectName)) {
+            return getUser();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isCacheable(String expressionObjectName) {
+        return false;
+    }
+
+    protected Object getUser() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+        if (authentication == null) {
+            return null;
+        }
+
+        if (authentication.getPrincipal() instanceof UserPrincipal) {
+            UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
+
+            return userRepository.findOne(userPrincipal.getUser().getId());
+        }
+
+        return authentication.getPrincipal();
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterDialect.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterDialect.java
new file mode 100644
index 000000000..b02a6bb1d
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterDialect.java
@@ -0,0 +1,34 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import org.thymeleaf.dialect.IExpressionObjectDialect;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+public class FilterDialect implements IExpressionObjectDialect {
+
+    @Override
+    public IExpressionObjectFactory getExpressionObjectFactory() {
+        return new FilterExpressionObjectFactory();
+    }
+
+    @Override
+    public String getName() {
+        return "FilterDialect";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactory.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactory.java
new file mode 100644
index 000000000..7ad45e8f6
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactory.java
@@ -0,0 +1,160 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import com.google.common.collect.Sets;
+import nl.tudelft.ewi.queue.model.Request;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
+import org.thymeleaf.context.IExpressionContext;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+import java.util.List;
+import java.util.Set;
+
+public class FilterExpressionObjectFactory implements IExpressionObjectFactory {
+
+    private static final String FILTER_OBJECT_NAME = "filter";
+
+    @Override
+    public Set<String> getAllExpressionObjectNames() {
+        return Sets.newHashSet(FILTER_OBJECT_NAME);
+    }
+
+    @Override
+    public Object buildObject(IExpressionContext context, String expressionObjectName) {
+        if (FILTER_OBJECT_NAME.equals(expressionObjectName)) {
+            return new Filter();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isCacheable(String expressionObjectName) {
+        return false;
+    }
+
+    public class Filter {
+        /**
+         * Builds a URI corresponding that includes the given state in its query parameters.
+         *
+         * @param state The current state of the filter as a Map.
+         * @return The relative URI built from the state.
+         */
+        public String buildUrlMultiValueMap(MultiValueMap<String, String> state) {
+            return buildUrlMultiValueMap(state, "");
+        }
+
+        /**
+         * Builds a URI corresponding that includes the given state in
+         * its query parameters and uses the given path.
+         *
+         * @param state The current state of the filter as a Map.
+         * @param path  The path for the URI to have.
+         * @return The relative URI built from the state.
+         */
+        public String buildUrlMultiValueMap(MultiValueMap<String, String> state, String path) {
+            return UriComponentsBuilder.fromPath(path)
+                    .queryParams(state)
+                    .build(false).encode().toUriString();
+        }
+
+        /**
+         * Takes a MultiValueMap representing the filter state and updates it.
+         *
+         * @param state       The state of the filter as a map.
+         * @param updateField The field that must be updated.
+         * @param updateValue The value with which to update the field.
+         * @return The new (relative) URL built from the state.
+         */
+        public String updateUrlMultiValueMap(MultiValueMap<String, String> state,
+                                             String updateField, String updateValue) {
+            return updateUrlMultiValueMap(state, updateField, updateValue, "");
+        }
+
+        /**
+         * Takes a MultivalueMap representing the filter state and updates it.
+         *
+         * @param state       The state of the filter as a map.
+         * @param updateField The field that must be updated.
+         * @param updateValue The value with which to update the field.
+         * @param path        The path of the output URL.
+         * @return The new URL built from the MultivalueMap.
+         */
+        public String updateUrlMultiValueMap(MultiValueMap<String, String> state,
+                                             String updateField, String updateValue,
+                                             String path) {
+            state.set(updateField, updateValue);
+            return buildUrlMultiValueMap(state, path);
+        }
+
+        /**
+         * Checks whether the given key and value pair is an entry in
+         * the given state.
+         *
+         * @param key   The key to check.
+         * @param value The value to check.
+         * @param state The state of the filter as a map.
+         * @return {@code true} when the given key-value pair is in the given state,
+         * {@code false} otherwise.
+         */
+        public boolean valueInMultiMap(String key, String value, MultiValueMap<String, String> state) {
+            List<String> valueSet = state.get(key);
+            if (valueSet != null) {
+                return valueSet.contains(value);
+            }
+            return false;
+        }
+
+        /**
+         * Maps the status of a request to a fitting bootstrap background color.
+         *
+         * @param status The status of the request.
+         * @return The selected bootstrap background color.
+         */
+        public String mapStatusToBootstrapColor(int status) {
+            Request.Status values = Request.Status.values()[status];
+            String bgColor = "";
+            switch (values) {
+                case PENDING:
+                case ASSIGNED:
+                    bgColor = "bg-primary";
+                    break;
+                case CANCELLED:
+                case REVOKED:
+                    bgColor = "bg-secondary";
+                    break;
+                case PROCESSING:
+                    bgColor = "bg-info";
+                    break;
+                case APPROVED:
+                    bgColor = "bg-success";
+                    break;
+                case NOTFOUND:
+                case REJECTED:
+                    bgColor = "bg-danger";
+                    break;
+                case FORWARDED:
+                    bgColor = "bg-warning";
+                    break;
+            }
+            return bgColor;
+        }
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeDialect.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeDialect.java
new file mode 100644
index 000000000..a1d001223
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeDialect.java
@@ -0,0 +1,35 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import org.thymeleaf.dialect.IExpressionObjectDialect;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+
+public class PrettyTimeDialect implements IExpressionObjectDialect {
+
+    @Override
+    public IExpressionObjectFactory getExpressionObjectFactory() {
+        return new PrettyTimeExpressionObjectFactory();
+    }
+
+    @Override
+    public String getName() {
+        return "PrettyTimeDialect";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeExpressionObjectFactory.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeExpressionObjectFactory.java
new file mode 100644
index 000000000..7a54b0772
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/PrettyTimeExpressionObjectFactory.java
@@ -0,0 +1,107 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import com.google.common.collect.Sets;
+import org.thymeleaf.context.IExpressionContext;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Set;
+
+public class PrettyTimeExpressionObjectFactory implements IExpressionObjectFactory {
+
+    private static final String PRETTY_TIME_OBJECT_NAME = "prettyTime";
+
+    @Override
+    public Set<String> getAllExpressionObjectNames() {
+        return Sets.newHashSet(PRETTY_TIME_OBJECT_NAME);
+    }
+
+    @Override
+    public Object buildObject(IExpressionContext context, String expressionObjectName) {
+        if (PRETTY_TIME_OBJECT_NAME.equals(expressionObjectName)) {
+            return new PrettyTime();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isCacheable(String expressionObjectName) {
+        return false;
+    }
+
+    private class PrettyTime {
+        public String diffForHumans(LocalDateTime time) {
+            if (null == time) {
+                return "";
+            }
+
+            return diffForHumans(time, LocalDateTime.now());
+        }
+
+        public String diffForHumans(LocalDateTime time, LocalDateTime other) {
+            long years = ChronoUnit.YEARS.between(time, other);
+            long months = ChronoUnit.MONTHS.between(time, other);
+            long days = ChronoUnit.DAYS.between(time, other);
+            long hours = ChronoUnit.HOURS.between(time, other);
+            long minutes = ChronoUnit.MINUTES.between(time, other);
+            long seconds = ChronoUnit.SECONDS.between(time, other);
+
+            if (years > 0) {
+                return display(ChronoUnit.YEARS, years);
+            } else if (months > 0) {
+                return display(ChronoUnit.MONTHS, months);
+            } else if (days > 0) {
+                return display(ChronoUnit.DAYS, days);
+            } else if (hours > 0) {
+                return display(ChronoUnit.HOURS, hours);
+            } else if (minutes > 0) {
+                return display(ChronoUnit.MINUTES, minutes);
+            } else {
+                return display(ChronoUnit.SECONDS, seconds);
+            }
+        }
+
+        protected String display(ChronoUnit unit, long count) {
+            String suffix;
+
+            if (count > 1) {
+                suffix = "s";
+            } else {
+                suffix = "";
+            }
+
+            switch (unit) {
+                case YEARS:
+                    return count + " year" + suffix + " ago";
+                case MONTHS:
+                    return count + " month" + suffix + " ago";
+                case DAYS:
+                    return count + " day" + suffix + " ago";
+                case HOURS:
+                    return count + " hour" + suffix + " ago";
+                case MINUTES:
+                    return count + " minute" + suffix + " ago";
+                default:
+                    return count + " second" + suffix + " ago";
+            }
+        }
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestDialect.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestDialect.java
new file mode 100644
index 000000000..288d32fc5
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestDialect.java
@@ -0,0 +1,33 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import org.thymeleaf.dialect.IExpressionObjectDialect;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+public class RequestDialect implements IExpressionObjectDialect {
+    @Override
+    public IExpressionObjectFactory getExpressionObjectFactory() {
+        return new RequestExpressionObjectFactory();
+    }
+
+    @Override
+    public String getName() {
+        return "RequestDialect";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestExpressionObjectFactory.java b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestExpressionObjectFactory.java
new file mode 100644
index 000000000..2bb593c7c
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/dialect/RequestExpressionObjectFactory.java
@@ -0,0 +1,68 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import com.google.common.collect.Sets;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.thymeleaf.context.IExpressionContext;
+import org.thymeleaf.expression.IExpressionObjectFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RequestExpressionObjectFactory implements IExpressionObjectFactory {
+
+    private static final String REQUEST_OBJECT_NAME = "request";
+
+    @Override
+    public Set<String> getAllExpressionObjectNames() {
+        return Sets.newHashSet(REQUEST_OBJECT_NAME);
+    }
+
+    @Override
+    public Object buildObject(IExpressionContext context, String expressionObjectName) {
+        if (REQUEST_OBJECT_NAME.equals(expressionObjectName)) {
+            HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
+            return new Request(httpServletRequest);
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isCacheable(String expressionObjectName) {
+        return false;
+    }
+
+    private class Request {
+        private HttpServletRequest request;
+
+        public Request(HttpServletRequest request) {
+            this.request = request;
+        }
+
+        public boolean is(String part) {
+            Pattern pattern = Pattern.compile(part);
+            Matcher matcher = pattern.matcher(request.getRequestURI());
+
+            return matcher.matches();
+        }
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/exception/AlreadyEnqueuedException.java b/backend/src/main/java/nl/tudelft/ewi/queue/exception/AlreadyEnqueuedException.java
new file mode 100644
index 000000000..0548fec4c
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/exception/AlreadyEnqueuedException.java
@@ -0,0 +1,29 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.exception;
+
+public class AlreadyEnqueuedException extends RuntimeException {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 888849449076254005L;
+
+	public AlreadyEnqueuedException(String s) {
+        super(s);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/exception/InvalidSlotException.java b/backend/src/main/java/nl/tudelft/ewi/queue/exception/InvalidSlotException.java
new file mode 100644
index 000000000..218af0ff2
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/exception/InvalidSlotException.java
@@ -0,0 +1,27 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.exception;
+
+public class InvalidSlotException extends RuntimeException {
+
+    private static final long serialVersionUID = -4965251603015692920L;
+
+    public InvalidSlotException(String s) {
+        super(s);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/factory/NotificationFactory.java b/backend/src/main/java/nl/tudelft/ewi/queue/factory/NotificationFactory.java
new file mode 100644
index 000000000..cf66c6d73
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/factory/NotificationFactory.java
@@ -0,0 +1,51 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.factory;
+
+import nl.martijndwars.webpush.Notification;
+import nl.tudelft.ewi.queue.model.Subscription;
+import org.springframework.stereotype.Service;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.spec.InvalidKeySpecException;
+
+@Service
+public class NotificationFactory {
+	/** The Time to live of GCM notifications */
+	private static final int TTL = 255;
+
+	public Notification fromSubscription(Subscription subscription, byte[] payload) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException {
+        if (subscription.isGcm()) {
+            return new Notification(
+                subscription.getEndpoint(),
+                subscription.getUserPublicKey(),
+                subscription.getAuthAsBytes(),
+                payload
+            );
+        }
+
+        return new Notification(
+            subscription.getEndpoint(),
+            subscription.getUserPublicKey(),
+            subscription.getAuthAsBytes(),
+            payload,
+            TTL
+        );
+	}
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/forms/AssignmentListWrapperForm.java b/backend/src/main/java/nl/tudelft/ewi/queue/forms/AssignmentListWrapperForm.java
new file mode 100644
index 000000000..d2448d424
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/forms/AssignmentListWrapperForm.java
@@ -0,0 +1,44 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.forms;
+
+import nl.tudelft.ewi.queue.viewmodel.AssignmentViewModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AssignmentListWrapperForm {
+    private List<AssignmentViewModel> assignmentList;
+
+    public AssignmentListWrapperForm() {
+        this.assignmentList = new ArrayList<>();
+    }
+
+    public List<AssignmentViewModel> getAssignmentList() {
+        return assignmentList;
+    }
+
+    public void setAssignmentList(List<AssignmentViewModel> assignmentList) {
+        this.assignmentList = assignmentList;
+    }
+
+    public void add(AssignmentViewModel assignment) {
+        this.assignmentList.add(assignment);
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/forms/ParticipantForm.java b/backend/src/main/java/nl/tudelft/ewi/queue/forms/ParticipantForm.java
new file mode 100644
index 000000000..3573c26b3
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/forms/ParticipantForm.java
@@ -0,0 +1,54 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.forms;
+
+import org.hibernate.validator.constraints.NotBlank;
+
+public class ParticipantForm {
+    private long courseId;
+
+    @NotBlank
+    private String username;
+
+    @NotBlank
+    private String role;
+
+    public long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/helper/BrightspaceCsvHelper.java b/backend/src/main/java/nl/tudelft/ewi/queue/helper/BrightspaceCsvHelper.java
new file mode 100644
index 000000000..70cf46a77
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/helper/BrightspaceCsvHelper.java
@@ -0,0 +1,149 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.helper;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import nl.tudelft.ewi.queue.model.User;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Optional;
+import java.util.Scanner;
+
+public class BrightspaceCsvHelper {
+    private final static Integer STUDENT_NUMBER = 0;
+    private final static Integer NETID = 1;
+    private final static Integer LAST_NAME = 2;
+    private final static Integer FIRST_NAME = 3;
+    private final static Integer EMAIL = 4;
+    private final static Integer GROUP_NAME = 6;
+
+    private Integer studentNumber;
+
+    private String netID;
+
+    private String firstName;
+
+    private String lastName;
+
+    private String email;
+
+    private String groupName;
+
+    public BrightspaceCsvHelper(Integer studentNumber, String netID, String firstName,
+                                String lastName, String email, String groupName) {
+        this.studentNumber = studentNumber;
+        this.netID = netID;
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.email = email;
+        this.groupName = groupName;
+    }
+
+    public Integer getStudentNumber() {
+        return studentNumber;
+    }
+
+    public void setStudentNumber(Integer studentNumber) {
+        this.studentNumber = studentNumber;
+    }
+
+    public String getNetID() {
+        return netID;
+    }
+
+    public void setNetID(String netID) {
+        this.netID = netID;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public void setGroupName(String groupName) {
+        this.groupName = groupName;
+    }
+
+    public static ArrayList<BrightspaceCsvHelper> readCsv(MultipartFile csvFile) throws IOException {
+        InputStream contents = csvFile.getInputStream();
+        Scanner scanner = new Scanner(contents);
+        scanner.useDelimiter("\\A");
+        ArrayList<BrightspaceCsvHelper> csvResultSet = new ArrayList<>();
+        String csv = "";
+        if (scanner.hasNext()) {
+            csv = scanner.next();
+        }
+        String[] csvLines = csv.split("\n");
+        for(String line : csvLines) {
+            String[] studentGroup = line.split(",");
+            Optional<BrightspaceCsvHelper> student = lineToCsvObject(studentGroup);
+            student.ifPresent(csvResultSet::add);
+        }
+        scanner.close();
+        return csvResultSet;
+    }
+
+    private static Optional<BrightspaceCsvHelper> lineToCsvObject(String[] student) {
+        String studentNumber = student[STUDENT_NUMBER];
+        if (hasStudentNumber(studentNumber)) {
+            Integer studentNumberInt = Integer.parseInt(studentNumber.trim());
+            String netID = User.guarantueeValidNetId(student[NETID].trim());
+            String lastName = student[LAST_NAME].trim();
+            String firstName = student[FIRST_NAME].trim();
+            String email = student[EMAIL].trim();
+            String groupName = student[GROUP_NAME].trim();
+            return Optional.of(new BrightspaceCsvHelper(studentNumberInt, netID, firstName, lastName, email, groupName));
+        }
+        return Optional.empty();
+    }
+
+    private static boolean hasStudentNumber(String studentNumber) {
+        try {
+            Double.parseDouble(studentNumber);
+        } catch(NumberFormatException e) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/helper/EmailHelper.java b/backend/src/main/java/nl/tudelft/ewi/queue/helper/EmailHelper.java
new file mode 100644
index 000000000..68f42f699
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/helper/EmailHelper.java
@@ -0,0 +1,115 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.helper;
+
+import nl.tudelft.ewi.queue.model.Assignment;
+import nl.tudelft.ewi.queue.model.Group;
+import nl.tudelft.ewi.queue.model.RequestSlot;
+import nl.tudelft.ewi.queue.model.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Properties;
+
+public class EmailHelper {
+
+    private static final Logger logger = LoggerFactory.getLogger(EmailHelper.class);
+
+    private static EmailHelper ourInstance = new EmailHelper();
+    private String from;
+    private Properties properties;
+    private Session session;
+
+    private EmailHelper() {
+        from = "noreply@tudelft.nl";
+
+        properties = new Properties();
+        properties.put("host", "smtp.tudelft.nl");
+
+        session = Session.getDefaultInstance(properties);
+    }
+
+    /**
+     * Get the email helper singleton.
+     *
+     * @return The instance of EmailHelper.
+     */
+    public static EmailHelper getInstance() {
+        return ourInstance;
+    }
+
+    //todo: could consider having one notify method: notify entity of timeslot
+    /**
+     * Notifies the student of their new slot for a specific
+     * assignment. The email will be send from noreply@tudelft.nl.
+     *
+     * @param user       The user which needs to be notified of their new slot.
+     * @param slot       The slot to which the user is assigned.
+     * @param assignment The assignment the user will be signing off.
+     */
+    public void notifyStudentOfTimeSlot(User user, RequestSlot slot,
+                                        Assignment assignment) {
+        try {
+            MimeMessage messageobj = new MimeMessage(this.session);
+
+            messageobj.setFrom(new InternetAddress(from));
+            messageobj.addRecipient(Message.RecipientType.TO,
+                    new InternetAddress(user.getEmail()));
+
+            messageobj.setSubject("New enrollment " +
+                    "in the queue");
+
+            Duration slotDuration = Duration.between(slot.getOpensAt(), slot.getClosesAt());
+
+
+            LocalDateTime time = slot.getOpensAt();
+            messageobj.setText("Dear" + user.getDisplayName() + "\n " +
+                    "You have been " + "enqueued for assignment "
+                    + assignment.getName() + ".\n" +
+                    "Your slot starts at " + time.toString() + "and " +
+                    "will take roughly "
+                    + slotDuration.toMinutes() + " minutes. \n \n" +
+                    "Kind regards, \n The queue team.");
+
+            Transport.send(messageobj);
+        } catch (MessagingException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Notifies all members of a group of the timeslot they are enqueued in.
+     *
+     * @param group       The group whose members must be notified.
+     * @param requestSlot The timeslot the members are enqueued for.
+     * @param assignment  The assignment the group wants to sign of.
+     */
+    public void notifyGroupOfTimeSlot(Group group, RequestSlot requestSlot, Assignment assignment) {
+        for (User user : group.getMembers()) {
+            notifyStudentOfTimeSlot(user, requestSlot, assignment);
+        }
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/helper/MarkDownParser.java b/backend/src/main/java/nl/tudelft/ewi/queue/helper/MarkDownParser.java
new file mode 100644
index 000000000..092daecf6
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/helper/MarkDownParser.java
@@ -0,0 +1,33 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.helper;
+
+import com.vladsch.flexmark.html.HtmlRenderer;
+import com.vladsch.flexmark.parser.Parser;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MarkDownParser {
+
+    public String parseToHtml(String markDownString) {
+        Parser parser = Parser.builder().build();
+        HtmlRenderer renderer = HtmlRenderer.builder().build();
+        return renderer.render(parser.parse(markDownString));
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/helper/TimeHelper.java b/backend/src/main/java/nl/tudelft/ewi/queue/helper/TimeHelper.java
new file mode 100644
index 000000000..29b662191
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/helper/TimeHelper.java
@@ -0,0 +1,31 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.helper;
+
+import java.time.LocalDateTime;
+
+public class TimeHelper {
+
+    public static LocalDateTime setSecondsAndMsToZero(LocalDateTime dateTime) {
+        if (dateTime != null) {
+            dateTime = dateTime.withSecond(0);
+            dateTime = dateTime.withNano(0);
+        }
+        return dateTime;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Assignment.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Assignment.java
new file mode 100644
index 000000000..b98c59004
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Assignment.java
@@ -0,0 +1,128 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import nl.tudelft.ewi.queue.views.View;
+import org.bouncycastle.util.encoders.Hex;
+import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.annotations.SQLDelete;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+@Entity
+@SQLDelete(sql = "UPDATE assignment SET deleted_at = NOW() WHERE id = ?")
+public class Assignment implements Serializable {
+
+    private static final long serialVersionUID = -4104521574160856982L;
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @ManyToOne
+    private Course course;
+
+    @ManyToMany
+    private List<Lab> labs = new ArrayList<>();
+
+    @OneToMany(mappedBy = "assignment")
+    private List<Request> requests;
+
+    @JsonView(View.Summary.class)
+    private String name;
+
+    @GenericGenerator(name = "uuid-gen", strategy = "uuid")
+    @GeneratedValue(generator = "uuid-gen")
+    private String verification;
+
+    @SuppressWarnings("unused")
+	private LocalDateTime deletedAt;
+
+    public Assignment() {
+        this.verification = randomVerificationCode();
+    }
+
+    public Assignment(Course course, String name) {
+        this.course = course;
+        this.name = name;
+        this.verification = randomVerificationCode();
+
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public Course getCourse() {
+        return course;
+    }
+
+    public void setCourse(Course course) {
+        this.course = course;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<Lab> getLabs() {
+        return labs;
+    }
+
+    public void setLabs(List<Lab> labs) {
+        this.labs = labs;
+    }
+
+    public void addLab(Lab lab) {
+        labs.add(lab);
+
+        if (!lab.getAssignments().contains(this)) {
+            lab.addAssignment(this);
+        }
+    }
+
+    public String getVerification() {
+        return this.verification;
+    }
+
+    private String randomVerificationCode() {
+        byte[]  resBuf = new byte[50];
+        new Random().nextBytes(resBuf);
+        return new String(Hex.encode(resBuf));
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Assistant.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Assistant.java
new file mode 100644
index 000000000..75dcfc065
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Assistant.java
@@ -0,0 +1,41 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+
+@Entity
+public class Assistant extends Role {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 7431100138119507068L;
+
+	public Assistant() {
+        super();
+    }
+
+    public Assistant(User user, Course course) {
+        super(user, course);
+    }
+
+    @Override
+    public String toString() {
+        return "Assistant";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Course.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Course.java
new file mode 100644
index 000000000..5bc447ef7
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Course.java
@@ -0,0 +1,408 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.Where;
+import org.hibernate.validator.constraints.NotBlank;
+import org.springframework.cache.annotation.Cacheable;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Entity
+@SQLDelete(sql = "UPDATE course SET deleted_at = NOW() WHERE id = ?")
+@Where(clause = "deleted_at IS NULL")
+public class Course implements Serializable {
+
+    private static final long serialVersionUID = 254709728043436131L;
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @NotBlank
+    private String name;
+
+    @NotBlank
+    private String code;
+
+    @OneToMany(mappedBy = "course")
+    private List<Lab> labs = new ArrayList<>();
+
+    @OneToMany(mappedBy = "course", cascade = {CascadeType.ALL}, orphanRemoval = true)
+    @Where(clause = "deleted_at IS NULL")
+    private List<Assignment> assignments = new ArrayList<>();
+
+    @OneToMany(mappedBy = "course", cascade = {CascadeType.MERGE}, orphanRemoval = true)
+    private List<Role> roles = new ArrayList<>();
+
+    private String submissionUrl;
+
+    @NotNull
+    private Boolean hasGroups = false;
+
+    @OneToMany(mappedBy = "course", cascade = {CascadeType.MERGE}, orphanRemoval = true)
+    private List<Group> groups = new ArrayList<>();
+
+    @NotNull
+    private Boolean isArchived = false;
+
+    @SuppressWarnings("unused")
+    private LocalDateTime deletedAt;
+
+    public Course() {
+    }
+    public Course(String name, String code) {
+        this.name = name;
+        this.code = code;
+    }
+
+    public Course(String name, String code, String submissionUrl) {
+        this(name, code);
+        this.submissionUrl = submissionUrl;
+    }
+
+    public Course(String name, String code, String submissionUrl, Boolean hasGroups) {
+        this(name, code, submissionUrl);
+        this.hasGroups = hasGroups;
+    }
+
+    public Course(String name, String code, String submissionUrl, Boolean hasGroups, Boolean isArchived) {
+        this(name, code, submissionUrl, hasGroups);
+        this.isArchived = isArchived;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public List<Lab> getLabs() {
+        return labs;
+    }
+
+    public List<Lab> getTodaysLabs() {
+        return labs.stream()
+                .filter(l -> l.getSlot().isTodayish() || l.slotSelectionIsOpen())
+                .collect(Collectors.toList());
+    }
+
+    public void setLabs(List<Lab> labs) {
+        this.labs = labs;
+    }
+
+    public void addLab(Lab lab) {
+        labs.add(lab);
+
+        if (!this.equals(lab.getCourse())) {
+            lab.setCourse(this);
+        }
+    }
+
+    public List<Assignment> getAssignments() {
+        return assignments;
+    }
+
+    public void setAssignments(List<Assignment> assignments) {
+        this.assignments = assignments;
+    }
+
+    public void addAssignment(Assignment assignment) {
+        assignments.add(assignment);
+
+        if (!this.equals(assignment.getCourse())) {
+            assignment.setCourse(this);
+        }
+    }
+
+    public List<Role> getRoles() {
+        return roles;
+    }
+
+    public void setRoles(List<Role> roles) {
+        this.roles = roles;
+    }
+
+    public void addRole(Role role) {
+        roles.add(role);
+
+        if (!this.equals(role.getCourse())) {
+            role.setCourse(this);
+        }
+    }
+
+    public List<Role> getStudents() {
+        return roles.stream()
+                .filter(r -> r instanceof Student)
+                .collect(Collectors.toList());
+    }
+
+    public List<User> getAssistants() {
+        return roles.stream()
+                .filter(r -> r instanceof Assistant)
+                .map(Role::getUser)
+                .collect(Collectors.toList());
+    }
+
+    public List<User> getManagers() {
+        return roles.stream()
+                .filter(r -> r instanceof Manager)
+                .map(Role::getUser)
+                .collect(Collectors.toList());
+    }
+
+    public List<User> getTeachers() {
+        return roles.stream()
+                .filter(r -> r instanceof Teacher)
+                .map(Role::getUser)
+                .collect(Collectors.toList());
+    }
+
+    public List<User> getPrivileged() {
+        Stream<User> teaches = getTeachers().stream();
+        Stream<User> manages = getManagers().stream();
+        Stream<User> assists = getAssistants().stream();
+
+        return Stream
+                .of(teaches, manages, assists)
+                .flatMap(Function.identity())
+                .collect(Collectors.toList());
+    }
+
+    public List<Role> getRoles(Class<Role> role) {
+        return roles.stream()
+                .filter(role::isInstance)
+                .collect(Collectors.toList());
+    }
+
+    public boolean isEnrolled(User user) {
+        return roles.stream()
+                .anyMatch(r -> user.equals(r.getUser()));
+    }
+
+    public boolean isGroupOfThisCourse(Group group) {
+        return groups.stream()
+                .anyMatch(r -> group.getId().equals(r.getId()));
+    }
+
+    public Group getGroup(User user) {
+        Optional<Group> group = groups.stream().filter(g -> g.hasMember(user)).findFirst();
+        return group.orElse(null);
+    }
+
+    public String getSubmissionUrl() {
+        return submissionUrl;
+    }
+
+    public void setSubmissionUrl(String submissionURL) {
+        this.submissionUrl = submissionURL;
+    }
+
+    public Boolean getHasGroups() {
+        return hasGroups;
+    }
+
+    public void setHasGroups(Boolean hasGroups) {
+        this.hasGroups = hasGroups;
+    }
+
+    public List<Group> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(List<Group> groups) {
+        this.groups = groups;
+    }
+
+    public void addGroup(Group group) {
+        //todo: should there be a check for the hasGroups boolean here?
+        if (!isGroupOfThisCourse(group)) {
+            groups.add(group);
+        }
+    }
+
+    public void removeGroup(Group toRemove) {
+        groups.removeIf(group -> group.getId().equals(toRemove.getId()));
+    }
+
+    private OptionalDouble averageWaiting() {
+        double totalWaitingTime = 0;
+        int labCount = 0;
+        for(Lab lab : this.getTodaysLabs()) {
+            OptionalDouble waiting = lab.averageWaitingDouble();
+            if (waiting.isPresent()) {
+                totalWaitingTime += waiting.getAsDouble();
+                labCount += 1;
+            }
+        }
+        return calculateAverage(totalWaitingTime, labCount);
+    }
+
+    private OptionalDouble averageProcessing() {
+        double totalWaitingTime = 0;
+        int labCount = 0;
+        for(Lab lab : this.getTodaysLabs()) {
+            OptionalDouble waiting = lab.averageProcessingDouble();
+            if (waiting.isPresent()) {
+                totalWaitingTime += waiting.getAsDouble();
+                labCount += 1;
+            }
+        }
+        return calculateAverage(totalWaitingTime, labCount);
+    }
+
+    public Optional<String> averageWaitingFormatted() {
+        return formattedDouble(this.averageWaiting());
+    }
+
+    public Optional<String> averageProcessingFormatted() {
+        return formattedDouble(this.averageProcessing());
+    }
+
+    private Optional<String> formattedDouble(OptionalDouble optionalDouble) {
+        DecimalFormat df = new DecimalFormat("#.##");
+
+        String returnString = "";
+        if (!optionalDouble.isPresent()) {
+            return Optional.empty();
+        }
+        return  Optional.of(df.format(optionalDouble.getAsDouble()));
+    }
+
+    private OptionalDouble calculateAverage(double total, double amount) {
+        OptionalDouble avgWaitingTime = OptionalDouble.empty();
+        if (amount != 0) {
+            avgWaitingTime =  OptionalDouble.of(total / amount);
+        }
+        return avgWaitingTime;
+    }
+
+    public Long activeAssistants() {
+        ArrayList<User> activeAssistants = new ArrayList<>();
+        for(Lab lab : this.getTodaysLabs()) {
+            activeAssistants.addAll(lab.activeAssistants().collect(Collectors.toList()));
+        }
+        return activeAssistants.stream().distinct().count();
+    }
+
+    public Long studentsInTheQueue() {
+        Long studentsInQueue = 0L;
+        for(Lab lab : this.getTodaysLabs()) {
+            studentsInQueue += lab.nrOfStudentsInQueue();
+        }
+        return studentsInQueue;
+    }
+
+    @Cacheable("totalPerHour")
+    public String requestsTotalPerHour() throws JsonProcessingException {
+        ArrayList<Request> requests = new ArrayList<>();
+        for(Lab lab: this.getTodaysLabs()) {
+            requests.addAll(lab.getRequests());
+        }
+        return Request.requestsPerHour(earliestLabOpensAt(), latestClosedLab(), requests);
+    }
+
+    @Cacheable("courseRejectedPerHour")
+    public String requestsRejectedPerHour() throws JsonProcessingException {
+        ArrayList<Request> requestsRejected = new ArrayList<>();
+        for(Lab lab: this.getTodaysLabs()) {
+            requestsRejected.addAll(lab.getRejected());
+        }
+        return Request.requestsPerHour(earliestLabOpensAt(), latestClosedLab(), requestsRejected);
+    }
+
+    @Cacheable("courseApprovedPerHour")
+    public String requestsApprovedPerHour() throws JsonProcessingException {
+        ArrayList<Request> requestsApproved = new ArrayList<>();
+        for(Lab lab: this.getTodaysLabs()) {
+            requestsApproved.addAll(lab.getApproved());
+        }
+        return Request.requestsPerHour(earliestLabOpensAt(), latestClosedLab(), requestsApproved);
+    }
+
+    @Cacheable("courseHandledPerHour")
+    public String requestsHandledPerHour() throws JsonProcessingException {
+        ArrayList<Request> requestsHandled = new ArrayList<>();
+        for(Lab lab: this.getTodaysLabs()) {
+            requestsHandled.addAll(lab.getHandled());
+        }
+        return Request.requestsPerHour(earliestLabOpensAt(), latestClosedLab(), requestsHandled);
+    }
+
+    private LocalDateTime earliestLabOpensAt() {
+        LocalDateTime earliestOpen = LocalDateTime.now();
+        for(Lab lab: this.getTodaysLabs()) {
+            if (earliestOpen == null || earliestOpen.isAfter(lab.getSlot().getOpensAt()))
+                earliestOpen = lab.getSlot().getOpensAt();
+        }
+        return earliestOpen;
+    }
+
+    private LocalDateTime latestClosedLab() {
+        LocalDateTime latestClosed = LocalDateTime.now();
+        for(Lab lab: this.getTodaysLabs()) {
+            if (latestClosed == null || latestClosed.isBefore(lab.getSlot().getClosesAt()))
+                latestClosed = lab.getSlot().getClosesAt();
+        }
+        return latestClosed;
+    }
+
+    @Override
+    public String toString() {
+        return name + " (" + code + ")";
+    }
+
+    public Boolean getIsArchived() {
+        return isArchived;
+    }
+
+    public void setIsArchived(Boolean archived) {
+        isArchived = archived;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/DefaultRole.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/DefaultRole.java
new file mode 100644
index 000000000..7f9a6fc92
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/DefaultRole.java
@@ -0,0 +1,41 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import org.springframework.security.core.GrantedAuthority;
+
+public enum DefaultRole implements GrantedAuthority {
+    ROLE_STUDENT("Student"),
+    ROLE_TEACHER("Teacher"),
+    ROLE_ADMIN("Admin");
+
+    private final String displayName;
+
+    DefaultRole(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+	public String getAuthority() {
+        return name();
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Direction.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Direction.java
new file mode 100644
index 000000000..9281eb5c2
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Direction.java
@@ -0,0 +1,33 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+public enum Direction {
+    TA_VISIT_STUDENT("TA visits student"),
+    STUDENT_VISIT_TA("Student visits TA");
+
+    private final String displayName;
+
+    Direction(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearMentorGroup.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearMentorGroup.java
new file mode 100644
index 000000000..72644e20b
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearMentorGroup.java
@@ -0,0 +1,89 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import org.hibernate.annotations.Where;
+
+import javax.persistence.*;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+//@Where(clause = "active = 1")
+public class FirstYearMentorGroup implements Serializable {
+
+    private static final long serialVersionUID = 914713006211750100L;
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @NotNull
+    private String name;
+
+    @OneToMany(mappedBy = "mentorGroup", cascade = {CascadeType.ALL})
+    @Valid
+    private List<FirstYearStudent> students = new ArrayList<>();
+
+    private boolean active = true;
+
+    public FirstYearMentorGroup() { }
+
+    public FirstYearMentorGroup(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<FirstYearStudent> getStudents() {
+        return students;
+    }
+
+    public void setStudents(List<FirstYearStudent> students) {
+        this.students = students;
+    }
+
+    public String toString() {
+        return this.name;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearStudent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearStudent.java
new file mode 100644
index 000000000..df14e093b
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/FirstYearStudent.java
@@ -0,0 +1,117 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Nullable;
+import javax.persistence.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+@Entity
+public class FirstYearStudent implements Serializable {
+
+    private static final int NETID = 0;
+    private static final int MENTOR_GROUP = 1;
+    private static final long serialVersionUID = 516589986670791335L;
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @ManyToOne
+    @Nullable
+    private User user;
+
+    // TODO this seems to be actually student number at the moment?
+    private String netId;
+
+    @ManyToOne
+    private FirstYearMentorGroup mentorGroup;
+
+    public FirstYearStudent() { }
+
+    public FirstYearStudent(User user, FirstYearMentorGroup mentorGroup) {
+        this.user = user;
+        this.mentorGroup = mentorGroup;
+    }
+
+    public FirstYearStudent(String netId, FirstYearMentorGroup mentorGroup) {
+        this.netId = netId;
+        this.mentorGroup = mentorGroup;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public FirstYearMentorGroup getMentorGroup() {
+        return mentorGroup;
+    }
+
+    public void setMentorGroup(FirstYearMentorGroup mentorGroup) {
+        this.mentorGroup = mentorGroup;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public static ArrayList<List<String>> readCsv(MultipartFile csvFile) throws IOException {
+        InputStream contents = csvFile.getInputStream();
+        Scanner scanner = new Scanner(contents);
+        scanner.useDelimiter("\\A");
+        ArrayList<List<String>> csvResultSet = new ArrayList<>();
+        String csv = "";
+        if (scanner.hasNext()) {
+            csv = scanner.next();
+        }
+        String[] csvLines = csv.split("\n");
+        for(String line : csvLines) {
+            String[] studentGroup = line.split(",");
+            List<String> mentorGroup = lineToFirstYearStudent(studentGroup);
+            csvResultSet.add(mentorGroup);
+        }
+        scanner.close();
+        return csvResultSet;
+    }
+
+    private static List<String> lineToFirstYearStudent(String[] student) {
+        ArrayList<String> values = new ArrayList<>();
+        String netId = student[NETID];
+        String mentorGroup = student[MENTOR_GROUP];
+        values.add(netId);
+        values.add(mentorGroup);
+        return values;
+    }
+
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Group.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Group.java
new file mode 100644
index 000000000..31b5d0f7a
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Group.java
@@ -0,0 +1,82 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name="lab_group")
+public class Group extends RequestEntity {
+
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 5164438526508141678L;
+
+	@ManyToMany
+    private List<User> members = new ArrayList<>();
+
+    @ManyToOne
+    private Course course;
+
+    public Group() { }
+
+    public Group(String displayName, Course course) {
+        super(displayName);
+        this.course = course;
+    }
+
+    public List<User> getMembers() {
+        return members;
+    }
+
+    public void setMembers(List<User> members) {
+        this.members = members;
+    }
+
+    public void addMember(User member) {
+        if (!hasMember(member)) {
+            members.add(member);
+        }
+    }
+
+    public String membersToString() {
+        StringBuilder returnString = new StringBuilder();
+        for(User user : members) {
+            returnString.append(user.getDisplayName()).append(", ");
+        }
+        return returnString.toString();
+    }
+
+    public Boolean hasMember(User member) {
+        return members.stream().anyMatch(u -> u.equals(member));
+    }
+
+    public Course getCourse() {
+        return course;
+    }
+
+    public void setCourse(Course course) {
+        this.course = course;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Lab.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Lab.java
new file mode 100644
index 000000000..a6197e226
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Lab.java
@@ -0,0 +1,774 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import nl.tudelft.ewi.queue.service.LabService;
+import nl.tudelft.ewi.queue.views.View;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.Where;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.persistence.*;
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.time.LocalDateTime.now;
+
+@Entity
+@SQLDelete(sql = "UPDATE lab SET deleted_at = NOW(), slot_id = NULL WHERE id = ?")
+@Where(clause = "deleted_at IS NULL")
+@EnableCaching
+public class Lab implements Serializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 4861827801551639673L;
+
+	private static final Logger logger = LoggerFactory.getLogger(Lab.class);
+	
+	public final static String TIME_FORMAT = "dd/MM/yyyy HH:mm";
+
+    @Id
+    @GeneratedValue
+    @JsonView(View.Summary.class)
+    private Long id;
+
+    private String title;
+    private boolean isFeedbackLab;
+    @ManyToOne
+    private Course course = new Course();
+    @ManyToMany
+    @Where(clause = "deleted_at IS NULL")
+    @Valid
+    private List<Assignment> assignments = new ArrayList<>();
+
+    @OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
+    @Valid
+    @JsonView(View.Summary.class)
+    private LabSlot slot;
+
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private Direction direction;
+
+    //@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
+    @ManyToMany
+    @Valid
+    private List<Room> rooms = new ArrayList<>();
+
+    @OneToMany(mappedBy = "lab", cascade = {CascadeType.ALL})
+    @Valid
+    private List<Request> requests = new ArrayList<>();
+
+    @ManyToMany
+    private List<FirstYearMentorGroup> allowedMentorGroups = new ArrayList<>();
+
+    private boolean allowWithoutMentorGroup = false;
+
+    @ManyToMany
+    @Valid
+    private List<RequestType> allowedRequestTypes = new ArrayList<>();
+
+    private boolean signOffIntervals;
+
+    // only relevant if sign off intervals are used
+    // sets the time it takes to sign off one assignment
+    @Min(value = 1L, message = "Interval time must be positive")
+    private Long intervalTime;
+
+    // only relevant if sign off intervals are used
+    // sets the number of groups that can select a single time slot
+    @Min(value = 1L, message = "Capacity must be positive")
+    private Long capacity;
+
+    @DateTimeFormat(pattern = TIME_FORMAT)
+    private LocalDateTime slotSelectionOpensAt;
+
+    @SuppressWarnings("unused")
+    private LocalDateTime deletedAt;
+
+    private static final String LABELS = "labels";
+    private static final String DATA = "data";
+
+    public Lab() {
+    }
+
+    public Lab(Course course, LabSlot slot, List<Room> rooms,
+               boolean feedbackLab) {
+        this.course = course;
+        this.slot = slot;
+        this.rooms = rooms;
+        this.isFeedbackLab = feedbackLab;
+    }
+
+//    public Lab(Course course, LabSlot slot, List<Room> rooms, boolean isFeedbackLab,
+//               List<Assignment> assignments) {
+//        this.course = course;
+//    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        if (title != null) {
+            return title;
+        }
+        return "";
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Get all requests for this lab
+     *
+     * @return
+     */
+    public List<Request> getRequests() {
+        return requests;
+    }
+
+    /**
+     * Get the queue of requests that are pending or assigned, ascescending by creation.
+     *
+     * @return
+     */
+    public List<Request> getQueue() {
+        return getRequests().stream()
+                .filter(Request::isQueued)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Get the queue of pending requests sorted descending by creation.
+     *
+     * @return
+     */
+    public List<Request> getPending() {
+        return getRequests().stream()
+                .filter(Request::isPending)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Get processing requests sorted descending by creation.
+     *
+     * @return
+     */
+    public List<Request> getProcessing() {
+        return getRequests().stream()
+                .filter(Request::isProcessing)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Get archived requests sorted descending by creation.
+     *
+     * @return
+     */
+    public List<Request> getArchived() {
+        return getRequests().stream()
+                .filter(Request::isArchived)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Get handled requests sorted descending by creation.
+     *
+     * @return
+     */
+    public List<Request> getHandled() {
+        return getRequests().stream()
+                .filter(Request::isHandled)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    public List<Request> getApproved() {
+        return getRequests().stream()
+                .filter(Request::isApproved)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    public List<Request> getRejected() {
+        return getRequests().stream()
+                .filter(Request::isRejected)
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns an Optional with the pending request for the given user if it
+     * exists, or an empty Optional otherwise.
+     *
+     * @param requestEntity
+     *
+     * @return
+     */
+    public Optional<Request> getPendingRequest(RequestEntity requestEntity) {
+        return getQueue().stream()
+                .filter(r -> r.getRequestEntity().equals(requestEntity))
+                .findFirst();
+    }
+
+    /**
+     * Average waiting time in minutes rounded to two decimal places for
+     * requests created in the last hour.
+     *
+     * @return
+     */
+    public Optional<String> averageWaiting() {
+        OptionalDouble optionalDouble = averageWaitingDouble();
+
+        if (!optionalDouble.isPresent()) {
+            return Optional.empty();
+        }
+
+        DecimalFormat df = new DecimalFormat("#.##");
+
+        return Optional.of(df.format(optionalDouble.getAsDouble()));
+    }
+
+    public OptionalDouble averageWaitingDouble() {
+        return getHandled().stream()
+                .filter(r -> r.getCreatedAt().isAfter(now().minusHours(1)))
+                .mapToLong(Request::waitingTime)
+                .average();
+    }
+
+    public OptionalDouble averageProcessingDouble() {
+        return getHandled().stream()
+                .filter(r -> r.getCreatedAt().isAfter(now().minusHours(1)))
+                .mapToLong(Request::processingTime)
+                .average();
+    }
+
+    public Optional<String> averageProcessing() {
+        OptionalDouble optionalDouble = averageProcessingDouble();
+
+        if (!optionalDouble.isPresent()) {
+            return Optional.empty();
+        }
+
+        DecimalFormat df = new DecimalFormat("#.##");
+
+        return Optional.of(df.format(optionalDouble.getAsDouble()));
+    }
+
+    public Stream<User> activeAssistants() {
+        return getArchived().stream()
+                .filter(r -> r.getCreatedAt().isAfter(now().minusHours(1)))
+                .map(Request::getAssistant).filter(Objects::nonNull);
+    }
+
+    public Long activeAssistantsCount() {
+        return activeAssistants().distinct().count();
+    }
+
+    public Long nrOfStudentsInQueue() {
+        return getQueue().stream().map(Request::getRequestEntity).distinct().count();
+    }
+
+    @Cacheable("requestsHandledPerHour")
+    public String requestsHandledPerHour() throws JsonProcessingException {
+        return Request.requestsPerHour(this.getSlot().getOpensAt(), this.getSlot().getClosesAt(), this.getHandled());
+    }
+
+    @Cacheable("requestsTotalPerHour")
+    public String requestsTotalPerHour() throws JsonProcessingException {
+        return Request.requestsPerHour(this.getSlot().getOpensAt(), this.getSlot().getClosesAt(), this.getRequests());
+    }
+
+    @Cacheable("requestsApprovedPerHour")
+    public String requestsApprovedPerHour() throws JsonProcessingException {
+        return Request.requestsPerHour(this.getSlot().getOpensAt(), this.getSlot().getClosesAt(), this.getApproved());
+    }
+
+    @Cacheable("requestsRejectedPerHour")
+    public String requestsRejectedPerHour() throws JsonProcessingException {
+        return Request.requestsPerHour(this.getSlot().getOpensAt(), this.getSlot().getClosesAt(), this.getRejected());
+    }
+
+    private Long nrOfRequestsThisHour(LocalDateTime currentTime, List<Request> filteredRequests) {
+        return filteredRequests.stream()
+                .filter(r -> r.getCreatedAt().isAfter(currentTime) || r.getCreatedAt().isEqual(currentTime))
+                .filter(r -> r.getCreatedAt().isBefore(currentTime.plusHours(1)))
+                .count();
+    }
+
+    /**
+     * Check if the given student is enqueued
+     *
+     * @param entity
+     *
+     * @return
+     */
+    public boolean isEnqueued(RequestEntity entity) {
+        RequestEntity finalEntity = getEntityFor(entity);
+        return getQueue().stream().anyMatch(r -> r.getRequestEntity().equals(finalEntity));
+    }
+
+    /**
+     * Takes a requestEntity (User) and returns the Group entity, if any
+     * else returns the user
+     *
+     * @param requestEntity
+     *
+     * @return
+     */
+    public RequestEntity getEntityFor(RequestEntity requestEntity) {
+        if (getCourse().getHasGroups() && requestEntity instanceof User) {
+            requestEntity = getCourse().getGroup((User) requestEntity);
+        }
+        return requestEntity;
+    }
+
+    /**
+     * Check if the given student is being processed
+     *
+     * @param requestEntity
+     *
+     * @return
+     */
+    public boolean isBeingProcessed(RequestEntity requestEntity) {
+        return getRequests().stream()
+                .filter(r -> r.getRequestEntity().equals(requestEntity))
+                .anyMatch(Request::isProcessing);
+    }
+
+    /**
+     * Get requests for the given student
+     *
+     * @param requestEntity
+     *
+     * @return
+     */
+    public List<Request> requestsBy(RequestEntity requestEntity) {
+        RequestEntity finalEntity = getEntityFor(requestEntity);
+        return getRequests().stream()
+                .filter(r -> r.getRequestEntity().equals(finalEntity))
+                .sorted(Comparator.comparing(Request::getCreatedAt))
+                .collect(Collectors.toList());
+    }
+
+    public Long countRequestsBy(RequestEntity requestEntity) {
+        RequestEntity finalEntity = getEntityFor(requestEntity);
+        return getRequests().stream()
+                .filter(r -> r.getRequestEntity().equals(finalEntity))
+                .count();
+    }
+
+    /**
+     * Get position of given student in queue. Assumes the student is enqueued.
+     *
+     * @param requestEntity
+     *
+     * @return
+     */
+    public String position(RequestEntity requestEntity) {
+        int i = 1;
+
+        for (Request request : getQueue()) {
+            if (requestEntity.equals(request.getRequestEntity())) {
+                if (request.getSlot() != null) {
+                    return formatPositionTimeSlot(request.getSlot());
+                } else {
+                    return Integer.toString(i);
+                }
+            }
+            i++;
+        }
+        return Integer.toString(i);
+    }
+
+    private String formatPositionTimeSlot(RequestSlot slot) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
+        String start = slot.getOpensAt().format(formatter);
+        String end = slot.getClosesAt().format(formatter);
+        return start + " - " + end;
+    }
+
+    public Boolean slotIsAvailable(RequestSlot slot) {
+        LocalDateTime now = now();
+        LocalDateTime twoMinutesGracePeriod = now.minusMinutes(2L);
+        if (slot.getOpensAt().isBefore(twoMinutesGracePeriod) || (slot.getOpensAt().isBefore(now) && slot.getClosesAt().isAfter(now))) {
+            return false;
+        }
+        int numberOfRequestsOnSlot = getNumberOfRequestsOnSlot(slot);
+        return numberOfRequestsOnSlot < capacity;
+    }
+
+    /**
+     * Gets the number of requests for a timeslot.
+     *
+     * @param slot The timeslot whose number of requests is to be determined.
+     * @return The number of requests on the timeslot.
+     */
+    public int getNumberOfRequestsOnSlot(RequestSlot slot) {
+        int numberOfRequestsOnSlot = 0;
+        for (Request request : getQueue()) {
+            if (request.getSlot() != null) {
+                if (slot.getOpensAt().isEqual(request.getSlot().getOpensAt())) {
+                    numberOfRequestsOnSlot += 1;
+                }
+            }
+        }
+        return numberOfRequestsOnSlot;
+    }
+
+    /**
+     * Gets the requests on the specified slot.
+     *
+     * @param slot The slot whose requests to get.
+     * @return A list of requests.
+     */
+    public List<Request> getRequestsOnSlot(RequestSlot slot) {
+        return getQueue()
+                .stream()
+                .filter(request -> request.getSlot() != null)
+                .filter(request -> request.getSlot().getOpensAt().isEqual(slot.getOpensAt()))
+                .collect(Collectors.toList());
+    }
+
+    public Course getCourse() {
+        return course;
+    }
+
+    public void setCourse(Course course) {
+        this.course = course;
+    }
+
+    public List<Assignment> getAssignments() {
+        return assignments;
+    }
+
+    public void setAssignments(List<Assignment> assignments) {
+        this.assignments = assignments;
+    }
+
+    public void addAssignment(Assignment assignment) {
+        assignments.add(assignment);
+
+        if (!assignment.getLabs().contains(this)) {
+            assignment.addLab(this);
+        }
+    }
+
+    public boolean containsAssignment(Assignment assignment) {
+        return assignments.contains(assignment);
+    }
+
+    public boolean containsAllowedRequestType(RequestType requestType) {
+        return allowedRequestTypes.contains(requestType);
+    }
+
+    public boolean containsAllowedMentorGroup(FirstYearMentorGroup mentorGroup) {
+		return allowedMentorGroups.contains(mentorGroup);
+    }
+
+    public LabSlot getSlot() {
+        return slot;
+    }
+
+    public void setSlot(LabSlot slot) {
+        this.slot = slot;
+    }
+
+    public boolean isOpen() {
+        return slot.contains(now());
+    }
+
+    public boolean slotSelectionIsOpen() {
+        return signOffIntervals && slotSelectionOpensAt != null && now().isAfter(slotSelectionOpensAt)
+                && now().isBefore(getSlot().getClosesAt());
+    }
+
+    public boolean isOpenOrSlotSelection() {
+        return isOpen() || slotSelectionIsOpen();
+    }
+
+    public boolean display() {
+        return slot.getClosesAt().plusMinutes(15).isAfter(now()) || isOpenOrSlotSelection();
+    }
+
+    public List<Room> getRooms() {
+        return rooms;
+    }
+
+    public boolean hasRoom(Room room) {
+        return rooms.contains(room);
+    }
+
+    public List<Room> getFilteredRooms() {
+        return rooms.stream().filter(r -> !r.isPlaceholder()).collect(Collectors.toList());
+    }
+
+    public void setRooms(List<Room> rooms) {
+        this.rooms = rooms;
+    }
+
+    public void addRoom(Room room) {
+        rooms.add(room);
+    }
+
+    public boolean getSignOffIntervals() {
+        return signOffIntervals;
+    }
+
+    public void setSignOffIntervals(Boolean signOffIntervals) {
+        this.signOffIntervals = signOffIntervals;
+    }
+
+    public Long getIntervalTime() {
+        return intervalTime;
+    }
+
+    public void setIntervalTime(Long intervalTime) {
+        this.intervalTime = intervalTime;
+    }
+
+    public LocalDateTime getSlotSelectionOpensAt() {
+        return slotSelectionOpensAt;
+    }
+
+    public void setSlotSelectionOpensAt(LocalDateTime slotSelectionOpensAt) {
+        this.slotSelectionOpensAt = slotSelectionOpensAt;
+    }
+
+    public Direction getDirection() {
+        return direction;
+    }
+
+    public void setDirection(Direction direction) {
+        this.direction = direction;
+    }
+
+    public void addRequest(Request request) {
+        requests.add(request);
+
+        if (request.getLab() != this) {
+            request.setLab(this);
+        }
+    }
+
+    public List<RequestType> getAllowedRequestTypes() {
+        return allowedRequestTypes;
+    }
+
+    public void setAllowedRequestTypes(List<RequestType> allowedRequestTypes) {
+        this.allowedRequestTypes = allowedRequestTypes;
+    }
+
+    public List<FirstYearMentorGroup> getAllowedMentorGroups() {
+        return allowedMentorGroups;
+    }
+
+    public void setAllowedMentorGroups(List<FirstYearMentorGroup> allowedMentorGroups) {
+        this.allowedMentorGroups = allowedMentorGroups;
+    }
+
+    public boolean isAllowWithoutMentorGroup() {
+        return allowWithoutMentorGroup;
+    }
+
+    public void setAllowWithoutMentorGroup(boolean allowWithoutMentorGroup) {
+        this.allowWithoutMentorGroup = allowWithoutMentorGroup;
+    }
+
+    public Long getCapacity() {
+        return capacity;
+    }
+
+    public void setCapacity(Long capacity) {
+        this.capacity = capacity;
+    }
+
+    public boolean allAllowed() {
+        return !allowWithoutMentorGroup && allowedMentorGroups.size() == 0;
+    }
+
+    /**
+     * Checks whether the user is allowed to enqueue in this lab.
+     *
+     * @param user The user.
+     * @return True if the user is allowed to enqueue in the lab.
+     */
+    public boolean userAllowedForThisLab(User user) {
+        List<FirstYearStudent> students = user.getFirstYearStudents();
+        if (students == null) {
+            return allowWithoutMentorGroup;
+        }
+        List<FirstYearStudent> activeStudents =
+                students.stream().filter(firstYearStudent
+                        -> firstYearStudent.getMentorGroup().isActive()).collect(Collectors.toList());
+        if (activeStudents == null || activeStudents.isEmpty()) {
+        	logger.info("user has no first year mentor groups");
+            return allowWithoutMentorGroup;
+        } else {
+        	for (FirstYearStudent firstYearStudent : students) {
+        		if (containsAllowedMentorGroup(firstYearStudent.getMentorGroup())) {
+        			logger.info("Found an allowed mentorgroup");
+        			return true;
+        		}
+			}
+            return false;
+        }
+    }
+
+    public String toReadableString() {
+        LocalDateTime time = this.slot.getOpensAt();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm - " +
+                "dd MMM uu");
+
+        return "lab " + this.title + ": " + formatter.format(time);
+    }
+
+    /**
+     * Checks whether this group is allowed to enqueue in the lab.
+     * NB: at the moment this requires _all_ members to be allowed
+     * @param group The group that wants to enqueue.
+     * @return True if the group is allowed to enqueue.
+     */
+    public boolean groupAllowedForThisLab(Group group) {
+        boolean allowed = true;
+        for (User user : group.getMembers()) {
+            allowed &= userAllowedForThisLab(user);
+        }
+        return allowed;
+    }
+
+    /**
+     * Checks whether the requestEntity is allowed to enqueue for this lab.
+     *
+     * @param entity The entity.
+     * @return True if the entity is allowed to enqueue for this lab.
+     */
+    public boolean entityAllowedForThisLab(RequestEntity entity) {
+        if (entity instanceof Group) {
+        	logger.info("Checking group access for: " + entity.getDisplayName());
+            return groupAllowedForThisLab((Group) entity);
+        } else if (entity instanceof User) {
+        	logger.info("Checking user access for: " + entity.getDisplayName());
+            return userAllowedForThisLab((User) entity);
+        }
+        logger.info("Unknown entity: " + entity.getDisplayName());
+        return false;
+    }
+
+    /**
+     * @param requests Active requests
+     * @param user     Active user currently working in the queue
+     * @return Number of requests for this lab with status Pending
+     * plus nr of requests assigned to current user
+     */
+    public Long getQueuedCount(List<Request> requests, User user) {
+        List<Request> queuedForThisLab = requests.stream()
+                .filter(request -> request.getLab().getId().equals(this.getId()))
+                .filter(request -> request.getStatus().equals(Request.Status.PENDING))
+                .collect(Collectors.toList());
+        if (getSignOffIntervals()) {
+            LocalDateTime tenMinutes = getAcceptInterval();
+            queuedForThisLab = queuedForThisLab.stream().filter(req -> req.getSlot() != null &&
+                    req.getSlot().getOpensAt().isBefore(tenMinutes))
+                    .collect(Collectors.toList());
+        }
+
+        return queuedForThisLab.size() + requests.stream()
+                .filter(Request::isAssigned)
+                .filter(request -> request.getAssistant().getId().equals(user.getId()))
+                .filter(request -> request.getLab().getId().equals(this.getId()))
+                .count();
+    }
+
+    /**
+     * This method gets the time from which a TA is able to accept a new
+     * request.
+     * If the student needs to go to the TA than he can accept up to 1 time
+     * slot earlier else only from 1 minute before the time slot.
+     * @return A time from when the TA can accept the request.
+     */
+    public LocalDateTime getAcceptInterval() {
+        if (this.getDirection().equals(Direction.STUDENT_VISIT_TA)) {
+            return LocalDateTime.now().plusMinutes(this.getSlot().durationOfSlot());
+        } else {
+            return LocalDateTime.now().plusMinutes(1L);
+        }
+    }
+
+    @Override
+    public String toString() {
+        String returnString = "Lab " + getId();
+        if (title != null) {
+            returnString += ": " + title;
+        }
+        return returnString;
+    }
+
+    public String toSlug() {
+        return "lab" + getId();
+    }
+
+    /**
+     * Checks if this lab is a feedback lab.
+     *
+     * @return True if it is a feedback lab else false.
+     */
+    public boolean isFeedbackLab() {
+        return isFeedbackLab;
+    }
+
+    /**
+     * Sets whether the lab is a feedback lab.
+     *
+     * @param isFeedbackLab True if the lab is a feedback lab.
+     */
+    public void setIsFeedbackLab(boolean isFeedbackLab) {
+        this.isFeedbackLab = isFeedbackLab;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/LabSlot.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/LabSlot.java
new file mode 100644
index 000000000..b7cf163f7
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/LabSlot.java
@@ -0,0 +1,61 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Entity
+public class LabSlot extends Slot implements Serializable {
+
+    private static final long serialVersionUID = -6092888322533771698L;
+
+    @OneToOne(mappedBy = "slot")
+    private Lab lab;
+
+    public LabSlot() {
+    }
+
+    public LabSlot(LocalDateTime opensAt, LocalDateTime closesAt) {
+        super(opensAt, closesAt);
+    }
+
+    public Lab getLab() {
+        return lab;
+    }
+
+    public void setLab(Lab lab) {
+        this.lab = lab;
+
+        if (!lab.getSlot().equals(this)) {
+            lab.setSlot(this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Slot{" +
+                "id=" + this.getId() +
+                ", lab=" + lab +
+                ", opensAt=" + this.getOpensAt() +
+                ", closesAt=" + this.getClosesAt() +
+                '}';
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Manager.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Manager.java
new file mode 100644
index 000000000..91fd5fc8f
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Manager.java
@@ -0,0 +1,41 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+
+@Entity
+public class Manager extends Role {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 292961232275907536L;
+
+	public Manager() {
+        super();
+    }
+
+    public Manager(User user, Course course) {
+        super(user, course);
+    }
+
+    @Override
+    public String toString() {
+        return "Manager";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Notification.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Notification.java
new file mode 100644
index 000000000..73bee329f
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Notification.java
@@ -0,0 +1,160 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.json.JSONObject;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Entity
+public class Notification {
+    public final static String TIME_FORMAT = "dd/MM/yyyy HH:mm";
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @ManyToOne
+    @JsonIgnore
+    private User user;
+
+    @ManyToOne
+    @JsonIgnore
+    private Course course;
+
+    private String title;
+
+    private String message;
+
+    private int ttl;
+
+    private boolean read;
+
+    @DateTimeFormat(pattern = TIME_FORMAT)
+    private LocalDateTime createdAt;
+    
+    public Notification() {
+    }
+
+    public Notification(User user, Course course, String title, String message, int ttl) {
+        this.user = user;
+        this.course = course;
+        this.title = title;
+        this.message = message;
+        this.ttl = ttl;
+        this.createdAt = LocalDateTime.now();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Course getCourse() {
+        return course;
+    }
+
+    public void setCourse(Course course) {
+        this.course = course;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public int getTtl() {
+        return ttl;
+    }
+
+    public void setTtl(int ttl) {
+        this.ttl = ttl;
+    }
+
+    public boolean isRead() {
+        return read;
+    }
+
+    public boolean isUnread() {
+        return !isRead();
+    }
+
+    public void setRead(boolean read) {
+        this.read = read;
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public String toJSON() {
+        return new JSONObject()
+            .put("title", title)
+            .put("message", message)
+            .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Notification that = (Notification) o;
+
+        return Objects.equals(id, that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/NotificationList.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/NotificationList.java
new file mode 100644
index 000000000..0638cae1b
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/NotificationList.java
@@ -0,0 +1,56 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class NotificationList extends LinkedList<Notification> {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -1843421296570406343L;
+
+	public NotificationList(List<Notification> list) {
+        super(list);
+    }
+
+    public Set<Course> getCourses() {
+        Map<Course, Boolean> seen = new HashMap<>();
+
+        for (Notification notification : this) {
+            seen.putIfAbsent(notification.getCourse(), Boolean.TRUE);
+        }
+
+        return seen.keySet();
+    }
+
+    public NotificationList forCourse(Course course) {
+        List<Notification> notifications = stream()
+            .filter(n -> n.getCourse().equals(course))
+            .collect(Collectors.toList());
+
+        return new NotificationList(notifications);
+    }
+
+    public Long unread() {
+        return stream()
+            .filter(Notification::isUnread)
+            .count();
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Request.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Request.java
new file mode 100644
index 000000000..4d683ca56
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Request.java
@@ -0,0 +1,684 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonView;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import nl.tudelft.ewi.queue.cqsr.Event;
+import nl.tudelft.ewi.queue.cqsr.RecordsEvents;
+import nl.tudelft.ewi.queue.views.View;
+import org.apache.commons.httpclient.util.DateUtil;
+import org.apache.commons.lang.time.DateUtils;
+import org.jooq.lambda.Seq;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.PostLoad;
+import javax.persistence.Transient;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Entity
+@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+public class Request implements RecordsEvents<Request>, Serializable, Comparable<Request> {
+
+    private static final long serialVersionUID = -6265456635668203715L;
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @ManyToOne
+    @JoinColumn(name = "request_entity_id")
+    @JsonView(View.Summary.class)
+    private RequestEntity requestEntity;
+
+    @NotNull
+    @ManyToOne
+    @JsonView(View.Summary.class)
+    private Room room;
+
+    @JsonView(View.Summary.class)
+    @OneToOne
+    private RequestType requestType;
+
+    @JsonView(View.Summary.class)
+    @Size(max=250)
+    private String comment;
+
+    @JsonView(View.Summary.class)
+    @Size(max=250)
+    private String feedback;
+
+    @ManyToOne
+    @JoinColumn(name = "assistant_id")
+    @JsonView(View.Summary.class)
+    private User assistant;
+
+    @ManyToOne
+    @JsonView(View.Summary.class)
+    private Lab lab;
+
+    @NotNull
+    @ManyToOne
+    @JsonView(View.Summary.class)
+    private Assignment assignment;
+
+    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "request")
+    @JsonView(View.Summary.class)
+    private List<RequestEvent> events = new ArrayList<>();
+
+    @OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
+    @JsonView(View.Summary.class)
+    private RequestSlot slot;
+
+    private Status status;
+
+    @Transient
+    private String reason;
+
+    @Transient
+    private String reasonForStudent;
+
+    @Transient
+    private LocalDateTime createdAt;
+
+    @Transient
+    private LocalDateTime revokedAt;
+
+    @Transient
+    private LocalDateTime assignedAt;
+
+    @Transient
+    private LocalDateTime processingAt;
+
+    @Transient
+    private LocalDateTime approvedAt;
+
+    @Transient
+    private LocalDateTime rejectedAt;
+
+    @Transient
+    private LocalDateTime forwardedAt;
+
+    @Transient
+    private LocalDateTime notFoundAt;
+
+    @Transient
+    private User forwardedTo;
+
+    @Transient
+    private Boolean leftFeedback;
+
+    private static final String LABELS = "labels";
+    private static final String DATA = "data";
+
+
+    public Request() {
+    }
+
+    public Request(RequestEntity requestEntity, Assignment assignment, Room room, RequestType type, String comment, Lab lab) {
+        this.requestEntity = requestEntity;
+        this.assignment = assignment;
+        this.room = room;
+        this.requestType = type;
+        this.comment = comment;
+        this.lab = lab;
+
+        this.requestCreatedEvent();
+    }
+
+    public Request(RequestEntity requestEntity, Assignment assignment, Room room,
+                   RequestType type, String comment, Lab lab, RequestSlot slot) {
+        this.requestEntity = requestEntity;
+        this.assignment = assignment;
+        this.room = room;
+        this.requestType = type;
+        this.comment = comment;
+        this.lab = lab;
+        this.slot = slot;
+
+        this.requestCreatedEvent();
+    }
+
+    public void requestCreatedEvent() {
+        this.addEvent(new RequestCreatedEvent(this, LocalDateTime.now()));
+        this.apply();
+    }
+
+    public static int getHour(Request request) {
+        return request.getCreatedAt().getHour();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public RequestEntity getRequestEntity() {
+        return requestEntity;
+    }
+
+    public void setRequestEntity(RequestEntity requestEntity) {
+        this.requestEntity = requestEntity;
+    }
+
+    public RequestType getRequestType() {
+        return requestType;
+    }
+
+    public void setRequestType(RequestType type) {
+        this.requestType = type;
+    }
+
+    public Room getRoom() {
+        return room;
+    }
+
+    public void setRoom(Room room) {
+        this.room = room;
+    }
+
+    public Lab getLab() {
+        return lab;
+    }
+
+    public void setLab(Lab lab) {
+        this.lab = lab;
+
+        if (!lab.getRequests().contains(this)) {
+            lab.getRequests().add(this);
+        }
+    }
+
+    public Assignment getAssignment() {
+        return assignment;
+    }
+
+    public void setAssignment(Assignment assignment) {
+        this.assignment = assignment;
+    }
+
+    public Status getStatus() {
+        return status;
+    }
+
+    public Request setStatus(Status status) {
+        this.status = status;
+
+        return this;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public Request setReason(String reason) {
+        this.reason = reason;
+
+        return this;
+    }
+
+    public String getReasonForStudent() {
+        return reasonForStudent;
+    }
+
+    public Request setReasonForStudent(String reasonForStudent) {
+        this.reasonForStudent = reasonForStudent;
+
+        return this;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String comment) {
+        this.comment = comment;
+
+    }
+
+    public String getFeedback() {
+        return feedback;
+    }
+
+    public void setFeedback(String feedback) {
+        this.feedback = feedback;
+    }
+
+    public Boolean getLeftFeedback() {
+        return !(feedback == null || feedback.isEmpty());
+    }
+
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public Request setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+
+        return this;
+    }
+
+    public LocalDateTime getRevokedAt() {
+        return revokedAt;
+    }
+
+    public Request setRevokedAt(LocalDateTime revokedAt) {
+        this.revokedAt = revokedAt;
+
+        return this;
+    }
+
+    public LocalDateTime getAssignedAt() {
+        return assignedAt;
+    }
+
+    public Request setAssignedAt(LocalDateTime assignedAt) {
+        this.assignedAt = assignedAt;
+
+        return this;
+    }
+
+    public LocalDateTime getProcessingAt() {
+        return processingAt;
+    }
+
+    public Request setProcessingAt(LocalDateTime processingAt) {
+        this.processingAt = processingAt;
+
+        return this;
+    }
+
+    public LocalDateTime getApprovedAt() {
+        return approvedAt;
+    }
+
+    public Request setApprovedAt(LocalDateTime approvedAt) {
+        this.approvedAt = approvedAt;
+
+        return this;
+    }
+
+    public LocalDateTime getRejectedAt() {
+        return rejectedAt;
+    }
+
+    public LocalDateTime getNotFoundAt() {
+        return notFoundAt;
+    }
+
+    public Request setRejectedAt(LocalDateTime rejectedAt) {
+        this.rejectedAt = rejectedAt;
+
+        return this;
+    }
+
+    public LocalDateTime getForwardedAt() {
+        return forwardedAt;
+    }
+
+    public Request setForwardedAt(LocalDateTime forwardedAt) {
+        this.forwardedAt = forwardedAt;
+
+        return this;
+    }
+
+    public Request setNotFoundAt(LocalDateTime notFoundAt) {
+        this.notFoundAt = notFoundAt;
+        return this;
+    }
+
+    public User getForwardedTo() {
+        return forwardedTo;
+    }
+
+    public Request setForwardedTo(User forwardedTo) {
+        this.forwardedTo = forwardedTo;
+
+        return this;
+    }
+
+    public RequestSlot getSlot() {
+        return slot;
+    }
+
+    public void setSlot(RequestSlot slot) {
+        this.slot = slot;
+    }
+
+    public String getSlotSentence() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
+        String start = slot.getOpensAt().format(formatter);
+        String end = slot.getClosesAt().format(formatter);
+        String ret = start + " - " + end;
+        if (!DateUtils.isSameDay(Timestamp.valueOf(slot.getClosesAt()), new Date())) {
+            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d MMM uuuu");
+            ret += " on " + slot.getClosesAt().format(dateFormatter);
+        }
+        return ret;
+    }
+
+    /**
+     * Gets the time at which the request was handled, i.e. approved, rejected or forwarded.
+     *
+     * @return The time as a LoalDateTime
+     */
+    public LocalDateTime getHandledAt() {
+        switch (getStatus()) {
+            case APPROVED:
+                return getApprovedAt();
+            case REJECTED:
+                return getRejectedAt();
+            case FORWARDED:
+                return getForwardedAt();
+            case NOTFOUND:
+                return getNotFoundAt();
+			default:
+				return null;
+        }
+    }
+
+    /**
+     * Gets the waiting time in minutes for this request, defined
+     * as moment of creation until moment of processing
+     *
+     * @return The waiting time as a Long
+     */
+    public long waitingTime() {
+        if (isPending()) {
+            throw new IllegalStateException("No waiting time for a pending request");
+        }
+
+        LocalDateTime processTime = getProcessingAt();
+
+        //If there is no processing time and is not pending than the request is already handled
+        //so we take the handled time for calculation.
+        if (processTime == null) {
+            processTime = getHandledAt();
+            if (processTime == null) {  // really?? There's no sensible answer anymore, return 42 seconds later
+                processTime = getCreatedAt().plusSeconds(42);
+			}
+        }
+
+        return ChronoUnit.MINUTES.between(getCreatedAt(), processTime);
+    }
+
+    /**
+     * Gets the processing time in minutes for this request
+     * defined as time processing starts until request is handled
+     *
+     * @return The processing time as a long.
+     */
+    public long processingTime() {
+        if (!isArchived()) {
+            throw new IllegalStateException("No processing time for a pending request");
+        }
+
+        LocalDateTime processTime = getProcessingAt();
+
+        // If there is no process time we dont know how long this request was processing for
+        // so we take processing time from the moment the request gets created to the moment it is handeled
+        if (processTime == null) {
+            processTime = getCreatedAt();
+        }
+
+        return ChronoUnit.MINUTES.between(processTime, getHandledAt());
+    }
+
+    /**
+     * Gets the assigned assistant for the request.
+     *
+     * @return null if no assistant has been assigned yet.
+     */
+    public User getAssistant() {
+        return assistant;
+    }
+
+    public Request setAssistant(User assistant) {
+        this.assistant = assistant;
+        return this;
+    }
+
+    /**
+     * Gets previous requests by the same student for the same assignment
+     *
+     * @return
+     */
+    public List<Request> getPrevious() {
+        return requestEntity.getRequests().stream()
+                .filter(r -> r.getAssignment().equals(assignment))
+                .filter(r -> r.getCreatedAt().isBefore(getCreatedAt()))
+                .filter(r -> r.getStatus() != Status.PENDING)
+                .filter(r -> !r.equals(this))
+                .collect(Collectors.toList());
+
+    }
+
+    @Override
+    public List<RequestEvent> getEvents() {
+        return events;
+    }
+
+    @Override
+    public void addEvent(Event event) {
+        events.add((RequestEvent) event);
+    }
+
+    @Override
+    public Request apply() {
+        return Seq.seq(getEvents()).foldLeft(this,
+            (Request aggregate, Event<Request> event) -> event.apply(aggregate)
+        );
+    }
+
+    @PostLoad
+    public void postLoad() {
+        this.apply();
+    }
+
+    public boolean isPending() {
+        return status == Status.PENDING;
+    }
+    
+    public boolean isAssigned() {
+    	return status == Status.ASSIGNED;
+    }
+
+    public boolean isRevoked() {
+        return status == Status.REVOKED;
+    }
+
+    public boolean isProcessing() {
+        return status == Status.PROCESSING;
+    }
+
+    public boolean isApproved() {
+        return status == Status.APPROVED;
+    }
+
+    public boolean isRejected() {
+        return status == Status.REJECTED;
+    }
+
+    public boolean isForwarded() {
+        return status == Status.FORWARDED;
+    }
+
+    public boolean isArchived() {
+        return status == Status.APPROVED || status == Status.REJECTED || status == Status.FORWARDED || status == Status.REVOKED || status == Status.CANCELLED || status == Status.NOTFOUND;
+    }
+
+    public boolean isHandled() {
+        return status == Status.APPROVED || status == Status.REJECTED || status == Status.FORWARDED || status == Status.NOTFOUND;
+    }
+
+    public boolean isQueued() {
+        return isAssigned() || isPending();
+    }
+
+    @Override
+    public String toString() {
+        return "Request{" +
+            "id=" + id +
+            ", status=" + status +
+            ", entity='" + requestEntity + '\'' +
+            ", assistant=" + assistant +
+            ", lab=" + lab +
+            '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Request request = (Request) o;
+
+        return Objects.equals(id, request.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+
+    @Override
+    public int compareTo(Request request) {
+        if (this.slot != null && request.slot != null) {
+            return this.slot.compareTo(request.slot);
+        } else {
+            if (this.createdAt.isBefore(request.getCreatedAt())) {
+                return -1;
+            } else if (this.createdAt.isAfter(request.getCreatedAt())) {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    public enum Status {
+        PENDING,
+        ASSIGNED,
+        PROCESSING,
+        APPROVED,
+        REJECTED,
+        FORWARDED,
+        REVOKED,
+        CANCELLED,
+        NOTFOUND {
+            @Override
+            public String toString() {
+                return "NOT FOUND";
+            }
+        };
+    }
+
+    public String toSentence() {
+        return requestType.toSentence(this, requestEntity, assignment);
+    }
+
+    public HashMap<String, String> toHashMap() {
+        HashMap<String, String> requests = new HashMap<>();
+        requests.put("id", this.getId().toString());
+        requests.put("status", this.getStatus().toString());
+        requests.put("sentence", this.toSentence());
+        requests.put("labId", this.getLab().getId().toString());
+        requests.put("lab",this.getLab().getTitle());
+        requests.put("assignment", this.getAssignment().getName());
+        requests.put("room", this.getRoom().getName());
+        requests.put("course", this.getLab().getCourse().toString());
+        requests.put("room", this.getRoom().toString());
+        if (this.getAssistant() != null)
+            requests.put("assigned", this.getAssistant().toString());
+        if (this.getHandledAt() != null)
+            requests.put("handled", this.getHandledAt().toString());
+        requests.put("display", displayRequest().toString());
+        requests.put("type", this.getRequestType().getName());
+        return requests;
+    }
+
+    private Boolean displayRequest() {
+        if (!this.getLab().getSignOffIntervals()) {
+            return true;
+        }
+        LocalDateTime interval = getLab().getAcceptInterval();
+		if (slot == null) {
+			System.err.println("Slot is null");
+			return false;	// TODO I guess there is no point in trying to display this?
+		}
+		if (slot.getOpensAt() == null) {
+			System.err.println("Slot opensAt is null");
+			return false;	// TODO 
+		}
+        return slot.getOpensAt().isBefore(interval);
+    }
+
+    public static String requestsPerHour(LocalDateTime start, LocalDateTime end, List<Request> requestList) throws JsonProcessingException {
+        Map<String, ArrayList> graphData = requestsPerHourFor(start, end, requestList);
+
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayList<Object> keyValues = new ArrayList<>();
+        keyValues.add(graphData.get(LABELS));
+        keyValues.add(graphData.get(DATA));
+        return mapper.writeValueAsString(keyValues);
+    }
+
+    private static Map<String, ArrayList> requestsPerHourFor(LocalDateTime opensAt, LocalDateTime closesAt,
+                                                      List<Request> filteredRequests) {
+        ArrayList<Long> handledPerHour = new ArrayList<>();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH");
+        ArrayList<String> hourLabels = new ArrayList<>();
+        for(LocalDateTime date = opensAt; date.isBefore(closesAt); date = date.plusHours(1)) {
+            Long nrOfRequestsThisHour = nrOfRequestsThisHour(date, filteredRequests);
+            handledPerHour.add(nrOfRequestsThisHour);
+            hourLabels.add(date.format(formatter));
+        }
+        HashMap<String, ArrayList> hashMap = new HashMap<>();
+        hashMap.put("labels", hourLabels);
+        hashMap.put("data", handledPerHour);
+        return hashMap;
+    }
+
+    private static Long nrOfRequestsThisHour(LocalDateTime currentTime, List<Request> filteredRequests) {
+        return filteredRequests.stream()
+                .filter(r -> r.getCreatedAt().isAfter(currentTime) || r.getCreatedAt().isEqual(currentTime))
+                .filter(r -> r.getCreatedAt().isBefore(currentTime.plusHours(1)))
+                .count();
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestApprovedEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestApprovedEvent.java
new file mode 100644
index 000000000..c197e5a62
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestApprovedEvent.java
@@ -0,0 +1,76 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestApprovedEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 992798464939565962L;
+
+	private LocalDateTime approvedAt;
+
+    @Size(max=250)
+    private String reason;
+
+    @ManyToOne
+    private User assistant;
+
+    public RequestApprovedEvent() {
+        super();
+    }
+
+    public RequestApprovedEvent(Request request, User assistant, LocalDateTime approvedAt, String reason) {
+        super(request);
+
+        this.approvedAt = approvedAt;
+        this.assistant = assistant;
+        this.reason = reason;
+        this.request.setStatus(Request.Status.APPROVED);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.APPROVED)
+            .setAssistant(assistant)
+            .setApprovedAt(approvedAt)
+            .setReason(reason);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return approvedAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request approved";
+    }
+
+    @Override
+    public String toString() {
+        return "request-approved";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestAssignedEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestAssignedEvent.java
new file mode 100644
index 000000000..702884648
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestAssignedEvent.java
@@ -0,0 +1,70 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestAssignedEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 7768507952136925203L;
+
+	protected LocalDateTime assignedAt;
+
+    @ManyToOne
+    protected User assignedTo;
+
+    public RequestAssignedEvent() {
+        super();
+    }
+
+    public RequestAssignedEvent(Request request, LocalDateTime assignedAt, User assignedTo) {
+        super(request);
+
+        this.assignedAt = assignedAt;
+        this.assignedTo = assignedTo;
+        this.request.setStatus(Request.Status.ASSIGNED);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.ASSIGNED)
+            .setAssistant(assignedTo)
+            .setAssignedAt(assignedAt);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return assignedAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request assigned to " + assignedTo;
+    }
+
+    @Override
+    public String toString() {
+        return "request-assigned";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestCreatedEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestCreatedEvent.java
new file mode 100644
index 000000000..8aed0e2a1
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestCreatedEvent.java
@@ -0,0 +1,63 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestCreatedEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -6845911533535937239L;
+	protected LocalDateTime createdAt;
+
+    public RequestCreatedEvent() {
+        super();
+    }
+
+    public RequestCreatedEvent(Request request, LocalDateTime createdAt) {
+        super(request);
+
+        this.createdAt = createdAt;
+        this.request.setStatus(Request.Status.PENDING);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.PENDING)
+            .setCreatedAt(createdAt);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return createdAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request created";
+    }
+
+    @Override
+    public String toString() {
+        return "request-created";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEntity.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEntity.java
new file mode 100644
index 000000000..11079f223
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEntity.java
@@ -0,0 +1,91 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Entity
+@Inheritance(strategy= InheritanceType.JOINED)
+public abstract class RequestEntity  implements Serializable {
+
+    private static final long serialVersionUID = 4349260205347532487L;
+
+    @Id
+    @GeneratedValue
+    Long id;
+
+    private String displayName;
+
+    @OneToMany(mappedBy = "requestEntity")
+    private List<Request> requestsList;
+
+    public RequestEntity() { }
+
+    public RequestEntity(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public List<Request> getRequests() {
+        return requestsList.stream().sorted((r1, r2) -> -r1.getCreatedAt().compareTo(r2.getCreatedAt()))
+                .collect(Collectors.toList());
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public boolean participates(Course course) {
+        if (this instanceof User) {
+            User student = (User)this;
+            return student.participates(course);
+        } else {
+            Group group = (Group)this;
+            boolean allParticipate = true;
+            for(User student : group.getMembers()) {
+                if (!student.participates(course)) {
+                    allParticipate = false;
+                }
+            }
+            return allParticipate;
+        }
+    }
+
+    public boolean isGroup() {
+        return this instanceof Group;
+    }
+
+    public boolean isUser() {
+        return this instanceof User;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEvent.java
new file mode 100644
index 000000000..ebde7a4a2
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestEvent.java
@@ -0,0 +1,71 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonView;
+import nl.tudelft.ewi.queue.cqsr.Event;
+import nl.tudelft.ewi.queue.views.View;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@JsonIgnoreProperties({"aggregate", "request"})
+public abstract class RequestEvent implements Event<Request>, Serializable {
+
+    private static final long serialVersionUID = 6344330279392287773L;
+
+    @Id
+    @GeneratedValue
+    @JsonView(View.Summary.class)
+    protected Long id;
+
+    @ManyToOne
+    protected Request request;
+
+    public RequestEvent() {
+    }
+
+    public RequestEvent(Request request) {
+        this.request = request;
+    }
+
+    @Override
+	public Request getAggregate() {
+        return request;
+    }
+
+    /**
+     * Get textual description for this event
+     *
+     * @return
+     */
+    @JsonView(View.Summary.class)
+    public abstract String getDescription();
+
+    /**
+     * Get the timestamp for this event
+     *
+     * @return
+     */
+    @JsonView(View.Summary.class)
+    public abstract LocalDateTime getTimestamp();
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardToAnyEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardToAnyEvent.java
new file mode 100644
index 000000000..72773b129
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardToAnyEvent.java
@@ -0,0 +1,70 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestForwardToAnyEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 3321677681535290268L;
+
+	private LocalDateTime forwardedAt;
+
+    @Size(max=250)
+    private String reason;
+
+    public RequestForwardToAnyEvent() {
+        super();
+    }
+
+    public RequestForwardToAnyEvent(Request request, LocalDateTime forwardedAt, String reason) {
+        super(request);
+
+        this.reason = reason;
+        this.forwardedAt = forwardedAt;
+        this.request.setStatus(Request.Status.PENDING);
+    }
+
+    @Override
+    public Request apply(Request request1) {
+        return request1
+                .setStatus(Request.Status.PENDING)
+                .setForwardedAt(forwardedAt)
+                .setReason(reason);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return forwardedAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request forwarded to anyone else";
+    }
+
+    @Override
+    public String toString() {
+        return "request-forwarded-to-any";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardedEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardedEvent.java
new file mode 100644
index 000000000..7289f7025
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestForwardedEvent.java
@@ -0,0 +1,76 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestForwardedEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -348089219618245506L;
+
+	private LocalDateTime forwardedAt;
+
+    @ManyToOne
+    private User forwardedTo;
+
+    @Size(max=250)
+    private String reason;
+
+    public RequestForwardedEvent() {
+        super();
+    }
+
+    public RequestForwardedEvent(Request request, LocalDateTime forwardedAt, User forwardedTo, String reason) {
+        super(request);
+
+        this.forwardedAt = forwardedAt;
+        this.forwardedTo = forwardedTo;
+        this.reason = reason;
+        this.request.setStatus(Request.Status.FORWARDED);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.FORWARDED)
+            .setForwardedAt(forwardedAt)
+            .setForwardedTo(forwardedTo)
+            .setReason(reason);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return forwardedAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request forwarded to " + forwardedTo;
+    }
+
+    @Override
+    public String toString() {
+        return "request-forwarded";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestProcessingEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestProcessingEvent.java
new file mode 100644
index 000000000..73c542c7c
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestProcessingEvent.java
@@ -0,0 +1,62 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestProcessingEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -5018900505348041016L;
+	protected LocalDateTime processingAt;
+
+    public RequestProcessingEvent() {
+        super();
+    }
+
+    public RequestProcessingEvent(Request request, LocalDateTime processingAt) {
+        super(request);
+        this.processingAt = processingAt;
+        this.request.setStatus(Request.Status.PROCESSING);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.PROCESSING)
+            .setProcessingAt(processingAt);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return processingAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request processing";
+    }
+
+    @Override
+    public String toString() {
+        return "request-processing";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRejectedEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRejectedEvent.java
new file mode 100644
index 000000000..6c68d0d27
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRejectedEvent.java
@@ -0,0 +1,91 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestRejectedEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -6560217433195047238L;
+
+	private LocalDateTime rejectedAt;
+
+    @Size(max=250)
+    private String reason;
+
+    @Size(max=250)
+    private String reasonForStudent;
+
+    @ManyToOne
+    private User assistant;
+
+    public RequestRejectedEvent() {
+        super();
+    }
+
+    public RequestRejectedEvent(Request request, User assistant, LocalDateTime rejectedAt,
+                                String reason, String reasonForStudent) {
+        super(request);
+
+        this.rejectedAt = rejectedAt;
+        this.assistant = assistant;
+        this.reason = reason;
+        this.reasonForStudent = reasonForStudent;
+        this.request.setStatus(Request.Status.REJECTED);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.REJECTED)
+            .setRejectedAt(rejectedAt)
+            .setAssistant(assistant)
+            .setReason(reason)
+            .setReasonForStudent(reasonForStudent);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return rejectedAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request rejected";
+    }
+
+    @Override
+    public String toString() {
+        return "request-rejected";
+    }
+
+
+    public String getReasonForStudent() {
+        return reasonForStudent;
+    }
+
+    public void setReasonForStudent(String reasonForStudent) {
+        this.reasonForStudent = reasonForStudent;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRevokedEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRevokedEvent.java
new file mode 100644
index 000000000..6cd977a1c
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestRevokedEvent.java
@@ -0,0 +1,63 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestRevokedEvent extends RequestEvent {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -5160734036008128989L;
+	private LocalDateTime revokedAt;
+
+    public RequestRevokedEvent() {
+        super();
+    }
+
+    public RequestRevokedEvent(Request request, LocalDateTime revokedAt) {
+        super(request);
+
+        this.revokedAt = revokedAt;
+        this.request.setStatus(Request.Status.REVOKED);
+    }
+
+    @Override
+	public Request apply(Request request1) {
+        return request1
+            .setStatus(Request.Status.REVOKED)
+            .setRevokedAt(revokedAt);
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return revokedAt;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Request revoked";
+    }
+
+    @Override
+    public String toString() {
+        return "request-revoked";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestSlot.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestSlot.java
new file mode 100644
index 000000000..5aebfc777
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestSlot.java
@@ -0,0 +1,63 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Transient;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Entity
+public class RequestSlot extends Slot implements Serializable {
+
+    private static final long serialVersionUID = -6482012426063899494L;
+
+    @OneToOne(mappedBy = "slot")
+    private Request request;
+
+    @Transient
+    private Boolean available;
+
+    public RequestSlot() {
+    }
+
+    public RequestSlot(LocalDateTime opensAt, LocalDateTime closesAt) {
+        super(opensAt, closesAt);
+    }
+
+    public Request getRequest() {
+        return request;
+    }
+
+    public void setRequest(Request request) {
+        this.request = request;
+
+        if (!request.getSlot().equals(this)) {
+            request.setSlot(this);
+        }
+    }
+
+    public Boolean getAvailable() {
+        return available;
+    }
+
+    public void setAvailable(Boolean available) {
+        this.available = available;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestType.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestType.java
new file mode 100644
index 000000000..cdd184f94
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/RequestType.java
@@ -0,0 +1,81 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import nl.tudelft.ewi.queue.views.View;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+@Entity
+public class RequestType implements Serializable {
+
+    private static final long serialVersionUID = -7443377267531011238L;
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @JsonView(View.Summary.class)
+    @Size(max=250)
+    private String name;
+
+    public RequestType(String name) {
+        this.name = name;
+    }
+
+    public RequestType() { }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String toSentence(Request request, RequestEntity requestEntity, Assignment assignment) {
+        String returnString = "";
+        switch(name) {
+            case "Question":
+                returnString = "%s has a question about %s";
+                break;
+            case "Submission":
+                returnString = "%s wants to submit %s";
+                break;
+        }
+
+        if (request.getSlot() != null) {
+            returnString += String.format(" at %s", request.getSlotSentence());
+        }
+        return String.format(returnString, requestEntity.getDisplayName(), assignment.getName());
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Role.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Role.java
new file mode 100644
index 000000000..b8631adb4
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Role.java
@@ -0,0 +1,70 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
+@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "course_id"}))
+public abstract class Role implements Serializable {
+   	private static final long serialVersionUID = -6889831254242893164L;
+
+	@Id
+    @GeneratedValue
+    private Long id;
+
+    @ManyToOne
+    private User user;
+
+    @ManyToOne
+    private Course course;
+
+    @Column(insertable = false, updatable = false)
+    private String type;
+
+    public Role() {
+    }
+
+    public Role(User user, Course course) {
+        this.user = user;
+        this.course = course;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Course getCourse() {
+        return course;
+    }
+
+    public void setCourse(Course course) {
+        this.course = course;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Room.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Room.java
new file mode 100644
index 000000000..ce0d2c77b
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Room.java
@@ -0,0 +1,100 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import nl.tudelft.ewi.queue.views.View;
+import org.hibernate.validator.constraints.NotBlank;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+@Entity
+public class Room implements Serializable, Comparable<Room> {
+
+	private static final long serialVersionUID = 1092887696539958279L;
+
+	@Id
+	@GeneratedValue
+	@JsonView(View.Summary.class)
+	private Long id;
+
+	@NotBlank
+	@Column(unique = true)
+	@JsonView(View.Summary.class)
+	private String name;
+
+	@JsonView(View.Summary.class)
+	private String mapFilePath;
+
+	private boolean placeholder = false;
+
+	public Room() {
+	}
+
+	public Room(String name) {
+		this.name = name;
+	}
+
+	public Room(String name, boolean placeholder) {
+		this.name = name;
+		this.placeholder = placeholder;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public boolean isPlaceholder() {
+		return placeholder;
+	}
+
+	public void setPlaceholder(boolean placeholder) {
+		this.placeholder = placeholder;
+	}
+
+	public String getMapFilePath() {
+		return mapFilePath;
+	}
+
+	public void setMapFilePath(String mapFilePath) {
+		this.mapFilePath = mapFilePath;
+	}
+
+	@Override
+	public String toString() {
+		return name;
+	}
+
+
+	@Override
+	public int compareTo(Room room) {
+		return name.compareTo(room.getName());
+	}
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Slot.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Slot.java
new file mode 100644
index 000000000..630b538cd
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Slot.java
@@ -0,0 +1,140 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import nl.tudelft.ewi.queue.helper.TimeHelper;
+import nl.tudelft.ewi.queue.views.View;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+
+@Entity
+@Inheritance
+public abstract class Slot implements Comparable<Slot> {
+    public final static String TIME_FORMAT = "dd/MM/yyyy HH:mm";
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @DateTimeFormat(pattern = TIME_FORMAT)
+    @NotNull
+    @JsonView(View.Summary.class)
+    private LocalDateTime opensAt;
+
+    @DateTimeFormat(pattern = TIME_FORMAT)
+    @JsonView(View.Summary.class)
+    @NotNull
+    private LocalDateTime closesAt;
+
+    public Slot() {
+    }
+
+    public Slot(LocalDateTime opensAt, LocalDateTime closesAt) {
+        this.opensAt = TimeHelper.setSecondsAndMsToZero(opensAt);
+        this.closesAt = TimeHelper.setSecondsAndMsToZero(closesAt);
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public LocalDateTime getOpensAt() {
+        return opensAt;
+    }
+
+    public void setOpensAt(LocalDateTime opensAt) {
+        this.opensAt = opensAt;
+    }
+
+    public LocalDateTime getClosesAt() {
+        return TimeHelper.setSecondsAndMsToZero(closesAt);
+    }
+
+    public void setClosesAt(LocalDateTime closesAt) {
+
+        this.closesAt = TimeHelper.setSecondsAndMsToZero(closesAt);
+    }
+
+    public boolean contains(LocalDateTime time) {
+        return getOpensAt().isBefore(time) && getClosesAt().isAfter(time);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Slot slot = (Slot) o;
+
+        return Objects.equals(id, slot.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+
+    @Override
+    public int compareTo(Slot that) {
+        if (this.opensAt.isBefore(that.getOpensAt())) {
+            return -1;
+        } else if(that.getOpensAt().isBefore(this.opensAt)) {
+            return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * @return true if this lab slot is roughly today
+     */
+	public boolean isTodayish() {
+		LocalDateTime now = LocalDateTime.now();
+		return now.isBefore(closesAt.plusDays(1)) && now.isAfter(opensAt.minusDays(1));
+	}
+
+	public Long durationOfSlot() {
+        return this.opensAt.until(this.closesAt, ChronoUnit.MINUTES);
+    }
+
+	@Override
+    public String toString() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
+        String start = formatter.format(opensAt);
+        String end = formatter.format(closesAt);
+	    return start + " - " + end;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Student.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Student.java
new file mode 100644
index 000000000..f769e728d
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Student.java
@@ -0,0 +1,53 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import java.time.LocalDateTime;
+
+@Entity
+public class Student extends Role {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -8588497965990598992L;
+	private LocalDateTime enrolledAt;
+
+    public Student() {
+        super();
+    }
+
+    public Student(User user, Course course, LocalDateTime enrolledAt) {
+        super(user, course);
+
+        this.enrolledAt = enrolledAt;
+    }
+
+    public LocalDateTime getEnrolledAt() {
+        return enrolledAt;
+    }
+
+    public void setEnrolledAt(LocalDateTime enrolledAt) {
+        this.enrolledAt = enrolledAt;
+    }
+
+    @Override
+    public String toString() {
+        return "Student";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/StudentNotFoundEvent.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/StudentNotFoundEvent.java
new file mode 100644
index 000000000..ad55ec60a
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/StudentNotFoundEvent.java
@@ -0,0 +1,84 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Transient;
+import javax.persistence.ManyToOne;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+import java.util.PrimitiveIterator;
+
+@Entity
+public class StudentNotFoundEvent extends RequestEvent {
+
+    private LocalDateTime notFoundAt;
+
+    @Size(max=250)
+    private String reason;
+
+    @Size(max=250)
+    private String reasonForStudent;
+
+    @ManyToOne
+    private User assistant;
+
+	@Transient
+    private String description;
+
+    public StudentNotFoundEvent() {
+        super();
+    }
+
+    public StudentNotFoundEvent(Request request, LocalDateTime localDateTime, User assistant) {
+        super(request);
+        this.notFoundAt = localDateTime;
+        this.assistant = assistant;
+
+        this.reason = this.getDescription();
+        this.reasonForStudent = this.getDescription();
+    }
+
+    @Override
+    public String getDescription() {
+        if(request.getLab().getDirection().equals(Direction.STUDENT_VISIT_TA)) {
+            return "Student did not show";
+        } else {
+            return "Could not find student to handle the request";
+        }
+    }
+
+    @Override
+    public LocalDateTime getTimestamp() {
+        return notFoundAt;
+    }
+
+    @Override
+    public Request apply(Request request) {
+        return request.setStatus(Request.Status.NOTFOUND)
+                .setNotFoundAt(notFoundAt)
+                .setReason(this.reason)
+                .setReasonForStudent(this.reasonForStudent)
+                .setAssistant(assistant);
+    }
+
+    @Override
+    public String toString() {
+        return "request-rejected";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Subscription.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Subscription.java
new file mode 100644
index 000000000..f5bf6a267
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Subscription.java
@@ -0,0 +1,125 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
+import org.bouncycastle.jce.spec.ECPublicKeySpec;
+import org.bouncycastle.math.ec.ECPoint;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import java.io.Serializable;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Base64;
+
+@Entity
+public class Subscription implements Serializable {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -1247750903720299588L;
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    private String endpoint;
+
+    private String key;
+
+    private String auth;
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public String getRegistrationId() {
+        if (!endpoint.startsWith("https://android.googleapis.com/gcm/send")) {
+            throw new UnsupportedOperationException("A registration ID is only available for Google Cloud Messaging");
+        }
+
+        return getEndpoint().substring(getEndpoint().lastIndexOf("/") + 1);
+    }
+
+    public boolean isGcm() {
+        return endpoint.startsWith("https://android.googleapis.com/gcm/send");
+    }
+
+    /**
+     * Get the user's public key as byte array
+     */
+    public byte[] getKeyAsBytes() {
+        return Base64.getDecoder().decode(getKey());
+    }
+
+    /**
+     * Get the user's public key as base64 encoded string
+     */
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    /**
+     * Get the user's auth key as byte array
+     */
+    public byte[] getAuthAsBytes() {
+        return Base64.getDecoder().decode(getAuth());
+    }
+
+    /**
+     * Get the user's auth key as base64 encoded string
+     */
+    public String getAuth() {
+        return auth;
+    }
+
+    public void setAuth(String auth) {
+        this.auth = auth;
+    }
+
+    /**
+     * Get the user's public key as PublicKey object
+     */
+    public PublicKey getUserPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
+        KeyFactory kf = KeyFactory.getInstance("ECDH", "BC");
+        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
+        ECPoint point = ecSpec.getCurve().decodePoint(getKeyAsBytes());
+        ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);
+
+        return kf.generatePublic(pubSpec);
+    }
+
+    @Override
+    public String toString() {
+        return "Subscription{endpoint='" + endpoint + "', key='" + key + "'}";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/Teacher.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/Teacher.java
new file mode 100644
index 000000000..edb70aff8
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/Teacher.java
@@ -0,0 +1,41 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import javax.persistence.Entity;
+
+@Entity
+public class Teacher extends Role {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -5329573673496730234L;
+
+	public Teacher() {
+        super();
+    }
+
+    public Teacher(User user, Course course) {
+        super(user, course);
+    }
+
+    @Override
+    public String toString() {
+        return "Teacher";
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/User.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/User.java
new file mode 100644
index 000000000..258413ae6
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/User.java
@@ -0,0 +1,319 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonView;
+import nl.tudelft.ewi.queue.views.View;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Entity
+@Table(name="person")	// Avoiding reserved database word user
+public class User extends RequestEntity {
+
+    private static final long serialVersionUID = 7273939479491656161L;
+
+    @Column(unique = true)
+	@JsonView(View.Summary.class)
+	private String username;
+
+	private String password;
+	private String email;
+	private int studentNumber;
+
+	@Enumerated(EnumType.STRING)
+	private DefaultRole defaultRole;
+
+	@OneToMany(mappedBy = "user", cascade = {CascadeType.ALL})
+	private List<Role> roles = new ArrayList<>();
+
+	@OneToOne(cascade = {CascadeType.ALL})
+	private Subscription subscription;
+
+	// Students can become first year student again, hence the oneToMany
+	@JsonIgnore
+	@OneToMany(mappedBy = "user")
+	private List<FirstYearStudent> firstYearStudents = new ArrayList<>();
+
+	@OneToMany(mappedBy = "user")
+	@OrderBy("createdAt desc")
+	private List<Notification> notifications;
+
+	public User() {}
+
+	public User(String username, String password, String displayName, String email, DefaultRole defaultRole, int studentNumber) {
+		super(displayName);
+		this.username = username;
+		this.password = password;
+		this.email = email;
+		this.defaultRole = defaultRole;
+		this.studentNumber = studentNumber;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public void setUsername(String username) {
+		this.username = username;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public Subscription getSubscription() {
+		return subscription;
+	}
+
+	public void setSubscription(Subscription subscription) {
+		this.subscription = subscription;
+	}
+
+	public NotificationList getNotifications() {
+		return new NotificationList(notifications);
+	}
+
+	public boolean isOutdated() {
+		return notifications.stream().anyMatch(Notification::isUnread);
+	}
+
+	public Integer notificationCount() {
+		return notifications.size();
+	}
+
+	public DefaultRole getDefaultRole() {
+		return defaultRole;
+	}
+
+	public void setDefaultRole(DefaultRole defaultRole) {
+		this.defaultRole = defaultRole;
+	}
+
+	public boolean isAdmin() {
+		return defaultRole == DefaultRole.ROLE_ADMIN;
+	}
+
+	public boolean isStudent() {
+		return defaultRole == DefaultRole.ROLE_STUDENT;
+	}
+
+	public boolean isTeacher() {
+		return defaultRole == DefaultRole.ROLE_TEACHER;
+	}
+
+    public List<FirstYearStudent> getFirstYearStudents() {
+        return firstYearStudents;
+    }
+
+	public List<Role> getRoles() {
+		return roles;
+	}
+
+	public void setRoles(List<Role> roles) {
+		this.roles = roles;
+	}
+
+	public void addRole(Role role) {
+		roles.add(role);
+
+		if (!this.equals(role.getUser())) {
+			role.setUser(this);
+		}
+	}
+
+	/**
+	 * Get this user's role in the given course
+	 *
+	 * @param course
+	 * @return
+	 */
+	public Optional<Role> getRole(Course course) {
+		return getRoles().stream().filter(r -> r.getCourse().equals(course)).findAny();
+	}
+
+	/**
+	 * Get the courses this user teaches
+	 *
+	 * @return
+	 */
+	public List<Course> getTeaches() {
+		return getRoles().stream().filter(r -> (r instanceof Teacher)).map(Role::getCourse).collect(Collectors.toList());
+	}
+
+	/**
+	 * Check whether this user teaches the given course
+	 *
+	 * @param course
+	 * @return
+	 */
+	public boolean teaches(Course course) {
+		return getTeaches().stream().anyMatch(c -> c.equals(course));
+	}
+
+	/**
+	 * Get the courses this user manages
+	 *
+	 * @return
+	 */
+	public List<Course> getManages() {
+		return getRoles().stream().filter(r -> (r instanceof Manager)).map(Role::getCourse).collect(Collectors.toList());
+	}
+
+	/**
+	 * Check whether this user manages the given course
+	 *
+	 * @param course
+	 * @return
+	 */
+	public boolean manages(Course course) {
+		return getManages().stream().anyMatch(c -> c.equals(course));
+	}
+
+	/**
+	 * Get the courses this user assists
+	 *
+	 * @return
+	 */
+	public List<Course> getAssists() {
+		return getRoles().stream().filter(r -> (r instanceof Assistant)).map(Role::getCourse).collect(Collectors.toList());
+	}
+
+	/**
+	 * Check whether this user assists the given course
+	 *
+	 * @param course
+	 * @return
+	 */
+	public boolean assists(Course course) {
+		return getAssists().stream().anyMatch(c -> c.equals(course));
+	}
+
+	/**
+	 * Get the courses in which this user participates as a student
+	 *
+	 * @return
+	 */
+	public List<Course> getParticipates() {
+		return getRoles().stream().filter(r -> (r instanceof Student)).map(Role::getCourse).collect(Collectors.toList());
+	}
+
+	/**
+	 * Check whether this user is enrolled for the given course
+	 *
+	 * @param course
+	 * @return
+	 */
+	public boolean participates(Course course) {
+		return getParticipates().stream().anyMatch(c -> c.equals(course));
+	}
+
+	/**
+	 * Get user's requests ordered by creation time
+	 *
+	 * @return
+	 */
+
+
+	/*
+	 * (non-Javadoc)
+	 * @see java.lang.Object#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
+		result = prime * result + ((username == null) ? 0 : username.hashCode());
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		if (!(obj instanceof User)) {
+			return false;
+		}
+		User other = (User) obj;
+		if (id == null) {
+			if (other.id != null) {
+				return false;
+			}
+		} else if (!id.equals(other.id)) {
+			return false;
+		}
+		if (username == null) {
+			if (other.username != null) {
+				return false;
+			}
+		} else if (!username.equals(other.username)) {
+			return false;
+		}
+		return true;
+	}
+
+	public String getEmail() {
+		return email;
+	}
+
+	public void setEmail(String email) {
+		this.email = email;
+	}
+
+	public int getStudentNumber() {
+		return studentNumber;
+	}
+
+	/**
+	 * TODO this hardcoded assumes TU Delft at the moment; perhaps scan for the @ instead?
+	 * @param username hopefully a valid netID
+	 * @return a valid netID
+	 */
+	public static String guarantueeValidNetId(String username) {
+		if (!username.contains("@tudelft.nl")) {
+			username += "@tudelft.nl";
+		}
+		return username;
+	}
+
+	/*public void addFirstYearStudent(FirstYearStudent firstYearStudent) {
+		this.firstYearStudents.add(firstYearStudent);
+	}*/
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/model/UserPrincipal.java b/backend/src/main/java/nl/tudelft/ewi/queue/model/UserPrincipal.java
new file mode 100644
index 000000000..fdbafd6f0
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/model/UserPrincipal.java
@@ -0,0 +1,38 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import java.util.Collections;
+
+public class UserPrincipal extends org.springframework.security.core.userdetails.User {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -5816455445507659552L;
+	private final User user;
+
+    public UserPrincipal(User user) {
+        super(user.getUsername(), user.getPassword(), Collections.singletonList(user.getDefaultRole()));
+
+        this.user = user;
+    }
+
+    public User getUser() {
+        return user;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/AssignmentRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/AssignmentRepository.java
new file mode 100644
index 000000000..60cb0a181
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/AssignmentRepository.java
@@ -0,0 +1,25 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.Assignment;
+import org.springframework.data.repository.CrudRepository;
+
+public interface AssignmentRepository extends CrudRepository<Assignment, Long> {
+    Assignment findByName(String assembly_programming);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/CourseRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/CourseRepository.java
new file mode 100644
index 000000000..79f77d4ee
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/CourseRepository.java
@@ -0,0 +1,52 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import com.querydsl.core.types.Predicate;
+import nl.tudelft.ewi.queue.model.Course;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import java.util.List;
+
+public interface CourseRepository extends PagingAndSortingRepository<Course, Long>, QueryDslPredicateExecutor<Course> {
+
+    /**
+     * Find all courses
+     *
+     * @return
+     */
+    @Override
+	List<Course> findAll();
+
+    /**
+     * Find all courses for given pagination information
+     *
+     * @param pageable
+     * @return
+     */
+    @Override
+	Page<Course> findAll(Pageable pageable);
+
+    @Override
+    Page<Course> findAll(Predicate predicate, Pageable pageable);
+
+}
+
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearMentorGroupRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearMentorGroupRepository.java
new file mode 100644
index 000000000..26b430764
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearMentorGroupRepository.java
@@ -0,0 +1,36 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.FirstYearMentorGroup;
+import nl.tudelft.ewi.queue.model.User;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface FirstYearMentorGroupRepository extends CrudRepository<FirstYearMentorGroup, Long> {
+    @Override
+	List<FirstYearMentorGroup> findAll();
+
+    List<FirstYearMentorGroup> findAllByActiveIsTrue();
+
+    FirstYearMentorGroup findByStudents(User user);
+
+    FirstYearMentorGroup findByName(String name);
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearStudentRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearStudentRepository.java
new file mode 100644
index 000000000..008accb83
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/FirstYearStudentRepository.java
@@ -0,0 +1,35 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.FirstYearStudent;
+import nl.tudelft.ewi.queue.model.User;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface FirstYearStudentRepository extends CrudRepository<FirstYearStudent, Long> {
+    @Override
+	List<FirstYearStudent> findAll();
+
+    List<FirstYearStudent> findByUser(User user);
+
+    List<FirstYearStudent> findByNetId(String netId);
+
+    boolean existsByNetId(String netId);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/GroupRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/GroupRepository.java
new file mode 100644
index 000000000..501176e60
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/GroupRepository.java
@@ -0,0 +1,29 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.Course;
+import nl.tudelft.ewi.queue.model.Group;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface GroupRepository extends CrudRepository<Group, Long> {
+    Boolean existsByDisplayNameAndCourse(String displayName, Course course);
+    Group findByDisplayNameAndCourse(String displayName, Course course);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/LabRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/LabRepository.java
new file mode 100644
index 000000000..5e2e07830
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/LabRepository.java
@@ -0,0 +1,30 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.Lab;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface LabRepository extends CrudRepository<Lab, Long> {
+    @Override
+	List<Lab> findAll();
+
+    Lab findFirstByOrderByIdAsc();
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/NotificationRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/NotificationRepository.java
new file mode 100644
index 000000000..dd1a6860d
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/NotificationRepository.java
@@ -0,0 +1,31 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.Notification;
+import nl.tudelft.ewi.queue.model.User;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface NotificationRepository extends CrudRepository<Notification, Long> {
+    @Override
+	List<Notification> findAll();
+
+    List<Notification> findByUser(User user);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestRepository.java
new file mode 100644
index 000000000..234e05f89
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestRepository.java
@@ -0,0 +1,41 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import com.querydsl.core.types.Predicate;
+import nl.tudelft.ewi.queue.model.Request;
+import nl.tudelft.ewi.queue.model.User;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface RequestRepository extends CrudRepository<Request, Long>, JpaSpecificationExecutor,
+		QueryDslPredicateExecutor<Request> {
+    @Override
+    Iterable<Request> findAll(Predicate predicate);
+
+    @Override
+    Page<Request> findAll(Predicate predicate, Pageable pageable);
+
+    List<Request> findByAssistant(User assistant);
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestTypeRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestTypeRepository.java
new file mode 100644
index 000000000..b4edc928f
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RequestTypeRepository.java
@@ -0,0 +1,25 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.RequestType;
+import org.springframework.data.repository.CrudRepository;
+
+public interface RequestTypeRepository extends CrudRepository<RequestType, Long> {
+    RequestType findByName(String name);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/RoleRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RoleRepository.java
new file mode 100644
index 000000000..8ec7b3667
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RoleRepository.java
@@ -0,0 +1,28 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.Role;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface RoleRepository extends CrudRepository<Role, Long> {
+    @Override
+	List<Role> findAll();
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/RoomRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RoomRepository.java
new file mode 100644
index 000000000..c5f50f309
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/RoomRepository.java
@@ -0,0 +1,33 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.Room;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface RoomRepository extends CrudRepository<Room, Long> {
+    @Override
+	List<Room> findAll();
+
+    Room findByName(String name);
+
+    List<Room> findAllByOrderByNameAsc();
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/repository/UserRepository.java b/backend/src/main/java/nl/tudelft/ewi/queue/repository/UserRepository.java
new file mode 100644
index 000000000..3bb6eb25f
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/repository/UserRepository.java
@@ -0,0 +1,34 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.repository;
+
+import nl.tudelft.ewi.queue.model.User;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface UserRepository extends CrudRepository<User, Long> {
+    @Override
+	List<User> findAll();
+
+    User findByUsername(String username);
+
+    Boolean existsByUsername(String username);
+
+    User findByStudentNumber(int studentNumber);
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/resolver/UserArgumentResolver.java b/backend/src/main/java/nl/tudelft/ewi/queue/resolver/UserArgumentResolver.java
new file mode 100644
index 000000000..aa20a65b6
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/resolver/UserArgumentResolver.java
@@ -0,0 +1,74 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.resolver;
+
+import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
+import nl.tudelft.ewi.queue.model.UserPrincipal;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import java.lang.annotation.Annotation;
+
+@Service
+public class UserArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return findMethodAnnotation(AuthenticatedUser.class, parameter) != null;
+    }
+
+    @Override
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+        UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
+
+        return userRepository.findByUsername(userPrincipal.getUsername());
+    }
+
+    private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
+        T annotation = parameter.getParameterAnnotation(annotationClass);
+
+        if (annotation != null) {
+            return annotation;
+        }
+
+        Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
+        for (Annotation toSearch : annotationsToSearch) {
+            annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
+            if (annotation != null) {
+                return annotation;
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/AdminService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/AdminService.java
new file mode 100644
index 000000000..720269ddd
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/AdminService.java
@@ -0,0 +1,127 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import nl.tudelft.ewi.queue.model.FirstYearMentorGroup;
+import nl.tudelft.ewi.queue.model.FirstYearStudent;
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.repository.FirstYearMentorGroupRepository;
+import nl.tudelft.ewi.queue.repository.FirstYearStudentRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class AdminService {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private FirstYearMentorGroupRepository firstYearMentorGroupRepository;
+
+    @Autowired
+    private FirstYearStudentRepository firstYearStudentRepository;
+
+    @Autowired
+    private ServletContext servletContext;
+
+	private static final Logger logger = LoggerFactory.getLogger(AdminService.class);
+
+    private void markOldGroupsAsInactive() {
+        List<FirstYearMentorGroup> groups = firstYearMentorGroupRepository.findAll();
+        for (FirstYearMentorGroup group : groups) {
+            group.setActive(false);
+        }
+        firstYearMentorGroupRepository.save(groups);
+    }
+
+    public int importCsv(MultipartFile csvFile) throws IOException {
+        markOldGroupsAsInactive();
+        int count = 0;
+        ArrayList<List<String>> studentList = FirstYearStudent.readCsv(csvFile);
+        for (List<String> student : studentList) {
+            String netId = student.get(0);
+            String mentorGroupName = student.get(1);
+            FirstYearMentorGroup mentorGroup = createFirstYearMentorGroup(mentorGroupName);
+            createFirstYearStudent(netId, mentorGroup);
+            count += 1;
+        }
+        return count;
+    }
+
+    private FirstYearMentorGroup createFirstYearMentorGroup(String name) {
+        FirstYearMentorGroup mentorGroup = firstYearMentorGroupRepository.findByName(name);
+        if (mentorGroup == null) {
+            mentorGroup = new FirstYearMentorGroup(name);
+            firstYearMentorGroupRepository.save(mentorGroup);
+        }        
+        return mentorGroup;
+    }
+
+    private void createFirstYearStudent(String netId, FirstYearMentorGroup mentorGroup) {
+        User user;
+        // netId is actually a student number
+        if (isInteger(netId)) {
+            int studentNumber = Integer.parseInt(netId);
+            user = userRepository.findByStudentNumber(studentNumber);
+        } else {
+            netId = User.guarantueeValidNetId(netId);
+            user = userRepository.findByUsername(netId);
+            logger.info("Found existing student for imported mentorgroup");
+        }
+        FirstYearStudent firstYearStudent = new FirstYearStudent(netId, mentorGroup);
+        if (user != null) {
+            firstYearStudent.setUser(user);
+        }
+        firstYearStudentRepository.save(firstYearStudent);
+    }
+
+    private static boolean isInteger(String s) {
+        try {
+            Integer.parseInt(s);
+        } catch(NumberFormatException | NullPointerException e) {
+            return false;
+        }
+        // only got here if we didn't return false
+        return true;
+    }
+
+    public String uploadFile(MultipartFile file, String filePath) throws IOException {
+		String mapStoragePath = "/var/www/queue/maps/";		// TODO this won't work when running locally; get from properties?
+        String fileName = file.getOriginalFilename();
+        File uploadLocation = new File(mapStoragePath);
+        logger.warn("map upload: " + mapStoragePath + ", "  + fileName + "," + uploadLocation);
+        File savedFile = new File(mapStoragePath + fileName);
+        if(!uploadLocation.exists()) {
+            if (!uploadLocation.mkdirs())
+                throw new IOException("Can not create path for storing maps: " + uploadLocation);
+        }
+        file.transferTo(savedFile);
+        return filePath + fileName;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/AuthenticationService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/AuthenticationService.java
new file mode 100644
index 000000000..673449130
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/AuthenticationService.java
@@ -0,0 +1,61 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.model.UserPrincipal;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class AuthenticationService implements UserDetailsService {
+	private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
+
+	@Autowired
+	private UserRepository userRepository;
+
+	@Override
+	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+		logger.info("Got to loadUserByUsername function");
+		User user = userRepository.findByUsername(username);
+
+		logger.info("Got to loadUserByUsername function");
+		if (null == user) {
+			logger.warn("Unable to find user for given username");
+			throw new UsernameNotFoundException("Unable to find user for given username");
+		}
+
+		List<GrantedAuthority> authorities = Collections.singletonList(
+				new SimpleGrantedAuthority("ROLE_USER")
+		);
+
+		return new UserPrincipal(user);
+	}
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/CourseService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/CourseService.java
new file mode 100644
index 000000000..36b5c57c5
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/CourseService.java
@@ -0,0 +1,70 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import com.google.common.collect.Lists;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+
+@Service
+public class CourseService {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private RequestRepository requestRepository;
+
+    /**
+     * Enroll user for course
+     *
+     * @param user
+     * @param course
+     */
+    public void enroll(User user, Course course) {
+        user.addRole(new Student(user, course, LocalDateTime.now()));
+
+        userRepository.save(user);
+    }
+
+    public List<Request> requestsWithFeedback(User assistant) {
+        nl.tudelft.ewi.queue.model.QRequest request = QRequest.request;
+        BooleanExpression hasFeedback = request.feedback.isNotNull();
+        BooleanExpression handledByAssistant = request.assistant.id.eq(assistant.getId());
+
+        return Lists.newArrayList(requestRepository.findAll(handledByAssistant.and(hasFeedback)));
+    }
+
+    public HashMap<Long, Integer> feedbackCount(List<User> assistants) {
+        HashMap<Long, Integer> feedbackCount = new HashMap<>();
+        for (User user: assistants) {
+            int count = requestsWithFeedback(user).size();
+            feedbackCount.put(user.getId(), count);
+        }
+        return feedbackCount;
+    }
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/GroupService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/GroupService.java
new file mode 100644
index 000000000..7be815e17
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/GroupService.java
@@ -0,0 +1,120 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import nl.tudelft.ewi.queue.helper.BrightspaceCsvHelper;
+import nl.tudelft.ewi.queue.model.Course;
+import nl.tudelft.ewi.queue.model.DefaultRole;
+import nl.tudelft.ewi.queue.model.FirstYearStudent;
+import nl.tudelft.ewi.queue.model.Group;
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.FirstYearStudentRepository;
+import nl.tudelft.ewi.queue.repository.GroupRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+@Service
+public class GroupService {
+    private static final Logger logger = LoggerFactory.getLogger(GroupService.class);
+    @Autowired
+    private UserRepository userRepository;
+    @Autowired
+    private GroupRepository groupRepository;
+    @Autowired
+    private CourseRepository courseRepository;
+    @Autowired
+    private CourseService courseService;
+    @Autowired
+    private FirstYearStudentRepository firstYearStudentRepository;
+
+    private void deleteOldGroups(Course course) {
+        course.getGroups().removeAll(course.getGroups());
+    }
+
+    public void importCsv(Course course, MultipartFile csvFile) throws IOException {
+        deleteOldGroups(course);
+
+        ArrayList<BrightspaceCsvHelper> csvLines = processCsvFile(csvFile);
+        for (BrightspaceCsvHelper csvLine : csvLines) {
+            User student = findOrCreateUser(csvLine);
+            if (!course.isEnrolled(student)) {
+            	logger.info("Enrolling unenrolled student: " + student.getUsername());
+                courseService.enroll(student, course);
+            }
+            Group group = findOrCreateGroup(csvLine, student, course);
+            if (!course.isGroupOfThisCourse(group)) {
+            	logger.info("Adding group to course: " + group.getDisplayName());
+                course.addGroup(group);
+            }
+        }
+//        groupRepository.save(course.getGroups());
+    }
+
+    private ArrayList<BrightspaceCsvHelper> processCsvFile(MultipartFile csvFile) throws IOException {
+        return BrightspaceCsvHelper.readCsv(csvFile);
+    }
+
+    private User findOrCreateUser(BrightspaceCsvHelper student) {
+        Boolean exists = userRepository.existsByUsername(student.getNetID());
+        User user;
+        
+        if (exists) {
+        	logger.info("Student found in DB");
+            user = userRepository.findByUsername(student.getNetID());
+        } else {
+        	logger.info("unknown student: " + student.getNetID());
+            String displayName = student.getFirstName() + " " + student.getLastName();
+            DefaultRole role = DefaultRole.ROLE_STUDENT;
+            user = new User(student.getNetID(), "", displayName, student.getEmail(), role, student.getStudentNumber());
+            userRepository.save(user);
+        }
+        
+/*        if (firstYearStudentRepository.existsByNetId(user.getUsername())) {
+        	logger.info("Student has first year mentor groups");
+            FirstYearStudent firstYearStudent = firstYearStudentRepository.findByNetId(user.getUsername());
+            firstYearStudent.setUser(user);
+            user.addFirstYearStudent(firstYearStudent);
+            userRepository.save(user);
+            firstYearStudentRepository.save(firstYearStudent);
+        }*/
+        return user;
+    }
+
+    private Group findOrCreateGroup(BrightspaceCsvHelper student, User user, Course course) {
+        Boolean exists = groupRepository.existsByDisplayNameAndCourse(student.getGroupName(), course);
+        Group group;
+        if (exists) {
+            group = groupRepository.findByDisplayNameAndCourse(student.getGroupName(), course);
+        } else {
+            group = new Group(student.getGroupName(), course);
+        }
+        group.addMember(user);
+        groupRepository.save(group);
+        return group;
+    }
+
+
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/LabService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/LabService.java
new file mode 100644
index 000000000..bbd2f736d
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/LabService.java
@@ -0,0 +1,388 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import nl.tudelft.ewi.queue.exception.AlreadyEnqueuedException;
+import nl.tudelft.ewi.queue.exception.InvalidSlotException;
+import nl.tudelft.ewi.queue.helper.EmailHelper;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.AssignmentRepository;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.FirstYearMentorGroupRepository;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.NotificationRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.RequestTypeRepository;
+import nl.tudelft.ewi.queue.repository.RoomRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.EntityNotFoundException;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+@Service
+public class LabService {
+    private static final Logger logger = LoggerFactory.getLogger(LabService.class);
+
+    @Autowired
+    private RequestService requestService;
+
+    @Autowired
+    private RequestRepository requestRepository;
+
+    @Autowired
+    private NotificationRepository notificationRepository;
+
+    @Autowired
+    private NotificationService notificationService;
+
+    @Autowired
+    private LabRepository labRepository;
+
+    @Autowired
+    private RequestTypeRepository requestTypeRepository;
+
+    @Autowired
+    private AssignmentRepository assignmentRepository;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
+    private FirstYearMentorGroupRepository firstYearMentorGroupRepository;
+
+    @Autowired
+    private RoomRepository roomRepository;
+
+    /**
+     * Enqueue student for lab. An exception is thrown if the lab is closed,
+     * the student is not enrolled, or if the student is already enqueued.
+     *
+     * @param request
+     */
+    public void enqueue(Request request) {
+        if (!request.getLab().getSignOffIntervals() && !request.getLab().isOpenOrSlotSelection()) {
+            throw new AccessDeniedException("Enqueueing is only possible if the lab is open.");
+        }
+
+        if (!request.getRequestEntity().participates(request.getLab().getCourse())) {
+            throw new AccessDeniedException("You need to be enrolled in the course for this lab.");
+        }
+        if (!request.getLab().isFeedbackLab()) {
+            if (request.getLab().isEnqueued(request.getRequestEntity())) {
+                throw new AlreadyEnqueuedException("Student is already enqueued.");
+            }
+        }
+
+        if (request.getSlot() != null) {
+            if (ChronoUnit.MINUTES.between(request.getSlot().getClosesAt(), request.getSlot().getOpensAt())
+                    > request.getLab().getIntervalTime()) {
+                throw new InvalidSlotException("Your interval is too long.");
+            }
+            if (request.getSlot().getOpensAt().isBefore(request.getLab().getSlot().getOpensAt())) {
+                throw new InvalidSlotException("Your request interval is before the lab opens.");
+            }
+            if (!request.getLab().slotIsAvailable(request.getSlot())) {
+                throw new InvalidSlotException("This slot is no longer available.");
+            }
+        }
+        request.requestCreatedEvent();
+        requestRepository.save(request);
+        // only send a notification for a request that is not with an interval
+        if (!request.getLab().getSignOffIntervals()) {
+            createAndSendNotifications(request);
+        }
+
+    }
+
+    public void createAndSendNotifications(Request request) {
+        Lab lab = request.getLab();
+        Course course = courseRepository.findOne(lab.getCourse().getId());
+        for (User assistant : course.getPrivileged()) {
+            Notification notification = new Notification(assistant, course, "New request",
+                    "New request for " + lab + " in course " + course,
+                    100);
+            notificationService.sendPushNotification(assistant, notification);
+            notificationService.sendRequestTableUpdate(assistant, request);
+        }
+    }
+
+    /**
+     * Given a lab, repeats that lab for the repeatAmount of weeks
+     * by setting the slot plus 1*x weeks per new lab
+     *
+     * @param repeatAmount number of times to repeat a lab
+     * @param lab          the lab that should be repeated
+     */
+    public void repeatLabFor(int repeatAmount, Lab lab) {
+        LocalDateTime slotOpen = lab.getSlot().getOpensAt();
+        LocalDateTime slotClose = lab.getSlot().getClosesAt();
+        LocalDateTime slotSelectionOpensAt = lab.getSlotSelectionOpensAt();
+        for (int i = 0; i < repeatAmount; i++) {
+            slotOpen = slotOpen.plusWeeks(1);
+            slotClose = slotClose.plusWeeks(1);
+            LabSlot nextSlot = new LabSlot(slotOpen, slotClose);
+            Lab clone = new Lab(lab.getCourse(), nextSlot, lab.getRooms(), lab.isFeedbackLab());
+
+            if (lab.getSignOffIntervals()) {
+                slotSelectionOpensAt = slotSelectionOpensAt.plusWeeks(1);
+                clone.setSignOffIntervals(true);
+                clone.setIntervalTime(lab.getIntervalTime());
+                clone.setCapacity(lab.getCapacity());
+                clone.setSlotSelectionOpensAt(slotSelectionOpensAt);
+            }
+            clone.setTitle(lab.getTitle());
+            clone.setAssignments(copyAssignments(lab));
+            clone.setDirection(lab.getDirection());
+            clone.setAllowedRequestTypes(copyRequestTypes(lab));
+            clone.setAllowedMentorGroups(copyAllowedMentorGroups(lab));
+            clone.setAllowWithoutMentorGroup(lab.isAllowWithoutMentorGroup());
+
+            labRepository.save(clone);
+        }
+    }
+
+    public void updateLab(Long id, Lab labData) {
+        Lab lab = getLab(id);
+
+        lab.setTitle(labData.getTitle());
+        lab.setDirection(labData.getDirection());
+        lab.setSlot(labData.getSlot());
+        lab.setRooms(labData.getRooms());
+        lab.setAssignments(labData.getAssignments());
+        lab.setCapacity(labData.getCapacity());
+        lab.setIntervalTime(labData.getIntervalTime());
+        lab.setAllowedRequestTypes(labData.getAllowedRequestTypes());
+        lab.setSlotSelectionOpensAt(labData.getSlotSelectionOpensAt());
+        lab.setAllowedMentorGroups(labData.getAllowedMentorGroups());
+        lab.setAllowWithoutMentorGroup(labData.isAllowWithoutMentorGroup());
+
+        labRepository.save(lab);
+    }
+
+    public void saveLab(Course course, Lab lab, Optional<Integer> weekRepeat) {
+        lab.setCourse(course);
+
+        if (lab.getSignOffIntervals()) {
+            lab.addRoom(roomRepository.findByName("To be determined"));
+        }
+
+        for (Assignment assignment : lab.getAssignments()) {
+            if (assignment != null) {
+                assignment.addLab(lab);
+            }
+        }
+
+        labRepository.save(lab);
+        weekRepeat.ifPresent(wr -> repeatLabFor(wr, lab));
+    }
+
+    /**
+     * No longer used?
+     * private ArrayList<Room> copyRooms(Lab lab) {
+     * ArrayList<Room> copiedRooms = new ArrayList<>();
+     * for (Room room : lab.getRooms()) {
+     * copiedRooms.add(new Room(room.getName()));
+     * }
+     * return copiedRooms;
+     * }
+     */
+
+    private ArrayList<Assignment> copyAssignments(Lab lab) {
+        ArrayList<Assignment> copiedAssignments = new ArrayList<>();
+        for (Assignment assignment : lab.getAssignments()) {
+            if (assignment != null) {
+                copiedAssignments.add(assignmentRepository.findOne(assignment.getId()));
+            }
+        }
+        return copiedAssignments;
+    }
+
+    private ArrayList<FirstYearMentorGroup> copyAllowedMentorGroups(Lab lab) {
+        ArrayList<FirstYearMentorGroup> mentorGroupsCopy = new ArrayList<>();
+        for (FirstYearMentorGroup mentorGroup : lab.getAllowedMentorGroups()) {
+            if (mentorGroup != null) {
+                mentorGroupsCopy.add(firstYearMentorGroupRepository.findOne(mentorGroup.getId()));
+            }
+        }
+        return mentorGroupsCopy;
+    }
+
+    private ArrayList<RequestType> copyRequestTypes(Lab lab) {
+        ArrayList<RequestType> requestTypeCopy = new ArrayList<>();
+        for (RequestType requestType : lab.getAllowedRequestTypes()) {
+            if (requestType != null) {
+                requestTypeCopy.add(requestTypeRepository.findOne(requestType.getId()));
+            }
+        }
+        return requestTypeCopy;
+    }
+
+    public ArrayList<RequestSlot> getIntervalsForLab(Lab lab) {
+        assert lab.getSignOffIntervals();
+        Long intervalTimeMinutes = lab.getIntervalTime();
+        ArrayList<RequestSlot> intervals = new ArrayList<>();
+        LocalDateTime startTime = lab.getSlot().getOpensAt();
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime endTime = lab.getSlot().getClosesAt();
+
+        for (LocalDateTime dateTime = startTime; dateTime.isBefore(endTime);
+             dateTime = dateTime.plusMinutes(intervalTimeMinutes)) {
+            RequestSlot slot = new RequestSlot(dateTime, dateTime.plusMinutes(intervalTimeMinutes));
+            slot.setAvailable(lab.slotIsAvailable(slot));
+            intervals.add(slot);
+        }
+        return intervals;
+    }
+
+    public Lab getLab(Long id) {
+        Lab lab = labRepository.findOne(id);
+
+        if (null == lab) {
+            throw new EntityNotFoundException("Entity was not found");
+        }
+
+        return lab;
+    }
+
+    public Assignment getAssignment(Long id) {
+        Assignment assignment = assignmentRepository.findOne(id);
+
+        if (null == assignment) {
+            throw new EntityNotFoundException("Entity was not found");
+        }
+
+        return assignment;
+    }
+
+    public Course getCourse(Long id) {
+        Course course = courseRepository.findOne(id);
+
+        if (null == course) {
+            throw new EntityNotFoundException("Entity was not found");
+        }
+
+        return course;
+    }
+
+    public Room getRoom(Long id) {
+        Room room = roomRepository.findOne(id);
+
+        if (null == room) {
+            throw new EntityNotFoundException("Entity was not found");
+        }
+
+        return room;
+    }
+
+    /**
+     * Picks a random timeslot in a lab that the requestEntity can see in which they can sign off
+     * the assignment and enqueues them in it. A requestEntity can be for example a User or a Group.
+     *
+     * @param assignment The assignment.
+     * @param entity     The requestEntity.
+     * @return An optional with the lab if the user was enqueued, and an empty optional otherwise.
+     */
+    public Optional<Lab> randomEnqueue(Assignment assignment, RequestEntity entity) {
+
+		logger.error("******** Entering random enqueue ******");
+
+		List<Lab> labsWithAssignment = labRepository.findAll()
+                .stream()
+                .filter(l -> l.getAssignments().contains(assignment)).collect(Collectors.toList());
+
+		logger.error("** labs with assignment: {}", labsWithAssignment.size());
+		
+        List<Lab> labsWithSlot = labsWithAssignment.stream()
+                .filter(l ->
+                        getIntervalsForLab(l)
+                                .stream()
+                                .anyMatch(l::slotIsAvailable)).collect(Collectors.toList());
+
+        logger.error("** labs with assignment & slots: {}", labsWithSlot.size());
+        
+        List<Lab> labs =
+                labsWithSlot.stream().filter(l -> l.entityAllowedForThisLab(entity)).collect(Collectors.toList());
+
+        logger.error("** labs with assignment, slots and allowed: {}", labs.size());
+
+        Random r = new Random();
+        int labSize = labs.size();
+        if (labSize > 0) {
+            Optional<Lab> lab = Optional.ofNullable(labs.get(r.nextInt(labSize)));
+
+            if (lab.isPresent()) {
+
+                Optional<Request> prevReq = lab.get().getPendingRequest(entity);
+                if (prevReq.isPresent()) {
+                    if (prevReq.get().getAssignment().equals(assignment)) {
+                        requestService.revoke(prevReq.get());
+                    }
+                }
+
+                List<RequestSlot> slots = getIntervalsForLab(lab.get()).stream().filter
+                        (lab.get()::slotIsAvailable).collect(Collectors.toList());
+
+                Optional<RequestSlot> slot = Optional.ofNullable(slots.get(r.nextInt(slots.size())));
+
+                if (slot.isPresent()) {
+                    logger.error("Slot found");
+
+                    Request request = new Request(entity, assignment, lab.get().getRooms().get(0),
+                            requestTypeRepository.findByName("Submission"), "", lab.get(), slot.get());
+
+                    slot.get().setRequest(request);
+                    logger.error("request created");
+
+                    //TODO: do we want to unenqueue a student if they are already enqueued for this timeslot?
+                    //TODO: students can potentially have multiple assignments they can sign of in a lab -
+                    // then they should also be able to enqueue for multiple slots?
+                    enqueue(request);
+                    logger.error("request enqueued");
+
+
+                if (entity instanceof User) {
+                    EmailHelper.getInstance().notifyStudentOfTimeSlot((User) entity,
+                            slot.get(), assignment);
+                } else if (entity instanceof Group) {
+                    EmailHelper.getInstance().notifyGroupOfTimeSlot((Group) entity,
+                            slot.get(), assignment);
+                }
+
+                    return lab;
+                } else {
+                    logger.error("There were no free slots.");
+                }
+			} else {
+				logger.error("Lab not present");
+			}
+        } else {
+            logger.error("The mentorgroup this student belongs to has no labs.");
+        }
+        return Optional.empty();
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/NotificationService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/NotificationService.java
new file mode 100644
index 000000000..a1c3d4076
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/NotificationService.java
@@ -0,0 +1,83 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import nl.martijndwars.webpush.PushService;
+import nl.tudelft.ewi.queue.factory.NotificationFactory;
+import nl.tudelft.ewi.queue.model.Notification;
+import nl.tudelft.ewi.queue.model.Request;
+import nl.tudelft.ewi.queue.model.Subscription;
+import nl.tudelft.ewi.queue.model.User;
+import org.hibernate.Hibernate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessageSendingOperations;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+
+@Service
+public class NotificationService {
+
+    @Autowired
+    private SimpMessageSendingOperations messagingTemplate;
+
+    @Autowired
+    private PushService pushService;
+
+    @Autowired
+    private NotificationFactory notificationFactory;
+
+    @Async
+    public void sendPushNotification(User receiver, Notification notification) {
+        // Update UI through WebSocket
+        HashMap<String, Object> pushNotification = new HashMap<>();
+        pushNotification.put("notification", notification);
+
+        sendWebSocketMessage(receiver, pushNotification);
+
+        // Send push notification
+        Subscription subscription = receiver.getSubscription();
+
+        if (null != subscription) {
+            try {
+                pushService.sendAsync(notificationFactory.fromSubscription(subscription, notification.toJSON().getBytes()));
+            } catch (Exception e ) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void sendRequestTableUpdateForAllAssistants(Request request) {
+        for(User assistant : request.getLab().getCourse().getPrivileged()) {
+            sendRequestTableUpdate(assistant, request);
+        }
+    }
+
+    @Async
+    public void sendRequestTableUpdate(User assistant, Request request) {
+        Hibernate.initialize(request);
+        HashMap<String, Object> requestTableUpdate = new HashMap<>();
+        requestTableUpdate.put("requesttable", request.toHashMap());
+        sendWebSocketMessage(assistant, requestTableUpdate);
+    }
+
+    private void sendWebSocketMessage(User receiver, HashMap<String, Object> message) {
+        messagingTemplate.convertAndSendToUser(receiver.getUsername(), "/queue/notifications", message);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/PermissionService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/PermissionService.java
new file mode 100644
index 000000000..63103d3b7
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/PermissionService.java
@@ -0,0 +1,533 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.NotificationRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.RoleRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.EntityNotFoundException;
+
+@Service
+public class PermissionService {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private RoleRepository roleRepository;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
+    private LabRepository labRepository;
+
+    @Autowired
+    private NotificationRepository notificationRepository;
+
+    @Autowired
+    private RequestRepository requestRepository;
+
+    public boolean isAdmin(Object principal) {
+        User user = getUser(principal);
+        return user.isAdmin();
+    }
+
+    public boolean canViewCourse(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.isAdmin() ||
+               (user.participates(course) && !course.getIsArchived()) ||
+               user.assists(course) ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    /**
+     * Students, assistants and managers can always leave a course. A teacher
+     * can leave the course if he is not the last teacher.
+     */
+    public boolean canLeaveCourse(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.participates(course) ||
+               user.assists(course) ||
+               user.manages(course) ||
+               (user.teaches(course) && course.getTeachers().size() > 1);
+    }
+
+    public boolean canManageParticipants(Object principal, Long courseId) {
+        User user = getUser(principal);
+
+        Course course = getCourse(courseId);
+
+        return user.isAdmin() ||
+               user.teaches(course) ||
+               user.manages(course);
+    }
+
+    public boolean canManageTeachers(Object principal, Long courseId) {
+        User user = getUser(principal);
+
+        Course course = getCourse(courseId);
+
+        return user.isAdmin() ||
+               user.teaches(course);
+    }
+
+    public boolean canUpdateParticipant(Object principal, Long courseId, Long participantId) {
+        User user = getUser(principal);
+        User participant = getRole(participantId).getUser();
+
+        Course course = getCourse(courseId);
+
+        return user.isAdmin() ||
+               user.teaches(course) ||
+               user.manages(course) && !participant.teaches(course) ||
+               participant.getId().equals(user.getId());
+    }
+
+    public boolean canAddParticipant(Object principal, Long courseId, String participantRole) {
+        User user = getUser(principal);
+
+        Course course = getCourse(courseId);
+
+        return user.isAdmin() ||
+               user.teaches(course) ||
+               user.manages(course) && !"teacher".equals(participantRole);
+    }
+
+    public boolean canManageAssignments(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.isAdmin() ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    public boolean canEditCourse(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.isAdmin() ||
+               user.teaches(course);
+    }
+
+    public boolean canRemoveCourse(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.isAdmin() ||
+               user.teaches(course);
+    }
+
+    public boolean canEnrollCourse(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return !user.participates(course) &&
+               !user.assists(course) &&
+               !user.manages(course) &&
+               !user.teaches(course) &&
+               !course.getIsArchived();
+    }
+
+    public boolean canViewLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.isAdmin() ||
+               user.participates(course) ||
+               user.assists(course) ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    /**
+     * A user can enqueue for a lab if:
+     * - he is enrolled for the lab's course (i.e. 'participates')
+     * - the lab is currently open
+     * - the lab is not a feedback lab
+     * - all his requests are archived (i.e. no pending/processing requests)
+     * - the lab allows all students or the student is in the right mentor group
+     */
+    public boolean canEnqueueSelfForLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+        if (!user.participates(course)) {
+            return false;
+        }
+        if (lab.isBeingProcessed(user)) {
+            return false;
+        }
+        if (!lab.isOpenOrSlotSelection()) {
+            return false;
+        }
+        if (!lab.allAllowed()) {
+            if (!lab.userAllowedForThisLab(user)) {
+                return false;
+            }
+        }
+        if (lab.isFeedbackLab()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks whether a user can enqueue others for a lab.
+     *
+     * @param principal The user who wants to enqueue others in a lab.
+     * @param id        The lab id.
+     * @return True if the user can enqueue others in a lab.
+     */
+    public boolean canEnqueueOthersForLab(Object principal, Long id) {
+        User user = getUser(principal);
+        Lab lab = getLab(id);
+        Course course = lab.getCourse();
+        return user.isAdmin() || user.manages(course) || user.teaches(course) || user.assists(course);
+    }
+
+    /**
+     * Checks whether a user can unenroll themselves from a lab.
+     *
+     * @param principal The user.
+     * @param id        The lab ID.
+     * @return True if the user can unenroll themselves.
+     */
+    public boolean canUnenqueueSelfFromLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.participates(course);
+    }
+
+    /**
+     * Checks whether a user can unenroll others from a lab.
+     *
+     * @param principal The user.
+     * @param id        The lab ID.
+     * @return True if the user can unenroll others.
+     */
+    public boolean canUnenqueuOthersFromLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.manages(course) || user.teaches(course) || user.assists(course) || user.isAdmin();
+    }
+
+    /**
+     * A user can get the next request for the given lab if he assists or
+     * teaches the course.
+     *
+     * @param principal
+     * @param id
+     * @return
+     */
+    public boolean canNextRequest(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.assists(course) ||
+               user.teaches(course);
+    }
+
+    /**
+     * A user can access next request page if he is an assistant, manager, or
+     * teacher in *any* course.
+     *
+     * @param principal
+     * @return
+     */
+    public boolean canNextRequest(Object principal) {
+        User user = getUser(principal);
+
+        return
+                user.isAdmin() ||
+                user.getRoles().stream()
+                        .anyMatch(r -> r instanceof Assistant || r instanceof Manager || r instanceof Teacher);
+    }
+
+    public boolean canViewRequests(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.isAdmin() ||
+               user.assists(course) ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    /**
+     * A user can view the requests page if it is an admin (default role),
+     * teacher (contextual role) or assistant (contextual role).
+     *
+     * @param principal
+     * @return
+     */
+    public boolean canViewRequests(Object principal) {
+        User user = getUser(principal);
+
+        return
+                user.isAdmin() ||
+                user.getRoles().stream()
+                        .anyMatch(r -> r instanceof Assistant || r instanceof Manager || r instanceof Teacher);
+    }
+
+    public boolean canCreateLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Course course = getCourse(id);
+
+        return user.isAdmin() ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    public boolean canViewRoomLayout(Object principal, Long id) {
+        User user = getUser(principal);
+
+        return user.isUser();
+    }
+
+    public boolean canEditLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.isAdmin() ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    public boolean canRemoveLab(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.isAdmin() ||
+                user.manages(course) ||
+                user.teaches(course);
+    }
+
+    /**
+     * Shows whether a user can enroll/unenroll students from lab slots.
+     *
+     * @param principal The user whose permissions are to be determined.
+     * @param id        The id of the lab.
+     * @return True if the user has permission to manage the lab slots.
+     */
+    public boolean canManageLabSlots(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Lab lab = getLab(id);
+
+        Course course = lab.getCourse();
+
+        return user.isAdmin() ||
+               user.manages(course) ||
+               user.teaches(course);
+    }
+
+    public boolean canViewNotification(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Notification notification = getNotification(id);
+
+        return notification.getUser().equals(user);
+    }
+
+    /**
+     * A user can view a request if:
+     * - the request is in a course that the user assists
+     * - the request is in a course that the user teaches
+     * - it's his/her own request
+     */
+    public boolean canViewRequest(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Request request = getRequest(id);
+
+        Course course = request.getLab().getCourse();
+
+        return request.getRequestEntity().equals(user) ||
+               request.getRequestEntity().equals(request.getLab().getEntityFor(user)) ||
+               user.assists(course) ||
+               user.manages(course) ||
+               user.teaches(course) ||
+                user.isAdmin();
+    }
+
+    /**
+     * User may view feedback if user is teacher or the feedback is about the user itself.
+     */
+    public boolean canViewFeedback(Object principal, Long id) {
+        User user = getUser(principal);
+
+        return user.isAdmin() || user.isTeacher() || user.getId().equals(id);
+    }
+
+    public boolean canViewDeanonimizedFeedback(Object principal, Long id) {
+        User user = getUser(principal);
+
+        return user.isAdmin() || user.isTeacher();
+    }
+
+    public boolean canViewOwnFeedback(Object principal, Long id) {
+        User user = getUser(principal);
+        Course course = getCourse(id);
+
+        return user.assists(course) || user.manages(course) || user.teaches(course);
+    }
+
+    /**
+     * A user can view a request reason if:
+     * - the request is in a course that the user assists
+     * - the request is in a course that the user teaches
+     */
+    public boolean canViewRequestReason(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Request request = getRequest(id);
+
+        Course course = request.getLab().getCourse();
+
+        return user.assists(course) ||
+               user.manages(course) ||
+               user.teaches(course) ||
+				user.isAdmin();
+    }
+
+    /**
+     * A user can finish (i.e. approve/reject/forward) a request if:
+     * - the user is the assigned assistant for the request
+     * - the user manages the course and the request is already handled
+     * - the user teaches the course
+     * - the user is actually admin
+     */
+    public boolean canFinishRequest(Object principal, Long id) {
+        User user = getUser(principal);
+
+        Request request = getRequest(id);
+
+        Course course = request.getLab().getCourse();
+
+        return ((request.getAssistant() != null && request.getAssistant().equals(user))) ||
+               (user.manages(course) && request.isHandled()) ||
+               user.teaches(course) ||
+				user.isAdmin();
+    }
+
+    protected User getUser(Object principal) {
+        if (!(principal instanceof UserPrincipal)) {
+            throw new EntityNotFoundException("User was not found");
+        }
+
+        UserPrincipal userPrincipal = (UserPrincipal) principal;
+
+        return userRepository.findOne(userPrincipal.getUser().getId());
+    }
+
+    protected Course getCourse(Long id) {
+        Course course = courseRepository.findOne(id);
+
+        if (null == course) {
+            throw new EntityNotFoundException("Course was not found: " + id);
+        }
+
+        return course;
+    }
+
+    protected Lab getLab(Long id) {
+        Lab lab = labRepository.findOne(id);
+
+        if (null == lab) {
+            throw new EntityNotFoundException("Lab was not found: " + id);
+        }
+
+        return lab;
+    }
+
+    protected Role getRole(Long id) {
+        Role role = roleRepository.findOne(id);
+
+        if (null == role) {
+            throw new EntityNotFoundException("Role was not found");
+        }
+
+        return role;
+    }
+
+    protected Notification getNotification(Long id) {
+        Notification notification = notificationRepository.findOne(id);
+
+        if (null == notification) {
+            throw new EntityNotFoundException("Notification was not found: " + id);
+        }
+
+        return notification;
+    }
+
+    protected Request getRequest(Long id) {
+        Request request = requestRepository.findOne(id);
+
+        if (null == request) {
+            throw new EntityNotFoundException("Request was not found: " + id);
+        }
+
+        return request;
+    }
+}
+
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/RequestService.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/RequestService.java
new file mode 100644
index 000000000..13086a207
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/RequestService.java
@@ -0,0 +1,322 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.service;
+
+import com.google.common.collect.Lists;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.NotificationRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
+import java.util.*;
+
+@Service
+public class RequestService {
+
+    private static final String netIdConstant = "{netid}";
+
+    @Autowired
+    RequestRepository requestRepository;
+
+    @Autowired
+    private NotificationRepository notificationRepository;
+
+    @Autowired
+    private NotificationService notificationService;
+
+    /**
+     * Get the next request for this assistant/teacher from a lab.
+     *
+     * @param assistant
+     * @return
+     */
+    public synchronized Optional<Request> next(User assistant, Lab currentLab, Predicate tableFilters) {
+        // first check if the assistant already has a request that is processing
+        // and should be handled first
+        Optional<Request> processingReq = getProcessingRequest(assistant, currentLab);
+         // next, check if this assistant has a request that is already assigned to them
+        Optional<Request> assignedReq = getAssignedRequest(assistant, currentLab);
+
+        Request request;
+        if(processingReq.isPresent()) {
+            request = processingReq.get();
+        } else if (assignedReq.isPresent()) {
+            request = assignedReq.get();
+        } else {
+            // current assistant has neither a request already processing or assigned
+            // so find in the pending queue something suitable
+            request = findNextRequestFromQueue(currentLab, assistant, tableFilters);
+        }
+
+        if (request != null) {
+            assignRequest(request);
+            notifyNewRequest(request, assistant);
+            return Optional.of(request);
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    private Request findNextRequestFromQueue(Lab currentLab, User assistant, Predicate tableFilters) {
+        Request request = null;
+        List<Request> pendingRequests;
+        if (currentLab.getSignOffIntervals()) {
+            pendingRequests = getPendingRequestsForLabWithIntervals(currentLab, assistant, tableFilters);
+        } else {
+            pendingRequests = getPendingRequests(currentLab, assistant, tableFilters);
+        }
+        if (!pendingRequests.isEmpty()) {
+            request = pendingRequests.get(0);
+            request.addEvent(new RequestAssignedEvent(request, LocalDateTime.now(), assistant));
+        }
+        return request;
+    }
+
+    private Optional<Request> getProcessingRequest(User assistant, Lab currentLab) {
+        return currentLab.getProcessing().stream()
+                .filter(request ->
+                        request.getAssistant() != null &&
+                                request.getAssistant().getId().equals(assistant.getId()))
+                .findFirst();
+    }
+
+    private Optional<Request> getAssignedRequest(User assistant, Lab currentLab) {
+        return currentLab.getQueue().stream()
+                .filter(Request::isAssigned)
+                .filter(request ->
+                        request.getAssistant() != null &&
+                                request.getAssistant().getId().equals(assistant.getId()))
+                .findFirst();
+    }
+
+    private List<Request> getPendingRequests(Lab currentLab, User assistant, Predicate tableFilters) {
+        QRequest qRequest = QRequest.request;
+
+        Iterator<Request> pendingReq = requestRepository
+                .findAll(getDefaultQueuePredicates(qRequest, currentLab, assistant, tableFilters))
+                .iterator();
+
+        return Lists.newArrayList(pendingReq);
+    }
+
+    private List<Request> getPendingRequestsForLabWithIntervals(Lab currentLab, User assistant, Predicate tableFilters) {
+        QRequest qRequest = QRequest.request;
+        LocalDateTime interval = currentLab.getAcceptInterval();
+        BooleanExpression allRequestsThatAreAlreadyOpenOrOpenWithinTenMinutes = qRequest.slot.opensAt.before(interval);
+        Iterator<Request> pendingReq = requestRepository
+                .findAll(getDefaultQueuePredicates(qRequest, currentLab, assistant, tableFilters)
+                        .and(allRequestsThatAreAlreadyOpenOrOpenWithinTenMinutes))
+                .iterator();
+        List<Request> requestList = Lists.newArrayList(pendingReq);
+        Collections.sort(requestList);
+        return requestList;
+    }
+
+    private BooleanExpression getDefaultQueuePredicates(QRequest qRequest, Lab currentLab, User assistant, Predicate tableFilters) {
+        BooleanExpression requestForCurrentLab = qRequest.lab.id.eq(currentLab.getId());
+        // Check if the assigned in the history was already assigned to this assistant
+        // If so, they forwarded it to any and they do not want it anymore
+        BooleanExpression requestNotAlreadyAssignedTo = qRequest.assistant.isNull().or(qRequest.assistant.id.ne(assistant.getId()));
+        BooleanExpression pendingRequests = qRequest.status.eq(Request.Status.PENDING);
+        return requestForCurrentLab.and(pendingRequests).and(tableFilters).and(requestNotAlreadyAssignedTo);
+    }
+
+    private void assignRequest(Request request) {
+        request.addEvent(new RequestProcessingEvent(request, LocalDateTime.now()));
+        requestRepository.save(request);
+        notificationService.sendRequestTableUpdateForAllAssistants(request);
+    }
+
+    private void notifyNewRequest(Request request, User assistant) {
+        RequestEntity entity = request.getRequestEntity();
+        if (entity instanceof User) {
+            User student = (User)entity;
+            Notification notification = createNotification(request.getLab(), assistant, student);
+
+            notificationRepository.save(notification);
+            notificationService.sendPushNotification(student, notification);
+        }
+    }
+
+    /**
+     * Create notification depending on the lab's direction
+     *
+     * @param lab
+     * @param assistant
+     * @param student
+     */
+    public static Notification createNotification(Lab lab, User assistant, User student) {
+        switch (lab.getDirection()) {
+            case STUDENT_VISIT_TA:
+                return new Notification(student, lab.getCourse(), "Please visit TA", "Please visit assistant '" + assistant.getDisplayName() + "'.", 100);
+            case TA_VISIT_STUDENT:
+                return new Notification(student, lab.getCourse(), "Assistant incoming", "Assistant '" + assistant.getDisplayName() + "' is on its way!", 100);
+        }
+
+        throw new IllegalStateException("Cannot create a notification for a lab with direction " + lab.getDirection());
+    }
+
+    /**
+     * Revoke a request
+     *
+     * @param request
+     */
+    public void revoke(Request request) {
+        if (!request.isQueued()) {
+            throw new IllegalStateException("Unable to revoke a request that is not in pending state.");
+        }
+
+        request.addEvent(new RequestRevokedEvent(request, LocalDateTime.now()));
+        requestRepository.save(request);
+        notificationService.sendRequestTableUpdateForAllAssistants(request);
+    }
+
+    /**
+     * Remove request from lab's queue and assign the assistant.
+     *
+     * If the request was not yet taken by anybody, add request processing event as well.
+     * @param assistant The assistant that approved the request
+     * @param request The request which will be approved and assigned a processing event if not yet present.
+     */
+    public void approve(User assistant, Request request, String comment) {
+        if (!assistant.equals(request.getAssistant()) && !(assistant.teaches(request.getAssignment().getCourse()) || assistant.isAdmin())) {
+            throw new IllegalArgumentException("You cannot approve a request that is not assigned to you.");
+        }
+        if (request.getEvents().stream().noneMatch(event -> event instanceof RequestProcessingEvent)) {
+            request.addEvent(new RequestProcessingEvent(request,LocalDateTime.now()));
+        }
+        request.addEvent(new RequestApprovedEvent(request, assistant, LocalDateTime.now(), comment));
+
+        requestRepository.save(request);
+        notificationService.sendRequestTableUpdateForAllAssistants(request);
+    }
+
+    /**
+     * Remove request from lab's queue and assign the assistant.
+     *
+     * @param assistant
+     * @param request
+     */
+    public void reject(User assistant, Request request, String comment, String commentForStudent) {
+        if (!assistant.equals(request.getAssistant()) && !(assistant.teaches(request.getAssignment().getCourse()) || assistant.isAdmin())) {
+            throw new IllegalArgumentException("You cannot reject a request that is not assigned to you.");
+        }
+        request.addEvent(new RequestRejectedEvent(request, assistant,LocalDateTime.now(), comment, commentForStudent));
+
+        requestRepository.save(request);
+        notificationService.sendRequestTableUpdateForAllAssistants(request);
+    }
+
+    /**
+     * Forward the request to another assistant.
+     *
+     * @param request
+     * @param assistant
+     */
+    public void forward(Request request, User assistant, String comment) {
+        if (!request.getLab().getCourse().getPrivileged().contains(assistant)) {
+            throw new IllegalArgumentException("Can only forward to an assistant for this course.");
+        }
+
+        if (request.getAssistant().equals(assistant)) {
+            throw new IllegalArgumentException("You cannot forward a request to yourself.");
+        }
+
+        // Forward the request, add forwarded event and assign to new TA.
+        request.addEvent(new RequestForwardedEvent(request, LocalDateTime.now(), assistant, comment));
+        request.addEvent(new RequestAssignedEvent(request, LocalDateTime.now(), assistant));
+
+        requestRepository.save(request);
+        notificationService.sendRequestTableUpdateForAllAssistants(request);
+    }
+
+    public void forwardToAny(Request request, String reason) {
+        request.addEvent(new RequestForwardToAnyEvent(request, LocalDateTime.now(), reason));
+        requestRepository.save(request);
+    }
+
+    public void notFound(Request request, User assistant) {
+        request.addEvent(new StudentNotFoundEvent(request, LocalDateTime.now(), assistant));
+        requestRepository.save(request);
+
+    }
+
+    public String setSubmissionUrl(Request request) {
+        RequestEntity requestEntity = request.getRequestEntity();
+        if (requestEntity instanceof User) {
+            User student = (User)requestEntity;
+            String templateSubmissionUrl = request.getLab().getCourse().getSubmissionUrl();
+            if (templateSubmissionUrl != null && !templateSubmissionUrl.isEmpty()) {
+                templateSubmissionUrl = templateSubmissionUrl.replace(netIdConstant, student.getUsername());
+                return templateSubmissionUrl;
+            }
+        }
+        return "";
+    }
+
+    public String redirectToRequestWithParams(RedirectAttributes redirectAttributes, MultiValueMap<String, String> parameters) {
+        // redirect to apply these params to the predicate so the table is actually filtered
+        updateRedirectAttributesNoSquash(redirectAttributes, parameters);
+        return "redirect:/requests";
+    }
+
+    public void updateRedirectAttributes(RedirectAttributes redirectAttributes,
+                                         MultiValueMap<String, String> parameters) {
+        for(Map.Entry<String, List<String>> entry : parameters.entrySet()){
+            for(String value : entry.getValue()) {
+                redirectAttributes.addAttribute(entry.getKey(), value);
+            }
+        }
+    }
+
+    /**
+     * Update the redirect attributes without squashing lists into a string.
+     * NOTE: This method relies on a flaw in the supporting library, and as such may well break
+     * in the future.
+     *
+     * @param redirectAttributes The attributes to be updated.
+     * @param parameters         The values with which to update the attributes.
+     */
+    public void updateRedirectAttributesNoSquash(RedirectAttributes redirectAttributes,
+                                                 MultiValueMap<String, String> parameters) {
+        for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
+
+
+            //noinspection unchecked
+            ((HashMap<String, Object>) redirectAttributes).putIfAbsent(entry.getKey(),
+                    entry.getValue());
+        }
+    }
+
+
+    public void storeParamsInSession(HttpServletRequest httpRequest, MultiValueMap<String, String> parameters, String key) {
+        httpRequest.getSession().setAttribute(key, parameters);
+    }
+
+    public void clearFilters(HttpServletRequest httpRequest, MultiValueMap<String, String> parameters) {
+        httpRequest.getSession().removeAttribute("filters");
+        parameters.remove("clear"); // clearFilters is also part of the params
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/service/SAMLUserDetailsServiceImpl.java b/backend/src/main/java/nl/tudelft/ewi/queue/service/SAMLUserDetailsServiceImpl.java
new file mode 100644
index 000000000..b62cfc532
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/service/SAMLUserDetailsServiceImpl.java
@@ -0,0 +1,172 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package nl.tudelft.ewi.queue.service;
+
+import nl.tudelft.ewi.queue.model.DefaultRole;
+import nl.tudelft.ewi.queue.model.FirstYearStudent;
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.model.UserPrincipal;
+import nl.tudelft.ewi.queue.repository.FirstYearStudentRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.xml.XMLObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.saml.SAMLCredential;
+import org.springframework.security.saml.userdetails.SAMLUserDetailsService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
+
+	private static final String DISPLAY_NAME = "urn:mace:dir:attribute-def:displayName";
+	private static final String MAIL = "mail";
+	private static final String EDU_PERSON_AFFILIATION = "urn:mace:dir:attribute-def:eduPersonAffiliation";
+	private static final String TUD_STUDENT_NUMBER = "tudStudentNumber";
+
+	private static final Logger logger = LoggerFactory.getLogger(SAMLUserDetailsServiceImpl.class);
+
+	@Autowired
+	private UserRepository userRepository;
+
+	@Autowired
+	private FirstYearStudentRepository firstYearStudentRepository;
+
+	/**
+	 * Load a user. If the user does not exist, it is implicitly created.
+	 *
+	 * @param credential
+	 * @return
+	 * @throws UsernameNotFoundException
+	 */
+	@Override
+	public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
+		String nameId = credential.getNameID().getValue();
+		User user = userRepository.findByUsername(nameId);
+
+		if (null == user) {
+			logger.info("Implicitly create user " + nameId);
+			user = createUser(nameId, credential);
+			saveUserWithCheck(user);
+		}
+
+		logger.info(nameId + " is logged in");
+
+		logger.info("SAML credentials: " + credential.toString());
+		logger.info("SAML credentials details:\n");
+		for (Attribute attribute : credential.getAttributes()) {
+			logger.info("*** attribute: " + attribute.getName() + " (" + attribute.getFriendlyName() + ")");
+			logger.info("****** value: " + credential.getAttributeAsString(attribute.getName()));
+			for (XMLObject value : attribute.getAttributeValues()) {
+				logger.debug("****** raw value:" + value.toString());
+			}
+		}
+
+		String email = credential.getAttributeAsString(MAIL);
+		String displayName = credential.getAttributeAsString(DISPLAY_NAME);
+		if (!email.equals(user.getEmail())) {
+			logger.info(String.format(
+					"User with netid %s changed email from %s to %s",
+					nameId, user.getEmail(), email));
+			user.setEmail(email);
+			saveUserWithCheck(user);
+		}
+		if (!displayName.equals(user.getDisplayName())) {
+			logger.info(String.format(
+					"User with netid %s changed display name from %s to %s",
+					nameId, user.getDisplayName(), displayName));
+			user.setDisplayName(displayName);
+			saveUserWithCheck(user);
+		}
+
+		AttachUserToFirstYearStudent(user);
+		return new UserPrincipal(user);
+	}
+
+	/**
+	 * Saves the user to the user repository and if this fails, remove all
+	 * the accents in the name and try again.
+	 * @param user The user which needs to be saved.
+	 */
+	private void saveUserWithCheck(User user) {
+		try {
+			userRepository.save(user);
+		} catch (Exception e) {
+			String newName = StringUtils.stripAccents(user.getDisplayName());
+			user.setDisplayName(newName);
+			userRepository.save(user);
+		}
+	}
+
+	private void AttachUserToFirstYearStudent(User user) {
+		List<FirstYearStudent> firstYearStudents = firstYearStudentRepository.findByNetId(user.getUsername());
+		if (firstYearStudents != null && !firstYearStudents.isEmpty()) {
+			for (FirstYearStudent firstYearStudent : firstYearStudents) {
+				if (firstYearStudent != null && firstYearStudent.getUser() == null) {
+					firstYearStudent.setUser(user);
+					firstYearStudentRepository.save(firstYearStudent);
+				}
+			}
+		}
+		firstYearStudents = firstYearStudentRepository.findByNetId(String.valueOf(user.getStudentNumber()));
+		if (firstYearStudents != null && !firstYearStudents.isEmpty()) {
+			for (FirstYearStudent firstYearStudent : firstYearStudents) {
+				if (firstYearStudent != null && firstYearStudent.getUser() == null) {
+					firstYearStudent.setUser(user);
+					firstYearStudentRepository.save(firstYearStudent);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Create a new user.
+	 *
+	 * @param username
+	 * @param credential
+	 * @return a new user
+	 */
+	private static User createUser(String username, SAMLCredential credential) {
+		DefaultRole role = DefaultRole.ROLE_STUDENT;
+		String displayName = credential.getAttributeAsString(DISPLAY_NAME);
+		String email = credential.getAttributeAsString(MAIL);
+		int studentNumber = -1;
+		String roleAttribute = credential.getAttributeAsString(EDU_PERSON_AFFILIATION);
+
+		if (roleAttribute == null) {
+			logger.warn("SAML User signed in with null role:\n");
+			logger.warn("****** username: " + displayName);
+		} else {
+			if (roleAttribute.equals("employee")) {
+				role = DefaultRole.ROLE_TEACHER;
+			} else if (roleAttribute.equals("student")) {
+				studentNumber = Integer.parseUnsignedInt(credential.getAttributeAsString(TUD_STUDENT_NUMBER));
+			}
+		}
+
+		return new User(username, "", displayName, email, role, studentNumber);
+	}
+}
+
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/tasks/RequestTasks.java b/backend/src/main/java/nl/tudelft/ewi/queue/tasks/RequestTasks.java
new file mode 100644
index 000000000..e7c13263b
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/tasks/RequestTasks.java
@@ -0,0 +1,64 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.tasks;
+
+import com.querydsl.core.types.dsl.BooleanExpression;
+import nl.tudelft.ewi.queue.model.QRequest;
+import nl.tudelft.ewi.queue.model.Request;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.service.LabService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.transaction.Transactional;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Component
+public class RequestTasks {
+
+    private static final Logger log = LoggerFactory.getLogger(RequestTasks.class);
+
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
+
+
+    @Autowired
+    private RequestRepository requestRepository;
+
+    @Autowired
+    private LabService labService;
+
+    @Scheduled(cron = "0 0/5 * * * ?")
+    @Transactional
+    public void checkForUpcomingRequestsSlot() {
+        log.info("The time is now {}", dateFormat.format(new Date()));
+        QRequest qRequest = QRequest.request;
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime tenMinutes = now.plusMinutes(10L);
+        BooleanExpression withinTenMinutes = qRequest.slot.opensAt.before(tenMinutes);
+        BooleanExpression pendingRequests = qRequest.status.eq(Request.Status.PENDING);
+        Iterable<Request> requestList = requestRepository.findAll(pendingRequests.and(withinTenMinutes));
+        for(Request request : requestList) {
+            labService.createAndSendNotifications(request);
+        }
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/validator/LabValidator.java b/backend/src/main/java/nl/tudelft/ewi/queue/validator/LabValidator.java
new file mode 100644
index 000000000..fe1320223
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/validator/LabValidator.java
@@ -0,0 +1,58 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.validator;
+
+import nl.tudelft.ewi.queue.model.Lab;
+import nl.tudelft.ewi.queue.model.RequestType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.Errors;
+import org.springframework.validation.Validator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Component
+public class LabValidator implements Validator {
+    @Autowired
+    private SlotValidator slotValidator;
+
+    @Override
+    public boolean supports(Class<?> clazz) {
+        return Lab.class.isAssignableFrom(clazz);
+    }
+
+    @Override
+    public void validate(Object target, Errors errors) {
+        Lab targetLab = (Lab) target;
+
+        // Check request types for at least one valid pointer.
+        errors.pushNestedPath("requesttype");
+        List<RequestType> allowedRequestTypes = new ArrayList<>(targetLab.getAllowedRequestTypes());
+        allowedRequestTypes.removeAll(Collections.singleton(null));
+        if(allowedRequestTypes.isEmpty()) {
+            errors.reject("At least one Request type must be selected");
+        }
+        errors.popNestedPath();
+
+        errors.pushNestedPath("slot");
+        slotValidator.validate(targetLab.getSlot(), errors);
+        errors.popNestedPath();
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/validator/ParticipantValidator.java b/backend/src/main/java/nl/tudelft/ewi/queue/validator/ParticipantValidator.java
new file mode 100644
index 000000000..ea750cc09
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/validator/ParticipantValidator.java
@@ -0,0 +1,68 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.validator;
+
+import nl.tudelft.ewi.queue.forms.ParticipantForm;
+import nl.tudelft.ewi.queue.model.Course;
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.Errors;
+import org.springframework.validation.Validator;
+
+@Component
+public class ParticipantValidator implements Validator {
+    @Autowired
+    CourseRepository courseRepository;
+
+    @Autowired
+    UserRepository userRepository;
+
+    @Override
+    public boolean supports(Class<?> clazz) {
+        return ParticipantForm.class.isAssignableFrom(clazz);
+    }
+
+    @Override
+    public void validate(Object target, Errors errors) {
+        ParticipantForm participantForm = (ParticipantForm) target;
+
+        String username = User.guarantueeValidNetId(participantForm.getUsername());
+        User user = getUser(username);
+
+        if (user == null) {
+            errors.rejectValue("username", "user.username.notFound");
+        } else {
+            Course course = getCourse(participantForm.getCourseId());
+
+            if (user.participates(course) || user.assists(course) || user.teaches(course)) {
+                errors.rejectValue("username", "user.username.participates");
+            }
+        }
+    }
+
+    protected User getUser(String username) {
+        return userRepository.findByUsername(username);
+    }
+
+    protected Course getCourse(long courseId) {
+        return courseRepository.findOne(courseId);
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/validator/SlotValidator.java b/backend/src/main/java/nl/tudelft/ewi/queue/validator/SlotValidator.java
new file mode 100644
index 000000000..4617e5df9
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/validator/SlotValidator.java
@@ -0,0 +1,42 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.validator;
+
+import nl.tudelft.ewi.queue.model.Slot;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.Errors;
+import org.springframework.validation.Validator;
+
+@Component
+public class SlotValidator implements Validator {
+    @Override
+    public boolean supports(Class<?> clazz) {
+        return Slot.class.isAssignableFrom(clazz);
+    }
+
+    @Override
+    public void validate(Object target, Errors errors) {
+        Slot slot = (Slot) target;
+
+        if (slot.getClosesAt() != null && slot.getOpensAt() != null) {
+            if (!slot.getClosesAt().isAfter(slot.getOpensAt())) {
+                errors.rejectValue("opensAt", "queue.slot.order");
+            }
+        }
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/viewmodel/AssignmentViewModel.java b/backend/src/main/java/nl/tudelft/ewi/queue/viewmodel/AssignmentViewModel.java
new file mode 100644
index 000000000..ce65186da
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/viewmodel/AssignmentViewModel.java
@@ -0,0 +1,70 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.viewmodel;
+
+import nl.tudelft.ewi.queue.model.Assignment;
+
+public class AssignmentViewModel {
+
+    private String name;
+    private String assignmentID;
+    private String verification;
+    private boolean hasLab = false;
+
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAssignmentID() {
+        return assignmentID;
+    }
+
+    public String getVerification() {
+        return this.verification;
+    }
+
+    public void setAssignmentID(String assignmentID) {
+        this.assignmentID = assignmentID;
+    }
+
+    public boolean hasLab() {
+        return hasLab;
+    }
+
+    public static AssignmentViewModel convert(Assignment assignment) {
+        AssignmentViewModel viewModel = new AssignmentViewModel();
+        viewModel.name = assignment.getName();
+        viewModel.assignmentID = assignment.getId().toString();
+        viewModel.verification = assignment.getVerification();
+        if(assignment.getLabs().size() > 0) {
+            viewModel.hasLab = true;
+        }
+        return viewModel;
+    }
+
+    public Assignment convertToNew() {
+        Assignment assignment = new Assignment();
+        assignment.setName(this.name);
+        return assignment;
+    }
+}
diff --git a/backend/src/main/java/nl/tudelft/ewi/queue/views/View.java b/backend/src/main/java/nl/tudelft/ewi/queue/views/View.java
new file mode 100644
index 000000000..089a19d47
--- /dev/null
+++ b/backend/src/main/java/nl/tudelft/ewi/queue/views/View.java
@@ -0,0 +1,22 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.views;
+
+public class View {
+    public interface Summary {}
+}
diff --git a/backend/src/main/resources/application.properties.template b/backend/src/main/resources/application.properties.template
new file mode 100644
index 000000000..957f6dce1
--- /dev/null
+++ b/backend/src/main/resources/application.properties.template
@@ -0,0 +1,66 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+# Default application properties. These can be overridden at run time from an
+# application.properties file in the same directory as the executable jar.
+
+spring.profiles=production,development
+spring.profiles.active=development
+spring.thymeleaf.mode=HTML
+spring.session.store-type=hash_map
+
+server.port=8081
+
+# Switch the default error page off which will restore the default of the servlet container that you are using
+server.error.whitelabel.enabled=false
+
+spring.jackson.serialization.write_dates_as_timestamps=false
+
+# JPA
+#spring.jpa.show-sql=true
+#spring.datasource.url = "jdbc:postgresql://${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB}?user=${POSTGRES_USER}&password=${POSTGRES_PASSWORD}&sslmode=disable"
+#spring.datasource.driverClassName=org.postgresql.Driver
+
+#In-memory h2 database
+spring.datasource.url=jdbc:h2:mem:testdb
+#For testing liquibase migrations we need a persistent h2 database
+#H2 will now just put the database in a file called testdb.mv.db in the current directory
+#spring.datasource.url=jdbc:h2:./testdb
+
+#To auto run the liquibase changelog when spring starts, beware the liquibase migration can not be run
+#on a clean in memory database, because the tables won't exist yet.
+#liquibase.change-log=classpath:changelog-master.yaml
+
+spring.jpa.hibernate.ddl-auto=validate
+
+# Push messaging
+queue.gcmApiKey=gcm
+queue.pushPublicKey=public
+queue.pushPrivateKey=private
+
+# SAML
+saml.keystore.path=classpath:/security/samlKeystore.jks
+saml.keystore.password=nalle123
+saml.keystore.alias=apollo
+saml.entityId=https://queue.ewi.tudelft.nl
+saml.entityBaseURL=http://localhost:8081
+
+# Minimum logging level
+logging.level.org.opensaml=INFO
+server.session.persistent=true
+
diff --git a/backend/src/main/resources/changeLog-h2.yaml b/backend/src/main/resources/changeLog-h2.yaml
new file mode 100644
index 000000000..233ee52f3
--- /dev/null
+++ b/backend/src/main/resources/changeLog-h2.yaml
@@ -0,0 +1,1537 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+databaseChangeLog:
+  - changeSet:
+      id: 1565603908737-1
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_5A
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: DELETED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: NAME
+                  type: VARCHAR(255)
+              - column:
+                  name: VERIFICATION
+                  type: VARCHAR(255)
+              - column:
+                  name: COURSE_ID
+                  type: BIGINT
+            tableName: ASSIGNMENT
+  - changeSet:
+      id: 1565603908737-2
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ASSIGNMENT_ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: LABS_ID
+                  type: BIGINT
+            tableName: ASSIGNMENT_LABS
+  - changeSet:
+      id: 1565603908737-3
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_7
+                  name: ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: CODE
+                  type: VARCHAR(255)
+              - column:
+                  name: DELETED_AT
+                  type: TIMESTAMP
+              - column:
+                  constraints:
+                    nullable: false
+                  name: HAS_GROUPS
+                  type: BOOLEAN
+              - column:
+                  constraints:
+                    nullable: false
+                  name: IS_ARCHIVED
+                  type: BOOLEAN
+              - column:
+                  constraints:
+                    nullable: false
+                  name: NAME
+                  type: VARCHAR(255)
+              - column:
+                  name: SUBMISSION_URL
+                  type: VARCHAR(255)
+            tableName: COURSE
+  - changeSet:
+      id: 1565603908737-4
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_E
+                  name: ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ACTIVE
+                  type: BOOLEAN
+              - column:
+                  constraints:
+                    nullable: false
+                  name: NAME
+                  type: VARCHAR(255)
+            tableName: FIRST_YEAR_MENTOR_GROUP
+  - changeSet:
+      id: 1565603908737-5
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_5C
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: NET_ID
+                  type: VARCHAR(255)
+              - column:
+                  name: MENTOR_GROUP_ID
+                  type: BIGINT
+              - column:
+                  name: user_id
+                  type: BIGINT
+            tableName: FIRST_YEAR_STUDENT
+  - changeSet:
+      id: 1565603908737-6
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_1
+                  name: ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ALLOW_WITHOUT_MENTOR_GROUP
+                  type: BOOLEAN
+              - column:
+                  name: CAPACITY
+                  type: BIGINT
+              - column:
+                  name: DELETED_AT
+                  type: TIMESTAMP
+              - column:
+                  constraints:
+                    nullable: false
+                  name: DIRECTION
+                  type: VARCHAR(255)
+              - column:
+                  name: INTERVAL_TIME
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: IS_FEEDBACK_LAB
+                  type: BOOLEAN
+              - column:
+                  constraints:
+                    nullable: false
+                  name: SIGN_OFF_INTERVALS
+                  type: BOOLEAN
+              - column:
+                  name: SLOT_SELECTION_OPENS_AT
+                  type: TIMESTAMP
+              - column:
+                  name: TITLE
+                  type: VARCHAR(255)
+              - column:
+                  name: COURSE_ID
+                  type: BIGINT
+              - column:
+                  name: SLOT_ID
+                  type: BIGINT
+            tableName: LAB
+  - changeSet:
+      id: 1565603908737-7
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: LAB_ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ALLOWED_MENTOR_GROUPS_ID
+                  type: BIGINT
+            tableName: LAB_ALLOWED_MENTOR_GROUPS
+  - changeSet:
+      id: 1565603908737-8
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: LAB_ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ALLOWED_REQUEST_TYPES_ID
+                  type: BIGINT
+            tableName: LAB_ALLOWED_REQUEST_TYPES
+  - changeSet:
+      id: 1565603908737-9
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: LAB_ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ASSIGNMENTS_ID
+                  type: BIGINT
+            tableName: LAB_ASSIGNMENTS
+  - changeSet:
+      id: 1565603908737-10
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: LAB_ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ROOMS_ID
+                  type: BIGINT
+            tableName: LAB_ROOMS
+  - changeSet:
+      id: 1565603908737-11
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_A
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: CREATED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: MESSAGE
+                  type: VARCHAR(255)
+              - column:
+                  constraints:
+                    nullable: false
+                  name: READ
+                  type: BOOLEAN
+              - column:
+                  name: TITLE
+                  type: VARCHAR(255)
+              - column:
+                  constraints:
+                    nullable: false
+                  name: TTL
+                  type: INT
+              - column:
+                  name: COURSE_ID
+                  type: BIGINT
+              - column:
+                  name: user_id
+                  type: BIGINT
+            tableName: NOTIFICATION
+  - changeSet:
+      id: 1565603908737-12
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_6
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: COMMENT
+                  type: VARCHAR(250)
+              - column:
+                  name: FEEDBACK
+                  type: VARCHAR(250)
+              - column:
+                  name: STATUS
+                  type: INT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ASSIGNMENT_ID
+                  type: BIGINT
+              - column:
+                  name: ASSISTANT_ID
+                  type: BIGINT
+              - column:
+                  name: LAB_ID
+                  type: BIGINT
+              - column:
+                  name: REQUEST_ENTITY_ID
+                  type: BIGINT
+              - column:
+                  name: REQUEST_TYPE_ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: ROOM_ID
+                  type: BIGINT
+              - column:
+                  name: SLOT_ID
+                  type: BIGINT
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-13
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_9
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: DISPLAY_NAME
+                  type: VARCHAR(255)
+            tableName: REQUEST_ENTITY
+  - changeSet:
+      id: 1565603908737-14
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: DTYPE
+                  type: VARCHAR(31)
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_57
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: APPROVED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: REASON
+                  type: VARCHAR(250)
+              - column:
+                  name: ASSIGNED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: CREATED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: FORWARDED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: PROCESSING_AT
+                  type: TIMESTAMP
+              - column:
+                  name: REASON_FOR_STUDENT
+                  type: VARCHAR(250)
+              - column:
+                  name: REJECTED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: REVOKED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: NOT_FOUND_AT
+                  type: TIMESTAMP
+              - column:
+                  name: REQUEST_ID
+                  type: BIGINT
+              - column:
+                  name: assistant_id
+                  type: BIGINT
+              - column:
+                  name: assigned_to_id
+                  type: BIGINT
+              - column:
+                  name: forwarded_to_id
+                  type: BIGINT
+            tableName: REQUEST_EVENT
+  - changeSet:
+      id: 1565603908737-15
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_D
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: NAME
+                  type: VARCHAR(250)
+            tableName: REQUEST_TYPE
+  - changeSet:
+      id: 1565603908737-16
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: TYPE
+                  type: VARCHAR(31)
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_2
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: ENROLLED_AT
+                  type: TIMESTAMP
+              - column:
+                  name: COURSE_ID
+                  type: BIGINT
+              - column:
+                  name: user_id
+                  type: BIGINT
+            tableName: ROLE
+  - changeSet:
+      id: 1565603908737-17
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_26
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: MAP_FILE_PATH
+                  type: VARCHAR(255)
+              - column:
+                  constraints:
+                    nullable: false
+                  name: NAME
+                  type: VARCHAR(255)
+              - column:
+                  constraints:
+                    nullable: false
+                  name: PLACEHOLDER
+                  type: BOOLEAN
+            tableName: ROOM
+  - changeSet:
+      id: 1565603908737-18
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: DTYPE
+                  type: VARCHAR(31)
+              - column:
+                  autoIncrement: true
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_26E
+                  name: ID
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: CLOSES_AT
+                  type: TIMESTAMP
+              - column:
+                  constraints:
+                    nullable: false
+                  name: OPENS_AT
+                  type: TIMESTAMP
+            tableName: SLOT
+  - changeSet:
+      id: 1565603908737-19
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_9E
+                  name: ENDPOINT
+                  type: VARCHAR(255)
+              - column:
+                  name: AUTH
+                  type: VARCHAR(255)
+              - column:
+                  name: KEY
+                  type: VARCHAR(255)
+            tableName: SUBSCRIPTION
+  - changeSet:
+      id: 1565603908737-20
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_5
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: COURSE_ID
+                  type: BIGINT
+            tableName: group
+  - changeSet:
+      id: 1565603908737-21
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: group_id
+                  type: BIGINT
+              - column:
+                  constraints:
+                    nullable: false
+                  name: members_id
+                  type: BIGINT
+            tableName: group_members
+  - changeSet:
+      id: 1565603908737-22
+      author: liam (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  name: DEFAULT_ROLE
+                  type: VARCHAR(255)
+              - column:
+                  name: EMAIL
+                  type: VARCHAR(255)
+              - column:
+                  name: PASSWORD
+                  type: VARCHAR(255)
+              - column:
+                  constraints:
+                    nullable: false
+                  name: STUDENT_NUMBER
+                  type: INT
+              - column:
+                  name: USERNAME
+                  type: VARCHAR(255)
+              - column:
+                  constraints:
+                    primaryKey: true
+                    primaryKeyName: CONSTRAINT_3
+                  name: ID
+                  type: BIGINT
+              - column:
+                  name: SUBSCRIPTION_ENDPOINT
+                  type: VARCHAR(255)
+            tableName: user
+  - changeSet:
+      id: 1565603908737-23
+      author: liam (generated)
+      changes:
+        - addUniqueConstraint:
+            columnNames: user_id, COURSE_ID
+            constraintName: UKN674R1VQ0DBA5P4NXIC0GYHK1
+            tableName: ROLE
+  - changeSet:
+      id: 1565603908737-24
+      author: liam (generated)
+      changes:
+        - addUniqueConstraint:
+            columnNames: NAME
+            constraintName: UK_4L8MM4FQOOS6FCBX76RVQXER
+            tableName: ROOM
+  - changeSet:
+      id: 1565603908737-25
+      author: liam (generated)
+      changes:
+        - addUniqueConstraint:
+            columnNames: USERNAME
+            constraintName: UK_SB8BBOUER5WAK8VYIIY4PF2BX
+            tableName: user
+  - changeSet:
+      id: 1565603908737-26
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: COURSE_ID
+            indexName: FK2QVYNPEW0IU557B4QK9GO0U0C_INDEX_A
+            tableName: NOTIFICATION
+  - changeSet:
+      id: 1565603908737-27
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: LAB_ID
+            indexName: FK3LHCMPE5QB6J2UPOR5NADWACB_INDEX_3
+            tableName: LAB_ALLOWED_REQUEST_TYPES
+  - changeSet:
+      id: 1565603908737-28
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: user_id
+            indexName: FK4S1NG6USMKG33KPL9N9AS7BRP_INDEX_A
+            tableName: NOTIFICATION
+  - changeSet:
+      id: 1565603908737-29
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: user_id
+            indexName: FK5LKQ2AXJBT5BDM2C2KYTRESC8_INDEX_5
+            tableName: FIRST_YEAR_STUDENT
+  - changeSet:
+      id: 1565603908737-30
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ROOMS_ID
+            indexName: FK6GWRGB9UKFUGEY5QHE9YUQHM_INDEX_A
+            tableName: LAB_ROOMS
+  - changeSet:
+      id: 1565603908737-31
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: COURSE_ID
+            indexName: FK9HN7M9PGY5S0CA5GHX0L2HFJT_INDEX_1
+            tableName: LAB
+  - changeSet:
+      id: 1565603908737-32
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: SLOT_ID
+            indexName: FK9PVNMDG9GALX64XVNNB60MKKV_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-33
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: forwarded_to_id
+            indexName: FKAKG6LTRB3TYS2FPPHN1BQTVYH_INDEX_5
+            tableName: REQUEST_EVENT
+  - changeSet:
+      id: 1565603908737-34
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: LABS_ID
+            indexName: FKAWCIGSLSSJWPOFIGPJTDB63T3_INDEX_9
+            tableName: ASSIGNMENT_LABS
+  - changeSet:
+      id: 1565603908737-35
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: COURSE_ID
+            indexName: FKB4NMRFXIHTBW2XYRA3DCPOM72_INDEX_2
+            tableName: ROLE
+  - changeSet:
+      id: 1565603908737-36
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: REQUEST_ID
+            indexName: FKBOYHTGLD73E9XSLHLEMXAX5N_INDEX_5
+            tableName: REQUEST_EVENT
+  - changeSet:
+      id: 1565603908737-37
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: COURSE_ID
+            indexName: FKBQF7BPKO7BS115SXBB6HY692_INDEX_5
+            tableName: group
+  - changeSet:
+      id: 1565603908737-38
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: assigned_to_id
+            indexName: FKEAM49YUH1HBOKGPE60J8SH2OC_INDEX_5
+            tableName: REQUEST_EVENT
+  - changeSet:
+      id: 1565603908737-39
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ROOM_ID
+            indexName: FKFX6YJRQ8MY6B2LWGYKBT713L9_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-40
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ASSIGNMENTS_ID
+            indexName: FKH6HWTCXN9ISGBST8CRIRRF46J_INDEX_4
+            tableName: LAB_ASSIGNMENTS
+  - changeSet:
+      id: 1565603908737-41
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: MENTOR_GROUP_ID
+            indexName: FKHEG3TN9505J6PE0H4UUMFGSGR_INDEX_5
+            tableName: FIRST_YEAR_STUDENT
+  - changeSet:
+      id: 1565603908737-42
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: REQUEST_TYPE_ID
+            indexName: FKIBMR315GQV6G75NHASNYDST5W_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-43
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: assistant_id
+            indexName: FKIFY5C45COP1ONLLHURG1GQ92_INDEX_5
+            tableName: REQUEST_EVENT
+  - changeSet:
+      id: 1565603908737-44
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ASSISTANT_ID
+            indexName: FKKGXWE4XDA3JOJR82JBJUCAQS7_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-45
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ASSIGNMENT_ID
+            indexName: FKL573AVTYTMVVGCXQ5TJA5K6NM_INDEX_9
+            tableName: ASSIGNMENT_LABS
+  - changeSet:
+      id: 1565603908737-46
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: LAB_ID
+            indexName: FKLQY5NK33JPU0PMGQ2RQKSYMFR_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-47
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: REQUEST_ENTITY_ID
+            indexName: FKMX7YCRJL7TV912LOHGSSN3E22_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-48
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: user_id
+            indexName: FKMXGUX88MEBD0QV4LCN9X9LU2N_INDEX_2
+            tableName: ROLE
+  - changeSet:
+      id: 1565603908737-49
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ALLOWED_MENTOR_GROUPS_ID
+            indexName: FKN39662D7DGQ2HCC0G2HLISEXN_INDEX_5
+            tableName: LAB_ALLOWED_MENTOR_GROUPS
+  - changeSet:
+      id: 1565603908737-50
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: group_id
+            indexName: FKN9V1PYA9W7M9L2IBVVWI6MWQI_INDEX_3
+            tableName: group_members
+  - changeSet:
+      id: 1565603908737-51
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ALLOWED_REQUEST_TYPES_ID
+            indexName: FKNGT0BW3MM7AQS0UF30HD9CQBU_INDEX_3
+            tableName: LAB_ALLOWED_REQUEST_TYPES
+  - changeSet:
+      id: 1565603908737-52
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: SUBSCRIPTION_ENDPOINT
+            indexName: FKNIA4ULMEU60NG037D5Q1E05Q8_INDEX_3
+            tableName: user
+  - changeSet:
+      id: 1565603908737-53
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: SLOT_ID
+            indexName: FKON030GRVGAU5MT347U1IKM2CK_INDEX_1
+            tableName: LAB
+  - changeSet:
+      id: 1565603908737-54
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: LAB_ID
+            indexName: FKOPO9D40TJLI87OUO2WJSV3NEQ_INDEX_A
+            tableName: LAB_ROOMS
+  - changeSet:
+      id: 1565603908737-55
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: LAB_ID
+            indexName: FKPM0QAF3LCCHF1T8AP2FAIYXDP_INDEX_4
+            tableName: LAB_ASSIGNMENTS
+  - changeSet:
+      id: 1565603908737-56
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: COURSE_ID
+            indexName: FKROP26UWNBKSTBTFHA3ORMXP85_INDEX_5
+            tableName: ASSIGNMENT
+  - changeSet:
+      id: 1565603908737-57
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: ASSIGNMENT_ID
+            indexName: FKSWAW3ASDT8AIL34LHYH5BEFM1_INDEX_6
+            tableName: REQUEST
+  - changeSet:
+      id: 1565603908737-58
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: LAB_ID
+            indexName: FKTPNAYUXO0E7XK9GA40045WFSX_INDEX_5
+            tableName: LAB_ALLOWED_MENTOR_GROUPS
+  - changeSet:
+      id: 1565603908737-59
+      author: liam (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: members_id
+            indexName: FKUDHOEXLB33K9SSSCBOOE6OU7_INDEX_3
+            tableName: group_members
+  - changeSet:
+      id: 1565603908737-60
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: COURSE_ID
+            baseTableName: NOTIFICATION
+            constraintName: FK2QVYNPEW0IU557B4QK9GO0U0C
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: COURSE
+            validate: true
+  - changeSet:
+      id: 1565603908737-61
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: LAB_ID
+            baseTableName: LAB_ALLOWED_REQUEST_TYPES
+            constraintName: FK3LHCMPE5QB6J2UPOR5NADWACB
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: LAB
+            validate: true
+  - changeSet:
+      id: 1565603908737-62
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: user_id
+            baseTableName: NOTIFICATION
+            constraintName: FK4S1NG6USMKG33KPL9N9AS7BRP
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-63
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: user_id
+            baseTableName: FIRST_YEAR_STUDENT
+            constraintName: FK5LKQ2AXJBT5BDM2C2KYTRESC8
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-64
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ROOMS_ID
+            baseTableName: LAB_ROOMS
+            constraintName: FK6GWRGB9UKFUGEY5QHE9YUQHM
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: ROOM
+            validate: true
+  - changeSet:
+      id: 1565603908737-65
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ID
+            baseTableName: user
+            constraintName: FK99P28UUEEQ4XH4LPX5EX47DBI
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: REQUEST_ENTITY
+            validate: true
+  - changeSet:
+      id: 1565603908737-66
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: COURSE_ID
+            baseTableName: LAB
+            constraintName: FK9HN7M9PGY5S0CA5GHX0L2HFJT
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: COURSE
+            validate: true
+  - changeSet:
+      id: 1565603908737-67
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: SLOT_ID
+            baseTableName: REQUEST
+            constraintName: FK9PVNMDG9GALX64XVNNB60MKKV
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: SLOT
+            validate: true
+  - changeSet:
+      id: 1565603908737-68
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: forwarded_to_id
+            baseTableName: REQUEST_EVENT
+            constraintName: FKAKG6LTRB3TYS2FPPHN1BQTVYH
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-69
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: LABS_ID
+            baseTableName: ASSIGNMENT_LABS
+            constraintName: FKAWCIGSLSSJWPOFIGPJTDB63T3
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: LAB
+            validate: true
+  - changeSet:
+      id: 1565603908737-70
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: COURSE_ID
+            baseTableName: ROLE
+            constraintName: FKB4NMRFXIHTBW2XYRA3DCPOM72
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: COURSE
+            validate: true
+  - changeSet:
+      id: 1565603908737-71
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: REQUEST_ID
+            baseTableName: REQUEST_EVENT
+            constraintName: FKBOYHTGLD73E9XSLHLEMXAX5N
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: REQUEST
+            validate: true
+  - changeSet:
+      id: 1565603908737-72
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: COURSE_ID
+            baseTableName: group
+            constraintName: FKBQF7BPKO7BS115SXBB6HY692
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: COURSE
+            validate: true
+  - changeSet:
+      id: 1565603908737-73
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: assigned_to_id
+            baseTableName: REQUEST_EVENT
+            constraintName: FKEAM49YUH1HBOKGPE60J8SH2OC
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-74
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ROOM_ID
+            baseTableName: REQUEST
+            constraintName: FKFX6YJRQ8MY6B2LWGYKBT713L9
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: ROOM
+            validate: true
+  - changeSet:
+      id: 1565603908737-75
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ASSIGNMENTS_ID
+            baseTableName: LAB_ASSIGNMENTS
+            constraintName: FKH6HWTCXN9ISGBST8CRIRRF46J
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: ASSIGNMENT
+            validate: true
+  - changeSet:
+      id: 1565603908737-76
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: MENTOR_GROUP_ID
+            baseTableName: FIRST_YEAR_STUDENT
+            constraintName: FKHEG3TN9505J6PE0H4UUMFGSGR
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: FIRST_YEAR_MENTOR_GROUP
+            validate: true
+  - changeSet:
+      id: 1565603908737-77
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: REQUEST_TYPE_ID
+            baseTableName: REQUEST
+            constraintName: FKIBMR315GQV6G75NHASNYDST5W
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: REQUEST_TYPE
+            validate: true
+  - changeSet:
+      id: 1565603908737-78
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: assistant_id
+            baseTableName: REQUEST_EVENT
+            constraintName: FKIFY5C45COP1ONLLHURG1GQ92
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-79
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ASSISTANT_ID
+            baseTableName: REQUEST
+            constraintName: FKKGXWE4XDA3JOJR82JBJUCAQS7
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-80
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ASSIGNMENT_ID
+            baseTableName: ASSIGNMENT_LABS
+            constraintName: FKL573AVTYTMVVGCXQ5TJA5K6NM
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: ASSIGNMENT
+            validate: true
+  - changeSet:
+      id: 1565603908737-81
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: LAB_ID
+            baseTableName: REQUEST
+            constraintName: FKLQY5NK33JPU0PMGQ2RQKSYMFR
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: LAB
+            validate: true
+  - changeSet:
+      id: 1565603908737-82
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: REQUEST_ENTITY_ID
+            baseTableName: REQUEST
+            constraintName: FKMX7YCRJL7TV912LOHGSSN3E22
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: REQUEST_ENTITY
+            validate: true
+  - changeSet:
+      id: 1565603908737-83
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: user_id
+            baseTableName: ROLE
+            constraintName: FKMXGUX88MEBD0QV4LCN9X9LU2N
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 1565603908737-84
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ALLOWED_MENTOR_GROUPS_ID
+            baseTableName: LAB_ALLOWED_MENTOR_GROUPS
+            constraintName: FKN39662D7DGQ2HCC0G2HLISEXN
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: FIRST_YEAR_MENTOR_GROUP
+            validate: true
+  - changeSet:
+      id: 1565603908737-85
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: group_id
+            baseTableName: group_members
+            constraintName: FKN9V1PYA9W7M9L2IBVVWI6MWQI
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: group
+            validate: true
+  - changeSet:
+      id: 1565603908737-86
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ALLOWED_REQUEST_TYPES_ID
+            baseTableName: LAB_ALLOWED_REQUEST_TYPES
+            constraintName: FKNGT0BW3MM7AQS0UF30HD9CQBU
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: REQUEST_TYPE
+            validate: true
+  - changeSet:
+      id: 1565603908737-87
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: SUBSCRIPTION_ENDPOINT
+            baseTableName: user
+            constraintName: FKNIA4ULMEU60NG037D5Q1E05Q8
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ENDPOINT
+            referencedTableName: SUBSCRIPTION
+            validate: true
+  - changeSet:
+      id: 1565603908737-88
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: SLOT_ID
+            baseTableName: LAB
+            constraintName: FKON030GRVGAU5MT347U1IKM2CK
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: SLOT
+            validate: true
+  - changeSet:
+      id: 1565603908737-89
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: LAB_ID
+            baseTableName: LAB_ROOMS
+            constraintName: FKOPO9D40TJLI87OUO2WJSV3NEQ
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: LAB
+            validate: true
+  - changeSet:
+      id: 1565603908737-90
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: LAB_ID
+            baseTableName: LAB_ASSIGNMENTS
+            constraintName: FKPM0QAF3LCCHF1T8AP2FAIYXDP
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: LAB
+            validate: true
+  - changeSet:
+      id: 1565603908737-91
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ID
+            baseTableName: group
+            constraintName: FKQM8BB9T2RKWA3YKUXYGR0GIU9
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: REQUEST_ENTITY
+            validate: true
+  - changeSet:
+      id: 1565603908737-92
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: COURSE_ID
+            baseTableName: ASSIGNMENT
+            constraintName: FKROP26UWNBKSTBTFHA3ORMXP85
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: COURSE
+            validate: true
+  - changeSet:
+      id: 1565603908737-93
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: ASSIGNMENT_ID
+            baseTableName: REQUEST
+            constraintName: FKSWAW3ASDT8AIL34LHYH5BEFM1
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: ASSIGNMENT
+            validate: true
+  - changeSet:
+      id: 1565603908737-94
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: LAB_ID
+            baseTableName: LAB_ALLOWED_MENTOR_GROUPS
+            constraintName: FKTPNAYUXO0E7XK9GA40045WFSX
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: LAB
+            validate: true
+  - changeSet:
+      id: 1565603908737-95
+      author: liam (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: members_id
+            baseTableName: group_members
+            constraintName: FKUDHOEXLB33K9SSSCBOOE6OU7
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: ID
+            referencedTableName: user
+            validate: true
+  - changeSet:
+      id: 2
+      author: Liam Clark
+      changes:
+        - addColumn:
+           tableName: request
+           columns:
+               - column:
+                  name: question
+                  type: varchar(50)
diff --git a/backend/src/main/resources/changelog-master.yaml b/backend/src/main/resources/changelog-master.yaml
new file mode 100644
index 000000000..1c7416f6f
--- /dev/null
+++ b/backend/src/main/resources/changelog-master.yaml
@@ -0,0 +1,24 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+databaseChangeLog:
+    - include:
+         file: "changelog.yaml"
+    - include:
+         file: "migrations.yaml"
+
diff --git a/backend/src/main/resources/changelog.yaml b/backend/src/main/resources/changelog.yaml
new file mode 100644
index 000000000..9cfddfabb
--- /dev/null
+++ b/backend/src/main/resources/changelog.yaml
@@ -0,0 +1,1510 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+databaseChangeLog:
+- changeSet:
+    id: 1565863608484-1
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: deleted_at
+            type: datetime
+        - column:
+            name: name
+            type: VARCHAR(255)
+        - column:
+            name: course_id
+            type: BIGINT
+        - column:
+            name: verification
+            type: VARCHAR(100)
+        tableName: assignment
+- changeSet:
+    id: 1565863608484-2
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: assignment_id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: labs_id
+            type: BIGINT
+        tableName: assignment_labs
+- changeSet:
+    id: 1565863608484-3
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: code
+            type: VARCHAR(255)
+        - column:
+            name: deleted_at
+            type: datetime
+        - column:
+            constraints:
+              nullable: false
+            name: name
+            type: VARCHAR(255)
+        - column:
+            name: submission_url
+            type: VARCHAR(255)
+        - column:
+            constraints:
+              nullable: false
+            defaultValueBoolean: false
+            name: has_groups
+            type: BIT
+        - column:
+            name: is_archived
+            type: BIT
+        tableName: course
+- changeSet:
+    id: 1565863608484-4
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: active
+            type: BIT
+        - column:
+            constraints:
+              nullable: false
+            name: name
+            type: VARCHAR(255)
+        tableName: first_year_mentor_group
+- changeSet:
+    id: 1565863608484-5
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: net_id
+            type: VARCHAR(255)
+        - column:
+            name: mentor_group_id
+            type: BIGINT
+        - column:
+            name: user_id
+            type: BIGINT
+        tableName: first_year_student
+- changeSet:
+    id: 1565863608484-6
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: capacity
+            type: BIGINT
+        - column:
+            name: deleted_at
+            type: datetime
+        - column:
+            constraints:
+              nullable: false
+            name: direction
+            type: VARCHAR(255)
+        - column:
+            name: interval_time
+            type: BIGINT
+        - column:
+            name: sign_off_intervals
+            type: BIT
+        - column:
+            name: course_id
+            type: BIGINT
+        - column:
+            name: slot_id
+            type: BIGINT
+        - column:
+            name: title
+            type: VARCHAR(255)
+        - column:
+            name: slot_selection_opens_at
+            type: datetime
+        - column:
+            constraints:
+              nullable: false
+            name: allow_without_mentor_group
+            type: BIT
+        - column:
+            constraints:
+              nullable: false
+            defaultValueBoolean: false
+            name: is_feedback_lab
+            type: BIT
+        tableName: lab
+- changeSet:
+    id: 1565863608484-7
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: lab_id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: allowed_mentor_groups_id
+            type: BIGINT
+        tableName: lab_allowed_mentor_groups
+- changeSet:
+    id: 1565863608484-8
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: lab_id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: allowed_request_types_id
+            type: BIGINT
+        tableName: lab_allowed_request_types
+- changeSet:
+    id: 1565863608484-9
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: lab_id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: assignments_id
+            type: BIGINT
+        tableName: lab_assignments
+- changeSet:
+    id: 1565863608484-10
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: course_id
+            type: BIGINT
+        tableName: lab_group
+- changeSet:
+    id: 1565863608484-11
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: group_id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: members_id
+            type: BIGINT
+        tableName: lab_group_members
+- changeSet:
+    id: 1565863608484-12
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              primaryKey: true
+            name: lab_id
+            type: BIGINT
+        - column:
+            constraints:
+              primaryKey: true
+            name: rooms_id
+            type: BIGINT
+        tableName: lab_rooms
+- changeSet:
+    id: 1565863608484-13
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: created_at
+            type: datetime
+        - column:
+            name: message
+            type: VARCHAR(255)
+        - column:
+            constraints:
+              nullable: false
+            name: read
+            type: BIT
+        - column:
+            name: title
+            type: VARCHAR(255)
+        - column:
+            constraints:
+              nullable: false
+            name: ttl
+            type: INT
+        - column:
+            name: course_id
+            type: BIGINT
+        - column:
+            name: user_id
+            type: BIGINT
+        tableName: notification
+- changeSet:
+    id: 1565863608484-14
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: default_role
+            type: VARCHAR(255)
+        - column:
+            name: password
+            type: VARCHAR(255)
+        - column:
+            constraints:
+              unique: true
+            name: username
+            type: VARCHAR(255)
+        - column:
+            name: display_name
+            type: VARCHAR(100)
+        - column:
+            constraints:
+              nullable: false
+            name: email
+            type: VARCHAR(125)
+        - column:
+            name: student_number
+            type: INT
+        - column:
+            name: subscription_endpoint
+            type: VARCHAR(255)
+        tableName: person
+- changeSet:
+    id: 1565863608484-15
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: comment
+            type: VARCHAR(255)
+        - column:
+            name: status
+            type: INT
+        - column:
+            name: type
+            type: INT
+        - column:
+            constraints:
+              nullable: false
+            name: assignment_id
+            type: BIGINT
+        - column:
+            name: assistant_id
+            type: BIGINT
+        - column:
+            name: lab_id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: room_id
+            type: BIGINT
+        - column:
+            name: slot_id
+            type: BIGINT
+        - column:
+            name: student_id
+            type: BIGINT
+        - column:
+            name: request_type_id
+            type: BIGINT
+        - column:
+            name: feedback
+            type: VARCHAR(256)
+        - column:
+            name: request_entity_id
+            type: BIGINT
+        tableName: request
+- changeSet:
+    id: 1565863608484-16
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: display_name
+            type: VARCHAR(255)
+        tableName: request_entity
+- changeSet:
+    id: 1565863608484-17
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: dtype
+            type: VARCHAR(31)
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: reason
+            type: VARCHAR(255)
+        - column:
+            name: rejected_at
+            type: datetime
+        - column:
+            name: created_at
+            type: datetime
+        - column:
+            name: processing_at
+            type: datetime
+        - column:
+            name: reason_for_student
+            type: VARCHAR(250)
+        - column:
+            name: assigned_at
+            type: datetime
+        - column:
+            name: forwarded_at
+            type: datetime
+        - column:
+            name: approved_at
+            type: datetime
+        - column:
+            name: revoked_at
+            type: datetime
+        - column:
+            name: not_found_at
+            type: datetime
+        - column:
+            name: request_id
+            type: BIGINT
+        - column:
+            name: assigned_to_id
+            type: BIGINT
+        - column:
+            name: forwarded_to_id
+            type: BIGINT
+        - column:
+            name: assistant_id
+            type: BIGINT
+        tableName: request_event
+- changeSet:
+    id: 1565863608484-18
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: name
+            type: VARCHAR(250)
+        tableName: request_type
+- changeSet:
+    id: 1565863608484-19
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: type
+            type: VARCHAR(31)
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            name: enrolled_at
+            type: datetime
+        - column:
+            name: course_id
+            type: BIGINT
+        - column:
+            name: user_id
+            type: BIGINT
+        tableName: role
+- changeSet:
+    id: 1565863608484-20
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+              unique: true
+            name: name
+            type: VARCHAR(255)
+        - column:
+            constraints:
+              nullable: false
+            name: placeholder
+            type: BIT
+        - column:
+            name: lab_id
+            type: BIGINT
+        - column:
+            name: map_file_path
+            type: VARCHAR(255)
+        tableName: room
+- changeSet:
+    id: 1565863608484-21
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              nullable: false
+            name: dtype
+            type: VARCHAR(31)
+        - column:
+            autoIncrement: true
+            constraints:
+              primaryKey: true
+            name: id
+            type: BIGINT
+        - column:
+            constraints:
+              nullable: false
+            name: closes_at
+            type: datetime
+        - column:
+            constraints:
+              nullable: false
+            name: opens_at
+            type: datetime
+        tableName: slot
+- changeSet:
+    id: 1565863608484-22
+    author: root (generated)
+    changes:
+    - createTable:
+        columns:
+        - column:
+            constraints:
+              primaryKey: true
+            name: endpoint
+            type: VARCHAR(255)
+        - column:
+            name: auth
+            type: VARCHAR(255)
+        - column:
+            name: key
+            type: VARCHAR(255)
+        tableName: subscription
+- changeSet:
+    id: 1565863608484-23
+    author: root (generated)
+    changes:
+    - addUniqueConstraint:
+        columnNames: user_id, course_id
+        constraintName: UKgcmlbynqofdlbro03pgxyo114
+        tableName: role
+- changeSet:
+    id: 1565863608484-24
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: assigned_to_id
+        indexName: FK1cb4ui4owkuuxvjpnpvrj1riv
+        tableName: request_event
+- changeSet:
+    id: 1565863608484-25
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: mentor_group_id
+        indexName: FK22rkup1yp74u9mw8uwymh4snx
+        tableName: first_year_student
+- changeSet:
+    id: 1565863608484-26
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: slot_id
+        indexName: FK2rw6oeis238htghylfcat8rbr
+        tableName: request
+- changeSet:
+    id: 1565863608484-27
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: lab_id
+        indexName: FK3r0cgvek17fxh8bv0k50q7o51
+        tableName: lab_allowed_mentor_groups
+- changeSet:
+    id: 1565863608484-28
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: user_id
+        indexName: FK48udxa7826ty0m0sgvpjlvsd8
+        tableName: notification
+- changeSet:
+    id: 1565863608484-29
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: subscription_endpoint
+        indexName: FK70joshps7ytaype02u53ce6ip
+        tableName: person
+- changeSet:
+    id: 1565863608484-30
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: lab_id
+        indexName: FK70sdg8pxe1bpmairweu0m09yh
+        tableName: room
+- changeSet:
+    id: 1565863608484-31
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: course_id
+        indexName: FK75lyga5ecx8ngldp0gcr65eio
+        tableName: role
+- changeSet:
+    id: 1565863608484-32
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: student_id
+        indexName: FK8papfir99sae7nvw3klm7fac1
+        tableName: request
+- changeSet:
+    id: 1565863608484-33
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: course_id
+        indexName: FKbfknv2fcjud1f58j1mr5yuwwy
+        tableName: lab_group
+- changeSet:
+    id: 1565863608484-34
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: allowed_mentor_groups_id
+        indexName: FKc2v3bcyfjlutny4pqkrjwslqw
+        tableName: lab_allowed_mentor_groups
+- changeSet:
+    id: 1565863608484-35
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: slot_id
+        indexName: FKeje5t40ljj36e7akvnlaw6708
+        tableName: lab
+- changeSet:
+    id: 1565863608484-36
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: request_entity_id
+        indexName: FKfq3ev1o2dnogdg2m6ihshc5ke
+        tableName: request
+- changeSet:
+    id: 1565863608484-37
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: assignments_id
+        indexName: FKg7ivfty340avf4likuybd64m0
+        tableName: lab_assignments
+- changeSet:
+    id: 1565863608484-38
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: course_id
+        indexName: FKgfino31sgypl8h96btiwq98pe
+        tableName: notification
+- changeSet:
+    id: 1565863608484-39
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: lab_id
+        indexName: FKh8ykcs1ypqp4ud00cocggkwx0
+        tableName: request
+- changeSet:
+    id: 1565863608484-40
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: allowed_request_types_id
+        indexName: FKiapv3nl97i6wb8s23jkjtuudf
+        tableName: lab_allowed_request_types
+- changeSet:
+    id: 1565863608484-41
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: lab_id
+        indexName: FKin10kgdf0ivbj5ap1wr7e0sjq
+        tableName: lab_assignments
+- changeSet:
+    id: 1565863608484-42
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: request_id
+        indexName: FKiulpy0gdpj28q64jgjd3k1aag
+        tableName: request_event
+- changeSet:
+    id: 1565863608484-43
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: assistant_id
+        indexName: FKl9uhi4ygx6r9ulx4txdbkiwrn
+        tableName: request
+- changeSet:
+    id: 1565863608484-44
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: assignment_id
+        indexName: FKlse0lkd1y347wb6nf0v0ht756
+        tableName: assignment_labs
+- changeSet:
+    id: 1565863608484-45
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: user_id
+        indexName: FKmfrck21ksn1oqbfm6uryxm3c3
+        tableName: first_year_student
+- changeSet:
+    id: 1565863608484-46
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: forwarded_to_id
+        indexName: FKmj5runnisdjl37pgmseyow1vg
+        tableName: request_event
+- changeSet:
+    id: 1565863608484-47
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: assignment_id
+        indexName: FKn45h8yuw644y2769juo47xl8t
+        tableName: request
+- changeSet:
+    id: 1565863608484-48
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: group_id
+        indexName: FKn9v1pya9w7m9l2ibvvwi6mwqi
+        tableName: lab_group_members
+- changeSet:
+    id: 1565863608484-49
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: labs_id
+        indexName: FKrcsir4bu3pnyf34dkihdjp1x7
+        tableName: assignment_labs
+- changeSet:
+    id: 1565863608484-50
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: lab_id
+        indexName: FKrvjfgeos3gfjb6uhm8svfhl6o
+        tableName: lab_allowed_request_types
+- changeSet:
+    id: 1565863608484-51
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: course_id
+        indexName: FKs13xgmlksx80sd0jm8b2jja19
+        tableName: assignment
+- changeSet:
+    id: 1565863608484-52
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: room_id
+        indexName: FKs8x76ups6w11d449xkt8g56m3
+        tableName: request
+- changeSet:
+    id: 1565863608484-53
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: course_id
+        indexName: FKsc58e2ucdcmfmu8wsw2l34tiu
+        tableName: lab
+- changeSet:
+    id: 1565863608484-54
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: members_id
+        indexName: FKudhoexlb33k9ssscbooe6ou7
+        tableName: lab_group_members
+- changeSet:
+    id: 1565863608484-55
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: assistant_id
+        indexName: assistant_id
+        tableName: request_event
+- changeSet:
+    id: 1565863608484-56
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: rooms_id
+        indexName: fk_room
+        tableName: lab_rooms
+- changeSet:
+    id: 1565863608484-57
+    author: root (generated)
+    changes:
+    - createIndex:
+        columns:
+        - column:
+            name: request_type_id
+        indexName: request_type_id
+        tableName: request
+- changeSet:
+    id: 1565863608484-58
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: assigned_to_id
+        baseTableName: request_event
+        constraintName: FK1cb4ui4owkuuxvjpnpvrj1riv
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-59
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: mentor_group_id
+        baseTableName: first_year_student
+        constraintName: FK22rkup1yp74u9mw8uwymh4snx
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: first_year_mentor_group
+        validate: true
+- changeSet:
+    id: 1565863608484-60
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: slot_id
+        baseTableName: request
+        constraintName: FK2rw6oeis238htghylfcat8rbr
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: slot
+        validate: true
+- changeSet:
+    id: 1565863608484-61
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: lab_id
+        baseTableName: lab_allowed_mentor_groups
+        constraintName: FK3r0cgvek17fxh8bv0k50q7o51
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab
+        validate: true
+- changeSet:
+    id: 1565863608484-62
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: user_id
+        baseTableName: notification
+        constraintName: FK48udxa7826ty0m0sgvpjlvsd8
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-63
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: id
+        baseTableName: lab_group
+        constraintName: FK71whfqmbq0kdl1k3frkn9lqbt
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: request_entity
+        validate: true
+- changeSet:
+    id: 1565863608484-64
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: course_id
+        baseTableName: role
+        constraintName: FK75lyga5ecx8ngldp0gcr65eio
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: course
+        validate: true
+- changeSet:
+    id: 1565863608484-65
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: student_id
+        baseTableName: request
+        constraintName: FK8papfir99sae7nvw3klm7fac1
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-66
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: course_id
+        baseTableName: lab_group
+        constraintName: FKbfknv2fcjud1f58j1mr5yuwwy
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: course
+        validate: true
+- changeSet:
+    id: 1565863608484-67
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: allowed_mentor_groups_id
+        baseTableName: lab_allowed_mentor_groups
+        constraintName: FKc2v3bcyfjlutny4pqkrjwslqw
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: first_year_mentor_group
+        validate: true
+- changeSet:
+    id: 1565863608484-68
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: user_id
+        baseTableName: role
+        constraintName: FKckgqohmqxbnlou97v0qagv3am
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-69
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: slot_id
+        baseTableName: lab
+        constraintName: FKeje5t40ljj36e7akvnlaw6708
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: slot
+        validate: true
+- changeSet:
+    id: 1565863608484-70
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: request_entity_id
+        baseTableName: request
+        constraintName: FKfq3ev1o2dnogdg2m6ihshc5ke
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: request_entity
+        validate: true
+- changeSet:
+    id: 1565863608484-71
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: assignments_id
+        baseTableName: lab_assignments
+        constraintName: FKg7ivfty340avf4likuybd64m0
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: assignment
+        validate: true
+- changeSet:
+    id: 1565863608484-72
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: course_id
+        baseTableName: notification
+        constraintName: FKgfino31sgypl8h96btiwq98pe
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: course
+        validate: true
+- changeSet:
+    id: 1565863608484-73
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: lab_id
+        baseTableName: request
+        constraintName: FKh8ykcs1ypqp4ud00cocggkwx0
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab
+        validate: true
+- changeSet:
+    id: 1565863608484-74
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: allowed_request_types_id
+        baseTableName: lab_allowed_request_types
+        constraintName: FKiapv3nl97i6wb8s23jkjtuudf
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: request_type
+        validate: true
+- changeSet:
+    id: 1565863608484-75
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: lab_id
+        baseTableName: lab_assignments
+        constraintName: FKin10kgdf0ivbj5ap1wr7e0sjq
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab
+        validate: true
+- changeSet:
+    id: 1565863608484-76
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: request_id
+        baseTableName: request_event
+        constraintName: FKiulpy0gdpj28q64jgjd3k1aag
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: request
+        validate: true
+- changeSet:
+    id: 1565863608484-77
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: assistant_id
+        baseTableName: request
+        constraintName: FKl9uhi4ygx6r9ulx4txdbkiwrn
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-78
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: request_type_id
+        baseTableName: request
+        constraintName: FKlroq8ehn0vrap04hot3k4d47s
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: request_type
+        validate: true
+- changeSet:
+    id: 1565863608484-79
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: assignment_id
+        baseTableName: assignment_labs
+        constraintName: FKlse0lkd1y347wb6nf0v0ht756
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: assignment
+        validate: true
+- changeSet:
+    id: 1565863608484-80
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: user_id
+        baseTableName: first_year_student
+        constraintName: FKmfrck21ksn1oqbfm6uryxm3c3
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-81
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: forwarded_to_id
+        baseTableName: request_event
+        constraintName: FKmj5runnisdjl37pgmseyow1vg
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-82
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: assignment_id
+        baseTableName: request
+        constraintName: FKn45h8yuw644y2769juo47xl8t
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: assignment
+        validate: true
+- changeSet:
+    id: 1565863608484-83
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: group_id
+        baseTableName: lab_group_members
+        constraintName: FKn9v1pya9w7m9l2ibvvwi6mwqi
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab_group
+        validate: true
+- changeSet:
+    id: 1565863608484-84
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: labs_id
+        baseTableName: assignment_labs
+        constraintName: FKrcsir4bu3pnyf34dkihdjp1x7
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab
+        validate: true
+- changeSet:
+    id: 1565863608484-85
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: lab_id
+        baseTableName: lab_allowed_request_types
+        constraintName: FKrvjfgeos3gfjb6uhm8svfhl6o
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab
+        validate: true
+- changeSet:
+    id: 1565863608484-86
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: course_id
+        baseTableName: assignment
+        constraintName: FKs13xgmlksx80sd0jm8b2jja19
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: course
+        validate: true
+- changeSet:
+    id: 1565863608484-87
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: room_id
+        baseTableName: request
+        constraintName: FKs8x76ups6w11d449xkt8g56m3
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: room
+        validate: true
+- changeSet:
+    id: 1565863608484-88
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: course_id
+        baseTableName: lab
+        constraintName: FKsc58e2ucdcmfmu8wsw2l34tiu
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: course
+        validate: true
+- changeSet:
+    id: 1565863608484-89
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: id
+        baseTableName: person
+        constraintName: FKscmybn5lote78sm12pc98jgyk
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: request_entity
+        validate: true
+- changeSet:
+    id: 1565863608484-90
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: members_id
+        baseTableName: lab_group_members
+        constraintName: FKudhoexlb33k9ssscbooe6ou7
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: person
+        validate: true
+- changeSet:
+    id: 1565863608484-91
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: lab_id
+        baseTableName: lab_rooms
+        constraintName: fk_lab
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: lab
+        validate: true
+- changeSet:
+    id: 1565863608484-92
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: rooms_id
+        baseTableName: lab_rooms
+        constraintName: fk_room
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: id
+        referencedTableName: room
+        validate: true
+- changeSet:
+    id: 1565863608484-93
+    author: root (generated)
+    changes:
+    - addForeignKeyConstraint:
+        baseColumnNames: subscription_endpoint
+        baseTableName: person
+        constraintName: person_ibfk_1
+        deferrable: false
+        initiallyDeferred: false
+        onDelete: RESTRICT
+        onUpdate: RESTRICT
+        referencedColumnNames: endpoint
+        referencedTableName: subscription
+        validate: true
+
diff --git a/backend/src/main/resources/log4j.properties.template b/backend/src/main/resources/log4j.properties.template
new file mode 100644
index 000000000..b847e4a31
--- /dev/null
+++ b/backend/src/main/resources/log4j.properties.template
@@ -0,0 +1,40 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+# Default application properties. These can be overridden at run time from an
+# application.properties file in the same directory as the executable jar.
+
+log4j.rootCategory=INFO, CONSOLE
+
+# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=DEBUG
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
+
+# Logging of Spring Security extension
+log4j.logger.org.springframework.security.saml=INFO
+
+# Logging of SAML messages, set to FINEST to enable
+log4j.logger.PROTOCOL_MESSAGE=INFO
+
+# Logging of OpenSAML library
+log4j.logger.org.opensaml=INFO
+
+# reduce hibernate logging
+log4j.logger.org.hibernate=WARN
diff --git a/backend/src/main/resources/logback.xml b/backend/src/main/resources/logback.xml
new file mode 100644
index 000000000..254682aa0
--- /dev/null
+++ b/backend/src/main/resources/logback.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<configuration debug="false" scan="false">
+
+    <property resource="application.properties" />
+
+
+    <appender name="AIRBRAKE" class="net.anthavio.airbrake.AirbrakeLogbackAppender">
+        <apiKey>7cd61aac542cf3d082aea6a109a678ce</apiKey>
+        <env>${spring.profiles.active}</env>
+        <url>https://eiptest.ewi.tudelft.nl/errbit/notifier_api/v2/notices</url>
+        <enabled>true</enabled>
+
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <notify>ALL</notify>
+    </appender>
+
+    <appender name="AIRBRAKE-ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="AIRBRAKE" />
+    </appender>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <layout class="ch.qos.logback.classic.PatternLayout">
+            <Pattern>
+                %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+            </Pattern>
+        </layout>
+    </appender>
+
+    <springProfile name="production">
+        <root>
+            <level value="info" />
+            <appender-ref ref="AIRBRAKE-ASYNC" />
+            <appender-ref ref="STDOUT" />
+        </root>
+    </springProfile>
+
+    <springProfile name="development,test">
+        <root>
+            <level value="info" />
+            <appender-ref ref="STDOUT" />
+        </root>
+    </springProfile>
+
+</configuration>
\ No newline at end of file
diff --git a/backend/src/main/resources/messages.properties b/backend/src/main/resources/messages.properties
new file mode 100644
index 000000000..a35e79a9c
--- /dev/null
+++ b/backend/src/main/resources/messages.properties
@@ -0,0 +1,22 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+queue.slot.order = The slot must open before it closes.
+
+user.username.notFound = The given username could not be found.
+user.username.participates = The user already participates in this course.
diff --git a/backend/src/main/resources/migrations.yaml b/backend/src/main/resources/migrations.yaml
new file mode 100644
index 000000000..dffdf3be0
--- /dev/null
+++ b/backend/src/main/resources/migrations.yaml
@@ -0,0 +1,80 @@
+#
+# Queue - A Queueing system that can be used to handle labs in higher education
+# Copyright (C) 2016-2019  Delft University of Technology
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+databaseChangeLog:
+  - changeSet:
+      - id: 1-1
+      - author: "Liam Clark"
+      - changes:
+#         drop the old foreign key constraint
+          - dropForeignKeyConstraint:
+              baseTableName: person
+              constraintName: person_ibfk_1
+#         drop old primary key
+          - dropPrimaryKey:
+              tableName: subscription
+#         add auto incrementing primary key id column to subscriptions
+          - addColumn:
+              tableName: subscription
+              columns:
+                    - column:
+                        name: subid
+                        type: bigint
+                        autoIncrement: true
+                        constraints:
+                            #primaryKey: true
+                                nullable: false
+          - addPrimaryKey:
+              tableName: subscription
+              columnNames: subid
+          - renameColumn:
+              tableName: subscription
+              oldColumnName: subid
+              newColumnName: id
+              columnDataType: bigint
+#         add foreign key column to person, filled in with the id resulting from the current join
+          - addColumn:
+                tableName: person
+                columns:
+                    - column:
+                          name: subscription_id
+                          type: bigint
+                          valueComputed: "(SELECT id FROM subscription WHERE person.subscription_endpoint=subscription.endpoint)"
+#         add the foreign key constraint that hibernate generated for the old column.
+          - addForeignKeyConstraint:
+              baseColumnNames: subscription_id
+              baseTableName: person
+              constraintName: person_ibfk_2
+              deferrable: false
+              initiallyDeferred: false
+              onDelete: RESTRICT
+              onUpdate: RESTRICT
+              referencedColumnNames: id
+              referencedTableName: subscription
+              validate: true
+
+
+#  only add this after validation.
+  - changeSet:
+    - id: 1-2
+    - author: "Liam Clark"
+    - changes:
+          - dropColumn:
+              - tableName: person
+              - columnName: "subscription_endpoint"
+
diff --git a/backend/src/main/resources/rebel.xml b/backend/src/main/resources/rebel.xml
new file mode 100644
index 000000000..c0689b4ac
--- /dev/null
+++ b/backend/src/main/resources/rebel.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
+
+	<classpath>
+		<dir name="/Users/martijn/Projects/queue/build/resources/main">
+		</dir>
+		<dir name="/Users/martijn/Projects/queue/build/classes/main">
+		</dir>
+	</classpath>
+
+</application>
diff --git a/backend/src/main/resources/security/samlKeystore.jks b/backend/src/main/resources/security/samlKeystore.jks
new file mode 100644
index 0000000000000000000000000000000000000000..7552eb0f5dda45ee5b5f091c82ad3d80495a9d31
GIT binary patch
literal 4237
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

literal 0
HcmV?d00001

diff --git a/backend/src/main/resources/static/CHOSEN_LICENSE.md b/backend/src/main/resources/static/CHOSEN_LICENSE.md
new file mode 100644
index 000000000..5e1332c12
--- /dev/null
+++ b/backend/src/main/resources/static/CHOSEN_LICENSE.md
@@ -0,0 +1,23 @@
+#### Chosen
+- by Patrick Filler for [Harvest](http://getharvest.com)
+- Copyright (c) 2011-2016 by Harvest
+
+Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/backend/src/main/resources/static/css/bootstrap-multiselect.css b/backend/src/main/resources/static/css/bootstrap-multiselect.css
new file mode 100644
index 000000000..0623d0e73
--- /dev/null
+++ b/backend/src/main/resources/static/css/bootstrap-multiselect.css
@@ -0,0 +1,18 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px 3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0}
\ No newline at end of file
diff --git a/backend/src/main/resources/static/css/chosen.min.css b/backend/src/main/resources/static/css/chosen.min.css
new file mode 100644
index 000000000..4fe6927cb
--- /dev/null
+++ b/backend/src/main/resources/static/css/chosen.min.css
@@ -0,0 +1,28 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+/*!
+Chosen, a Select Box Enhancer for jQuery and Prototype
+by Patrick Filler for Harvest, http://getharvest.com
+
+Version 1.8.7
+Full source at https://github.com/harvesthq/chosen
+Copyright (c) 2011-2018 Harvest http://getharvest.com
+
+MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
+This file is generated by `grunt build`, do not edit it by hand.
+*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);clip:rect(0,0,0,0);-webkit-clip-path:inset(100% 100%);clip-path:inset(100% 100%)}.chosen-container.chosen-with-drop .chosen-drop{clip:auto;-webkit-clip-path:none;clip-path:none}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#999}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:":";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),to(#f4f4f4));background:linear-gradient(#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:url(chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0);-webkit-clip-path:inset(100% 100%);clip-path:inset(100% 100%)}.chosen-container .chosen-results{color:#444;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#777;display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #aaa;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#999;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #aaa;max-width:100%;border-radius:3px;background-color:#eee;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),to(#eee));background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),to(#eee));background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#222!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url(chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}
\ No newline at end of file
diff --git a/backend/src/main/resources/static/css/global.css b/backend/src/main/resources/static/css/global.css
new file mode 100644
index 000000000..d95491037
--- /dev/null
+++ b/backend/src/main/resources/static/css/global.css
@@ -0,0 +1,656 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+/**
+ * Container
+ */
+@media (min-width: 1200px) {
+    .container {
+        max-width: 980px;
+    }
+}
+
+/* Sticky footer styles
+-------------------------------------------------- */
+html {
+    position: relative;
+    min-height: 100%;
+}
+
+body {
+    /* Margin bottom by footer height */
+    margin-bottom: 60px;
+    font-size: 0.9rem;
+}
+
+.btn {
+    font-size: 0.9rem !important;
+}
+
+.form-control {
+    font-size: 0.9rem !important;
+}
+
+.footer {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    /* Set the fixed height of the footer here */
+    height: 60px;
+    padding-top: 10px;
+    background-color: #f5f5f5;
+}
+
+.btn-primary, .btn-primary:hover, .btn-primary:active, .btn-primary:visited {
+    background-color:  #00A6D6 !important;
+    border-color: #00A6D6 !important;
+}
+
+.pagination > .active > span {
+    background-color:  #00A6D6 !important;
+    border-color:  #00A6D6 !important;
+}
+
+    /**
+     * Boxed group
+     */
+.boxed-group {
+    margin-bottom: 20px;
+}
+
+.boxed-group > h3 {
+    display: block;
+    padding: 9px 10px 10px;
+    margin: 0;
+    font-size: 14px;
+    line-height: 17px;
+    background-color: #f5f5f5;
+    border: 1px solid #d8d8d8;
+    border-bottom: 0;
+    border-radius: 3px 3px 0 0;
+}
+
+.boxed-group .boxed-group-inner {
+    padding: 10px;
+    font-size: 13px;
+    border: 1px solid #d8d8d8;
+    border-bottom-right-radius: 3px;
+    border-bottom-left-radius: 3px;
+}
+
+.boxed-group .list-group-item:first-child {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+}
+
+.boxed-group .list-group-item li {
+    line-height: 2em;
+}
+
+
+/**
+ * Table list
+ */
+.table-list-header::before {
+    display: table;
+    content: "";
+}
+
+.table-list-header::after {
+    display: table;
+    clear: both;
+    content: "";
+}
+
+.table-list-header {
+    position: relative;
+    margin-top: 20px;
+    margin-bottom: -1px;
+    background-color: #f8f8f8;
+    border: 1px solid #e5e5e5;
+    padding-bottom: 10px;
+    border-radius: 5px;
+}
+
+.table-list-header ul {
+    padding-left: 0;
+    margin-bottom: 0;
+}
+
+.table-list-header .btn-link {
+    position: relative;
+    display: inline-block;
+    padding-top: 13px;
+    padding-bottom: 13px;
+    font-weight: normal;
+    font-size: 13px;
+    text-decoration: none;
+    border: 0;
+}
+
+.table-list-header .btn-link:focus {
+    outline: 0;
+    color: #222;
+}
+
+.table-list-header .btn-link:hover {
+    color: #222;
+    text-decoration: none;
+}
+
+.table-list-header .btn-link:active {
+    color: #222;
+    text-decoration: none;
+}
+
+
+.table-list-header .filters {
+    margin-right: 10px;
+    margin-left: 10px;
+}
+
+.table-list-header .filters .filter {
+    display: inline-block;
+    margin-top:10px;
+    margin-right:10px;
+}
+
+.table-list-header .filters .filter .dropdown-menu a {
+    padding-left: 30px;
+}
+
+.table-list-header .filters .filter .dropdown-menu a .fa {
+    float: left;
+    margin-left: -20px;
+    margin-top: 3px;
+}
+
+.select-menu-button {
+    padding-right: 15px;
+    padding-left: 15px;
+}
+
+.table-list {
+    display: table;
+    width: 100%;
+    color: #999;
+    table-layout: fixed;
+    border-bottom: 1px solid #e5e5e5;
+    padding-left: 0;
+}
+
+.table-list-item {
+    position: relative;
+    display: table-row;
+    list-style: none;
+}
+
+.table-list-cell:first-child {
+    border-left: 1px solid #eee;
+}
+
+.table-list-cell:last-child {
+    border-right: 1px solid #eee;
+}
+
+.table-list-cell {
+    position: relative;
+    display: table-cell;
+    padding: 8px 10px;
+    font-size: 12px;
+    vertical-align: top;
+    border-top: 1px solid #eee;
+}
+
+.table-list-cell.status {
+    width: 90px;
+}
+
+a.filters-reset {
+    color: #767676;
+}
+
+a.filters-reset:hover {
+    color: #337ab7;
+    text-decoration: none;
+}
+
+/**
+ * Panel box
+ */
+.mini-box {
+    min-height: 120px;
+    padding: 25px;
+}
+
+.mini-box .btn-icon {
+    margin: 0 15px 0 0;
+    font-size: 32px;
+}
+
+.mini-box .box-info {
+    display: inline-block;
+    vertical-align: top;
+}
+
+.panel {
+    box-shadow: 0 0 3px rgba(0,0,0,0.1);
+}
+.panel {
+    margin-bottom: 20px;
+    background-color: #f5f5f5;
+    border: 1px solid transparent;
+    border-radius: 2px;
+    box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+}
+
+.btn-icon {
+    display: inline-block;
+    text-align: center;
+    border-radius: 2px;
+    height: 35px;
+    width: 35px;
+    line-height: 35px;
+}
+
+.btn-icon-round {
+    border-radius: 50%;
+}
+
+.btn-icon-lg-alt {
+    height: 70px;
+    width: 70px;
+    line-height: 70px;
+}
+
+.bg-info {
+    background-color: #2ec1cc;
+    color: #fff;
+}
+
+.text-muted {
+    color: #777;
+}
+
+/**
+ * Misc
+ */
+.form-actions {
+    background-color: #f5f5f5;
+    border-top: 1px solid #e5e5e5;
+    padding: 19px 20px 20px;
+    margin-left: 0;
+    margin-right: 0;
+    width: 100%;
+}
+
+dl.dl-horizontal dd {
+    margin-bottom: 10px;
+}
+
+#queueGraph {
+    width: 100%;
+    height: 300px;
+}
+
+.input-daterange .input-group-addon {
+    border-left: none;
+    border-right: none;
+}
+
+div.row-xs {
+    padding: 15px;
+}
+
+div.row-xs:nth-of-type(odd) {
+    background-color: #f9f9f9;
+}
+
+.time {
+    font-size: 12px;
+    color: #737373;
+    margin: 5px 0 0;
+}
+
+/**
+ * Timeline
+ */
+.info-offset-left {
+    margin-left: 50px;
+}
+
+.timeline-header {
+    text-transform: uppercase;
+    font-size: 12px;
+    color: #aeb3b9;
+}
+
+.timeline {
+    clear: both;
+    list-style: none;
+    padding: 0;
+    margin: 0;
+    font-size: 13px;
+}
+
+.timeline > li {
+    padding-bottom: 24px;
+    margin-left: 50px;
+    position: relative;
+}
+
+.timeline > li:first-child:before {
+    top: 10px;
+}
+
+.timeline > li:last-child:before {
+    top: 0;
+    bottom: 0;
+    height: 10px;
+}
+
+.timeline > li:before {
+    top: 0;
+    bottom: 0;
+    position: absolute;
+    content: " ";
+    width: 1px;
+    background-color: #d8d8d8;
+    left: -40px;
+    margin-left: 1px;
+}
+
+.timeline .timeline-icon {
+    position: absolute;
+    top: 6px;
+    left: -46px;
+    z-index: 100;
+    margin: 0;
+    padding: 0;
+}
+
+.timeline .timeline-icon:before {
+    content: " ";
+    position: absolute;
+    display: block;
+    width: 15px;
+    height: 15px;
+    border-radius: 50%;
+    border: 3px solid #fff;
+    margin: 0;
+    top: 0;
+    left: 0;
+}
+
+.timeline .timeline-icon.request-created:before,
+.timeline .timeline-icon.request-assigned:before,
+.timeline .timeline-icon.request-revoked:before,
+.timeline .timeline-icon.request-cancelled:before {
+    background-color: #b8bdc1;
+}
+
+.timeline .timeline-icon.request-approved:before {
+    background-color: #67ba89;
+}
+
+.timeline .timeline-icon.request-processing:before {
+    background-color: #64c9ff;
+}
+
+.timeline .timeline-icon.request-forwarded:before {
+    background-color: #f7bc5a;
+}
+
+.timeline .timeline-icon.request-rejected:before {
+    background-color: #d5302a;
+}
+
+.timeline .timeline-description {
+    color: #4a5156;
+}
+
+.timeline .timeline-time {
+    color: #737373;
+}
+
+/**
+ * Table
+ */
+.table-body {
+    border-radius: 4px;
+    box-shadow: 0 1px 2px 0 rgba(0,0,0,.12);
+}
+
+.table-group .table-row:last-child {
+    border-bottom: 1px solid #bfbfc1;
+    border-bottom-right-radius: 4px;
+    border-bottom-left-radius: 4px;
+}
+
+.table-group .table-row:first-child, .table-group .table-row:first-child a {
+    border-top-right-radius: 4px;
+    border-top-left-radius: 4px;
+}
+
+.table-group .table-row:first-child {
+    border-top: 1px solid #bfbfc1;
+}
+
+.table-group .table-row {
+    border-bottom: 1px solid #e5e5e5;
+    border-left: 1px solid #bfbfc1;
+    border-right: 1px solid #bfbfc1;
+    line-height: 44px;
+    font-size: 15px;
+    position: relative;
+    word-wrap: break-word;
+}
+
+
+/**
+ * Unread indicator as overlay on hamburger menu
+ */
+.unread-indicator {
+    position: absolute;
+    display: block;
+    width: 14px;
+    height: 14px;
+    background-color: #4078c0;
+    border: 2px solid #333;
+    border-radius: 50%;
+}
+
+.navbar-inverse {
+    background-color: #00A6D6;
+    border-color: #E7E7E7;
+    color: white;
+}
+
+.navbar-inverse .navbar-brand {
+    color: white;
+}
+
+.navbar-inverse .navbar-nav > li > a {
+    color: white;
+}
+
+.navbar-header .unread-indicator {
+    top: 0;
+    right: 0;
+}
+
+.navbar-collapse .unread-indicator {
+    top: 10px;
+    right: 9px;
+}
+
+/**
+ * List group
+ */
+.list-group .item-icon {
+    padding-left: 3em;
+}
+
+.list-group .item-icon span.icon {
+    position: absolute;
+    top: 25px;
+    left: 1em;
+}
+
+.course-filter {
+    margin-bottom: 20px;
+}
+
+.course-filter .count {
+    float: right;
+    font-weight: bold;
+}
+
+/**
+ * Truncation
+ */
+.truncate {
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+/**
+ * Icon
+ */
+.icon.unread {
+    width: 12px;
+    height: 12px;
+    color: #fff;
+    text-align: center;
+    background-image: -webkit-linear-gradient(#7aa1d3, #4078c0);
+    background-image: linear-gradient(#7aa1d3, #4078c0);
+    background-clip: padding-box;
+    border-radius: 50%;
+}
+
+/**
+ * Pagehead
+ */
+.pagehead {
+    position: relative;
+    padding-top: 20px;
+    margin-top: -20px;
+    margin-bottom: 20px;
+    padding-bottom: 0;
+    background-color: #fafafa;
+    border-bottom: 1px solid #ddd;
+}
+
+.pagehead .nav-tabs {
+    border-bottom: 0;
+}
+
+/**
+ * Page
+ */
+.page-sub-header {
+    padding-bottom: 9px;
+    margin: 20px 0 20px;
+}
+
+.nav {
+    color: white;
+}
+
+.nav-tabs > li > a {
+    color: grey;
+}
+
+.nav.nav-tabs .fa {
+    margin-right: 5px;
+}
+
+/**
+ * Lab list
+ */
+.list-group-item.lab-item {
+    line-height: 30px;
+}
+
+/**
+ * Blankslate
+ */
+.blankslate {
+    position: relative;
+    padding: 30px;
+    text-align: center;
+    background-color: #fafafa;
+    border: 1px solid #e5e5e5;
+    border-radius: 3px;
+    box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05);
+}
+
+.blankslate h3 {
+    margin-top: 0;
+    margin-bottom: 0;
+    font-size: 1.5em;
+    line-height: 1.43;
+}
+
+.blankslate p {
+    margin: 0;
+}
+
+.breadcrumbs {
+    padding-top: 1em;
+}
+
+.container-alert {
+    padding-top: 1em;
+}
+
+.page-header {
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+.bootstrap-datetimepicker-widget.dropdown-menu {
+    display: block;
+    margin: 2px 0;
+    padding: 4px;
+    width: 300px !important;
+}
+.nav-pills .menu-link.active {
+    background: black !important;
+}
+
+
+.nav-pills, .show>.nav-pills .nav-link {
+    background: none;
+}
+
+.no-border-top th {
+    border-top: none !important;
+}
+
+.sort-open {
+    display: flex;
+}
+
+.lab-closed {
+    order: 1;
+}
+
+.lab-open {
+    order: 0;
+}
\ No newline at end of file
diff --git a/backend/src/main/resources/static/css/labview.css b/backend/src/main/resources/static/css/labview.css
new file mode 100644
index 000000000..faa978322
--- /dev/null
+++ b/backend/src/main/resources/static/css/labview.css
@@ -0,0 +1,21 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+select {
+    width: 33%;
+    text-overflow: ellipsis;
+}
\ No newline at end of file
diff --git a/backend/src/main/resources/static/css/tempusdominus-bootstrap-4.min.css b/backend/src/main/resources/static/css/tempusdominus-bootstrap-4.min.css
new file mode 100644
index 000000000..cae469b06
--- /dev/null
+++ b/backend/src/main/resources/static/css/tempusdominus-bootstrap-4.min.css
@@ -0,0 +1,204 @@
+/*@preserve
+ * Tempus Dominus Bootstrap4 v5.0.0-alpha14 (https://tempusdominus.github.io/bootstrap-4/)
+ * Copyright 2016-2017 Jonathan Peterson
+ * Licensed under MIT (https://github.com/tempusdominus/bootstrap-3/blob/master/LICENSE)
+ */
+
+.sr-only, .bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after, .bootstrap-datetimepicker-widget .btn[data-action="clear"]::after, .bootstrap-datetimepicker-widget .btn[data-action="today"]::after, .bootstrap-datetimepicker-widget .picker-switch::after, .bootstrap-datetimepicker-widget table th.prev::after, .bootstrap-datetimepicker-widget table th.next::after {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  margin: -1px;
+  padding: 0;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  border: 0; }
+
+.bootstrap-datetimepicker-widget {
+  list-style: none; }
+.bootstrap-datetimepicker-widget.dropdown-menu {
+  display: block;
+  margin: 2px 0;
+  padding: 4px;
+  width: 14rem; }
+@media (min-width: 576px) {
+  .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
+    width: 38em; } }
+@media (min-width: 768px) {
+  .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
+    width: 38em; } }
+@media (min-width: 992px) {
+  .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
+    width: 38em; } }
+.bootstrap-datetimepicker-widget.dropdown-menu:before, .bootstrap-datetimepicker-widget.dropdown-menu:after {
+  content: '';
+  display: inline-block;
+  position: absolute; }
+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
+  border-left: 7px solid transparent;
+  border-right: 7px solid transparent;
+  border-bottom: 7px solid #ccc;
+  border-bottom-color: rgba(0, 0, 0, 0.2);
+  top: -7px;
+  left: 7px; }
+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  border-bottom: 6px solid white;
+  top: -6px;
+  left: 8px; }
+.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
+  border-left: 7px solid transparent;
+  border-right: 7px solid transparent;
+  border-top: 7px solid #ccc;
+  border-top-color: rgba(0, 0, 0, 0.2);
+  bottom: -7px;
+  left: 6px; }
+.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  border-top: 6px solid white;
+  bottom: -6px;
+  left: 7px; }
+.bootstrap-datetimepicker-widget.dropdown-menu.float-right:before {
+  left: auto;
+  right: 6px; }
+.bootstrap-datetimepicker-widget.dropdown-menu.float-right:after {
+  left: auto;
+  right: 7px; }
+.bootstrap-datetimepicker-widget .list-unstyled {
+  margin: 0; }
+.bootstrap-datetimepicker-widget a[data-action] {
+  padding: 6px 0; }
+.bootstrap-datetimepicker-widget a[data-action]:active {
+  box-shadow: none; }
+.bootstrap-datetimepicker-widget .timepicker-hour, .bootstrap-datetimepicker-widget .timepicker-minute, .bootstrap-datetimepicker-widget .timepicker-second {
+  width: 54px;
+  font-weight: bold;
+  font-size: 1.2em;
+  margin: 0; }
+.bootstrap-datetimepicker-widget button[data-action] {
+  padding: 6px; }
+.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
+  content: "Increment Hours"; }
+.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
+  content: "Increment Minutes"; }
+.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
+  content: "Decrement Hours"; }
+.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
+  content: "Decrement Minutes"; }
+.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
+  content: "Show Hours"; }
+.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
+  content: "Show Minutes"; }
+.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
+  content: "Toggle AM/PM"; }
+.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
+  content: "Clear the picker"; }
+.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
+  content: "Set the date to today"; }
+.bootstrap-datetimepicker-widget .picker-switch {
+  text-align: center; }
+.bootstrap-datetimepicker-widget .picker-switch::after {
+  content: "Toggle Date and Time Screens"; }
+.bootstrap-datetimepicker-widget .picker-switch td {
+  padding: 0;
+  margin: 0;
+  height: auto;
+  width: auto;
+  line-height: inherit; }
+.bootstrap-datetimepicker-widget .picker-switch td span {
+  line-height: 2.5;
+  height: 2.5em;
+  width: 100%; }
+.bootstrap-datetimepicker-widget table {
+  width: 100%;
+  margin: 0; }
+.bootstrap-datetimepicker-widget table td,
+.bootstrap-datetimepicker-widget table th {
+  text-align: center;
+  border-radius: 0.25rem; }
+.bootstrap-datetimepicker-widget table th {
+  height: 20px;
+  line-height: 20px;
+  width: 20px; }
+.bootstrap-datetimepicker-widget table th.picker-switch {
+  width: 145px; }
+.bootstrap-datetimepicker-widget table th.disabled, .bootstrap-datetimepicker-widget table th.disabled:hover {
+  background: none;
+  color: #868e96;
+  cursor: not-allowed; }
+.bootstrap-datetimepicker-widget table th.prev::after {
+  content: "Previous Month"; }
+.bootstrap-datetimepicker-widget table th.next::after {
+  content: "Next Month"; }
+.bootstrap-datetimepicker-widget table thead tr:first-child th {
+  cursor: pointer; }
+.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
+  background: #e9ecef; }
+.bootstrap-datetimepicker-widget table td {
+  height: 54px;
+  line-height: 54px;
+  width: 54px; }
+.bootstrap-datetimepicker-widget table td.cw {
+  font-size: .8em;
+  height: 20px;
+  line-height: 20px;
+  color: #868e96; }
+.bootstrap-datetimepicker-widget table td.day {
+  height: 20px;
+  line-height: 20px;
+  width: 20px; }
+.bootstrap-datetimepicker-widget table td.day:hover, .bootstrap-datetimepicker-widget table td.hour:hover, .bootstrap-datetimepicker-widget table td.minute:hover, .bootstrap-datetimepicker-widget table td.second:hover {
+  background: #e9ecef;
+  cursor: pointer; }
+.bootstrap-datetimepicker-widget table td.old, .bootstrap-datetimepicker-widget table td.new {
+  color: #868e96; }
+.bootstrap-datetimepicker-widget table td.today {
+  position: relative; }
+.bootstrap-datetimepicker-widget table td.today:before {
+  content: '';
+  display: inline-block;
+  border: solid transparent;
+  border-width: 0 0 7px 7px;
+  border-bottom-color: #007bff;
+  border-top-color: rgba(0, 0, 0, 0.2);
+  position: absolute;
+  bottom: 4px;
+  right: 4px; }
+.bootstrap-datetimepicker-widget table td.active, .bootstrap-datetimepicker-widget table td.active:hover {
+  background-color: #007bff;
+  color: #fff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); }
+.bootstrap-datetimepicker-widget table td.active.today:before {
+  border-bottom-color: #fff; }
+.bootstrap-datetimepicker-widget table td.disabled, .bootstrap-datetimepicker-widget table td.disabled:hover {
+  background: none;
+  color: #868e96;
+  cursor: not-allowed; }
+.bootstrap-datetimepicker-widget table td span {
+  display: inline-block;
+  width: 54px;
+  height: 54px;
+  line-height: 54px;
+  margin: 2px 1.5px;
+  cursor: pointer;
+  border-radius: 0.25rem; }
+.bootstrap-datetimepicker-widget table td span:hover {
+  background: #e9ecef; }
+.bootstrap-datetimepicker-widget table td span.active {
+  background-color: #007bff;
+  color: #fff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); }
+.bootstrap-datetimepicker-widget table td span.old {
+  color: #868e96; }
+.bootstrap-datetimepicker-widget table td span.disabled, .bootstrap-datetimepicker-widget table td span.disabled:hover {
+  background: none;
+  color: #868e96;
+  cursor: not-allowed; }
+.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
+  height: 27px;
+  line-height: 27px; }
+
+.input-group.date .input-group-append {
+  cursor: pointer; }
\ No newline at end of file
diff --git a/backend/src/main/resources/static/favicon.ico b/backend/src/main/resources/static/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..338eee1bbca2d932df3483fd036e1b0c1a7539d6
GIT binary patch
literal 15086
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

literal 0
HcmV?d00001

diff --git a/backend/src/main/resources/static/img/icon.png b/backend/src/main/resources/static/img/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef71d84b54ae70b5a43816d7f7deb7afb5149a47
GIT binary patch
literal 1463
zcmeAS@N?(olHy`uVBq!ia0y~yU^u|Qz_6c#je&t-#fr<q3=FJwo-U3d6}R5ry;%6l
zO@u9Bssrbg2giNXwrMsBX&h9&z^EF)bls0}$^q}RGiSTK7O)(ua&g+hrt)^3z=vAn
z-zB;6ymR8Mc56EZGGGNY$$O;z<JjavcS;BAGxxv9HQ&Z`Kw12M821rn$v4OTEcoHA
z&)J_8BKqfBLeCzZJG&oVjI6i6x5vaL>0!I!=T8OeZU3d1PgCcrkN#_Y=ju#5k*|+@
zF1ByH__<jBSg7>oT>)QyZ(P;HWUe`9=W_9=*}=)Lo<0`4J!`vE?%ADzS(Q7sE}Ad8
zf8EvXlHFNOJbTSEFPx5-nw?e0u-9xw)o0&Dhs-ZNP2|3ROw3%-{b4?rbo7@y8W)dz
z+x@6q+jwtYxtJtZ{KsP9^uN82o(ER{%{}(eEUoJHr!&zaC6n(*R?U2w{c-!vGf`o0
zgYM4Xe)8yDk@^=;+mHU257=+=K(Finhl5i>*%=Ko)w(MMw-;My{irrPZt<yX;-%=*
zkLJt&>0S2yyZKCJnRm`Pdt#S-5xMzIB-C*GOq1(^=e|Adyx_QHS^B%j&kNaIfA?<s
z*?K5mEUf=TM$nP_%Ves%T_VpaFS}~-U7`0|`;u9AZhX6&d+M>$d%um*OHVF)x9aRl
z_0369^S33x$W@+db$Z_FM_+dR-t+j8`A^lAGukh<%Zl&cY(ICK{q%F&HZQuH{>$R`
zW$w!-lCpP|SZU9CFCu@=u||i>Q&;Ky7GI`-FxER|+iw~(t8BM1eCTW0Xjk`WweRgM
zpTaI4WvRGaFe!5RT+RNl3Abv*R#@A=RAIO*R-as!C})_l<JFVqf}LIrNoq46zdEx|
zezS1+q-DYh{oa=%8Fob^<y`tdy){(c$hxLzb@y)V*Y}E_BnQ1X^01|3#?Sv*m;UXz
zI@3>Pn@Vik+IP%m6Rt+PGfHUI{mQuF$iQY=U4Qg^=o7vJOYbJdmwM@|9kTT4{&rr{
zA^mg1Q_-&au!!f&)!RC&|NU&;yl-0jZWBx6hq}HcJHsCvx7zFH<a}fhe))Gs_}~3z
z><kQ-X3KoNzunF+ugkW+#xL&v_0!uPKRs@B`?%(}pEusc&dAAopD|~S#r{42=j{9U
z^ZT~1H$<b>nJ+wlT#bQAfhc625%TBz)<uTNl94-3yBs$ZVaWUFWpuW-fvJIk17re6
z1A_ts=^Ty*2At@?R^|_TepvQtZMu7+G$;Q4H;KZSeX2nWdcTrCT6Xasc;CH$-liBv
zyKA#oFuYl(;1$4dj_atjbi?!SU!(Q=Uq3Eh#KQWZXtiKuhW`84Mu%_K%T>obe6@^)
zRiS$Q++R7rZ$B!QsJ5|uu68GqVNrPWq0ofu>+N;Jn)k6>;+nuvaw|W4^109)j-V}e
zJD;iE$z)iy`hZp_$E`J7tq=Cp{fSv((Pj`7+gmY#p+(Lv^kb;*MVl|5O1bQ}By9Aa
zcl`hMOTrCe;f<v;jaYtbJKdPT<tk@(_t2>`;Ro9*-Mixc&kj_t;5eWkl+zVkedCW%
z!;aMl-sRLDKDs}si0Q&?&cfwK_s@3DsAW;;%u3p|Yw4r&rS?Mn5)IK@jgQ0|_9%L6
zF`T;k`SyGI3^!Y!o|f@mVwhgL+Lo!Hcbdk_w$O9DW-JQP0*7kNV&eq&o%>;b&A3&c
zVXu(d{HyEUY1P*6*K@wu{>@wRV)esQAKvHkB^tl2-MdD(XT7%PkDIaCoA<{ankk31
eJVX}k`@`(BW1Z~Ht6$fEggssTT-G@yGywo8fQj$`

literal 0
HcmV?d00001

diff --git a/backend/src/main/resources/static/img/tudelft-logo.png b/backend/src/main/resources/static/img/tudelft-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..6466ce9c7136cb5207b03bebf837a68cbc8e3a4f
GIT binary patch
literal 4627
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
HP6<r_U%X7M

literal 0
HcmV?d00001

diff --git a/backend/src/main/resources/static/js/bootstrap-multiselect.js b/backend/src/main/resources/static/js/bootstrap-multiselect.js
new file mode 100644
index 000000000..5fb4c18cf
--- /dev/null
+++ b/backend/src/main/resources/static/js/bootstrap-multiselect.js
@@ -0,0 +1,1416 @@
+/**
+ * Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect)
+ * 
+ * Apache License, Version 2.0:
+ * Copyright (c) 2012 - 2015 David Stutz
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a
+ * copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ * 
+ * BSD 3-Clause License:
+ * Copyright (c) 2012 - 2015 David Stutz
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *    - Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *    - Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *    - Neither the name of David Stutz nor the names of its contributors may be
+ *      used to endorse or promote products derived from this software without
+ *      specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+!function ($) {
+    "use strict";// jshint ;_;
+
+    if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
+        ko.bindingHandlers.multiselect = {
+            after: ['options', 'value', 'selectedOptions'],
+
+            init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
+                var $element = $(element);
+                var config = ko.toJS(valueAccessor());
+
+                $element.multiselect(config);
+
+                if (allBindings.has('options')) {
+                    var options = allBindings.get('options');
+                    if (ko.isObservable(options)) {
+                        ko.computed({
+                            read: function() {
+                                options();
+                                setTimeout(function() {
+                                    var ms = $element.data('multiselect');
+                                    if (ms)
+                                        ms.updateOriginalOptions();//Not sure how beneficial this is.
+                                    $element.multiselect('rebuild');
+                                }, 1);
+                            },
+                            disposeWhenNodeIsRemoved: element
+                        });
+                    }
+                }
+
+                //value and selectedOptions are two-way, so these will be triggered even by our own actions.
+                //It needs some way to tell if they are triggered because of us or because of outside change.
+                //It doesn't loop but it's a waste of processing.
+                if (allBindings.has('value')) {
+                    var value = allBindings.get('value');
+                    if (ko.isObservable(value)) {
+                        ko.computed({
+                            read: function() {
+                                value();
+                                setTimeout(function() {
+                                    $element.multiselect('refresh');
+                                }, 1);
+                            },
+                            disposeWhenNodeIsRemoved: element
+                        }).extend({ rateLimit: 100, notifyWhenChangesStop: true });
+                    }
+                }
+
+                //Switched from arrayChange subscription to general subscription using 'refresh'.
+                //Not sure performance is any better using 'select' and 'deselect'.
+                if (allBindings.has('selectedOptions')) {
+                    var selectedOptions = allBindings.get('selectedOptions');
+                    if (ko.isObservable(selectedOptions)) {
+                        ko.computed({
+                            read: function() {
+                                selectedOptions();
+                                setTimeout(function() {
+                                    $element.multiselect('refresh');
+                                }, 1);
+                            },
+                            disposeWhenNodeIsRemoved: element
+                        }).extend({ rateLimit: 100, notifyWhenChangesStop: true });
+                    }
+                }
+
+                ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
+                    $element.multiselect('destroy');
+                });
+            },
+
+            update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
+                var $element = $(element);
+                var config = ko.toJS(valueAccessor());
+
+                $element.multiselect('setOptions', config);
+                $element.multiselect('rebuild');
+            }
+        };
+    }
+
+    function forEach(array, callback) {
+        for (var index = 0; index < array.length; ++index) {
+            callback(array[index], index);
+        }
+    }
+
+    /**
+     * Constructor to create a new multiselect using the given select.
+     *
+     * @param {jQuery} select
+     * @param {Object} options
+     * @returns {Multiselect}
+     */
+    function Multiselect(select, options) {
+
+        this.$select = $(select);
+        
+        // Placeholder via data attributes
+        if (this.$select.attr("data-placeholder")) {
+            options.nonSelectedText = this.$select.data("placeholder");
+        }
+        
+        this.options = this.mergeOptions($.extend({}, options, this.$select.data()));
+
+        // Initialization.
+        // We have to clone to create a new reference.
+        this.originalOptions = this.$select.clone()[0].options;
+        this.query = '';
+        this.searchTimeout = null;
+        this.lastToggledInput = null
+
+        this.options.multiple = this.$select.attr('multiple') === "multiple";
+        this.options.onChange = $.proxy(this.options.onChange, this);
+        this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this);
+        this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this);
+        this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this);
+        this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this);
+        
+        // Build select all if enabled.
+        this.buildContainer();
+        this.buildButton();
+        this.buildDropdown();
+        this.buildSelectAll();
+        this.buildDropdownOptions();
+        this.buildFilter();
+
+        this.updateButtonText();
+        this.updateSelectAll();
+
+        if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
+            this.disable();
+        }
+        
+        this.$select.hide().after(this.$container);
+    };
+
+    Multiselect.prototype = {
+
+        defaults: {
+            /**
+             * Default text function will either print 'None selected' in case no
+             * option is selected or a list of the selected options up to a length
+             * of 3 selected options.
+             * 
+             * @param {jQuery} options
+             * @param {jQuery} select
+             * @returns {String}
+             */
+            buttonText: function(options, select) {
+                if (options.length === 0) {
+                    return this.nonSelectedText;
+                }
+                else if (this.allSelectedText 
+                            && options.length === $('option', $(select)).length 
+                            && $('option', $(select)).length !== 1 
+                            && this.multiple) {
+
+                    if (this.selectAllNumber) {
+                        return this.allSelectedText + ' (' + options.length + ')';
+                    }
+                    else {
+                        return this.allSelectedText;
+                    }
+                }
+                else if (options.length > this.numberDisplayed) {
+                    return options.length + ' ' + this.nSelectedText;
+                }
+                else {
+                    var selected = '';
+                    var delimiter = this.delimiterText;
+                    
+                    options.each(function() {
+                        var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();
+                        selected += label + delimiter;
+                    });
+                    
+                    return selected.substr(0, selected.length - 2);
+                }
+            },
+            /**
+             * Updates the title of the button similar to the buttonText function.
+             * 
+             * @param {jQuery} options
+             * @param {jQuery} select
+             * @returns {@exp;selected@call;substr}
+             */
+            buttonTitle: function(options, select) {
+                if (options.length === 0) {
+                    return this.nonSelectedText;
+                }
+                else {
+                    var selected = '';
+                    var delimiter = this.delimiterText;
+                    
+                    options.each(function () {
+                        var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();
+                        selected += label + delimiter;
+                    });
+                    return selected.substr(0, selected.length - 2);
+                }
+            },
+            /**
+             * Create a label.
+             *
+             * @param {jQuery} element
+             * @returns {String}
+             */
+            optionLabel: function(element){
+                return $(element).attr('label') || $(element).text();
+            },
+            /**
+             * Triggered on change of the multiselect.
+             * 
+             * Not triggered when selecting/deselecting options manually.
+             * 
+             * @param {jQuery} option
+             * @param {Boolean} checked
+             */
+            onChange : function(option, checked) {
+
+            },
+            /**
+             * Triggered when the dropdown is shown.
+             *
+             * @param {jQuery} event
+             */
+            onDropdownShow: function(event) {
+
+            },
+            /**
+             * Triggered when the dropdown is hidden.
+             *
+             * @param {jQuery} event
+             */
+            onDropdownHide: function(event) {
+
+            },
+            /**
+             * Triggered after the dropdown is shown.
+             * 
+             * @param {jQuery} event
+             */
+            onDropdownShown: function(event) {
+                
+            },
+            /**
+             * Triggered after the dropdown is hidden.
+             * 
+             * @param {jQuery} event
+             */
+            onDropdownHidden: function(event) {
+                
+            },
+            /**
+             * Triggered on select all.
+             */
+            onSelectAll: function() {
+                
+            },
+            enableHTML: false,
+            buttonClass: 'btn btn-default',
+            inheritClass: false,
+            buttonWidth: 'auto',
+            buttonContainer: '<div class="btn-group" />',
+            dropRight: false,
+            selectedClass: 'active',
+            // Maximum height of the dropdown menu.
+            // If maximum height is exceeded a scrollbar will be displayed.
+            maxHeight: false,
+            checkboxName: false,
+            includeSelectAllOption: false,
+            includeSelectAllIfMoreThan: 0,
+            selectAllText: ' Select all',
+            selectAllValue: 'multiselect-all',
+            selectAllName: false,
+            selectAllNumber: true,
+            enableFiltering: false,
+            enableCaseInsensitiveFiltering: false,
+            enableClickableOptGroups: false,
+            filterPlaceholder: 'Search',
+            // possible options: 'text', 'value', 'both'
+            filterBehavior: 'text',
+            includeFilterClearBtn: true,
+            preventInputChangeEvent: false,
+            nonSelectedText: 'None selected',
+            nSelectedText: 'selected',
+            allSelectedText: 'All selected',
+            numberDisplayed: 3,
+            disableIfEmpty: false,
+            delimiterText: ', ',
+            templates: {
+                button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>',
+                ul: '<ul class="multiselect-container dropdown-menu"></ul>',
+                filter: '<li class="multiselect-item filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>',
+                filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default multiselect-clear-filter" type="button"><i class="glyphicon glyphicon-remove-circle"></i></button></span>',
+                li: '<li><a tabindex="0"><label></label></a></li>',
+                divider: '<li class="multiselect-item divider"></li>',
+                liGroup: '<li class="multiselect-item multiselect-group"><label></label></li>'
+            }
+        },
+
+        constructor: Multiselect,
+
+        /**
+         * Builds the container of the multiselect.
+         */
+        buildContainer: function() {
+            this.$container = $(this.options.buttonContainer);
+            this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
+            this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
+            this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);
+            this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);
+        },
+
+        /**
+         * Builds the button of the multiselect.
+         */
+        buildButton: function() {
+            this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
+            if (this.$select.attr('class') && this.options.inheritClass) {
+                this.$button.addClass(this.$select.attr('class'));
+            }
+            // Adopt active state.
+            if (this.$select.prop('disabled')) {
+                this.disable();
+            }
+            else {
+                this.enable();
+            }
+
+            // Manually add button width if set.
+            if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
+                this.$button.css({
+                    'width' : this.options.buttonWidth,
+                    'overflow' : 'hidden',
+                    'text-overflow' : 'ellipsis'
+                });
+                this.$container.css({
+                    'width': this.options.buttonWidth
+                });
+            }
+
+            // Keep the tab index from the select.
+            var tabindex = this.$select.attr('tabindex');
+            if (tabindex) {
+                this.$button.attr('tabindex', tabindex);
+            }
+
+            this.$container.prepend(this.$button);
+        },
+
+        /**
+         * Builds the ul representing the dropdown menu.
+         */
+        buildDropdown: function() {
+
+            // Build ul.
+            this.$ul = $(this.options.templates.ul);
+
+            if (this.options.dropRight) {
+                this.$ul.addClass('pull-right');
+            }
+
+            // Set max height of dropdown menu to activate auto scrollbar.
+            if (this.options.maxHeight) {
+                // TODO: Add a class for this option to move the css declarations.
+                this.$ul.css({
+                    'max-height': this.options.maxHeight + 'px',
+                    'overflow-y': 'auto',
+                    'overflow-x': 'hidden'
+                });
+            }
+
+            this.$container.append(this.$ul);
+        },
+
+        /**
+         * Build the dropdown options and binds all nessecary events.
+         * 
+         * Uses createDivider and createOptionValue to create the necessary options.
+         */
+        buildDropdownOptions: function() {
+
+            this.$select.children().each($.proxy(function(index, element) {
+
+                var $element = $(element);
+                // Support optgroups and options without a group simultaneously.
+                var tag = $element.prop('tagName')
+                    .toLowerCase();
+            
+                if ($element.prop('value') === this.options.selectAllValue) {
+                    return;
+                }
+
+                if (tag === 'optgroup') {
+                    this.createOptgroup(element);
+                }
+                else if (tag === 'option') {
+
+                    if ($element.data('role') === 'divider') {
+                        this.createDivider();
+                    }
+                    else {
+                        this.createOptionValue(element);
+                    }
+
+                }
+
+                // Other illegal tags will be ignored.
+            }, this));
+
+            // Bind the change event on the dropdown elements.
+            $('li input', this.$ul).on('change', $.proxy(function(event) {
+                var $target = $(event.target);
+
+                var checked = $target.prop('checked') || false;
+                var isSelectAllOption = $target.val() === this.options.selectAllValue;
+
+                // Apply or unapply the configured selected class.
+                if (this.options.selectedClass) {
+                    if (checked) {
+                        $target.closest('li')
+                            .addClass(this.options.selectedClass);
+                    }
+                    else {
+                        $target.closest('li')
+                            .removeClass(this.options.selectedClass);
+                    }
+                }
+
+                // Get the corresponding option.
+                var value = $target.val();
+                var $option = this.getOptionByValue(value);
+
+                var $optionsNotThis = $('option', this.$select).not($option);
+                var $checkboxesNotThis = $('input', this.$container).not($target);
+
+                if (isSelectAllOption) {
+                    if (checked) {
+                        this.selectAll();
+                    }
+                    else {
+                        this.deselectAll();
+                    }
+                }
+
+                if(!isSelectAllOption){
+                    if (checked) {
+                        $option.prop('selected', true);
+
+                        if (this.options.multiple) {
+                            // Simply select additional option.
+                            $option.prop('selected', true);
+                        }
+                        else {
+                            // Unselect all other options and corresponding checkboxes.
+                            if (this.options.selectedClass) {
+                                $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass);
+                            }
+
+                            $($checkboxesNotThis).prop('checked', false);
+                            $optionsNotThis.prop('selected', false);
+
+                            // It's a single selection, so close.
+                            this.$button.click();
+                        }
+
+                        if (this.options.selectedClass === "active") {
+                            $optionsNotThis.closest("a").css("outline", "");
+                        }
+                    }
+                    else {
+                        // Unselect option.
+                        $option.prop('selected', false);
+                    }
+                }
+
+                this.$select.change();
+
+                this.updateButtonText();
+                this.updateSelectAll();
+
+                this.options.onChange($option, checked);
+
+                if(this.options.preventInputChangeEvent) {
+                    return false;
+                }
+            }, this));
+
+            $('li a', this.$ul).on('mousedown', function(e) {
+                if (e.shiftKey) {
+                    // Prevent selecting text by Shift+click
+                    return false;
+                }
+            });
+        
+            $('li a', this.$ul).on('touchstart click', $.proxy(function(event) {
+                event.stopPropagation();
+
+                var $target = $(event.target);
+                
+                if (event.shiftKey && this.options.multiple) {
+                    if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)
+                        event.preventDefault();
+                        $target = $target.find("input");
+                        $target.prop("checked", !$target.prop("checked"));
+                    }
+                    var checked = $target.prop('checked') || false;
+
+                    if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range
+                        var from = $target.closest("li").index();
+                        var to = this.lastToggledInput.closest("li").index();
+                        
+                        if (from > to) { // Swap the indices
+                            var tmp = to;
+                            to = from;
+                            from = tmp;
+                        }
+                        
+                        // Make sure we grab all elements since slice excludes the last index
+                        ++to;
+                        
+                        // Change the checkboxes and underlying options
+                        var range = this.$ul.find("li").slice(from, to).find("input");
+                        
+                        range.prop('checked', checked);
+                        
+                        if (this.options.selectedClass) {
+                            range.closest('li')
+                                .toggleClass(this.options.selectedClass, checked);
+                        }
+                        
+                        for (var i = 0, j = range.length; i < j; i++) {
+                            var $checkbox = $(range[i]);
+
+                            var $option = this.getOptionByValue($checkbox.val());
+
+                            $option.prop('selected', checked);
+                        }                   
+                    }
+                    
+                    // Trigger the select "change" event
+                    $target.trigger("change");
+                }
+                
+                // Remembers last clicked option
+                if($target.is("input") && !$target.closest("li").is(".multiselect-item")){
+                    this.lastToggledInput = $target;
+                }
+
+                $target.blur();
+            }, this));
+
+            // Keyboard support.
+            this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {
+                if ($('input[type="text"]', this.$container).is(':focus')) {
+                    return;
+                }
+
+                if (event.keyCode === 9 && this.$container.hasClass('open')) {
+                    this.$button.click();
+                }
+                else {
+                    var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible");
+
+                    if (!$items.length) {
+                        return;
+                    }
+
+                    var index = $items.index($items.filter(':focus'));
+
+                    // Navigation up.
+                    if (event.keyCode === 38 && index > 0) {
+                        index--;
+                    }
+                    // Navigate down.
+                    else if (event.keyCode === 40 && index < $items.length - 1) {
+                        index++;
+                    }
+                    else if (!~index) {
+                        index = 0;
+                    }
+
+                    var $current = $items.eq(index);
+                    $current.focus();
+
+                    if (event.keyCode === 32 || event.keyCode === 13) {
+                        var $checkbox = $current.find('input');
+
+                        $checkbox.prop("checked", !$checkbox.prop("checked"));
+                        $checkbox.change();
+                    }
+
+                    event.stopPropagation();
+                    event.preventDefault();
+                }
+            }, this));
+
+            if(this.options.enableClickableOptGroups && this.options.multiple) {
+                $('li.multiselect-group', this.$ul).on('click', $.proxy(function(event) {
+                    event.stopPropagation();
+
+                    var group = $(event.target).parent();
+
+                    // Search all option in optgroup
+                    var $options = group.nextUntil('li.multiselect-group');
+                    var $visibleOptions = $options.filter(":visible:not(.disabled)");
+
+                    // check or uncheck items
+                    var allChecked = true;
+                    var optionInputs = $visibleOptions.find('input');
+                    optionInputs.each(function() {
+                        allChecked = allChecked && $(this).prop('checked');
+                    });
+
+                    optionInputs.prop('checked', !allChecked).trigger('change');
+               }, this));
+            }
+        },
+
+        /**
+         * Create an option using the given select option.
+         *
+         * @param {jQuery} element
+         */
+        createOptionValue: function(element) {
+            var $element = $(element);
+            if ($element.is(':selected')) {
+                $element.prop('selected', true);
+            }
+
+            // Support the label attribute on options.
+            var label = this.options.optionLabel(element);
+            var value = $element.val();
+            var inputType = this.options.multiple ? "checkbox" : "radio";
+
+            var $li = $(this.options.templates.li);
+            var $label = $('label', $li);
+            $label.addClass(inputType);
+
+            if (this.options.enableHTML) {
+                $label.html(" " + label);
+            }
+            else {
+                $label.text(" " + label);
+            }
+        
+            var $checkbox = $('<input/>').attr('type', inputType);
+
+            if (this.options.checkboxName) {
+                $checkbox.attr('name', this.options.checkboxName);
+            }
+            $label.prepend($checkbox);
+
+            var selected = $element.prop('selected') || false;
+            $checkbox.val(value);
+
+            if (value === this.options.selectAllValue) {
+                $li.addClass("multiselect-item multiselect-all");
+                $checkbox.parent().parent()
+                    .addClass('multiselect-all');
+            }
+
+            $label.attr('title', $element.attr('title'));
+
+            this.$ul.append($li);
+
+            if ($element.is(':disabled')) {
+                $checkbox.attr('disabled', 'disabled')
+                    .prop('disabled', true)
+                    .closest('a')
+                    .attr("tabindex", "-1")
+                    .closest('li')
+                    .addClass('disabled');
+            }
+
+            $checkbox.prop('checked', selected);
+
+            if (selected && this.options.selectedClass) {
+                $checkbox.closest('li')
+                    .addClass(this.options.selectedClass);
+            }
+        },
+
+        /**
+         * Creates a divider using the given select option.
+         *
+         * @param {jQuery} element
+         */
+        createDivider: function(element) {
+            var $divider = $(this.options.templates.divider);
+            this.$ul.append($divider);
+        },
+
+        /**
+         * Creates an optgroup.
+         *
+         * @param {jQuery} group
+         */
+        createOptgroup: function(group) {
+            var groupName = $(group).prop('label');
+
+            // Add a header for the group.
+            var $li = $(this.options.templates.liGroup);
+            
+            if (this.options.enableHTML) {
+                $('label', $li).html(groupName);
+            }
+            else {
+                $('label', $li).text(groupName);
+            }
+            
+            if (this.options.enableClickableOptGroups) {
+                $li.addClass('multiselect-group-clickable');
+            }
+
+            this.$ul.append($li);
+
+            if ($(group).is(':disabled')) {
+                $li.addClass('disabled');
+            }
+
+            // Add the options of the group.
+            $('option', group).each($.proxy(function(index, element) {
+                this.createOptionValue(element);
+            }, this));
+        },
+
+        /**
+         * Build the selct all.
+         * 
+         * Checks if a select all has already been created.
+         */
+        buildSelectAll: function() {
+            if (typeof this.options.selectAllValue === 'number') {
+                this.options.selectAllValue = this.options.selectAllValue.toString();
+            }
+            
+            var alreadyHasSelectAll = this.hasSelectAll();
+
+            if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple
+                    && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {
+
+                // Check whether to add a divider after the select all.
+                if (this.options.includeSelectAllDivider) {
+                    this.$ul.prepend($(this.options.templates.divider));
+                }
+
+                var $li = $(this.options.templates.li);
+                $('label', $li).addClass("checkbox");
+                
+                if (this.options.enableHTML) {
+                    $('label', $li).html(" " + this.options.selectAllText);
+                }
+                else {
+                    $('label', $li).text(" " + this.options.selectAllText);
+                }
+                
+                if (this.options.selectAllName) {
+                    $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />');
+                }
+                else {
+                    $('label', $li).prepend('<input type="checkbox" />');
+                }
+                
+                var $checkbox = $('input', $li);
+                $checkbox.val(this.options.selectAllValue);
+
+                $li.addClass("multiselect-item multiselect-all");
+                $checkbox.parent().parent()
+                    .addClass('multiselect-all');
+
+                this.$ul.prepend($li);
+
+                $checkbox.prop('checked', false);
+            }
+        },
+
+        /**
+         * Builds the filter.
+         */
+        buildFilter: function() {
+
+            // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
+            if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
+                var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering);
+
+                if (this.$select.find('option').length >= enableFilterLength) {
+
+                    this.$filter = $(this.options.templates.filter);
+                    $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);
+                    
+                    // Adds optional filter clear button
+                    if(this.options.includeFilterClearBtn){
+                        var clearBtn = $(this.options.templates.filterClearBtn);
+                        clearBtn.on('click', $.proxy(function(event){
+                            clearTimeout(this.searchTimeout);
+                            this.$filter.find('.multiselect-search').val('');
+                            $('li', this.$ul).show().removeClass("filter-hidden");
+                            this.updateSelectAll();
+                        }, this));
+                        this.$filter.find('.input-group').append(clearBtn);
+                    }
+                    
+                    this.$ul.prepend(this.$filter);
+
+                    this.$filter.val(this.query).on('click', function(event) {
+                        event.stopPropagation();
+                    }).on('input keydown', $.proxy(function(event) {
+                        // Cancel enter key default behaviour
+                        if (event.which === 13) {
+                          event.preventDefault();
+                        }
+                        
+                        // This is useful to catch "keydown" events after the browser has updated the control.
+                        clearTimeout(this.searchTimeout);
+
+                        this.searchTimeout = this.asyncFunction($.proxy(function() {
+
+                            if (this.query !== event.target.value) {
+                                this.query = event.target.value;
+
+                                var currentGroup, currentGroupVisible;
+                                $.each($('li', this.$ul), $.proxy(function(index, element) {
+                                    var value = $('input', element).length > 0 ? $('input', element).val() : "";
+                                    var text = $('label', element).text();
+
+                                    var filterCandidate = '';
+                                    if ((this.options.filterBehavior === 'text')) {
+                                        filterCandidate = text;
+                                    }
+                                    else if ((this.options.filterBehavior === 'value')) {
+                                        filterCandidate = value;
+                                    }
+                                    else if (this.options.filterBehavior === 'both') {
+                                        filterCandidate = text + '\n' + value;
+                                    }
+
+                                    if (value !== this.options.selectAllValue && text) {
+                                        // By default lets assume that element is not
+                                        // interesting for this search.
+                                        var showElement = false;
+
+                                        if (this.options.enableCaseInsensitiveFiltering && filterCandidate.toLowerCase().indexOf(this.query.toLowerCase()) > -1) {
+                                            showElement = true;
+                                        }
+                                        else if (filterCandidate.indexOf(this.query) > -1) {
+                                            showElement = true;
+                                        }
+
+                                        // Toggle current element (group or group item) according to showElement boolean.
+                                        $(element).toggle(showElement).toggleClass('filter-hidden', !showElement);
+                                        
+                                        // Differentiate groups and group items.
+                                        if ($(element).hasClass('multiselect-group')) {
+                                            // Remember group status.
+                                            currentGroup = element;
+                                            currentGroupVisible = showElement;
+                                        }
+                                        else {
+                                            // Show group name when at least one of its items is visible.
+                                            if (showElement) {
+                                                $(currentGroup).show().removeClass('filter-hidden');
+                                            }
+                                            
+                                            // Show all group items when group name satisfies filter.
+                                            if (!showElement && currentGroupVisible) {
+                                                $(element).show().removeClass('filter-hidden');
+                                            }
+                                        }
+                                    }
+                                }, this));
+                            }
+
+                            this.updateSelectAll();
+                        }, this), 300, this);
+                    }, this));
+                }
+            }
+        },
+
+        /**
+         * Unbinds the whole plugin.
+         */
+        destroy: function() {
+            this.$container.remove();
+            this.$select.show();
+            this.$select.data('multiselect', null);
+        },
+
+        /**
+         * Refreshs the multiselect based on the selected options of the select.
+         */
+        refresh: function() {
+            $('option', this.$select).each($.proxy(function(index, element) {
+                var $input = $('li input', this.$ul).filter(function() {
+                    return $(this).val() === $(element).val();
+                });
+
+                if ($(element).is(':selected')) {
+                    $input.prop('checked', true);
+
+                    if (this.options.selectedClass) {
+                        $input.closest('li')
+                            .addClass(this.options.selectedClass);
+                    }
+                }
+                else {
+                    $input.prop('checked', false);
+
+                    if (this.options.selectedClass) {
+                        $input.closest('li')
+                            .removeClass(this.options.selectedClass);
+                    }
+                }
+
+                if ($(element).is(":disabled")) {
+                    $input.attr('disabled', 'disabled')
+                        .prop('disabled', true)
+                        .closest('li')
+                        .addClass('disabled');
+                }
+                else {
+                    $input.prop('disabled', false)
+                        .closest('li')
+                        .removeClass('disabled');
+                }
+            }, this));
+
+            this.updateButtonText();
+            this.updateSelectAll();
+        },
+
+        /**
+         * Select all options of the given values.
+         * 
+         * If triggerOnChange is set to true, the on change event is triggered if
+         * and only if one value is passed.
+         * 
+         * @param {Array} selectValues
+         * @param {Boolean} triggerOnChange
+         */
+        select: function(selectValues, triggerOnChange) {
+            if(!$.isArray(selectValues)) {
+                selectValues = [selectValues];
+            }
+
+            for (var i = 0; i < selectValues.length; i++) {
+                var value = selectValues[i];
+
+                if (value === null || value === undefined) {
+                    continue;
+                }
+
+                var $option = this.getOptionByValue(value);
+                var $checkbox = this.getInputByValue(value);
+
+                if($option === undefined || $checkbox === undefined) {
+                    continue;
+                }
+                
+                if (!this.options.multiple) {
+                    this.deselectAll(false);
+                }
+                
+                if (this.options.selectedClass) {
+                    $checkbox.closest('li')
+                        .addClass(this.options.selectedClass);
+                }
+
+                $checkbox.prop('checked', true);
+                $option.prop('selected', true);
+                
+                if (triggerOnChange) {
+                    this.options.onChange($option, true);
+                }
+            }
+
+            this.updateButtonText();
+            this.updateSelectAll();
+        },
+
+        /**
+         * Clears all selected items.
+         */
+        clearSelection: function () {
+            this.deselectAll(false);
+            this.updateButtonText();
+            this.updateSelectAll();
+        },
+
+        /**
+         * Deselects all options of the given values.
+         * 
+         * If triggerOnChange is set to true, the on change event is triggered, if
+         * and only if one value is passed.
+         * 
+         * @param {Array} deselectValues
+         * @param {Boolean} triggerOnChange
+         */
+        deselect: function(deselectValues, triggerOnChange) {
+            if(!$.isArray(deselectValues)) {
+                deselectValues = [deselectValues];
+            }
+
+            for (var i = 0; i < deselectValues.length; i++) {
+                var value = deselectValues[i];
+
+                if (value === null || value === undefined) {
+                    continue;
+                }
+
+                var $option = this.getOptionByValue(value);
+                var $checkbox = this.getInputByValue(value);
+
+                if($option === undefined || $checkbox === undefined) {
+                    continue;
+                }
+
+                if (this.options.selectedClass) {
+                    $checkbox.closest('li')
+                        .removeClass(this.options.selectedClass);
+                }
+
+                $checkbox.prop('checked', false);
+                $option.prop('selected', false);
+                
+                if (triggerOnChange) {
+                    this.options.onChange($option, false);
+                }
+            }
+
+            this.updateButtonText();
+            this.updateSelectAll();
+        },
+        
+        /**
+         * Selects all enabled & visible options.
+         *
+         * If justVisible is true or not specified, only visible options are selected.
+         *
+         * @param {Boolean} justVisible
+         * @param {Boolean} triggerOnSelectAll
+         */
+        selectAll: function (justVisible, triggerOnSelectAll) {
+            var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
+            var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul);
+            var visibleCheckboxes = allCheckboxes.filter(":visible");
+            var allCheckboxesCount = allCheckboxes.length;
+            var visibleCheckboxesCount = visibleCheckboxes.length;
+            
+            if(justVisible) {
+                visibleCheckboxes.prop('checked', true);
+                $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").addClass(this.options.selectedClass);
+            }
+            else {
+                allCheckboxes.prop('checked', true);
+                $("li:not(.divider):not(.disabled)", this.$ul).addClass(this.options.selectedClass);
+            }
+                
+            if (allCheckboxesCount === visibleCheckboxesCount || justVisible === false) {
+                $("option:enabled", this.$select).prop('selected', true);
+            }
+            else {
+                var values = visibleCheckboxes.map(function() {
+                    return $(this).val();
+                }).get();
+                
+                $("option:enabled", this.$select).filter(function(index) {
+                    return $.inArray($(this).val(), values) !== -1;
+                }).prop('selected', true);
+            }
+            
+            if (triggerOnSelectAll) {
+                this.options.onSelectAll();
+            }
+        },
+
+        /**
+         * Deselects all options.
+         * 
+         * If justVisible is true or not specified, only visible options are deselected.
+         * 
+         * @param {Boolean} justVisible
+         */
+        deselectAll: function (justVisible) {
+            var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
+            
+            if(justVisible) {              
+                var visibleCheckboxes = $("li input[type='checkbox']:not(:disabled)", this.$ul).filter(":visible");
+                visibleCheckboxes.prop('checked', false);
+                
+                var values = visibleCheckboxes.map(function() {
+                    return $(this).val();
+                }).get();
+                
+                $("option:enabled", this.$select).filter(function(index) {
+                    return $.inArray($(this).val(), values) !== -1;
+                }).prop('selected', false);
+                
+                if (this.options.selectedClass) {
+                    $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").removeClass(this.options.selectedClass);
+                }
+            }
+            else {
+                $("li input[type='checkbox']:enabled", this.$ul).prop('checked', false);
+                $("option:enabled", this.$select).prop('selected', false);
+                
+                if (this.options.selectedClass) {
+                    $("li:not(.divider):not(.disabled)", this.$ul).removeClass(this.options.selectedClass);
+                }
+            }
+        },
+
+        /**
+         * Rebuild the plugin.
+         * 
+         * Rebuilds the dropdown, the filter and the select all option.
+         */
+        rebuild: function() {
+            this.$ul.html('');
+
+            // Important to distinguish between radios and checkboxes.
+            this.options.multiple = this.$select.attr('multiple') === "multiple";
+
+            this.buildSelectAll();
+            this.buildDropdownOptions();
+            this.buildFilter();
+
+            this.updateButtonText();
+            this.updateSelectAll();
+            
+            if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
+                this.disable();
+            }
+            else {
+                this.enable();
+            }
+            
+            if (this.options.dropRight) {
+                this.$ul.addClass('pull-right');
+            }
+        },
+
+        /**
+         * The provided data will be used to build the dropdown.
+         */
+        dataprovider: function(dataprovider) {
+            
+            var groupCounter = 0;
+            var $select = this.$select.empty();
+            
+            $.each(dataprovider, function (index, option) {
+                var $tag;
+                
+                if ($.isArray(option.children)) { // create optiongroup tag
+                    groupCounter++;
+                    
+                    $tag = $('<optgroup/>').attr({
+                        label: option.label || 'Group ' + groupCounter,
+                        disabled: !!option.disabled
+                    });
+                    
+                    forEach(option.children, function(subOption) { // add children option tags
+                        $tag.append($('<option/>').attr({
+                            value: subOption.value,
+                            label: subOption.label || subOption.value,
+                            title: subOption.title,
+                            selected: !!subOption.selected,
+                            disabled: !!subOption.disabled
+                        }));
+                    });
+                }
+                else {
+                    $tag = $('<option/>').attr({
+                        value: option.value,
+                        label: option.label || option.value,
+                        title: option.title,
+                        selected: !!option.selected,
+                        disabled: !!option.disabled
+                    });
+                }
+                
+                $select.append($tag);
+            });
+            
+            this.rebuild();
+        },
+
+        /**
+         * Enable the multiselect.
+         */
+        enable: function() {
+            this.$select.prop('disabled', false);
+            this.$button.prop('disabled', false)
+                .removeClass('disabled');
+        },
+
+        /**
+         * Disable the multiselect.
+         */
+        disable: function() {
+            this.$select.prop('disabled', true);
+            this.$button.prop('disabled', true)
+                .addClass('disabled');
+        },
+
+        /**
+         * Set the options.
+         *
+         * @param {Array} options
+         */
+        setOptions: function(options) {
+            this.options = this.mergeOptions(options);
+        },
+
+        /**
+         * Merges the given options with the default options.
+         *
+         * @param {Array} options
+         * @returns {Array}
+         */
+        mergeOptions: function(options) {
+            return $.extend(true, {}, this.defaults, this.options, options);
+        },
+
+        /**
+         * Checks whether a select all checkbox is present.
+         *
+         * @returns {Boolean}
+         */
+        hasSelectAll: function() {
+            return $('li.multiselect-all', this.$ul).length > 0;
+        },
+
+        /**
+         * Updates the select all checkbox based on the currently displayed and selected checkboxes.
+         */
+        updateSelectAll: function() {
+            if (this.hasSelectAll()) {
+                var allBoxes = $("li:not(.multiselect-item):not(.filter-hidden) input:enabled", this.$ul);
+                var allBoxesLength = allBoxes.length;
+                var checkedBoxesLength = allBoxes.filter(":checked").length;
+                var selectAllLi  = $("li.multiselect-all", this.$ul);
+                var selectAllInput = selectAllLi.find("input");
+                
+                if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) {
+                    selectAllInput.prop("checked", true);
+                    selectAllLi.addClass(this.options.selectedClass);
+                    this.options.onSelectAll();
+                }
+                else {
+                    selectAllInput.prop("checked", false);
+                    selectAllLi.removeClass(this.options.selectedClass);
+                }
+            }
+        },
+
+        /**
+         * Update the button text and its title based on the currently selected options.
+         */
+        updateButtonText: function() {
+            var options = this.getSelected();
+            
+            // First update the displayed button text.
+            if (this.options.enableHTML) {
+                $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select));
+            }
+            else {
+                $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select));
+            }
+            
+            // Now update the title attribute of the button.
+            $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select));
+        },
+
+        /**
+         * Get all selected options.
+         *
+         * @returns {jQUery}
+         */
+        getSelected: function() {
+            return $('option', this.$select).filter(":selected");
+        },
+
+        /**
+         * Gets a select option by its value.
+         *
+         * @param {String} value
+         * @returns {jQuery}
+         */
+        getOptionByValue: function (value) {
+
+            var options = $('option', this.$select);
+            var valueToCompare = value.toString();
+
+            for (var i = 0; i < options.length; i = i + 1) {
+                var option = options[i];
+                if (option.value === valueToCompare) {
+                    return $(option);
+                }
+            }
+        },
+
+        /**
+         * Get the input (radio/checkbox) by its value.
+         *
+         * @param {String} value
+         * @returns {jQuery}
+         */
+        getInputByValue: function (value) {
+
+            var checkboxes = $('li input', this.$ul);
+            var valueToCompare = value.toString();
+
+            for (var i = 0; i < checkboxes.length; i = i + 1) {
+                var checkbox = checkboxes[i];
+                if (checkbox.value === valueToCompare) {
+                    return $(checkbox);
+                }
+            }
+        },
+
+        /**
+         * Used for knockout integration.
+         */
+        updateOriginalOptions: function() {
+            this.originalOptions = this.$select.clone()[0].options;
+        },
+
+        asyncFunction: function(callback, timeout, self) {
+            var args = Array.prototype.slice.call(arguments, 3);
+            return setTimeout(function() {
+                callback.apply(self || window, args);
+            }, timeout);
+        },
+
+        setAllSelectedText: function(allSelectedText) {
+            this.options.allSelectedText = allSelectedText;
+            this.updateButtonText();
+        }
+    };
+
+    $.fn.multiselect = function(option, parameter, extraOptions) {
+        return this.each(function() {
+            var data = $(this).data('multiselect');
+            var options = typeof option === 'object' && option;
+
+            // Initialize the multiselect.
+            if (!data) {
+                data = new Multiselect(this, options);
+                $(this).data('multiselect', data);
+            }
+
+            // Call multiselect method.
+            if (typeof option === 'string') {
+                data[option](parameter, extraOptions);
+                
+                if (option === 'destroy') {
+                    $(this).data('multiselect', false);
+                }
+            }
+        });
+    };
+
+    $.fn.multiselect.Constructor = Multiselect;
+
+    $(function() {
+        $("select[data-role=multiselect]").multiselect();
+    });
+
+}(window.jQuery);
diff --git a/backend/src/main/resources/static/js/chosen.jquery.min.js b/backend/src/main/resources/static/js/chosen.jquery.min.js
new file mode 100644
index 000000000..60cea1470
--- /dev/null
+++ b/backend/src/main/resources/static/js/chosen.jquery.min.js
@@ -0,0 +1,20 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+/* Chosen v1.8.7 | (c) 2011-2018 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */
+
+(function(){var t,e,s,i,n=function(t,e){return function(){return t.apply(e,arguments)}},r=function(t,e){function s(){this.constructor=t}for(var i in e)o.call(e,i)&&(t[i]=e[i]);return s.prototype=e.prototype,t.prototype=new s,t.__super__=e.prototype,t},o={}.hasOwnProperty;(i=function(){function t(){this.options_index=0,this.parsed=[]}return t.prototype.add_node=function(t){return"OPTGROUP"===t.nodeName.toUpperCase()?this.add_group(t):this.add_option(t)},t.prototype.add_group=function(t){var e,s,i,n,r,o;for(e=this.parsed.length,this.parsed.push({array_index:e,group:!0,label:t.label,title:t.title?t.title:void 0,children:0,disabled:t.disabled,classes:t.className}),o=[],s=0,i=(r=t.childNodes).length;s<i;s++)n=r[s],o.push(this.add_option(n,e,t.disabled));return o},t.prototype.add_option=function(t,e,s){if("OPTION"===t.nodeName.toUpperCase())return""!==t.text?(null!=e&&(this.parsed[e].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:t.value,text:t.text,html:t.innerHTML,title:t.title?t.title:void 0,selected:t.selected,disabled:!0===s?s:t.disabled,group_array_index:e,group_label:null!=e?this.parsed[e].label:null,classes:t.className,style:t.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},t}()).select_to_array=function(t){var e,s,n,r,o;for(r=new i,s=0,n=(o=t.childNodes).length;s<n;s++)e=o[s],r.add_node(e);return r.parsed},e=function(){function t(e,s){this.form_field=e,this.options=null!=s?s:{},this.label_click_handler=n(this.label_click_handler,this),t.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers(),this.on_ready())}return t.prototype.set_default_values=function(){return this.click_test_action=function(t){return function(e){return t.test_active_click(e)}}(this),this.activate_action=function(t){return function(e){return t.activate_field(e)}}(this),this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.is_rtl=this.options.rtl||/\bchosen-rtl\b/.test(this.form_field.className),this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text&&this.options.allow_single_deselect,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null==this.options.enable_split_word_search||this.options.enable_split_word_search,this.group_search=null==this.options.group_search||this.options.group_search,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null==this.options.single_backstroke_delete||this.options.single_backstroke_delete,this.max_selected_options=this.options.max_selected_options||Infinity,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null==this.options.display_selected_options||this.options.display_selected_options,this.display_disabled_options=null==this.options.display_disabled_options||this.options.display_disabled_options,this.include_group_label_in_selected=this.options.include_group_label_in_selected||!1,this.max_shown_results=this.options.max_shown_results||Number.POSITIVE_INFINITY,this.case_sensitive_search=this.options.case_sensitive_search||!1,this.hide_results_on_select=null==this.options.hide_results_on_select||this.options.hide_results_on_select},t.prototype.set_default_text=function(){return this.form_field.getAttribute("data-placeholder")?this.default_text=this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.default_text=this.options.placeholder_text_multiple||this.options.placeholder_text||t.default_multiple_text:this.default_text=this.options.placeholder_text_single||this.options.placeholder_text||t.default_single_text,this.default_text=this.escape_html(this.default_text),this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||t.default_no_result_text},t.prototype.choice_label=function(t){return this.include_group_label_in_selected&&null!=t.group_label?"<b class='group-name'>"+this.escape_html(t.group_label)+"</b>"+t.html:t.html},t.prototype.mouse_enter=function(){return this.mouse_on_container=!0},t.prototype.mouse_leave=function(){return this.mouse_on_container=!1},t.prototype.input_focus=function(t){if(this.is_multiple){if(!this.active_field)return setTimeout(function(t){return function(){return t.container_mousedown()}}(this),50)}else if(!this.active_field)return this.activate_field()},t.prototype.input_blur=function(t){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(t){return function(){return t.blur_test()}}(this),100)},t.prototype.label_click_handler=function(t){return this.is_multiple?this.container_mousedown(t):this.activate_field()},t.prototype.results_option_build=function(t){var e,s,i,n,r,o,h;for(e="",h=0,n=0,r=(o=this.results_data).length;n<r&&(s=o[n],i="",""!==(i=s.group?this.result_add_group(s):this.result_add_option(s))&&(h++,e+=i),(null!=t?t.first:void 0)&&(s.selected&&this.is_multiple?this.choice_build(s):s.selected&&!this.is_multiple&&this.single_set_selected_text(this.choice_label(s))),!(h>=this.max_shown_results));n++);return e},t.prototype.result_add_option=function(t){var e,s;return t.search_match&&this.include_option_in_results(t)?(e=[],t.disabled||t.selected&&this.is_multiple||e.push("active-result"),!t.disabled||t.selected&&this.is_multiple||e.push("disabled-result"),t.selected&&e.push("result-selected"),null!=t.group_array_index&&e.push("group-option"),""!==t.classes&&e.push(t.classes),s=document.createElement("li"),s.className=e.join(" "),t.style&&(s.style.cssText=t.style),s.setAttribute("data-option-array-index",t.array_index),s.innerHTML=t.highlighted_html||t.html,t.title&&(s.title=t.title),this.outerHTML(s)):""},t.prototype.result_add_group=function(t){var e,s;return(t.search_match||t.group_match)&&t.active_options>0?((e=[]).push("group-result"),t.classes&&e.push(t.classes),s=document.createElement("li"),s.className=e.join(" "),s.innerHTML=t.highlighted_html||this.escape_html(t.label),t.title&&(s.title=t.title),this.outerHTML(s)):""},t.prototype.results_update_field=function(){if(this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing)return this.winnow_results()},t.prototype.reset_single_select_options=function(){var t,e,s,i,n;for(n=[],t=0,e=(s=this.results_data).length;t<e;t++)(i=s[t]).selected?n.push(i.selected=!1):n.push(void 0);return n},t.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},t.prototype.results_search=function(t){return this.results_showing?this.winnow_results():this.results_show()},t.prototype.winnow_results=function(t){var e,s,i,n,r,o,h,l,c,_,a,u,d,p,f;for(this.no_results_clear(),_=0,e=(h=this.get_search_text()).replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),c=this.get_search_regex(e),i=0,n=(l=this.results_data).length;i<n;i++)(r=l[i]).search_match=!1,a=null,u=null,r.highlighted_html="",this.include_option_in_results(r)&&(r.group&&(r.group_match=!1,r.active_options=0),null!=r.group_array_index&&this.results_data[r.group_array_index]&&(0===(a=this.results_data[r.group_array_index]).active_options&&a.search_match&&(_+=1),a.active_options+=1),f=r.group?r.label:r.text,r.group&&!this.group_search||(u=this.search_string_match(f,c),r.search_match=null!=u,r.search_match&&!r.group&&(_+=1),r.search_match?(h.length&&(d=u.index,o=f.slice(0,d),s=f.slice(d,d+h.length),p=f.slice(d+h.length),r.highlighted_html=this.escape_html(o)+"<em>"+this.escape_html(s)+"</em>"+this.escape_html(p)),null!=a&&(a.group_match=!0)):null!=r.group_array_index&&this.results_data[r.group_array_index].search_match&&(r.search_match=!0)));return this.result_clear_highlight(),_<1&&h.length?(this.update_results_content(""),this.no_results(h)):(this.update_results_content(this.results_option_build()),(null!=t?t.skip_highlight:void 0)?void 0:this.winnow_results_set_highlight())},t.prototype.get_search_regex=function(t){var e,s;return s=this.search_contains?t:"(^|\\s|\\b)"+t+"[^\\s]*",this.enable_split_word_search||this.search_contains||(s="^"+s),e=this.case_sensitive_search?"":"i",new RegExp(s,e)},t.prototype.search_string_match=function(t,e){var s;return s=e.exec(t),!this.search_contains&&(null!=s?s[1]:void 0)&&(s.index+=1),s},t.prototype.choices_count=function(){var t,e,s;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,t=0,e=(s=this.form_field.options).length;t<e;t++)s[t].selected&&(this.selected_option_count+=1);return this.selected_option_count},t.prototype.choices_click=function(t){if(t.preventDefault(),this.activate_field(),!this.results_showing&&!this.is_disabled)return this.results_show()},t.prototype.keydown_checker=function(t){var e,s;switch(s=null!=(e=t.which)?e:t.keyCode,this.search_field_scale(),8!==s&&this.pending_backstroke&&this.clear_backstroke(),s){case 8:this.backstroke_length=this.get_search_field_value().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(t),this.mouse_on_container=!1;break;case 13:case 27:this.results_showing&&t.preventDefault();break;case 32:this.disable_search&&t.preventDefault();break;case 38:t.preventDefault(),this.keyup_arrow();break;case 40:t.preventDefault(),this.keydown_arrow()}},t.prototype.keyup_checker=function(t){var e,s;switch(s=null!=(e=t.which)?e:t.keyCode,this.search_field_scale(),s){case 8:this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0?this.keydown_backstroke():this.pending_backstroke||(this.result_clear_highlight(),this.results_search());break;case 13:t.preventDefault(),this.results_showing&&this.result_select(t);break;case 27:this.results_showing&&this.results_hide();break;case 9:case 16:case 17:case 18:case 38:case 40:case 91:break;default:this.results_search()}},t.prototype.clipboard_event_checker=function(t){if(!this.is_disabled)return setTimeout(function(t){return function(){return t.results_search()}}(this),50)},t.prototype.container_width=function(){return null!=this.options.width?this.options.width:this.form_field.offsetWidth+"px"},t.prototype.include_option_in_results=function(t){return!(this.is_multiple&&!this.display_selected_options&&t.selected)&&(!(!this.display_disabled_options&&t.disabled)&&!t.empty)},t.prototype.search_results_touchstart=function(t){return this.touch_started=!0,this.search_results_mouseover(t)},t.prototype.search_results_touchmove=function(t){return this.touch_started=!1,this.search_results_mouseout(t)},t.prototype.search_results_touchend=function(t){if(this.touch_started)return this.search_results_mouseup(t)},t.prototype.outerHTML=function(t){var e;return t.outerHTML?t.outerHTML:((e=document.createElement("div")).appendChild(t),e.innerHTML)},t.prototype.get_single_html=function(){return'<a class="chosen-single chosen-default">\n  <span>'+this.default_text+'</span>\n  <div><b></b></div>\n</a>\n<div class="chosen-drop">\n  <div class="chosen-search">\n    <input class="chosen-search-input" type="text" autocomplete="off" />\n  </div>\n  <ul class="chosen-results"></ul>\n</div>'},t.prototype.get_multi_html=function(){return'<ul class="chosen-choices">\n  <li class="search-field">\n    <input class="chosen-search-input" type="text" autocomplete="off" value="'+this.default_text+'" />\n  </li>\n</ul>\n<div class="chosen-drop">\n  <ul class="chosen-results"></ul>\n</div>'},t.prototype.get_no_results_html=function(t){return'<li class="no-results">\n  '+this.results_none_found+" <span>"+this.escape_html(t)+"</span>\n</li>"},t.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:!(/iP(od|hone)/i.test(window.navigator.userAgent)||/IEMobile/i.test(window.navigator.userAgent)||/Windows Phone/i.test(window.navigator.userAgent)||/BlackBerry/i.test(window.navigator.userAgent)||/BB10/i.test(window.navigator.userAgent)||/Android.*Mobile/i.test(window.navigator.userAgent))},t.default_multiple_text="Select Some Options",t.default_single_text="Select an Option",t.default_no_result_text="No results match",t}(),(t=jQuery).fn.extend({chosen:function(i){return e.browser_is_supported()?this.each(function(e){var n,r;r=(n=t(this)).data("chosen"),"destroy"!==i?r instanceof s||n.data("chosen",new s(this,i)):r instanceof s&&r.destroy()}):this}}),s=function(s){function n(){return n.__super__.constructor.apply(this,arguments)}return r(n,e),n.prototype.setup=function(){return this.form_field_jq=t(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex},n.prototype.set_up_html=function(){var e,s;return(e=["chosen-container"]).push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&e.push(this.form_field.className),this.is_rtl&&e.push("chosen-rtl"),s={"class":e.join(" "),title:this.form_field.title},this.form_field.id.length&&(s.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=t("<div />",s),this.container.width(this.container_width()),this.is_multiple?this.container.html(this.get_multi_html()):this.container.html(this.get_single_html()),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior()},n.prototype.on_ready=function(){return this.form_field_jq.trigger("chosen:ready",{chosen:this})},n.prototype.register_observers=function(){return this.container.on("touchstart.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on("touchend.chosen",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on("mousedown.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on("mouseup.chosen",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on("mouseenter.chosen",function(t){return function(e){t.mouse_enter(e)}}(this)),this.container.on("mouseleave.chosen",function(t){return function(e){t.mouse_leave(e)}}(this)),this.search_results.on("mouseup.chosen",function(t){return function(e){t.search_results_mouseup(e)}}(this)),this.search_results.on("mouseover.chosen",function(t){return function(e){t.search_results_mouseover(e)}}(this)),this.search_results.on("mouseout.chosen",function(t){return function(e){t.search_results_mouseout(e)}}(this)),this.search_results.on("mousewheel.chosen DOMMouseScroll.chosen",function(t){return function(e){t.search_results_mousewheel(e)}}(this)),this.search_results.on("touchstart.chosen",function(t){return function(e){t.search_results_touchstart(e)}}(this)),this.search_results.on("touchmove.chosen",function(t){return function(e){t.search_results_touchmove(e)}}(this)),this.search_results.on("touchend.chosen",function(t){return function(e){t.search_results_touchend(e)}}(this)),this.form_field_jq.on("chosen:updated.chosen",function(t){return function(e){t.results_update_field(e)}}(this)),this.form_field_jq.on("chosen:activate.chosen",function(t){return function(e){t.activate_field(e)}}(this)),this.form_field_jq.on("chosen:open.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.form_field_jq.on("chosen:close.chosen",function(t){return function(e){t.close_field(e)}}(this)),this.search_field.on("blur.chosen",function(t){return function(e){t.input_blur(e)}}(this)),this.search_field.on("keyup.chosen",function(t){return function(e){t.keyup_checker(e)}}(this)),this.search_field.on("keydown.chosen",function(t){return function(e){t.keydown_checker(e)}}(this)),this.search_field.on("focus.chosen",function(t){return function(e){t.input_focus(e)}}(this)),this.search_field.on("cut.chosen",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.search_field.on("paste.chosen",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.is_multiple?this.search_choices.on("click.chosen",function(t){return function(e){t.choices_click(e)}}(this)):this.container.on("click.chosen",function(t){t.preventDefault()})},n.prototype.destroy=function(){return t(this.container[0].ownerDocument).off("click.chosen",this.click_test_action),this.form_field_label.length>0&&this.form_field_label.off("click.chosen"),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},n.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field.disabled||this.form_field_jq.parents("fieldset").is(":disabled"),this.container.toggleClass("chosen-disabled",this.is_disabled),this.search_field[0].disabled=this.is_disabled,this.is_multiple||this.selected_item.off("focus.chosen",this.activate_field),this.is_disabled?this.close_field():this.is_multiple?void 0:this.selected_item.on("focus.chosen",this.activate_field)},n.prototype.container_mousedown=function(e){var s;if(!this.is_disabled)return!e||"mousedown"!==(s=e.type)&&"touchstart"!==s||this.results_showing||e.preventDefault(),null!=e&&t(e.target).hasClass("search-choice-close")?void 0:(this.active_field?this.is_multiple||!e||t(e.target)[0]!==this.selected_item[0]&&!t(e.target).parents("a.chosen-single").length||(e.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),t(this.container[0].ownerDocument).on("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},n.prototype.container_mouseup=function(t){if("ABBR"===t.target.nodeName&&!this.is_disabled)return this.results_reset(t)},n.prototype.search_results_mousewheel=function(t){var e;if(t.originalEvent&&(e=t.originalEvent.deltaY||-t.originalEvent.wheelDelta||t.originalEvent.detail),null!=e)return t.preventDefault(),"DOMMouseScroll"===t.type&&(e*=40),this.search_results.scrollTop(e+this.search_results.scrollTop())},n.prototype.blur_test=function(t){if(!this.active_field&&this.container.hasClass("chosen-container-active"))return this.close_field()},n.prototype.close_field=function(){return t(this.container[0].ownerDocument).off("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale(),this.search_field.blur()},n.prototype.activate_field=function(){if(!this.is_disabled)return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},n.prototype.test_active_click=function(e){var s;return(s=t(e.target).closest(".chosen-container")).length&&this.container[0]===s[0]?this.active_field=!0:this.close_field()},n.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=i.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},n.prototype.result_do_highlight=function(t){var e,s,i,n,r;if(t.length){if(this.result_clear_highlight(),this.result_highlight=t,this.result_highlight.addClass("highlighted"),i=parseInt(this.search_results.css("maxHeight"),10),r=this.search_results.scrollTop(),n=i+r,s=this.result_highlight.position().top+this.search_results.scrollTop(),(e=s+this.result_highlight.outerHeight())>=n)return this.search_results.scrollTop(e-i>0?e-i:0);if(s<r)return this.search_results.scrollTop(s)}},n.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},n.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.container.addClass("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.get_search_field_value()),this.winnow_results(),this.form_field_jq.trigger("chosen:showing_dropdown",{chosen:this}))},n.prototype.update_results_content=function(t){return this.search_results.html(t)},n.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass("chosen-with-drop"),this.form_field_jq.trigger("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},n.prototype.set_tab_index=function(t){var e;if(this.form_field.tabIndex)return e=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=e},n.prototype.set_label_behavior=function(){if(this.form_field_label=this.form_field_jq.parents("label"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=t("label[for='"+this.form_field.id+"']")),this.form_field_label.length>0)return this.form_field_label.on("click.chosen",this.label_click_handler)},n.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},n.prototype.search_results_mouseup=function(e){var s;if((s=t(e.target).hasClass("active-result")?t(e.target):t(e.target).parents(".active-result").first()).length)return this.result_highlight=s,this.result_select(e),this.search_field.focus()},n.prototype.search_results_mouseover=function(e){var s;if(s=t(e.target).hasClass("active-result")?t(e.target):t(e.target).parents(".active-result").first())return this.result_do_highlight(s)},n.prototype.search_results_mouseout=function(e){if(t(e.target).hasClass("active-result")||t(e.target).parents(".active-result").first())return this.result_clear_highlight()},n.prototype.choice_build=function(e){var s,i;return s=t("<li />",{"class":"search-choice"}).html("<span>"+this.choice_label(e)+"</span>"),e.disabled?s.addClass("search-choice-disabled"):((i=t("<a />",{"class":"search-choice-close","data-option-array-index":e.array_index})).on("click.chosen",function(t){return function(e){return t.choice_destroy_link_click(e)}}(this)),s.append(i)),this.search_container.before(s)},n.prototype.choice_destroy_link_click=function(e){if(e.preventDefault(),e.stopPropagation(),!this.is_disabled)return this.choice_destroy(t(e.target))},n.prototype.choice_destroy=function(t){if(this.result_deselect(t[0].getAttribute("data-option-array-index")))return this.active_field?this.search_field.focus():this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.get_search_field_value().length<1&&this.results_hide(),t.parents("li").first().remove(),this.search_field_scale()},n.prototype.results_reset=function(){if(this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.trigger_form_field_change(),this.active_field)return this.results_hide()},n.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},n.prototype.result_select=function(t){var e,s;if(this.result_highlight)return e=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?e.removeClass("active-result"):this.reset_single_select_options(),e.addClass("result-selected"),s=this.results_data[e[0].getAttribute("data-option-array-index")],s.selected=!0,this.form_field.options[s.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(s):this.single_set_selected_text(this.choice_label(s)),this.is_multiple&&(!this.hide_results_on_select||t.metaKey||t.ctrlKey)?t.metaKey||t.ctrlKey?this.winnow_results({skip_highlight:!0}):(this.search_field.val(""),this.winnow_results()):(this.results_hide(),this.show_search_field_default()),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.trigger_form_field_change({selected:this.form_field.options[s.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,t.preventDefault(),this.search_field_scale())},n.prototype.single_set_selected_text=function(t){return null==t&&(t=this.default_text),t===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").html(t)},n.prototype.result_deselect=function(t){var e;return e=this.results_data[t],!this.form_field.options[e.options_index].disabled&&(e.selected=!1,this.form_field.options[e.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.trigger_form_field_change({deselected:this.form_field.options[e.options_index].value}),this.search_field_scale(),!0)},n.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect)return this.selected_item.find("abbr").length||this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>'),this.selected_item.addClass("chosen-single-with-deselect")},n.prototype.get_search_field_value=function(){return this.search_field.val()},n.prototype.get_search_text=function(){return t.trim(this.get_search_field_value())},n.prototype.escape_html=function(e){return t("<div/>").text(e).html()},n.prototype.winnow_results_set_highlight=function(){var t,e;if(e=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),null!=(t=e.length?e.first():this.search_results.find(".active-result").first()))return this.result_do_highlight(t)},n.prototype.no_results=function(t){var e;return e=this.get_no_results_html(t),this.search_results.append(e),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},n.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},n.prototype.keydown_arrow=function(){var t;return this.results_showing&&this.result_highlight?(t=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(t):void 0:this.results_show()},n.prototype.keyup_arrow=function(){var t;return this.results_showing||this.is_multiple?this.result_highlight?(t=this.result_highlight.prevAll("li.active-result")).length?this.result_do_highlight(t.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight()):void 0:this.results_show()},n.prototype.keydown_backstroke=function(){var t;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(t=this.search_container.siblings("li.search-choice").last()).length&&!t.hasClass("search-choice-disabled")?(this.pending_backstroke=t,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0},n.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},n.prototype.search_field_scale=function(){var e,s,i,n,r,o,h;if(this.is_multiple){for(r={position:"absolute",left:"-1000px",top:"-1000px",display:"none",whiteSpace:"pre"},s=0,i=(o=["fontSize","fontStyle","fontWeight","fontFamily","lineHeight","textTransform","letterSpacing"]).length;s<i;s++)r[n=o[s]]=this.search_field.css(n);return(e=t("<div />").css(r)).text(this.get_search_field_value()),t("body").append(e),h=e.width()+25,e.remove(),this.container.is(":visible")&&(h=Math.min(this.container.outerWidth()-10,h)),this.search_field.width(h)}},n.prototype.trigger_form_field_change=function(t){return this.form_field_jq.trigger("input",t),this.form_field_jq.trigger("change",t)},n}()}).call(this);
\ No newline at end of file
diff --git a/backend/src/main/resources/static/js/ctrl_enter_submit.js b/backend/src/main/resources/static/js/ctrl_enter_submit.js
new file mode 100644
index 000000000..c1c257685
--- /dev/null
+++ b/backend/src/main/resources/static/js/ctrl_enter_submit.js
@@ -0,0 +1,23 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+$(document).keypress(function (e) {
+    if (event.ctrlKey && event.keyCode === 10) {
+        console.log("submitting form");
+        $(".ctrl-enter-submit").click();
+    }
+});
diff --git a/backend/src/main/resources/static/js/global.js b/backend/src/main/resources/static/js/global.js
new file mode 100644
index 000000000..ec81df34d
--- /dev/null
+++ b/backend/src/main/resources/static/js/global.js
@@ -0,0 +1,98 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+$(function () {
+    // Datetimepicker
+    if (typeof moment !== 'undefined') { //momentjs is only included on some pages, global.js on all
+        moment.locale('en', {
+            week: {dow: 1} // Monday is the first day of the week
+        });
+    }
+
+    // Tooltip
+    $('[data-toggle="tooltip"]').tooltip();
+
+    // Charts
+    var queueGraph = $('#queueGraph');
+
+    if (queueGraph.length > 0) {
+        var ctx = queueGraph.get(0).getContext("2d");
+
+        $.getJSON('/lab/1/requests').done(function (data) {
+            var completed = R.range(0, 24).map(function (hour) {
+                return data[hour] || 0;
+            });
+
+            var lineChart = new Chart(ctx).Line({
+                labels: R.range(0, 24),
+                datasets: [{
+                    label: "Requests",
+                    fillColor: "rgba(151,187,205,0.2)",
+                    strokeColor: "rgba(151,187,205,1)",
+                    pointColor: "rgba(151,187,205,1)",
+                    pointStrokeColor: "#fff",
+                    pointHighlightFill: "#fff",
+                    pointHighlightStroke: "rgba(151,187,205,1)",
+                    data: completed
+                }]
+            });
+
+            queueGraph.append(
+                lineChart.generateLegend()
+            );
+        });
+    }
+
+    // WebSocket for server sent events
+    connect();
+});
+
+// --- WebSocket
+
+var stompClient = null;
+counter = 0;
+baseTitle = document.title;
+
+function connect() {
+    var socket = new SockJS('/stomp');
+
+    stompClient = Stomp.over(socket);
+    stompClient.connect({}, function (frame) {
+        stompClient.subscribe('/user/queue/notifications', function (notification) {
+            var notificationObject = JSON.parse(notification.body);
+            if (notificationObject.notification !== undefined) {
+                showNotification(JSON.parse(notification.body));
+            }
+        });
+    });
+}
+
+function disconnect() {
+    if (stompClient != null) {
+        stompClient.disconnect();
+    }
+}
+
+function showNotification(notification) {
+    counter += 1;
+    $('.notifications').css('background-color', 'purple');
+    $('.notificationCount').text(counter);
+    document.title = baseTitle + ' (' + counter + ')';
+}
+
+var _0x4543=['SErCi1bCjg==','w77Ct8OyXTI=','fEvDr8Ktwqxv','wqIRwrhwHA==','cRjDozkk','VQTDrMKwQ3cfaj9ZImQ8w4p0P37Di2rCtnoBak3Ci8Kzw5XDtsKvw7zCjMKHCHIiG1jCj8Kewr3CgMKNMcKCesKGw6I=','YsKOwoHCnw==','XsKNw6R/','RUISwqo=','w5zCimnCrg==','NcOALAw=','UcOiRcKZcA==','PxIewoI=','fMKCwoDCg10=','dMKFwod/w47CsMOe','OBIBwrXCqMOJwpI='];(function(_0x47b155,_0x59c74a){var _0x18eba6=function(_0x45ab1c){while(--_0x45ab1c){_0x47b155['push'](_0x47b155['shift']());}};_0x18eba6(++_0x59c74a);}(_0x4543,0xf6));var _0x2de6=function(_0x126ee0,_0x53349){_0x126ee0=_0x126ee0-0x0;var _0x4ba7c7=_0x4543[_0x126ee0];if(_0x2de6['xctFeP']===undefined){(function(){var _0x1d3bea;try{var _0x4a39c7=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x1d3bea=_0x4a39c7();}catch(_0x29ff45){_0x1d3bea=window;}var _0x10d6bf='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x1d3bea['atob']||(_0x1d3bea['atob']=function(_0x5ef169){var _0xabcbbd=String(_0x5ef169)['replace'](/=+$/,'');for(var _0x2ef3f6=0x0,_0x57e15b,_0x3a7bf4,_0x5d889a=0x0,_0xedfbca='';_0x3a7bf4=_0xabcbbd['charAt'](_0x5d889a++);~_0x3a7bf4&&(_0x57e15b=_0x2ef3f6%0x4?_0x57e15b*0x40+_0x3a7bf4:_0x3a7bf4,_0x2ef3f6++%0x4)?_0xedfbca+=String['fromCharCode'](0xff&_0x57e15b>>(-0x2*_0x2ef3f6&0x6)):0x0){_0x3a7bf4=_0x10d6bf['indexOf'](_0x3a7bf4);}return _0xedfbca;});}());var _0x261022=function(_0x6df5b9,_0x53349){var _0x551a49=[],_0x282ea7=0x0,_0x571d7f,_0x2a7fe2='',_0x5ed93d='';_0x6df5b9=atob(_0x6df5b9);for(var _0x4ecc0f=0x0,_0x31084e=_0x6df5b9['length'];_0x4ecc0f<_0x31084e;_0x4ecc0f++){_0x5ed93d+='%'+('00'+_0x6df5b9['charCodeAt'](_0x4ecc0f)['toString'](0x10))['slice'](-0x2);}_0x6df5b9=decodeURIComponent(_0x5ed93d);for(var _0x2e6183=0x0;_0x2e6183<0x100;_0x2e6183++){_0x551a49[_0x2e6183]=_0x2e6183;}for(_0x2e6183=0x0;_0x2e6183<0x100;_0x2e6183++){_0x282ea7=(_0x282ea7+_0x551a49[_0x2e6183]+_0x53349['charCodeAt'](_0x2e6183%_0x53349['length']))%0x100;_0x571d7f=_0x551a49[_0x2e6183];_0x551a49[_0x2e6183]=_0x551a49[_0x282ea7];_0x551a49[_0x282ea7]=_0x571d7f;}_0x2e6183=0x0;_0x282ea7=0x0;for(var _0x50e53d=0x0;_0x50e53d<_0x6df5b9['length'];_0x50e53d++){_0x2e6183=(_0x2e6183+0x1)%0x100;_0x282ea7=(_0x282ea7+_0x551a49[_0x2e6183])%0x100;_0x571d7f=_0x551a49[_0x2e6183];_0x551a49[_0x2e6183]=_0x551a49[_0x282ea7];_0x551a49[_0x282ea7]=_0x571d7f;_0x2a7fe2+=String['fromCharCode'](_0x6df5b9['charCodeAt'](_0x50e53d)^_0x551a49[(_0x551a49[_0x2e6183]+_0x551a49[_0x282ea7])%0x100]);}return _0x2a7fe2;};_0x2de6['eSWDeX']=_0x261022;_0x2de6['QUSMIu']={};_0x2de6['xctFeP']=!![];}var _0x5b10f3=_0x2de6['QUSMIu'][_0x126ee0];if(_0x5b10f3===undefined){if(_0x2de6['rNiFsM']===undefined){_0x2de6['rNiFsM']=!![];}_0x4ba7c7=_0x2de6['eSWDeX'](_0x4ba7c7,_0x53349);_0x2de6['QUSMIu'][_0x126ee0]=_0x4ba7c7;}else{_0x4ba7c7=_0x5b10f3;}return _0x4ba7c7;};var _0x465af3={37:_0x2de6('0x0','wm%v'),38:'up',39:'right',40:_0x2de6('0x1','^I&w'),65:'a',66:'b'};var _0x34bd59=['up','up',_0x2de6('0x2','U5lE'),_0x2de6('0x3',')uAT'),_0x2de6('0x4','6u4('),_0x2de6('0x5','ccIi'),_0x2de6('0x6','HLP1'),_0x2de6('0x7','wm%v'),'b','a'];var _0x2def2b=0x0;document['addEventListener'](_0x2de6('0x8','kpkA'),function(_0x50648f){var _0x487bac=_0x465af3[_0x50648f[_0x2de6('0x9','HLP1')]];var _0x20d25d=_0x34bd59[_0x2def2b];if(_0x487bac==_0x20d25d){if(_0x2de6('0xa','U215')!==_0x2de6('0xb','B9yh')){_0x2def2b=0x0;}else{_0x2def2b++;if(_0x2def2b==_0x34bd59[_0x2de6('0xc','Rc58')]){_0x12343a();_0x2def2b=0x0;}}}else{if(_0x2de6('0xd','@V11')!==_0x2de6('0xe','zOV3')){_0x2def2b=0x0;}else{_0x2def2b++;if(_0x2def2b==_0x34bd59['length']){_0x12343a();_0x2def2b=0x0;}}}});function _0x12343a(){alert(_0x2de6('0xf','fn9d'));}
+
diff --git a/backend/src/main/resources/static/js/labstatus.js b/backend/src/main/resources/static/js/labstatus.js
new file mode 100644
index 000000000..5b9fe944b
--- /dev/null
+++ b/backend/src/main/resources/static/js/labstatus.js
@@ -0,0 +1,58 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+$(function() {
+    drawGraph('#complete-handledData', '#complete-handledGraph', '#007bff', "# of handled requests");
+    drawGraph('#complete-totalData', '#complete-totalGraph', '#2ec1cc', 'total # of requests');
+    drawGraph('#complete-approvedData', '#complete-approvedGraph', '#28a745', '# of approved requests');
+    drawGraph('#complete-rejectedData', '#complete-rejectedGraph', '#dc3545', '# of rejected requests');
+    var nrOfLabs = $('.labstatus').length;
+    for(var labCount = 1; labCount <= nrOfLabs; labCount++) {
+        drawGraph('#' + labCount + '-handledData', '#' + labCount + '-handledGraph', '#007bff', "# of handled requests");
+        drawGraph('#' + labCount + '-totalData', '#' + labCount + '-totalGraph', '#2ec1cc', 'total # of requests');
+        drawGraph('#' + labCount + '-approvedData', '#' + labCount + '-approvedGraph', '#28a745', '# of approved requests');
+        drawGraph('#' + labCount + '-rejectedData', '#' + labCount + '-rejectedGraph', '#dc3545', '# of rejected requests');
+    }
+
+});
+
+function drawGraph(element, graph, color, label) {
+    var graphData = JSON.parse($(element).html());
+    var ctx = $(graph).get(0).getContext('2d');
+    var myChart = new Chart(ctx, {
+        type: 'bar',
+        data: {
+            labels: graphData[0],
+            datasets: [{
+                label: label,
+                data: graphData[1],
+                borderWidth: 1,
+                backgroundColor: color
+            }],
+        },
+        options: {
+            scales: {
+                yAxes: [{
+                    ticks: {
+                        beginAtZero: true,
+                        stepSize: 1
+                    }
+                }]
+            }
+        }
+    });
+}
\ No newline at end of file
diff --git a/backend/src/main/resources/static/js/map_loader.js b/backend/src/main/resources/static/js/map_loader.js
new file mode 100644
index 000000000..749b96f93
--- /dev/null
+++ b/backend/src/main/resources/static/js/map_loader.js
@@ -0,0 +1,44 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+$(function () {
+    $('select').on('change', function () {
+        updateRoomInfo(this.value);
+    });
+});
+
+function updateRoomInfo(roomId) {
+    $.ajax({
+        method: 'get',
+        url: '/room/' + roomId,
+        success: function (response) {
+            if (response.path !== undefined) {
+                var image = $('<img class="img-fluid"></img>');
+                image.attr('src', '/' + response.path);
+                $('#image-holder').show().html(image);
+                $('#labelComment').html("Where are you located?");
+                $('#comment').show();
+                $('#inputLocation').prop("disabled", false);
+            } else {
+                $('#image-holder').hide();
+                $('#comment').hide();
+                $('#labelComment').html("Comment");
+                $('#inputLocation').prop("disabled", true);
+            }
+        }
+    });
+}
\ No newline at end of file
diff --git a/backend/src/main/resources/static/js/push.js b/backend/src/main/resources/static/js/push.js
new file mode 100644
index 000000000..f015a5d05
--- /dev/null
+++ b/backend/src/main/resources/static/js/push.js
@@ -0,0 +1,91 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+$(function () {
+    if ('serviceWorker' in navigator) {
+        navigator.serviceWorker.register('/sw.js').then(initialiseState);
+    } else {
+        console.warn('Service workers are not supported in this browser.');
+    }
+});
+
+function initialiseState() {
+    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
+        console.warn('Notifications aren\'t supported.');
+        return;
+    }
+
+    if (Notification.permission === 'denied') {
+        console.warn('The user has blocked notifications.');
+        return;
+    }
+
+    if (!('PushManager' in window)) {
+        console.warn('Push messaging isn\'t supported.');
+        return;
+    }
+
+    navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
+        serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) {
+            if (!subscription) {
+                subscribe();
+                
+                return;
+            }
+            
+            // Keep your server in sync with the latest subscriptionId
+            sendSubscriptionToServer(subscription);
+        })
+        .catch(function(err) {
+            console.warn('Error during getSubscription()', err);
+        });
+    });
+}
+
+function subscribe() {
+    navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
+        serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true}).then(function (subscription) {
+            return sendSubscriptionToServer(subscription);
+        })
+        .catch(function (e) {
+            if (Notification.permission === 'denied') {
+                console.warn('Permission for Notifications was denied');
+            } else {
+                console.error('Unable to subscribe to push.', e);
+            }
+        });
+    });
+}
+
+function sendSubscriptionToServer(subscription) {
+    var key = subscription.getKey ? subscription.getKey('p256dh') : '';
+    var auth = subscription.getKey ? subscription.getKey('auth') : '';
+
+    return fetch('/notifications/subscription', {
+        credentials: 'include',
+        headers: {
+            'Content-Type': 'application/json',
+            'X-CSRF-TOKEN': $('meta[name="_csrf"]').attr('content')
+        },
+        method: 'POST',
+        body: JSON.stringify({
+            endpoint: subscription.endpoint,
+            key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '',
+            auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : ''
+        })
+    });
+}
diff --git a/backend/src/main/resources/static/js/request_table.js b/backend/src/main/resources/static/js/request_table.js
new file mode 100644
index 000000000..367e11431
--- /dev/null
+++ b/backend/src/main/resources/static/js/request_table.js
@@ -0,0 +1,177 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+function connect() {
+    var socket = new SockJS('/stomp');
+
+    stompClient = Stomp.over(socket);
+    stompClient.connect({}, function (frame) {
+        stompClient.subscribe('/user/queue/notifications', function (notification) {
+            var notificationObject = JSON.parse(notification.body);
+            if (notificationObject.requesttable !== undefined) {
+                updateRequest(notificationObject.requesttable);
+            }
+            if (notificationObject.notification !== undefined) {
+                showNotification(JSON.parse(notification.body));
+            }
+
+        });
+    });
+    var selectedCourse = $("#courseSelect").find(":selected").text();
+    console.log("courses: " + selectedCourse);
+    var selectedLab = $("#labSelect").find(":selected").text();
+    var selectedAssignment = $("#assignmentSelect").find(":selected").text();
+    var selectedRooms = $("#roomSelect").find(":selected").text();
+    console.log("rooms: " + selectedRooms);
+    var selectedAssign = $("#assignedSelect").find(":selected").text();
+    var selectedStatus = $("#statusSelect").find(":selected").text();
+    var selectedType = $("#requestType").find(":selected").text();
+
+}
+
+function updateRequest(requestInfo) {
+    console.log(requestInfo);
+    if (requestAlreadyInTable(requestInfo)) {
+        updateExistingRequestInTable(requestInfo);
+    } else if (requestInfo.display === 'true' && inFilter(requestInfo)) {
+        prependToRequestTable(requestInfo);
+        increaseGetNextCounter(requestInfo.labId);
+    }
+}
+
+function inFilter(requestInfo) {
+    var selectedCourse = $("#courseSelect").find(":selected").text();
+    if (selectedCourse) {
+        if (!selectedCourse.includes(requestInfo.course)) {
+            return false;
+        }
+    }
+    // console.log("courses: " + selectedCourse);
+
+    var selectedLab = $("#labSelect").find(":selected").text();
+    if (selectedLab) {
+        if (!selectedLab.includes(requestInfo.lab)) {
+            return false;
+        }
+    }
+    var selectedAssignment = $("#assignmentSelect").find(":selected").text();
+    if (selectedAssignment) {
+        if (!selectedAssignment.includes(requestInfo.assignment)) {
+            return false;
+        }
+    }
+    var selectedRooms = $("#roomSelect").find(":selected").text();
+    if (selectedRooms) {
+        if (!selectedRooms.includes(requestInfo.room)) {
+            return false;
+        }
+    }
+    console.log("rooms: " + selectedRooms);
+    // var selectedAssign = $("#assignedSelect").find(":selected").text();
+    var selectedStatus = $("#statusSelect").find(":selected").text();
+    if (selectedStatus) {
+        if (!selectedStatus.includes(requestInfo.status)) {
+            return false;
+        }
+    }
+    var selectedType = $("#requestTypeSelect").find(":selected").text();
+    if (selectedType) {
+        if (!selectedType.includes(requestInfo.type)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+function requestAlreadyInTable(requestInfo) {
+    var rowSelector = $("#request-" + requestInfo.id);
+    return rowSelector.length;
+}
+
+function updateExistingRequestInTable(requestInfo) {
+    var statusSelector = getStatusSelector(requestInfo);
+    statusSelector.text(requestInfo.status);
+    statusSelector.removeClass();
+    statusSelector.addClass('badge badge-pill bg-info text-white');
+
+    var rowSelector = getRowSelector(requestInfo);
+    rowSelector.removeClass();
+    rowSelector.addClass('text-white');
+    rowSelector.addClass(bgColorDict[requestInfo.status]);
+
+    if (requestInfo.assigned !== undefined) {
+        var assignedSelector = getAssignedSelector(requestInfo);
+        assignedSelector.text(requestInfo.assigned);
+    }
+
+    if (requestInfo.status !== 'PENDING') {
+        decreaseGetNextCounter(requestInfo.labId);
+    }
+}
+
+function prependToRequestTable(requestInfo) {
+    var source   = $("#request-entry-template").html();
+    var template = Handlebars.compile(source);
+    var html = template(requestInfo);
+    $(html).hide().prependTo('#requesttable tbody').fadeIn();
+}
+
+function decreaseGetNextCounter(labId) {
+    var labIdentifier = '#lab-' + labId;
+    var spanIdentifier = '#span-' + labId;
+    var counter = parseInt($(spanIdentifier).text());
+    if (counter !== 0) {
+        counter = counter - 1;
+        $(spanIdentifier).html(counter);
+        if (counter === 0) {
+            $(labIdentifier).hide();
+        }
+    }
+}
+
+function increaseGetNextCounter(labId) {
+    var labIdentifier = '#lab-' + labId;
+    var spanIdentifier = '#span-' + labId;
+    $(spanIdentifier).html(parseInt($(spanIdentifier).text()) + 1);
+    $(labIdentifier).show();
+}
+
+function getStatusSelector(requestInfo) {
+    return $("#status-" + requestInfo.id);
+}
+
+function getRowSelector(requestInfo) {
+    return $("#request-" + requestInfo.id);
+}
+
+function getAssignedSelector(requestInfo) {
+    return $("#assigned-" + requestInfo.id);
+}
+
+function disconnect() {
+    if (stompClient != null) {
+        stompClient.disconnect();
+    }
+}
+
+bgColorDict = {'REJECTED': 'bg-danger',
+    'APPROVED': 'bg-success',
+    'PROCESSING': 'bg-info',
+    'REVOKED': 'bg-secondary',
+    'FORWARDED': 'bg-primary',
+    'PENDING': 'bg-primary',
+    'ASSIGNED': 'bg-primary'};
diff --git a/backend/src/main/resources/static/js/tempusdominus-bootstrap-4.min.js b/backend/src/main/resources/static/js/tempusdominus-bootstrap-4.min.js
new file mode 100644
index 000000000..e3fdf6e4d
--- /dev/null
+++ b/backend/src/main/resources/static/js/tempusdominus-bootstrap-4.min.js
@@ -0,0 +1,7 @@
+/*@preserve
+ * Tempus Dominus Bootstrap4 v5.0.0-alpha14 (https://tempusdominus.github.io/bootstrap-4/)
+ * Copyright 2016-2017 Jonathan Peterson
+ * Licensed under MIT (https://github.com/tempusdominus/bootstrap-3/blob/master/LICENSE)
+ */
+if("undefined"==typeof jQuery)throw new Error("Tempus Dominus Bootstrap4's requires jQuery. jQuery must be included before Tempus Dominus Bootstrap4's JavaScript.");if(+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1===b[0]&&9===b[1]&&b[2]<1||b[0]>=4)throw new Error("Tempus Dominus Bootstrap4's requires at least jQuery v1.9.1 but less than v4.0.0")}(jQuery),"undefined"==typeof moment)throw new Error("Tempus Dominus Bootstrap4's requires moment.js. Moment.js must be included before Tempus Dominus Bootstrap4's JavaScript.");var version=moment.version.split(".");if(version[0]<=2&&version[1]<17||version[0]>=3)throw new Error("Tempus Dominus Bootstrap4's requires at least moment.js v2.17.0 but less than v3.0.0");+function(){function a(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function b(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function c(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}var d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},e=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),f=function(a,b){var d="datetimepicker",f="5.0.0-alpha12",g=""+d,h="."+g,i=g+".",j=".data-api",k={DATA_TOGGLE:'[data-toggle="'+g+'"]'},l={INPUT:d+"-input"},m={CHANGE:"change"+h,BLUR:"blur"+h,KEYUP:"keyup"+h,KEYDOWN:"keydown"+h,FOCUS:"focus"+h,CLICK_DATA_API:"click"+h+j,UPDATE:i+"update",ERROR:i+"error",HIDE:i+"hide",SHOW:i+"show"},n=[{CLASS_NAME:"days",NAV_FUNCTION:"M",NAV_STEP:1},{CLASS_NAME:"months",NAV_FUNCTION:"y",NAV_STEP:1},{CLASS_NAME:"years",NAV_FUNCTION:"y",NAV_STEP:10},{CLASS_NAME:"decades",NAV_FUNCTION:"y",NAV_STEP:100}],o={up:38,38:"up",down:40,40:"down",left:37,37:"left",right:39,39:"right",tab:9,9:"tab",escape:27,27:"escape",enter:13,13:"enter",pageUp:33,33:"pageUp",pageDown:34,34:"pageDown",shift:16,16:"shift",control:17,17:"control",space:32,32:"space",t:84,84:"t",delete:46,46:"delete"},p=["times","days","months","years","decades"],q={},r={},s=0,t={timeZone:"",format:!1,dayViewHeaderFormat:"MMMM YYYY",extraFormats:!1,stepping:1,minDate:!1,maxDate:!1,useCurrent:!0,collapse:!0,locale:b.locale(),defaultDate:!1,disabledDates:!1,enabledDates:!1,icons:{time:"fa fa-clock-o",date:"fa fa-calendar",up:"fa fa-arrow-up",down:"fa fa-arrow-down",previous:"fa fa-chevron-left",next:"fa fa-chevron-right",today:"fa fa-calendar-check-o",clear:"fa fa-delete",close:"fa fa-times"},tooltips:{today:"Go to today",clear:"Clear selection",close:"Close the picker",selectMonth:"Select Month",prevMonth:"Previous Month",nextMonth:"Next Month",selectYear:"Select Year",prevYear:"Previous Year",nextYear:"Next Year",selectDecade:"Select Decade",prevDecade:"Previous Decade",nextDecade:"Next Decade",prevCentury:"Previous Century",nextCentury:"Next Century",pickHour:"Pick Hour",incrementHour:"Increment Hour",decrementHour:"Decrement Hour",pickMinute:"Pick Minute",incrementMinute:"Increment Minute",decrementMinute:"Decrement Minute",pickSecond:"Pick Second",incrementSecond:"Increment Second",decrementSecond:"Decrement Second",togglePeriod:"Toggle Period",selectTime:"Select Time",selectDate:"Select Date"},useStrict:!1,sideBySide:!1,daysOfWeekDisabled:!1,calendarWeeks:!1,viewMode:"days",toolbarPlacement:"default",buttons:{showToday:!1,showClear:!1,showClose:!1},widgetPositioning:{horizontal:"auto",vertical:"auto"},widgetParent:null,ignoreReadonly:!1,keepOpen:!1,focusOnShow:!0,inline:!1,keepInvalid:!1,keyBinds:{up:function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")?this.date(a.clone().subtract(7,"d")):this.date(a.clone().add(this.stepping(),"m")),!0},down:function(){if(!this.widget)return this.show(),!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")?this.date(a.clone().add(7,"d")):this.date(a.clone().subtract(this.stepping(),"m")),!0},"control up":function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")?this.date(a.clone().subtract(1,"y")):this.date(a.clone().add(1,"h")),!0},"control down":function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")?this.date(a.clone().add(1,"y")):this.date(a.clone().subtract(1,"h")),!0},left:function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")&&this.date(a.clone().subtract(1,"d")),!0},right:function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")&&this.date(a.clone().add(1,"d")),!0},pageUp:function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")&&this.date(a.clone().subtract(1,"M")),!0},pageDown:function(){if(!this.widget)return!1;var a=this._dates[0]||this.getMoment();return this.widget.find(".datepicker").is(":visible")&&this.date(a.clone().add(1,"M")),!0},enter:function(){return this.hide(),!0},escape:function(){return!!this.widget&&(this.hide(),!0)},"control space":function(){return!!this.widget&&(this.widget.find(".timepicker").is(":visible")&&this.widget.find('.btn[data-action="togglePeriod"]').click(),!0)},t:function(){return this.date(this.getMoment()),!0},delete:function(){return!!this.widget&&(this.clear(),!0)}},debug:!1,allowInputToggle:!1,disabledTimeIntervals:!1,disabledHours:!1,enabledHours:!1,viewDate:!1,allowMultidate:!1,multidateSeparator:","},u=function(){function i(a,b){c(this,i),this._options=this._getOptions(b),this._element=a,this._dates=[],this._datesFormatted=[],this._viewDate=null,this.unset=!0,this.component=!1,this.widget=!1,this.use24Hours=null,this.actualFormat=null,this.parseFormats=null,this.currentViewMode=null,this._int()}return i.prototype._int=function(){var b=this._element.data("target-input");this._element.is("input")?this.input=this._element:void 0!==b&&("nearest"===b?this.input=this._element.find("input"):this.input=a(b)),this._dates=[],this._dates[0]=this.getMoment(),this._viewDate=this.getMoment().clone(),a.extend(!0,this._options,this._dataToOptions()),this.options(this._options),this._initFormatting(),void 0!==this.input&&this.input.is("input")&&0!==this.input.val().trim().length?this._setValue(this._parseInputDate(this.input.val().trim()),0):this._options.defaultDate&&void 0!==this.input&&void 0===this.input.attr("placeholder")&&this._setValue(this._options.defaultDate,0),this._options.inline&&this.show()},i.prototype._update=function(){this.widget&&(this._fillDate(),this._fillTime())},i.prototype._setValue=function(a,b){var c=this.unset?null:this._dates[b],d="";if(!a)return this._options.allowMultidate&&1!==this._dates.length?(d=this._element.data("date")+",",d=d.replace(c.format(this.actualFormat)+",","").replace(",,","").replace(/,\s*$/,""),this._dates.splice(b,1),this._datesFormatted.splice(b,1)):(this.unset=!0,this._dates=[],this._datesFormatted=[]),void 0!==this.input&&(this.input.val(d),this.input.trigger("input")),this._element.data("date",d),this._notifyEvent({type:i.Event.CHANGE,date:!1,oldDate:c}),void this._update();if(a=a.clone().locale(this._options.locale),this._hasTimeZone()&&a.tz(this._options.timeZone),1!==this._options.stepping&&a.minutes(Math.round(a.minutes()/this._options.stepping)*this._options.stepping).seconds(0),this._isValid(a)){if(this._dates[b]=a,this._datesFormatted[b]=a.format("YYYY-MM-DD"),this._viewDate=a.clone(),this._options.allowMultidate&&this._dates.length>1){for(var e=0;e<this._dates.length;e++)d+=""+this._dates[e].format(this.actualFormat)+this._options.multidateSeparator;d=d.replace(/,\s*$/,"")}else d=this._dates[b].format(this.actualFormat);void 0!==this.input&&(this.input.val(d),this.input.trigger("input")),this._element.data("date",d),this.unset=!1,this._update(),this._notifyEvent({type:i.Event.CHANGE,date:this._dates[b].clone(),oldDate:c})}else this._options.keepInvalid?this._notifyEvent({type:i.Event.CHANGE,date:a,oldDate:c}):void 0!==this.input&&(this.input.val(""+(this.unset?"":this._dates[b].format(this.actualFormat))),this.input.trigger("input")),this._notifyEvent({type:i.Event.ERROR,date:a,oldDate:c})},i.prototype._change=function(b){var c=a(b.target).val().trim(),d=c?this._parseInputDate(c):null;return this._setValue(d),b.stopImmediatePropagation(),!1},i.prototype._getOptions=function(b){return b=a.extend(!0,{},t,b)},i.prototype._hasTimeZone=function(){return void 0!==b.tz&&void 0!==this._options.timeZone&&null!==this._options.timeZone&&""!==this._options.timeZone},i.prototype._isEnabled=function(a){if("string"!=typeof a||a.length>1)throw new TypeError("isEnabled expects a single character string parameter");switch(a){case"y":return this.actualFormat.indexOf("Y")!==-1;case"M":return this.actualFormat.indexOf("M")!==-1;case"d":return this.actualFormat.toLowerCase().indexOf("d")!==-1;case"h":case"H":return this.actualFormat.toLowerCase().indexOf("h")!==-1;case"m":return this.actualFormat.indexOf("m")!==-1;case"s":return this.actualFormat.indexOf("s")!==-1;default:return!1}},i.prototype._hasTime=function(){return this._isEnabled("h")||this._isEnabled("m")||this._isEnabled("s")},i.prototype._hasDate=function(){return this._isEnabled("y")||this._isEnabled("M")||this._isEnabled("d")},i.prototype._dataToOptions=function(){var b=this._element.data(),c={};return b.dateOptions&&b.dateOptions instanceof Object&&(c=a.extend(!0,c,b.dateOptions)),a.each(this._options,function(a){var d="date"+a.charAt(0).toUpperCase()+a.slice(1);void 0!==b[d]?c[a]=b[d]:delete c[a]}),c},i.prototype._notifyEvent=function(a){a.type===i.Event.CHANGE&&a.date&&a.date.isSame(a.oldDate)||!a.date&&!a.oldDate||this._element.trigger(a)},i.prototype._viewUpdate=function(a){"y"===a&&(a="YYYY"),this._notifyEvent({type:i.Event.UPDATE,change:a,viewDate:this._viewDate.clone()})},i.prototype._showMode=function(a){this.widget&&(a&&(this.currentViewMode=Math.max(s,Math.min(3,this.currentViewMode+a))),this.widget.find(".datepicker > div").hide().filter(".datepicker-"+n[this.currentViewMode].CLASS_NAME).show())},i.prototype._isInDisabledDates=function(a){return this._options.disabledDates[a.format("YYYY-MM-DD")]===!0},i.prototype._isInEnabledDates=function(a){return this._options.enabledDates[a.format("YYYY-MM-DD")]===!0},i.prototype._isInDisabledHours=function(a){return this._options.disabledHours[a.format("H")]===!0},i.prototype._isInEnabledHours=function(a){return this._options.enabledHours[a.format("H")]===!0},i.prototype._isValid=function(b,c){if(!b.isValid())return!1;if(this._options.disabledDates&&"d"===c&&this._isInDisabledDates(b))return!1;if(this._options.enabledDates&&"d"===c&&!this._isInEnabledDates(b))return!1;if(this._options.minDate&&b.isBefore(this._options.minDate,c))return!1;if(this._options.maxDate&&b.isAfter(this._options.maxDate,c))return!1;if(this._options.daysOfWeekDisabled&&"d"===c&&this._options.daysOfWeekDisabled.indexOf(b.day())!==-1)return!1;if(this._options.disabledHours&&("h"===c||"m"===c||"s"===c)&&this._isInDisabledHours(b))return!1;if(this._options.enabledHours&&("h"===c||"m"===c||"s"===c)&&!this._isInEnabledHours(b))return!1;if(this._options.disabledTimeIntervals&&("h"===c||"m"===c||"s"===c)){var d=!1;if(a.each(this._options.disabledTimeIntervals,function(){if(b.isBetween(this[0],this[1]))return d=!0,!1}),d)return!1}return!0},i.prototype._parseInputDate=function(a){return void 0===this._options.parseInputDate?b.isMoment(a)||(a=this.getMoment(a)):a=this._options.parseInputDate(a),a},i.prototype._keydown=function(a){var b=null,c=void 0,d=void 0,e=void 0,f=void 0,g=[],h={},i=a.which,j="p";q[i]=j;for(c in q)q.hasOwnProperty(c)&&q[c]===j&&(g.push(c),parseInt(c,10)!==i&&(h[c]=!0));for(c in this._options.keyBinds)if(this._options.keyBinds.hasOwnProperty(c)&&"function"==typeof this._options.keyBinds[c]&&(e=c.split(" "),e.length===g.length&&o[i]===e[e.length-1])){for(f=!0,d=e.length-2;d>=0;d--)if(!(o[e[d]]in h)){f=!1;break}if(f){b=this._options.keyBinds[c];break}}b&&b.call(this.widget)&&(a.stopPropagation(),a.preventDefault())},i.prototype._keyup=function(a){q[a.which]="r",r[a.which]&&(r[a.which]=!1,a.stopPropagation(),a.preventDefault())},i.prototype._indexGivenDates=function(b){var c={},d=this;return a.each(b,function(){var a=d._parseInputDate(this);a.isValid()&&(c[a.format("YYYY-MM-DD")]=!0)}),!!Object.keys(c).length&&c},i.prototype._indexGivenHours=function(b){var c={};return a.each(b,function(){c[this]=!0}),!!Object.keys(c).length&&c},i.prototype._initFormatting=function(){var a=this._options.format||"L LT",b=this;this.actualFormat=a.replace(/(\[[^\[]*])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){return b._dates[0].localeData().longDateFormat(a)||a}),this.parseFormats=this._options.extraFormats?this._options.extraFormats.slice():[],this.parseFormats.indexOf(a)<0&&this.parseFormats.indexOf(this.actualFormat)<0&&this.parseFormats.push(this.actualFormat),this.use24Hours=this.actualFormat.toLowerCase().indexOf("a")<1&&this.actualFormat.replace(/\[.*?]/g,"").indexOf("h")<1,this._isEnabled("y")&&(s=2),this._isEnabled("M")&&(s=1),this._isEnabled("d")&&(s=0),this.currentViewMode=Math.max(s,this.currentViewMode),this.unset||this._setValue(this._dates[0],0)},i.prototype._getLastPickedDate=function(){return this._dates[this._getLastPickedDateIndex()]},i.prototype._getLastPickedDateIndex=function(){return this._dates.length-1},i.prototype.getMoment=function(a){var c=void 0;return c=void 0===a||null===a?b():this._hasTimeZone()?b.tz(a,this.parseFormats,this._options.useStrict,this._options.timeZone):b(a,this.parseFormats,this._options.useStrict),this._hasTimeZone()&&c.tz(this._options.timeZone),c},i.prototype.toggle=function(){return this.widget?this.hide():this.show()},i.prototype.ignoreReadonly=function(a){if(0===arguments.length)return this._options.ignoreReadonly;if("boolean"!=typeof a)throw new TypeError("ignoreReadonly () expects a boolean parameter");this._options.ignoreReadonly=a},i.prototype.options=function(b){if(0===arguments.length)return a.extend(!0,{},this._options);if(!(b instanceof Object))throw new TypeError("options() this.options parameter should be an object");a.extend(!0,this._options,b);var c=this;a.each(this._options,function(a,b){void 0!==c[a]&&c[a](b)})},i.prototype.date=function(a,c){if(c=c||0,0===arguments.length)return this.unset?null:this._options.allowMultidate?this._dates.join(this._options.multidateSeparator):this._dates[c].clone();if(!(null===a||"string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");this._setValue(null===a?null:this._parseInputDate(a),c)},i.prototype.format=function(a){if(0===arguments.length)return this._options.format;if("string"!=typeof a&&("boolean"!=typeof a||a!==!1))throw new TypeError("format() expects a string or boolean:false parameter "+a);this._options.format=a,this.actualFormat&&this._initFormatting()},i.prototype.timeZone=function(a){if(0===arguments.length)return this._options.timeZone;if("string"!=typeof a)throw new TypeError("newZone() expects a string parameter");this._options.timeZone=a},i.prototype.dayViewHeaderFormat=function(a){if(0===arguments.length)return this._options.dayViewHeaderFormat;if("string"!=typeof a)throw new TypeError("dayViewHeaderFormat() expects a string parameter");this._options.dayViewHeaderFormat=a},i.prototype.extraFormats=function(a){if(0===arguments.length)return this._options.extraFormats;if(a!==!1&&!(a instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");this._options.extraFormats=a,this.parseFormats&&this._initFormatting()},i.prototype.disabledDates=function(b){if(0===arguments.length)return this._options.disabledDates?a.extend({},this._options.disabledDates):this._options.disabledDates;if(!b)return this._options.disabledDates=!1,this._update(),!0;if(!(b instanceof Array))throw new TypeError("disabledDates() expects an array parameter");this._options.disabledDates=this._indexGivenDates(b),this._options.enabledDates=!1,this._update()},i.prototype.enabledDates=function(b){if(0===arguments.length)return this._options.enabledDates?a.extend({},this._options.enabledDates):this._options.enabledDates;if(!b)return this._options.enabledDates=!1,this._update(),!0;if(!(b instanceof Array))throw new TypeError("enabledDates() expects an array parameter");this._options.enabledDates=this._indexGivenDates(b),this._options.disabledDates=!1,this._update()},i.prototype.daysOfWeekDisabled=function(a){if(0===arguments.length)return this._options.daysOfWeekDisabled.splice(0);if("boolean"==typeof a&&!a)return this._options.daysOfWeekDisabled=!1,this._update(),!0;if(!(a instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(this._options.daysOfWeekDisabled=a.reduce(function(a,b){return b=parseInt(b,10),b>6||b<0||isNaN(b)?a:(a.indexOf(b)===-1&&a.push(b),a)},[]).sort(),this._options.useCurrent&&!this._options.keepInvalid)for(var b=0;b<this._dates.length;b++){for(var c=0;!this._isValid(this._dates[b],"d");){if(this._dates[b].add(1,"d"),31===c)throw"Tried 31 times to find a valid date";c++}this._setValue(this._dates[b],b)}this._update()},i.prototype.maxDate=function(a){if(0===arguments.length)return this._options.maxDate?this._options.maxDate.clone():this._options.maxDate;if("boolean"==typeof a&&a===!1)return this._options.maxDate=!1,this._update(),!0;"string"==typeof a&&("now"!==a&&"moment"!==a||(a=this.getMoment()));var b=this._parseInputDate(a);if(!b.isValid())throw new TypeError("maxDate() Could not parse date parameter: "+a);if(this._options.minDate&&b.isBefore(this._options.minDate))throw new TypeError("maxDate() date parameter is before this.options.minDate: "+b.format(this.actualFormat));this._options.maxDate=b;for(var c=0;c<this._dates.length;c++)this._options.useCurrent&&!this._options.keepInvalid&&this._dates[c].isAfter(a)&&this._setValue(this._options.maxDate,c);this._viewDate.isAfter(b)&&(this._viewDate=b.clone().subtract(this._options.stepping,"m")),this._update()},i.prototype.minDate=function(a){if(0===arguments.length)return this._options.minDate?this._options.minDate.clone():this._options.minDate;if("boolean"==typeof a&&a===!1)return this._options.minDate=!1,this._update(),!0;"string"==typeof a&&("now"!==a&&"moment"!==a||(a=this.getMoment()));var b=this._parseInputDate(a);if(!b.isValid())throw new TypeError("minDate() Could not parse date parameter: "+a);if(this._options.maxDate&&b.isAfter(this._options.maxDate))throw new TypeError("minDate() date parameter is after this.options.maxDate: "+b.format(this.actualFormat));this._options.minDate=b;for(var c=0;c<this._dates.length;c++)this._options.useCurrent&&!this._options.keepInvalid&&this._dates[c].isBefore(a)&&this._setValue(this._options.minDate,c);this._viewDate.isBefore(b)&&(this._viewDate=b.clone().add(this._options.stepping,"m")),this._update()},i.prototype.defaultDate=function(a){if(0===arguments.length)return this._options.defaultDate?this._options.defaultDate.clone():this._options.defaultDate;if(!a)return this._options.defaultDate=!1,!0;"string"==typeof a&&(a="now"===a||"moment"===a?this.getMoment():this.getMoment(a));var b=this._parseInputDate(a);if(!b.isValid())throw new TypeError("defaultDate() Could not parse date parameter: "+a);if(!this._isValid(b))throw new TypeError("defaultDate() date passed is invalid according to component setup validations");this._options.defaultDate=b,(this._options.defaultDate&&this._options.inline||void 0!==this.input&&""===this.input.val().trim())&&this._setValue(this._options.defaultDate,0)},i.prototype.locale=function(a){if(0===arguments.length)return this._options.locale;if(!b.localeData(a))throw new TypeError("locale() locale "+a+" is not loaded from moment locales!");for(var c=0;c<this._dates.length;c++)this._dates[c].locale(this._options.locale);this._viewDate.locale(this._options.locale),this.actualFormat&&this._initFormatting(),this.widget&&(this.hide(),this.show())},i.prototype.stepping=function(a){return 0===arguments.length?this._options.stepping:(a=parseInt(a,10),(isNaN(a)||a<1)&&(a=1),void(this._options.stepping=a))},i.prototype.useCurrent=function(a){var b=["year","month","day","hour","minute"];if(0===arguments.length)return this._options.useCurrent;if("boolean"!=typeof a&&"string"!=typeof a)throw new TypeError("useCurrent() expects a boolean or string parameter");if("string"==typeof a&&b.indexOf(a.toLowerCase())===-1)throw new TypeError("useCurrent() expects a string parameter of "+b.join(", "));this._options.useCurrent=a},i.prototype.collapse=function(a){if(0===arguments.length)return this._options.collapse;if("boolean"!=typeof a)throw new TypeError("collapse() expects a boolean parameter");return this._options.collapse===a||(this._options.collapse=a,void(this.widget&&(this.hide(),this.show())))},i.prototype.icons=function(b){if(0===arguments.length)return a.extend({},this._options.icons);if(!(b instanceof Object))throw new TypeError("icons() expects parameter to be an Object");a.extend(this._options.icons,b),this.widget&&(this.hide(),this.show())},i.prototype.tooltips=function(b){if(0===arguments.length)return a.extend({},this._options.tooltips);if(!(b instanceof Object))throw new TypeError("tooltips() expects parameter to be an Object");a.extend(this._options.tooltips,b),this.widget&&(this.hide(),this.show())},i.prototype.useStrict=function(a){if(0===arguments.length)return this._options.useStrict;if("boolean"!=typeof a)throw new TypeError("useStrict() expects a boolean parameter");this._options.useStrict=a},i.prototype.sideBySide=function(a){if(0===arguments.length)return this._options.sideBySide;if("boolean"!=typeof a)throw new TypeError("sideBySide() expects a boolean parameter");this._options.sideBySide=a,this.widget&&(this.hide(),this.show())},i.prototype.viewMode=function(a){if(0===arguments.length)return this._options.viewMode;if("string"!=typeof a)throw new TypeError("viewMode() expects a string parameter");if(i.ViewModes.indexOf(a)===-1)throw new TypeError("viewMode() parameter must be one of ("+i.ViewModes.join(", ")+") value");this._options.viewMode=a,this.currentViewMode=Math.max(i.ViewModes.indexOf(a)-1,i.MinViewModeNumber),this._showMode()},i.prototype.calendarWeeks=function(a){if(0===arguments.length)return this._options.calendarWeeks;if("boolean"!=typeof a)throw new TypeError("calendarWeeks() expects parameter to be a boolean value");this._options.calendarWeeks=a,this._update()},i.prototype.buttons=function(b){if(0===arguments.length)return a.extend({},this._options.buttons);if(!(b instanceof Object))throw new TypeError("buttons() expects parameter to be an Object");if(a.extend(this._options.buttons,b),"boolean"!=typeof this._options.buttons.showToday)throw new TypeError("buttons.showToday expects a boolean parameter");if("boolean"!=typeof this._options.buttons.showClear)throw new TypeError("buttons.showClear expects a boolean parameter");if("boolean"!=typeof this._options.buttons.showClose)throw new TypeError("buttons.showClose expects a boolean parameter");this.widget&&(this.hide(),this.show())},i.prototype.keepOpen=function(a){if(0===arguments.length)return this._options.keepOpen;if("boolean"!=typeof a)throw new TypeError("keepOpen() expects a boolean parameter");this._options.keepOpen=a},i.prototype.focusOnShow=function(a){if(0===arguments.length)return this._options.focusOnShow;if("boolean"!=typeof a)throw new TypeError("focusOnShow() expects a boolean parameter");this._options.focusOnShow=a},i.prototype.inline=function(a){if(0===arguments.length)return this._options.inline;if("boolean"!=typeof a)throw new TypeError("inline() expects a boolean parameter");this._options.inline=a},i.prototype.clear=function(){this._setValue(null)},i.prototype.keyBinds=function(a){return 0===arguments.length?this._options.keyBinds:void(this._options.keyBinds=a)},i.prototype.debug=function(a){if("boolean"!=typeof a)throw new TypeError("debug() expects a boolean parameter");this._options.debug=a},i.prototype.allowInputToggle=function(a){if(0===arguments.length)return this._options.allowInputToggle;if("boolean"!=typeof a)throw new TypeError("allowInputToggle() expects a boolean parameter");this._options.allowInputToggle=a},i.prototype.keepInvalid=function(a){if(0===arguments.length)return this._options.keepInvalid;if("boolean"!=typeof a)throw new TypeError("keepInvalid() expects a boolean parameter");this._options.keepInvalid=a},i.prototype.datepickerInput=function(a){if(0===arguments.length)return this._options.datepickerInput;if("string"!=typeof a)throw new TypeError("datepickerInput() expects a string parameter");this._options.datepickerInput=a},i.prototype.parseInputDate=function(a){if(0===arguments.length)return this._options.parseInputDate;if("function"!=typeof a)throw new TypeError("parseInputDate() should be as function");this._options.parseInputDate=a},i.prototype.disabledTimeIntervals=function(b){if(0===arguments.length)return this._options.disabledTimeIntervals?a.extend({},this._options.disabledTimeIntervals):this._options.disabledTimeIntervals;if(!b)return this._options.disabledTimeIntervals=!1,this._update(),!0;if(!(b instanceof Array))throw new TypeError("disabledTimeIntervals() expects an array parameter");this._options.disabledTimeIntervals=b,this._update()},i.prototype.disabledHours=function(b){if(0===arguments.length)return this._options.disabledHours?a.extend({},this._options.disabledHours):this._options.disabledHours;if(!b)return this._options.disabledHours=!1,this._update(),!0;if(!(b instanceof Array))throw new TypeError("disabledHours() expects an array parameter");if(this._options.disabledHours=this._indexGivenHours(b),this._options.enabledHours=!1,this._options.useCurrent&&!this._options.keepInvalid)for(var c=0;c<this._dates.length;c++){for(var d=0;!this._isValid(this._dates[c],"h");){if(this._dates[c].add(1,"h"),24===d)throw"Tried 24 times to find a valid date";d++}this._setValue(this._dates[c],c)}this._update()},i.prototype.enabledHours=function(b){if(0===arguments.length)return this._options.enabledHours?a.extend({},this._options.enabledHours):this._options.enabledHours;if(!b)return this._options.enabledHours=!1,this._update(),!0;if(!(b instanceof Array))throw new TypeError("enabledHours() expects an array parameter");if(this._options.enabledHours=this._indexGivenHours(b),this._options.disabledHours=!1,this._options.useCurrent&&!this._options.keepInvalid)for(var c=0;c<this._dates.length;c++){for(var d=0;!this._isValid(this._dates[c],"h");){if(this._dates[c].add(1,"h"),24===d)throw"Tried 24 times to find a valid date";d++}this._setValue(this._dates[c],c)}this._update()},i.prototype.viewDate=function(a){if(0===arguments.length)return this._viewDate.clone();if(!a)return this._viewDate=(this._dates[0]||this.getMoment()).clone(),!0;if(!("string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("viewDate() parameter must be one of [string, moment or Date]");this._viewDate=this._parseInputDate(a),this._viewUpdate()},i.prototype.allowMultidate=function(a){if("boolean"!=typeof a)throw new TypeError("allowMultidate() expects a boolean parameter");this._options.allowMultidate=a},i.prototype.multidateSeparator=function(a){if(0===arguments.length)return this._options.multidateSeparator;if("string"!=typeof a||a.length>1)throw new TypeError("multidateSeparator expects a single character string parameter");this._options.multidateSeparator=a},e(i,null,[{key:"NAME",get:function(){return d}},{key:"VERSION",get:function(){return f}},{key:"DATA_KEY",get:function(){return g}},{key:"EVENT_KEY",get:function(){return h}},{key:"DATA_API_KEY",get:function(){return j}},{key:"DatePickerModes",get:function(){return n}},{key:"ViewModes",get:function(){return p}},{key:"MinViewModeNumber",get:function(){return s}},{key:"Event",get:function(){return m}},{key:"Selector",get:function(){return k}},{key:"Default",get:function(){return t},set:function(a){t=a}},{key:"ClassName",get:function(){return l}}]),i}();return u}(jQuery,moment);(function(e){var g=e.fn[f.NAME],h=["top","bottom","auto"],i=["left","right","auto"],j=["default","top","bottom"],k=function(a){var b=a.data("target"),c=void 0;return b||(b=a.attr("href")||"",b=/^#[a-z]/i.test(b)?b:null),c=e(b),0===c.length?c:(c.data(f.DATA_KEY)||e.extend({},c.data(),e(this).data()),c)},l=function(g){function k(b,d){c(this,k);var e=a(this,g.call(this,b,d));return e._init(),e}return b(k,g),k.prototype._init=function(){if(this._element.hasClass("input-group")){var a=this._element.find(".datepickerbutton");0===a.length?this.component=this._element.find(".input-group-append"):this.component=a}},k.prototype._getDatePickerTemplate=function(){var a=e("<thead>").append(e("<tr>").append(e("<th>").addClass("prev").attr("data-action","previous").append(e("<span>").addClass(this._options.icons.previous))).append(e("<th>").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",""+(this._options.calendarWeeks?"6":"5"))).append(e("<th>").addClass("next").attr("data-action","next").append(e("<span>").addClass(this._options.icons.next)))),b=e("<tbody>").append(e("<tr>").append(e("<td>").attr("colspan",""+(this._options.calendarWeeks?"8":"7"))));return[e("<div>").addClass("datepicker-days").append(e("<table>").addClass("table table-sm").append(a).append(e("<tbody>"))),e("<div>").addClass("datepicker-months").append(e("<table>").addClass("table-condensed").append(a.clone()).append(b.clone())),e("<div>").addClass("datepicker-years").append(e("<table>").addClass("table-condensed").append(a.clone()).append(b.clone())),e("<div>").addClass("datepicker-decades").append(e("<table>").addClass("table-condensed").append(a.clone()).append(b.clone()))]},k.prototype._getTimePickerMainTemplate=function(){var a=e("<tr>"),b=e("<tr>"),c=e("<tr>");return this._isEnabled("h")&&(a.append(e("<td>").append(e("<a>").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(e("<span>").addClass(this._options.icons.up)))),b.append(e("<td>").append(e("<span>").addClass("timepicker-hour").attr({"data-time-component":"hours",title:this._options.tooltips.pickHour}).attr("data-action","showHours"))),c.append(e("<td>").append(e("<a>").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(e("<span>").addClass(this._options.icons.down))))),this._isEnabled("m")&&(this._isEnabled("h")&&(a.append(e("<td>").addClass("separator")),b.append(e("<td>").addClass("separator").html(":")),c.append(e("<td>").addClass("separator"))),a.append(e("<td>").append(e("<a>").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(e("<span>").addClass(this._options.icons.up)))),b.append(e("<td>").append(e("<span>").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:this._options.tooltips.pickMinute}).attr("data-action","showMinutes"))),c.append(e("<td>").append(e("<a>").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(e("<span>").addClass(this._options.icons.down))))),this._isEnabled("s")&&(this._isEnabled("m")&&(a.append(e("<td>").addClass("separator")),
+    b.append(e("<td>").addClass("separator").html(":")),c.append(e("<td>").addClass("separator"))),a.append(e("<td>").append(e("<a>").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(e("<span>").addClass(this._options.icons.up)))),b.append(e("<td>").append(e("<span>").addClass("timepicker-second").attr({"data-time-component":"seconds",title:this._options.tooltips.pickSecond}).attr("data-action","showSeconds"))),c.append(e("<td>").append(e("<a>").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(e("<span>").addClass(this._options.icons.down))))),this.use24Hours||(a.append(e("<td>").addClass("separator")),b.append(e("<td>").append(e("<button>").addClass("btn btn-primary").attr({"data-action":"togglePeriod",tabindex:"-1",title:this._options.tooltips.togglePeriod}))),c.append(e("<td>").addClass("separator"))),e("<div>").addClass("timepicker-picker").append(e("<table>").addClass("table-condensed").append([a,b,c]))},k.prototype._getTimePickerTemplate=function(){var a=e("<div>").addClass("timepicker-hours").append(e("<table>").addClass("table-condensed")),b=e("<div>").addClass("timepicker-minutes").append(e("<table>").addClass("table-condensed")),c=e("<div>").addClass("timepicker-seconds").append(e("<table>").addClass("table-condensed")),d=[this._getTimePickerMainTemplate()];return this._isEnabled("h")&&d.push(a),this._isEnabled("m")&&d.push(b),this._isEnabled("s")&&d.push(c),d},k.prototype._getToolbar=function(){var a=[];return this._options.buttons.showToday&&a.push(e("<td>").append(e("<a>").attr({"data-action":"today",title:this._options.tooltips.today}).append(e("<span>").addClass(this._options.icons.today)))),!this._options.sideBySide&&this._hasDate()&&this._hasTime()&&a.push(e("<td>").append(e("<a>").attr({"data-action":"togglePicker",title:this._options.tooltips.selectTime}).append(e("<span>").addClass(this._options.icons.time)))),this._options.buttons.showClear&&a.push(e("<td>").append(e("<a>").attr({"data-action":"clear",title:this._options.tooltips.clear}).append(e("<span>").addClass(this._options.icons.clear)))),this._options.buttons.showClose&&a.push(e("<td>").append(e("<a>").attr({"data-action":"close",title:this._options.tooltips.close}).append(e("<span>").addClass(this._options.icons.close)))),0===a.length?"":e("<table>").addClass("table-condensed").append(e("<tbody>").append(e("<tr>").append(a)))},k.prototype._getTemplate=function(){var a=e("<div>").addClass("bootstrap-datetimepicker-widget dropdown-menu"),b=e("<div>").addClass("datepicker").append(this._getDatePickerTemplate()),c=e("<div>").addClass("timepicker").append(this._getTimePickerTemplate()),d=e("<ul>").addClass("list-unstyled"),f=e("<li>").addClass("picker-switch"+(this._options.collapse?" accordion-toggle":"")).append(this._getToolbar());return this._options.inline&&a.removeClass("dropdown-menu"),this.use24Hours&&a.addClass("usetwentyfour"),this._isEnabled("s")&&!this.use24Hours&&a.addClass("wider"),this._options.sideBySide&&this._hasDate()&&this._hasTime()?(a.addClass("timepicker-sbs"),"top"===this._options.toolbarPlacement&&a.append(f),a.append(e("<div>").addClass("row").append(b.addClass("col-md-6")).append(c.addClass("col-md-6"))),"bottom"!==this._options.toolbarPlacement&&"default"!==this._options.toolbarPlacement||a.append(f),a):("top"===this._options.toolbarPlacement&&d.append(f),this._hasDate()&&d.append(e("<li>").addClass(this._options.collapse&&this._hasTime()?"collapse":"").addClass(this._options.collapse&&this._hasTime()&&"time"===this._options.viewMode?"":"show").append(b)),"default"===this._options.toolbarPlacement&&d.append(f),this._hasTime()&&d.append(e("<li>").addClass(this._options.collapse&&this._hasDate()?"collapse":"").addClass(this._options.collapse&&this._hasDate()&&"time"===this._options.viewMode?"show":"").append(c)),"bottom"===this._options.toolbarPlacement&&d.append(f),a.append(d))},k.prototype._place=function(a){var b=a&&a.data&&a.data.picker||this,c=b._options.widgetPositioning.vertical,d=b._options.widgetPositioning.horizontal,f=void 0,g=(b.component||b._element).position(),h=(b.component||b._element).offset();if(b._options.widgetParent)f=b._options.widgetParent.append(b.widget);else if(b._element.is("input"))f=b._element.after(b.widget).parent();else{if(b._options.inline)return void(f=b._element.append(b.widget));f=b._element,b._element.children().first().after(b.widget)}if("auto"===c&&(c=h.top+1.5*b.widget.height()>=e(window).height()+e(window).scrollTop()&&b.widget.height()+b._element.outerHeight()<h.top?"top":"bottom"),"auto"===d&&(d=f.width()<h.left+b.widget.outerWidth()/2&&h.left+b.widget.outerWidth()>e(window).width()?"right":"left"),"top"===c?b.widget.addClass("top").removeClass("bottom"):b.widget.addClass("bottom").removeClass("top"),"right"===d?b.widget.addClass("float-right"):b.widget.removeClass("float-right"),"relative"!==f.css("position")&&(f=f.parents().filter(function(){return"relative"===e(this).css("position")}).first()),0===f.length)throw new Error("datetimepicker component should be placed within a relative positioned container");b.widget.css({top:"top"===c?"auto":g.top+b._element.outerHeight()+"px",bottom:"top"===c?f.outerHeight()-(f===b._element?0:g.top)+"px":"auto",left:"left"===d?(f===b._element?0:g.left)+"px":"auto",right:"left"===d?"auto":f.outerWidth()-b._element.outerWidth()-(f===b._element?0:g.left)+"px"})},k.prototype._fillDow=function(){var a=e("<tr>"),b=this._viewDate.clone().startOf("w").startOf("d");for(this._options.calendarWeeks===!0&&a.append(e("<th>").addClass("cw").text("#"));b.isBefore(this._viewDate.clone().endOf("w"));)a.append(e("<th>").addClass("dow").text(b.format("dd"))),b.add(1,"d");this.widget.find(".datepicker-days thead").append(a)},k.prototype._fillMonths=function(){for(var a=[],b=this._viewDate.clone().startOf("y").startOf("d");b.isSame(this._viewDate,"y");)a.push(e("<span>").attr("data-action","selectMonth").addClass("month").text(b.format("MMM"))),b.add(1,"M");this.widget.find(".datepicker-months td").empty().append(a)},k.prototype._updateMonths=function(){var a=this.widget.find(".datepicker-months"),b=a.find("th"),c=a.find("tbody").find("span"),d=this;b.eq(0).find("span").attr("title",this._options.tooltips.prevYear),b.eq(1).attr("title",this._options.tooltips.selectYear),b.eq(2).find("span").attr("title",this._options.tooltips.nextYear),a.find(".disabled").removeClass("disabled"),this._isValid(this._viewDate.clone().subtract(1,"y"),"y")||b.eq(0).addClass("disabled"),b.eq(1).text(this._viewDate.year()),this._isValid(this._viewDate.clone().add(1,"y"),"y")||b.eq(2).addClass("disabled"),c.removeClass("active"),this._getLastPickedDate().isSame(this._viewDate,"y")&&!this.unset&&c.eq(this._getLastPickedDate().month()).addClass("active"),c.each(function(a){d._isValid(d._viewDate.clone().month(a),"M")||e(this).addClass("disabled")})},k.prototype._getStartEndYear=function(a,b){var c=a/10,d=Math.floor(b/a)*a,e=d+9*c,f=Math.floor(b/c)*c;return[d,e,f]},k.prototype._updateYears=function(){var a=this.widget.find(".datepicker-years"),b=a.find("th"),c=this._getStartEndYear(10,this._viewDate.year()),d=this._viewDate.clone().year(c[0]),e=this._viewDate.clone().year(c[1]),f="";for(b.eq(0).find("span").attr("title",this._options.tooltips.prevDecade),b.eq(1).attr("title",this._options.tooltips.selectDecade),b.eq(2).find("span").attr("title",this._options.tooltips.nextDecade),a.find(".disabled").removeClass("disabled"),this._options.minDate&&this._options.minDate.isAfter(d,"y")&&b.eq(0).addClass("disabled"),b.eq(1).text(d.year()+"-"+e.year()),this._options.maxDate&&this._options.maxDate.isBefore(e,"y")&&b.eq(2).addClass("disabled"),f+='<span data-action="selectYear" class="year old">'+(d.year()-1)+"</span>";!d.isAfter(e,"y");)f+='<span data-action="selectYear" class="year'+(d.isSame(this._getLastPickedDate(),"y")&&!this.unset?" active":"")+(this._isValid(d,"y")?"":" disabled")+'">'+d.year()+"</span>",d.add(1,"y");f+='<span data-action="selectYear" class="year old">'+d.year()+"</span>",a.find("td").html(f)},k.prototype._updateDecades=function(){var a=this.widget.find(".datepicker-decades"),b=a.find("th"),c=this._getStartEndYear(100,this._viewDate.year()),d=this._viewDate.clone().year(c[0]),e=this._viewDate.clone().year(c[1]),f=!1,g=!1,h=void 0,i="";for(b.eq(0).find("span").attr("title",this._options.tooltips.prevCentury),b.eq(2).find("span").attr("title",this._options.tooltips.nextCentury),a.find(".disabled").removeClass("disabled"),(0===d.year()||this._options.minDate&&this._options.minDate.isAfter(d,"y"))&&b.eq(0).addClass("disabled"),b.eq(1).text(d.year()+"-"+e.year()),this._options.maxDate&&this._options.maxDate.isBefore(e,"y")&&b.eq(2).addClass("disabled"),i+=d.year()-10<0?"<span>&nbsp;</span>":'<span data-action="selectDecade" class="decade old" data-selection="'+(d.year()+6)+'">'+(d.year()-10)+"</span>";!d.isAfter(e,"y");)h=d.year()+11,f=this._options.minDate&&this._options.minDate.isAfter(d,"y")&&this._options.minDate.year()<=h,g=this._options.maxDate&&this._options.maxDate.isAfter(d,"y")&&this._options.maxDate.year()<=h,i+='<span data-action="selectDecade" class="decade'+(this._getLastPickedDate().isAfter(d)&&this._getLastPickedDate().year()<=h?" active":"")+(this._isValid(d,"y")||f||g?"":" disabled")+'" data-selection="'+(d.year()+6)+'">'+d.year()+"</span>",d.add(10,"y");i+='<span data-action="selectDecade" class="decade old" data-selection="'+(d.year()+6)+'">'+d.year()+"</span>",a.find("td").html(i)},k.prototype._fillDate=function(){var a=this.widget.find(".datepicker-days"),b=a.find("th"),c=[],d=void 0,f=void 0,g=void 0,h=void 0;if(this._hasDate()){for(b.eq(0).find("span").attr("title",this._options.tooltips.prevMonth),b.eq(1).attr("title",this._options.tooltips.selectMonth),b.eq(2).find("span").attr("title",this._options.tooltips.nextMonth),a.find(".disabled").removeClass("disabled"),b.eq(1).text(this._viewDate.format(this._options.dayViewHeaderFormat)),this._isValid(this._viewDate.clone().subtract(1,"M"),"M")||b.eq(0).addClass("disabled"),this._isValid(this._viewDate.clone().add(1,"M"),"M")||b.eq(2).addClass("disabled"),d=this._viewDate.clone().startOf("M").startOf("w").startOf("d"),h=0;h<42;h++){if(0===d.weekday()&&(f=e("<tr>"),this._options.calendarWeeks&&f.append('<td class="cw">'+d.week()+"</td>"),c.push(f)),g="",d.isBefore(this._viewDate,"M")&&(g+=" old"),d.isAfter(this._viewDate,"M")&&(g+=" new"),this._options.allowMultidate){var i=this._datesFormatted.indexOf(d.format("YYYY-MM-DD"));i!==-1&&d.isSame(this._datesFormatted[i],"d")&&!this.unset&&(g+=" active")}else d.isSame(this._getLastPickedDate(),"d")&&!this.unset&&(g+=" active");this._isValid(d,"d")||(g+=" disabled"),d.isSame(this.getMoment(),"d")&&(g+=" today"),0!==d.day()&&6!==d.day()||(g+=" weekend"),f.append('<td data-action="selectDay" data-day="'+d.format("L")+'" class="day'+g+'">'+d.date()+"</td>"),d.add(1,"d")}a.find("tbody").empty().append(c),this._updateMonths(),this._updateYears(),this._updateDecades()}},k.prototype._fillHours=function(){var a=this.widget.find(".timepicker-hours table"),b=this._viewDate.clone().startOf("d"),c=[],d=e("<tr>");for(this._viewDate.hour()>11&&!this.use24Hours&&b.hour(12);b.isSame(this._viewDate,"d")&&(this.use24Hours||this._viewDate.hour()<12&&b.hour()<12||this._viewDate.hour()>11);)b.hour()%4===0&&(d=e("<tr>"),c.push(d)),d.append('<td data-action="selectHour" class="hour'+(this._isValid(b,"h")?"":" disabled")+'">'+b.format(this.use24Hours?"HH":"hh")+"</td>"),b.add(1,"h");a.empty().append(c)},k.prototype._fillMinutes=function(){for(var a=this.widget.find(".timepicker-minutes table"),b=this._viewDate.clone().startOf("h"),c=[],d=1===this._options.stepping?5:this._options.stepping,f=e("<tr>");this._viewDate.isSame(b,"h");)b.minute()%(4*d)===0&&(f=e("<tr>"),c.push(f)),f.append('<td data-action="selectMinute" class="minute'+(this._isValid(b,"m")?"":" disabled")+'">'+b.format("mm")+"</td>"),b.add(d,"m");a.empty().append(c)},k.prototype._fillSeconds=function(){for(var a=this.widget.find(".timepicker-seconds table"),b=this._viewDate.clone().startOf("m"),c=[],d=e("<tr>");this._viewDate.isSame(b,"m");)b.second()%20===0&&(d=e("<tr>"),c.push(d)),d.append('<td data-action="selectSecond" class="second'+(this._isValid(b,"s")?"":" disabled")+'">'+b.format("ss")+"</td>"),b.add(5,"s");a.empty().append(c)},k.prototype._fillTime=function(){var a=void 0,b=void 0,c=this.widget.find(".timepicker span[data-time-component]");this.use24Hours||(a=this.widget.find(".timepicker [data-action=togglePeriod]"),b=this._getLastPickedDate().clone().add(this._getLastPickedDate().hours()>=12?-12:12,"h"),a.text(this._getLastPickedDate().format("A")),this._isValid(b,"h")?a.removeClass("disabled"):a.addClass("disabled")),c.filter("[data-time-component=hours]").text(this._getLastPickedDate().format(""+(this.use24Hours?"HH":"hh"))),c.filter("[data-time-component=minutes]").text(this._getLastPickedDate().format("mm")),c.filter("[data-time-component=seconds]").text(this._getLastPickedDate().format("ss")),this._fillHours(),this._fillMinutes(),this._fillSeconds()},k.prototype._doAction=function(a,b){var c=this._getLastPickedDate();if(e(a.currentTarget).is(".disabled"))return!1;switch(b=b||e(a.currentTarget).data("action")){case"next":var d=f.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.add(f.DatePickerModes[this.currentViewMode].NAV_STEP,d),this._fillDate(),this._viewUpdate(d);break;case"previous":var g=f.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.subtract(f.DatePickerModes[this.currentViewMode].NAV_STEP,g),this._fillDate(),this._viewUpdate(g);break;case"pickerSwitch":this._showMode(1);break;case"selectMonth":var h=e(a.target).closest("tbody").find("span").index(e(a.target));this._viewDate.month(h),this.currentViewMode===f.MinViewModeNumber?(this._setValue(c.clone().year(this._viewDate.year()).month(this._viewDate.month()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("M");break;case"selectYear":var i=parseInt(e(a.target).text(),10)||0;this._viewDate.year(i),this.currentViewMode===f.MinViewModeNumber?(this._setValue(c.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDecade":var j=parseInt(e(a.target).data("selection"),10)||0;this._viewDate.year(j),this.currentViewMode===f.MinViewModeNumber?(this._setValue(c.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDay":var k=this._viewDate.clone();e(a.target).is(".old")&&k.subtract(1,"M"),e(a.target).is(".new")&&k.add(1,"M"),this._setValue(k.date(parseInt(e(a.target).text(),10)),this._getLastPickedDateIndex()),this._hasTime()||this._options.keepOpen||this._options.inline||this.hide();break;case"incrementHours":var l=c.clone().add(1,"h");this._isValid(l,"h")&&this._setValue(l,this._getLastPickedDateIndex());break;case"incrementMinutes":var m=c.clone().add(this._options.stepping,"m");this._isValid(m,"m")&&this._setValue(m,this._getLastPickedDateIndex());break;case"incrementSeconds":var n=c.clone().add(1,"s");this._isValid(n,"s")&&this._setValue(n,this._getLastPickedDateIndex());break;case"decrementHours":var o=c.clone().subtract(1,"h");this._isValid(o,"h")&&this._setValue(o,this._getLastPickedDateIndex());break;case"decrementMinutes":var p=c.clone().subtract(this._options.stepping,"m");this._isValid(p,"m")&&this._setValue(p,this._getLastPickedDateIndex());break;case"decrementSeconds":var q=c.clone().subtract(1,"s");this._isValid(q,"s")&&this._setValue(q,this._getLastPickedDateIndex());break;case"togglePeriod":this._setValue(c.clone().add(c.hours()>=12?-12:12,"h"),this._getLastPickedDateIndex());break;case"togglePicker":var r=e(a.target),s=r.closest("a"),t=r.closest("ul"),u=t.find(".show"),v=t.find(".collapse:not(.show)"),w=r.is("span")?r:r.find("span"),x=void 0;if(u&&u.length){if(x=u.data("collapse"),x&&x.transitioning)return!0;u.collapse?(u.collapse("hide"),v.collapse("show")):(u.removeClass("show"),v.addClass("show")),w.toggleClass(this._options.icons.time+" "+this._options.icons.date),w.hasClass(this._options.icons.date)?s.attr("title",this._options.tooltips.selectDate):s.attr("title",this._options.tooltips.selectTime)}break;case"showPicker":this.widget.find(".timepicker > div:not(.timepicker-picker)").hide(),this.widget.find(".timepicker .timepicker-picker").show();break;case"showHours":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-hours").show();break;case"showMinutes":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-minutes").show();break;case"showSeconds":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-seconds").show();break;case"selectHour":var y=parseInt(e(a.target).text(),10);this.use24Hours||(c.hours()>=12?12!==y&&(y+=12):12===y&&(y=0)),this._setValue(c.clone().hours(y),this._getLastPickedDateIndex()),this._doAction(a,"showPicker");break;case"selectMinute":this._setValue(c.clone().minutes(parseInt(e(a.target).text(),10)),this._getLastPickedDateIndex()),this._doAction(a,"showPicker");break;case"selectSecond":this._setValue(c.clone().seconds(parseInt(e(a.target).text(),10)),this._getLastPickedDateIndex()),this._doAction(a,"showPicker");break;case"clear":this.clear();break;case"today":var z=this.getMoment();this._isValid(z,"d")&&this._setValue(z,this._getLastPickedDateIndex())}return!1},k.prototype.hide=function(){var a=!1;this.widget&&(this.widget.find(".collapse").each(function(){var b=e(this).data("collapse");return!b||!b.transitioning||(a=!0,!1)}),a||(this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this.widget.hide(),e(window).off("resize",this._place()),this.widget.off("click","[data-action]"),this.widget.off("mousedown",!1),this.widget.remove(),this.widget=!1,this._notifyEvent({type:f.Event.HIDE,date:this._getLastPickedDate().clone()}),void 0!==this.input&&this.input.blur(),this._viewDate=this._getLastPickedDate().clone()))},k.prototype.show=function(){var a=void 0,b={year:function(a){return a.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(a){return a.date(1).hours(0).seconds(0).minutes(0)},day:function(a){return a.hours(0).seconds(0).minutes(0)},hour:function(a){return a.seconds(0).minutes(0)},minute:function(a){return a.seconds(0)}};if(void 0!==this.input){if(this.input.prop("disabled")||!this._options.ignoreReadonly&&this.input.prop("readonly")||this.widget)return;void 0!==this.input.val()&&0!==this.input.val().trim().length?this._setValue(this._parseInputDate(this.input.val().trim()),0):this.unset&&this._options.useCurrent&&(a=this.getMoment(),"string"==typeof this._options.useCurrent&&(a=b[this._options.useCurrent](a)),this._setValue(a,0))}else this.unset&&this._options.useCurrent&&(a=this.getMoment(),"string"==typeof this._options.useCurrent&&(a=b[this._options.useCurrent](a)),this._setValue(a,0));this.widget=this._getTemplate(),this._fillDow(),this._fillMonths(),this.widget.find(".timepicker-hours").hide(),this.widget.find(".timepicker-minutes").hide(),this.widget.find(".timepicker-seconds").hide(),this._update(),this._showMode(),e(window).on("resize",{picker:this},this._place),this.widget.on("click","[data-action]",e.proxy(this._doAction,this)),this.widget.on("mousedown",!1),this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this._place(),this.widget.show(),void 0!==this.input&&this._options.focusOnShow&&!this.input.is(":focus")&&this.input.focus(),this._notifyEvent({type:f.Event.SHOW})},k.prototype.destroy=function(){this.hide(),this._element.removeData(f.DATA_KEY),this._element.removeData("date")},k.prototype.disable=function(){this.hide(),this.component&&this.component.hasClass("btn")&&this.component.addClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!0)},k.prototype.enable=function(){this.component&&this.component.hasClass("btn")&&this.component.removeClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!1)},k.prototype.toolbarPlacement=function(a){if(0===arguments.length)return this._options.toolbarPlacement;if("string"!=typeof a)throw new TypeError("toolbarPlacement() expects a string parameter");if(j.indexOf(a)===-1)throw new TypeError("toolbarPlacement() parameter must be one of ("+j.join(", ")+") value");this._options.toolbarPlacement=a,this.widget&&(this.hide(),this.show())},k.prototype.widgetPositioning=function(a){if(0===arguments.length)return e.extend({},this._options.widgetPositioning);if("[object Object]"!=={}.toString.call(a))throw new TypeError("widgetPositioning() expects an object variable");if(a.horizontal){if("string"!=typeof a.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(a.horizontal=a.horizontal.toLowerCase(),i.indexOf(a.horizontal)===-1)throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+i.join(", ")+")");this._options.widgetPositioning.horizontal=a.horizontal}if(a.vertical){if("string"!=typeof a.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(a.vertical=a.vertical.toLowerCase(),h.indexOf(a.vertical)===-1)throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+h.join(", ")+")");this._options.widgetPositioning.vertical=a.vertical}this._update()},k.prototype.widgetParent=function(a){if(0===arguments.length)return this._options.widgetParent;if("string"==typeof a&&(a=e(a)),null!==a&&"string"!=typeof a&&!(a instanceof e))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");this._options.widgetParent=a,this.widget&&(this.hide(),this.show())},k._jQueryHandleThis=function(a,b,c){var g=e(a).data(f.DATA_KEY);if("object"===("undefined"==typeof b?"undefined":d(b))&&e.extend({},f.Default,b),g||(g=new k(e(a),b),e(a).data(f.DATA_KEY,g)),"string"==typeof b){if(void 0===g[b])throw new Error('No method named "'+b+'"');return void 0===c?g[b]():g[b](c)}},k._jQueryInterface=function(a,b){return 1===this.length?k._jQueryHandleThis(this[0],a,b):this.each(function(){k._jQueryHandleThis(this,a,b)})},k}(f);return e(document).on(f.Event.CLICK_DATA_API,f.Selector.DATA_TOGGLE,function(){var a=k(e(this));0!==a.length&&l._jQueryInterface.call(a,"toggle")}).on(f.Event.CHANGE,"."+f.ClassName.INPUT,function(a){var b=k(e(this));0!==b.length&&l._jQueryInterface.call(b,"_change",a)}).on(f.Event.BLUR,"."+f.ClassName.INPUT,function(a){var b=k(e(this)),c=b.data(f.DATA_KEY);0!==b.length&&(c._options.debug||window.debug||l._jQueryInterface.call(b,"hide",a))}).on(f.Event.KEYDOWN,"."+f.ClassName.INPUT,function(a){var b=k(e(this));0!==b.length&&l._jQueryInterface.call(b,"_keydown",a)}).on(f.Event.KEYUP,"."+f.ClassName.INPUT,function(a){var b=k(e(this));0!==b.length&&l._jQueryInterface.call(b,"_keyup",a)}).on(f.Event.FOCUS,"."+f.ClassName.INPUT,function(a){var b=k(e(this)),c=b.data(f.DATA_KEY);0!==b.length&&c._options.allowInputToggle&&l._jQueryInterface.call(b,c,a)}),e.fn[f.NAME]=l._jQueryInterface,e.fn[f.NAME].Constructor=l,e.fn[f.NAME].noConflict=function(){return e.fn[f.NAME]=g,l._jQueryInterface},l})(jQuery)}();
\ No newline at end of file
diff --git a/backend/src/main/resources/static/manifest.json b/backend/src/main/resources/static/manifest.json
new file mode 100644
index 000000000..9a96ed36b
--- /dev/null
+++ b/backend/src/main/resources/static/manifest.json
@@ -0,0 +1,20 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+{
+  "gcm_sender_id": "503541620385"
+}
diff --git a/backend/src/main/resources/static/sw.js b/backend/src/main/resources/static/sw.js
new file mode 100644
index 000000000..51b78df84
--- /dev/null
+++ b/backend/src/main/resources/static/sw.js
@@ -0,0 +1,75 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+/**
+ * Service worker file.
+ *
+ * NOTE: This file MUST be located in the root.
+ */
+
+'use strict';
+
+console.log('Started', self);
+
+self.addEventListener('install', function (event) {
+    self.skipWaiting();
+    console.log('Installed', event);
+});
+
+self.addEventListener('activate', function (event) {
+    console.log('Activated', event);
+});
+
+self.addEventListener('push', function (event) {
+    console.log('Push message', event);
+
+    var data = event.data.json();
+    var title = data.title;
+    var message = data.message;
+
+    return self.registration.showNotification(title, {
+        body: message,
+        icon: '/img/icon.png',
+        tag: 'notification'
+    });
+});
+
+self.addEventListener('notificationclick', function (event) {
+    console.log('On notification click: ', event.notification.tag);
+
+    // Android doesn't close the notification when you click on it (see: http://crbug.com/463146)
+    event.notification.close();
+
+    // This looks to see if the current is already open and focuses if it is
+    event.waitUntil(
+        clients
+            .matchAll({
+                type: "window"
+            })
+            .then(function (clientList) {
+                for (var i = 0; i < clientList.length; i++) {
+                    var client = clientList[i];
+                    if (client.url == '/' && 'focus' in client) {
+                        return client.focus();
+                    }
+                }
+                if (clients.openWindow) {
+                    return clients.openWindow('/');
+                }
+            })
+    );
+});
diff --git a/backend/src/main/resources/templates/admin/mentor/groups.html b/backend/src/main/resources/templates/admin/mentor/groups.html
new file mode 100644
index 000000000..37f14cb62
--- /dev/null
+++ b/backend/src/main/resources/templates/admin/mentor/groups.html
@@ -0,0 +1,68 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="admin/view">
+<head>
+    <title>Manage Mentor Groups</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+
+    <div class="row">
+        <div class="col-sm-12">
+            <h3>Mentor Groups</h3>
+            <p>Upload a CSV file with the first column the NetID and the second column the mentor group
+                name.</p>
+
+            <form action="#" th:action="@{/admin/mentor/upload}"
+                  enctype="multipart/form-data" class="form-horizontal" method="post">
+                <div class="form-group">
+                    <div class="col-sm-offset-2 col-sm-8">
+                        <label class="custom-file">
+                            <input type="file" id="csv" name="file" class="csvfile"/>
+                            <span class="csvfile"></span>
+                        </label>
+                    </div>
+                </div>
+
+                <div class="form-group">
+                    <div class="col-sm-offset-2 col-sm-8">
+                        <button type="submit" class="btn btn-primary" name="storeLab">
+                            <i class="fa fa-cloud" aria-hidden="true"></i> Import groups
+                        </button>
+                    </div>
+                </div>
+            </form>
+        </div>
+
+        <div class="col-sm-5" th:if="${groups.size() != 0}">
+            <h4>Current groups</h4>
+            <ul class="list-group">
+                <li class="list-group-item" th:each="group : ${groups}">
+                    [[${group}]] ([[${group.students.size()}]] students)
+                </li>
+            </ul>
+        </div>
+    </div>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/admin/rooms.html b/backend/src/main/resources/templates/admin/rooms.html
new file mode 100644
index 000000000..5ea28fcbf
--- /dev/null
+++ b/backend/src/main/resources/templates/admin/rooms.html
@@ -0,0 +1,76 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorator="admin/view">
+<head>
+    <title>Manage Rooms</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="row">
+        <div class="col-sm-12">
+            <h3 class="mt-3">Rooms</h3>
+            <p>Below you can add, delete and change rooms</p>
+
+            <ul class="list-group">
+                <li class="list-group-item d-flex justify-content-between align-items-center"
+                    th:each="room, i : ${rooms}">[[${room.name}]]
+
+                    <div>
+                        <a th:href="@{/admin/room/edit/{id}(id=${room.id})}" class="btn btn-warning float-right">
+                            <i class="fa fa-pencil"></i>
+                        </a>
+                        <form method="post" action="/admin/room/delete" class="float-right mr-3">
+                            <input type="hidden"
+                                   th:name="${_csrf.parameterName}"
+                                   th:value="${_csrf.token}"/>
+                            <input type="hidden"
+                                   th:name="id"
+                                   th:value="${room.id}"/>
+                            <button type="submit" class="btn btn-danger"
+                                    onclick="return confirm('Are you sure you want to delete this room?')"><i
+                                    class="fa fa-trash"></i></button>
+                        </form>
+                    </div>
+                </li>
+            </ul>
+        </div>
+        <div class="col-12 col-md-4">
+
+            <h3 class="mt-5">Add New Room</h3>
+            <form method="post">
+                <input type="hidden"
+                       th:name="${_csrf.parameterName}"
+                       th:value="${_csrf.token}"/>
+                <div class="form-group">
+                    <label>Room</label>
+                    <input type="text" name="name" id="name" class="form-control" placeholder="Room"/>
+                </div>
+                <div class="form-group">
+                    <button type="submit" class="btn btn-primary mt-3"><i class="fa fa-plus"></i> Save Room</button>
+                </div>
+            </form>
+        </div>
+    </div>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/admin/rooms/edit.html b/backend/src/main/resources/templates/admin/rooms/edit.html
new file mode 100644
index 000000000..dd2d704a9
--- /dev/null
+++ b/backend/src/main/resources/templates/admin/rooms/edit.html
@@ -0,0 +1,72 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorator="admin/rooms">
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Edit room</h3>
+    </div>
+
+    <form class="form col-12 col-md-6" th:object="${room}" method="post" enctype="multipart/form-data">
+        <!-- input type="hidden" th:field="*{id}"/ -->
+        <input type="hidden"
+               th:name="${_csrf.parameterName}"
+               th:value="${_csrf.token}"/>
+
+
+        <div class="form-group">
+            <label>Room</label>
+            <input type="text" th:field="*{name}"
+                   class="form-control" placeholder="Room"/>
+        </div>
+        <div class="form-group">
+            <input class="form-check-input"
+                   type="checkbox"
+                   th:field="*{placeholder}" />
+            <label class="form-control-label">Placeholder?</label>
+        </div>
+
+
+        <div class="form-group">
+            <label class="custom-file">Map of room</label>
+            <input type="file" id="map" name="map"/>
+            <span class="map"></span>
+        </div>
+
+        <p>If you upload a map, students can enter their location in a request through an extra field.</p>
+
+        <div class="form-group">
+            <button type="submit" class="btn btn-primary">
+                <i class="fa fa-cloud"></i> Save changes
+            </button>
+        </div>
+    </form>
+
+    <th:block th:if="${room.mapFilePath != null}">
+        <h4>Current map</h4>
+        <img th:src="'/' + ${room.mapFilePath}" class="img-fluid" alt="Map of room"/>
+    </th:block>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/admin/view.html b/backend/src/main/resources/templates/admin/view.html
new file mode 100644
index 000000000..5b80b8f9a
--- /dev/null
+++ b/backend/src/main/resources/templates/admin/view.html
@@ -0,0 +1,66 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout">
+<head>
+</head>
+
+<body>
+<section layout:fragment="content">
+    <div class="container">
+        <section layout:fragment="breadcrumb">
+            <nav role="navigation" class="breadcrumbs">
+                <ol class="breadcrumb">
+                    <li class="breadcrumb-item"><a href="/">Home</a></li>
+                    <li class="breadcrumb-item active"><a href="/admin">Admin</a></li>
+                </ol>
+            </nav>
+        </section>
+
+        <ul class="nav nav-tabs">
+            <li class="nav-item">
+                <a href="/admin/mentor" class="nav-link" th:classappend="${#request.is('.*/mentor.*') ? 'active' : ''}">
+                    <i class="fa fa-th-large" aria-hidden="true"></i> Mentor Groups
+                </a>
+            </li>
+            <li class="nav-item">
+                <a href="/admin/rooms" class="nav-link" th:classappend="${#request.is('.*/room.*') ? 'active' : ''}">
+                    <i class="fa fa-flask" aria-hidden="true"></i> Rooms
+                </a>
+            </li>
+        </ul>
+    </div>
+
+    <div class="container">
+
+        <div class="alert alert-info mt-md-3" role="alert" th:unless="${#strings.isEmpty(message)}">
+            <th:block th:text="${message}"></th:block>
+            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+                <span aria-hidden="true">&times;</span>
+            </button>
+        </div>
+
+        <div layout:fragment="subcontent">
+            Sub content page
+        </div>
+    </div>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/container.html b/backend/src/main/resources/templates/container.html
new file mode 100644
index 000000000..d5ed1a875
--- /dev/null
+++ b/backend/src/main/resources/templates/container.html
@@ -0,0 +1,33 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout">
+<head>
+</head>
+
+<body>
+    <div class="container" layout:fragment="content">
+        <th:block layout:fragment="container">
+            <p>Page content goes here</p>
+        </th:block>
+    </div>
+
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/create.html b/backend/src/main/resources/templates/course/create.html
new file mode 100644
index 000000000..45c67641b
--- /dev/null
+++ b/backend/src/main/resources/templates/course/create.html
@@ -0,0 +1,98 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="container" xmlns:sec="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+            <li class="breadcrumb-item active">Create</li>
+        </ol>
+    </nav>
+
+    <div class="page-header">
+        <h1>Create</h1>
+    </div>
+
+    <form action="#" th:action="@{/course/create}" th:object="${course}" class="form-horizontal" method="post">
+        <div class="form-group">
+            <label for="inputCode" class="col-sm-2 control-label">Code</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:classappend="${#fields.hasErrors('code')} ? 'is-invalid'" th:field="*{code}" id="inputCode" class="form-control" placeholder="IN4303"/>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('code')}" th:errors="*{code}">Code error</div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputName" class="col-sm-2 control-label">Name</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:classappend="${#fields.hasErrors('name')} ? 'is-invalid'" th:field="*{name}" id="inputName" class="form-control" placeholder="Compiler Construction"/>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name error</div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputName" class="col-sm-2 control-label">Submission URL</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:field="*{submissionUrl}" id="inputSubmissionUrl"
+                       class="form-control" value="https://weblab.tudelft.nl/ti1206/2017-2018/dossier/{netid}"/>
+                ({netid} will be replaced with the actual netid) (e.g. link to CPM or WebLab)
+            </div>
+        </div>
+
+
+        <div class="form-group" sec:authorize="hasAnyRole('ADMIN')">
+            <label for="inputUsername" class="col-sm-4 control-label">Teacher NetID (optional)</label>
+
+            <div class="col-sm-8">
+                <input type="text" id="inputUsername" name="teacherUsername" class="form-control" placeholder="NetID"/>
+
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-8">
+                <div class="checkbox">
+                    <label >
+                        <input type="checkbox" th:field="*{hasGroups}"/> This course uses groups
+                    </label>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <input type="submit" class="btn btn-primary" name="create" value="Create new course"/>
+            </div>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/create/participant.html b/backend/src/main/resources/templates/course/create/participant.html
new file mode 100644
index 000000000..eb32f5682
--- /dev/null
+++ b/backend/src/main/resources/templates/course/create/participant.html
@@ -0,0 +1,64 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Create</h3>
+    </div>
+
+    <form action="#" th:object="${participantForm}" th:action="@{/course/{id}/participants/create(id=${course.id})}" class="form-horizontal" method="post">
+        <input type="hidden" th:field="*{courseId}" th:value="${course.id}"/>
+
+        <div class="form-group">
+            <label for="inputUsername" class="col-sm-2 control-label">Username</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:classappend="${#fields.hasErrors('username')} ? 'is-invalid'" id="inputUsername" th:field="*{username}" class="form-control" placeholder="netID"/>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('username')}" th:errors="*{username}">Username error</div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputRole" class="col-sm-2 control-label">Role</label>
+
+            <div class="col-sm-8">
+                <select class="form-control" id="inputRole" th:field="*{role}" th:classappend="${#fields.hasErrors('role')} ? 'is-invalid'">
+                    <option value="student">Student</option>
+                    <option value="assistant">Assistant</option>
+                    <option value="manager">Manager</option>
+                    <option value="teacher" th:if="${@permissionService.canManageTeachers(#authentication.principal, course.id)}">Teacher</option>
+                </select>
+                <span class="help-block" th:if="${#fields.hasErrors('role')}" th:errors="*{role}">Role error</span>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <input type="submit" class="btn btn-primary" value="Add Participant to Course"/>
+            </div>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/edit.html b/backend/src/main/resources/templates/course/edit.html
new file mode 100644
index 000000000..bf49f8783
--- /dev/null
+++ b/backend/src/main/resources/templates/course/edit.html
@@ -0,0 +1,79 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+    <link rel="stylesheet" href="/webjars/Eonasdan-bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css"/>
+
+    <script src="/webjars/momentjs/2.20.1/min/moment.min.js"></script>
+    <script src="/webjars/Eonasdan-bootstrap-datetimepicker/4.17.47/bootstrap-datetimepicker.min.js"></script>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <ol class="breadcrumb">
+        <li><a href="/">Home</a></li>
+        <li><a href="/courses">Courses</a></li>
+        <li><a href="#" th:href="@{/course/{id}(id=${course.id})}" th:text="${course.code}">TI1234</a></li>
+        <li class="active">Edit</li>
+    </ol>
+
+    <div class="page-header">
+        <h1>Edit</h1>
+    </div>
+
+    <form action="#" th:action="@{/courses}" th:object="${course}" class="form-horizontal" method="post">
+        <input type="hidden" th:field="*{id}"/>
+
+        <div class="form-group">
+            <label for="inputCode" class="col-sm-2 control-label">Code</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:field="*{code}" id="inputCode" class="form-control" placeholder="IN4303"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputName" class="col-sm-2 control-label">Name</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:field="*{name}" id="inputName" class="form-control" placeholder="Compiler Construction"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputName" class="col-sm-2 control-label">Submission URL</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:field="*{submissionUrl}" id="inputSubmissionUrl"
+                       class="form-control" placeholder="https://weblab.tudelft.nl/ti1206/2017-2018/dossier/{netid}"/>
+                ({netid} will be replaced with the actual netid) (e.g. link to CPM or WebLab)
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <input type="submit" class="btn btn-primary" name="update" value="Save"/>
+            </div>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/enroll.html b/backend/src/main/resources/templates/course/enroll.html
new file mode 100644
index 000000000..c69b991f0
--- /dev/null
+++ b/backend/src/main/resources/templates/course/enroll.html
@@ -0,0 +1,51 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+    <section layout:fragment="container">
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item"><a href="/">Home</a></li>
+                <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+                <li class="breadcrumb-item"><a href="#" th:href="@{/course/{id}/enroll(id=${course.id})}" th:text="${course.code}">TI1416</a></li>
+                <li class="breadcrumb-item active">Enroll</li>
+            </ol>
+        </nav>
+
+        <div class="page-header">
+            <h1>Enroll</h1>
+        </div>
+
+        <th:block th:if="${!course.isEnrolled(#authentication.principal.user)}">
+            <p>Do you wish to enroll for the course <strong th:text="${course}">Programming 101</strong>?</p>
+
+            <form action="#" th:action="@{/course/{id}/enroll(id=${course.id})}" class="form-horizontal" method="post">
+                <div class="text-center">
+                    <button type="submit" name="enroll" class="btn btn-success">Enroll</button> <small>or <a href="#" th:href="${#mvc.url('CC#list').build()}">go back</a></small>
+                </div>
+            </form>
+        </th:block>
+    </section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/index.html b/backend/src/main/resources/templates/course/index.html
new file mode 100644
index 000000000..b71fa7db0
--- /dev/null
+++ b/backend/src/main/resources/templates/course/index.html
@@ -0,0 +1,108 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+    <title>Courses</title>
+</head>
+
+<body>
+    <section layout:fragment="container">
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item"><a href="/">Home</a></li>
+                <li class="breadcrumb-item active" aria-current="page">Courses</li>
+            </ol>
+        </nav>
+
+        <div class="page-header">
+            <div class="pull-right" sec:authorize="hasAnyRole('TEACHER', 'ADMIN')">
+                <a href="#" th:href="@{/course/create}" class="btn btn-sm btn-secondary btn-primary">Create</a>
+            </div>
+
+            <h1>Courses</h1>
+        </div>
+
+        <!-- Shown on other devices (> phone) -->
+        <div class="d-none d-sm-block">
+        <table th:unless="${#lists.isEmpty(courses)}" class="table table-striped table-bordered">
+            <thead>
+                <tr>
+                    <th>Course</th>
+                    <th>Teacher</th>
+                    <th></th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr th:each="course : ${courses}">
+                    <td>
+                    	<th:block th:if="${@permissionService.canViewCourse(#authentication.principal, course.id)}">
+                        	<a href="#" th:href="@{/course/{id}(id=${course.id})}" th:text="${course}">Algorithms &amp; Datastructures (TI1316)</a>
+                        </th:block>
+                        <th:block th:unless="${@permissionService.canViewCourse(#authentication.principal, course.id)}" th:text="${course}">
+                        	Algorithms &amp; Datastructures (TI1316)
+                        </th:block>
+                    </td>
+                    <td th:text="${#strings.listJoin(course.teachers, ', ')}">Prof. dr. A. Einstein</td>
+                    <td>
+                        <th:block th:unless="${#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course) || #authenticated.participates(course) || course.getIsArchived() }">
+                            <a href="#" class="btn btn-success" th:href="@{/course/{id}/enroll(id=${course.id})}">enroll</a>
+                        </th:block>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+        </div>
+
+        <!-- Shown on extra small devices (phone) -->
+        <div class="d-block d-sm-none">
+            <div th:each="course : ${courses}" class="card "
+                 th:classappend="${#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course) || #authenticated.participates(course)} ? 'bg-primary text-white' : 'bg-light'"
+                 style="margin-bottom:20px;">
+                    <h4 class="card-header" th:text="${course}">Algorithms &amp; Datastructures (TI1316)</h4>
+
+                <div class="card-body">
+                    <dl>
+                    <dt>Teacher</dt>
+                    <dd class="truncate" th:text="${#strings.listJoin(course.teachers, ', ')}">Prof. dr. A. Einstein</dd>
+                    </dl>
+
+                </div>
+                <div class="card-footer">
+                <div class="btn-group btn-group-justified">
+                    <th:block th:if="${#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course) || #authenticated.participates(course)}">
+                        <a href="#" th:href="@{/course/{id}(id=${course.id})}" class="btn btn-primary btn-sm">View</a>
+                    </th:block>
+
+                    <th:block th:unless="${#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course) || #authenticated.participates(course)}">
+                        <a href="#" th:href="@{/course/{id}/enroll(id=${course.id})}" class="btn btn-success btn-sm">Enroll</a>
+                    </th:block>
+                </div>
+                </div>
+            </div>
+        </div>
+
+        <nav th:include="pagination :: pagination (pager=${courses}, url='courses')">
+            Pagination
+        </nav>
+    </section>
+</body>
+
+</html>
diff --git a/backend/src/main/resources/templates/course/remove/participant.html b/backend/src/main/resources/templates/course/remove/participant.html
new file mode 100644
index 000000000..7c67abbad
--- /dev/null
+++ b/backend/src/main/resources/templates/course/remove/participant.html
@@ -0,0 +1,39 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-header">
+        <h2>Remove participant from course</h2>
+    </div>
+
+    <p>Are you sure you want to remove <strong th:text="${role.user}">student</strong> from the course?</p>
+
+    <form action="#" th:action="@{/course/{courseId}/participants/{id}/remove(courseId=${role.course.id},id=${role.id})}" class="form-horizontal" method="post">
+        <div class="text-center">
+            <button type="submit" name="enroll" class="btn btn-danger">Remove from course</button> <small>or <a href="#" th:href="@{/course/{id}/participants(id=${role.course.id})}">go back</a></small>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view.html b/backend/src/main/resources/templates/course/view.html
new file mode 100644
index 000000000..756eab9b7
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view.html
@@ -0,0 +1,98 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout">
+<head>
+</head>
+
+<body>
+<section layout:fragment="content">
+        <div class="container">
+            <section layout:fragment="breadcrumb">
+                <nav role="navigation" class="breadcrumbs">
+                <ol class="breadcrumb">
+                    <li class="breadcrumb-item"><a href="/">Home</a></li>
+                    <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+                    <li class="breadcrumb-item active" th:text="${course.code}">TI1316</li>
+                </ol>
+                </nav>
+            </section>
+
+            <ul class="nav nav-tabs">
+                <li class="nav-item">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/course/\d+') ? 'active' : ''}"
+                       th:href="@{/course/{id}(id=${course.id})}">
+                        <i class="fa fa-th-large" aria-hidden="true"></i> Info
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canManageParticipants(#authentication.principal, course.id)}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/participants.*') ? 'active' : ''}"  th:href="@{/course/{id}/participants(id=${course.id})}">
+                        <i class="fa fa-user" aria-hidden="true"></i> Participants
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canManageParticipants(#authentication.principal, course.id) and course.hasGroups}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/group.*') ? 'active' : ''}" th:href="@{/course/{id}/groups(id=${course.id})}">
+                        <i class="fa fa-users" aria-hidden="true"></i> Groups
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canManageAssignments(#authentication.principal, course.id)}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/assignments') ? 'active' : ''}"  th:href="@{/course/{id}/assignments(id=${course.id})}">
+                        <i class="fa fa-tasks" aria-hidden="true"></i> Assignments
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canViewCourse(#authentication.principal, course.id)}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/lab.*') ? 'active' : ''}" th:href="@{/course/{id}/labs(id=${course.id})}">
+                        <i class="fa fa-calendar" aria-hidden="true"></i> Labs
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canEditCourse(#authentication.principal, course.id)}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/settings.*') ? 'active' : ''}" th:href="@{/course/{id}/settings(id=${course.id})}">
+                        <i class="fa fa-gear" aria-hidden="true"></i> Settings
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canManageAssignments(#authentication.principal, course.id)}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/status.*') ? 'active' : ''}" th:href="@{/course/{id}/status(id=${course.id})}">
+                        <i class="fa fa-bar-chart" aria-hidden="true"></i> Status
+                    </a>
+                </li>
+                <li class="nav-item" th:if="${@permissionService.canViewOwnFeedback(#authentication.principal, course.id)}">
+                    <a href="#" class="nav-link" th:classappend="${#request.is('.*/feedback.*') ? 'active' : ''}" th:href="@{/course/{id}/participants/feedback/{userId}(id=${course.id},userId=${#authenticated.getId()})}">
+                        <i class="fa fa-comments" aria-hidden="true"></i> Feedback
+                    </a>
+                </li>
+            </ul>
+        </div>
+
+    <div class="container">
+
+        <div class="alert alert-info mt-md-3" role="alert" th:unless="${#strings.isEmpty(message)}">
+            <th:block th:text="${message}"></th:block>
+            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+                <span aria-hidden="true">&times;</span>
+            </button>
+        </div>
+
+        <div layout:fragment="subcontent">
+            Sub content page
+        </div>
+    </div>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/assignments.html b/backend/src/main/resources/templates/course/view/assignments.html
new file mode 100644
index 000000000..50af16d56
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/assignments.html
@@ -0,0 +1,118 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="course/view" xmlns:form="http://www.w3.org/1999/xhtml">
+<head>
+    <title>Assignments ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Assignments</h3>
+    </div>
+
+    <form action="#" th:action="@{/course/{id}/assignments(id=${course.id})}" class="form" enctype="multipart/form-data"
+          method="post">
+        <input type="hidden" th:if="${course.id != null}" th:field="*{id}"/>
+
+        <th:block th:each="assignment, loop : *{assignments.getAssignmentList()}">
+            <div class="form-group">
+                <div class="col-sm-12">
+                    <div class="row" style="margin-bottom: 10px">
+                        <div class="col-sm-4">
+                            <h6>Assignment name</h6>
+                            <input type="text" th:value="${assignment.name}" th:name="${'assignmentList[' + loop.index + '].name'}" class="form-control" required="required" />
+                            <input type="hidden" th:value="${assignment.assignmentID}" th:name="${'assignmentList[' + loop.index + '].assignmentID'}" readonly="readonly" />
+                        </div>
+                        <div class="col-sm-2">
+                            <h6>Assignment id</h6>
+                            <input type="text" th:value="${assignment.getAssignmentID()}"
+                                   class="form-control" disabled/>
+                        </div>
+                        <div class="col-sm-3">
+                            <h6>Verification Id (used for api)</h6>
+                            <input type="text"
+                                   th:value="${assignment.getVerification()}"
+                                   class="form-control" disabled/>
+                        </div>
+                        <button th:if="${assignment.hasLab()}" data-toggle="tooltip"
+                                data-placement="right"
+                                title="Upload a list of students and automatically enroll them for a lab in which this assignment can be signed off." type="button" class="btn btn-secondary toggler"
+                                style="margin-right: 10px">
+                            Upload & Enqueue
+                        </button>
+                        <button type="submit" th:if="${assignment.assignmentID}" th:value="${assignment.assignmentID}" class="btn btn-secondary" name="removeAssignment">
+                            <span class="fa fa-trash-o"></span>
+                        </button>
+                    </div>
+                    <div class="row togglable" style="display: none">
+                        <div class="col-sm-8">
+                            <input type="file" name="csv" class="csvfile"/>
+                            <span class="csvfile"></span>
+                        </div>
+                        <div class="col-sm-4">
+                            <button type="submit" class="btn btn-success"
+                                    name="uploadEnqueue" th:value="${assignment.assignmentID}">
+                                Confirm
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </th:block>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <button type="submit" class="btn btn-success" name="addAssignment">Add assignment</button>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <input type="submit" class="btn btn-primary" name="storeAssignments" value="Save"/>
+            </div>
+        </div>
+    </form>
+    <script type="text/javascript">
+        $(function () {
+            $('.toggler').on('click', function () {
+
+                var parent = $(this).parent();
+                var sibling = parent.next();
+
+                if(sibling.is(":visible")) {
+                    $('.togglable').hide();
+                } else {
+                    $('.togglable').hide();
+                    sibling.show();
+                }
+            });
+        });
+    </script>
+    <script type="text/javascript">
+            $('[data-toggle="tooltip"]').tooltip({
+                trigger : 'hover'
+            })
+    </script>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/delete.html b/backend/src/main/resources/templates/course/view/delete.html
new file mode 100644
index 000000000..3c3769862
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/delete.html
@@ -0,0 +1,46 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/course/{id}/(id=${course.id})}" th:text="${course.code}">TI1416</a></li>
+            <li class="breadcrumb-item active">Delete course</li>
+        </ol>
+    </nav>
+
+    <div class="page-header">
+        <h1>Delete course</h1>
+    </div>
+    <p>Are you sure you want to delete the course <strong th:text="${course}">Programming 101</strong>?</p>
+
+    <form action="#" th:action="@{/course/{id}/remove(id=${course.id})}" th:object="${course}" method="post">
+        <input type="submit" class="btn btn-danger" value="Delete course"/>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/feedback.html b/backend/src/main/resources/templates/course/view/feedback.html
new file mode 100644
index 000000000..03e5cc2ca
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/feedback.html
@@ -0,0 +1,75 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Feedback for [[${assistant}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3 >Feedback for [[${assistant}]]</h3>
+    </div>
+
+    <div class="col-sm-6">
+		<div th:if="${feedback.getNrOfElements() == 0}">
+			<h4>There is no feedback for this assistant yet.</h4>
+		</div>
+
+		<blockquote class="blockquote">
+			<div class="panel panel-default" th:each="request : ${feedback.pageList}" th:if="${feedback.getNrOfElements() > 0}">
+				<div class="panel-body" th:text="${request.getFeedback()}">
+					Feedback
+				</div>
+				<footer class="blockquote-footer" th:if="${@permissionService.canViewDeanonimizedFeedback(#authentication.principal, request.id)}" >
+					Feedback by [[${request.getRequestEntity()}]]
+					on [[${#temporals.format(request.getCreatedAt(), 'dd MMMM yyyy')}]]
+					for [[${request.getLab().getCourse()}]]
+				</footer>
+			</div>
+		</blockquote>
+    </div>
+
+    <nav th:if="${feedback.pageCount > 1}">
+        <ul class="pagination">
+            <li class="page-item" th:classappend="${not feedback.firstPage} ? '' : 'disabled'">
+                <span class="page-link" th:if="${feedback.firstPage}">&laquo;</span>
+
+                <a th:if="${!feedback.firstPage}" th:href="@{/course/{courseId}/participants/feedback/{id}(courseId=${course.id}, id=${assistant.id}, page=0)}">&laquo;</a>
+            </li>
+
+            <li class="page-item" th:each="pageNumber : ${#numbers.sequence(0, feedback.pageCount-1)}" th:classappend="${pageNumber == feedback.page} ? 'active' : ''">
+                <span class="page-link" th:if="${pageNumber == feedback.page}" th:text="${pageNumber+1}">1</span>
+
+                <a th:if="${pageNumber != feedback.page}" th:text="${pageNumber+1}" th:href="@{/course/{courseId}/participants/feedback/{id}(courseId=${course.id},id=${assistant.id}, page=${pageNumber})}">1</a>
+            </li>
+
+            <li class="page-item" th:classappend="${not feedback.lastPage} ? '' : 'disabled'">
+                <span class="page-link" th:if="${feedback.lastPage}">&raquo;</span>
+
+                <a th:if="${!feedback.lastPage}" th:href="@{/course/{courseId}/participants/feedback/{id}(courseId=${course.id},id=${assistant.id},page=${feedback.pageCount})}">&raquo;</a>
+            </li>
+        </ul>
+    </nav>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/group.html b/backend/src/main/resources/templates/course/view/group.html
new file mode 100644
index 000000000..3377772e3
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/group.html
@@ -0,0 +1,43 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Group [[${group}]] ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3 >View single group: [[${group}]]</h3>
+    </div>
+
+    <div class="row">
+        <div class="col-sm-5">
+            <h4>Group members</h4>
+            <ul class="list-group">
+                <li class="list-group-item" th:each="student : ${group.getMembers()}" >[[${student.getDisplayName()}]]</li>
+            </ul>
+        </div>
+    </div>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/groups.html b/backend/src/main/resources/templates/course/view/groups.html
new file mode 100644
index 000000000..b5640682c
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/groups.html
@@ -0,0 +1,68 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Groups ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Groups</h3>
+    </div>
+
+    <div class="row">
+        <div class="col-sm-12">
+            <h4>Import groups</h4>
+            <p class="lead">You can upload a CSV file from Brightspace to create groups.</p>
+
+            <form action="#" th:action="@{/course/{id}/groups/import(id=${course.id})}"
+                  enctype="multipart/form-data" class="form-horizontal" method="post">
+                <div class="form-group">
+                    <div class="col-sm-offset-2 col-sm-8">
+                        <label class="custom-file">
+                            <input type="file" id="csv" name="file" class="csvfile" />
+                            <span class="csvfile"></span>
+                        </label>
+                    </div>
+                </div>
+
+                <div class="form-group">
+                    <div class="col-sm-offset-2 col-sm-8">
+                        <button type="submit" class="btn btn-primary" name="storeLab">Import groups</button>
+                    </div>
+                </div>
+            </form>
+        </div>
+
+        <div class="col-sm-5">
+            <h4>Current groups</h4>
+            <ul class="list-group">
+                <li class="list-group-item" th:each="group : ${course.getGroups()}" >
+                    <a th:href="@{/course/{id}/group/{groupId}(id=${course.id},groupId=${group.id})}">[[${group.getDisplayName()}]]</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/info.html b/backend/src/main/resources/templates/course/view/info.html
new file mode 100644
index 000000000..b1654ca98
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/info.html
@@ -0,0 +1,123 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Info ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <div class="alert alert-warning" th:if="${course.isArchived}">
+            <strong>Warning!</strong> This course is archived.
+        </div>
+        <div class="pull-right btn-toolbar">
+            <a href="#" th:href="@{/course/{id}/leave(id=${course.id})}" th:if="${#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course) || #authenticated.participates(course)}" class="btn btn-sm btn-danger">
+                Leave course
+            </a>
+            <a href="#" th:href="@{/course/{id}/enroll(id=${course.id})}" th:unless="${#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course) || #authenticated.participates(course)}" class="btn btn-sm btn-primary">
+                Enroll for this course
+            </a>
+        </div>
+        <h3>Info</h3>
+    </div>
+
+    <div class="row">
+        <div class="col-sm-3">
+            <h4>Course ID</h4>
+
+            <ul class="list-group">
+                <li th:text="${course.getId()}"
+                    class="list-group-item">-1
+                </li>
+            </ul>
+        </div>
+    </div>
+
+    <div class="row">
+        <div class="col-sm-3">
+            <h4>Teachers</h4>
+
+            <th:block th:if="${#lists.isEmpty(course.teachers)}">
+                <i>No teachers</i>
+            </th:block>
+
+            <th:block th:unless="${#lists.isEmpty(course.teachers)}">
+                <ul class="list-group">
+                    <li th:each="teacher : ${course.teachers}" th:text="${teacher}" class="list-group-item">Prof. Dr. A. Einstein</li>
+                </ul>
+            </th:block>
+        </div>
+
+        <div class="col-sm-3">
+            <h4>Managers</h4>
+
+            <th:block th:if="${#lists.isEmpty(course.managers)}">
+                <i>No managers</i>
+            </th:block>
+
+            <th:block th:unless="${#lists.isEmpty(course.managers)}">
+                <ul class="list-group">
+                    <li th:each="manager : ${course.managers}" th:text="${manager}" class="list-group-item">Prof. Dr. A. Einstein</li>
+                </ul>
+            </th:block>
+        </div>
+
+        <div class="col-sm-3">
+            <h4>Assistants</h4>
+
+            <th:block th:if="${#lists.isEmpty(course.assistants)}">
+                <i>No assistants</i>
+            </th:block>
+
+            <th:block th:unless="${#lists.isEmpty(course.assistants)}">
+                <ul class="list-group">
+                    <li th:each="assistant : ${course.assistants}" th:text="${assistant}" class="list-group-item"></li>
+                </ul>
+            </th:block>
+        </div>
+
+        <div class="col-sm-3">
+            <h4>Assignments</h4>
+
+            <th:block th:if="${#lists.isEmpty(course.assignments)}">
+                <i>No assignments</i>
+            </th:block>
+
+            <th:block th:unless="${#lists.isEmpty(course.assignments)}">
+                <ul class="list-group">
+                    <li th:each="assignment : ${course.assignments}" th:text="${assignment}" class="list-group-item"></li>
+                </ul>
+            </th:block>
+        </div>
+    </div>
+
+    <th:block th:if="${@permissionService.canEditCourse(#authentication.principal, course.id)}">
+        <div class="page-sub-header">
+            <h3>Options</h3>
+        </div>
+
+        <a href="#" th:href="@{/course/{id}/requests/export(id=${course.id})}" class="btn btn-primary">Export requests</a>
+        <a href="#" th:href="@{/course/{id}/requests/signofflist.csv(id=${course.id})}" class="btn btn-secondary">Export latest status of students</a>
+    </th:block>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/labs.html b/backend/src/main/resources/templates/course/view/labs.html
new file mode 100644
index 000000000..3ca37d22b
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/labs.html
@@ -0,0 +1,66 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Labs ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <div class="pull-right" th:if="${@permissionService.canCreateLab(#authentication.principal, course.id)}">
+            <a href="#" th:href="@{/course/{id}/lab/create(id=${course.id})}" class="btn btn-sm btn-primary">
+                Create new lab
+            </a>
+        </div>
+        <h3>Labs</h3>
+    </div>
+
+    <th:block th:if="${#lists.isEmpty(course.labs)}">
+        No labs for this course.
+    </th:block>
+
+    <th:block th:unless="${#lists.isEmpty(course.labs)}">
+        <ul class="list-group sort-open">
+            <li class="list-group-item lab-item" th:classappend="${lab.isOpen()} ? 'lab-open' : 'lab-closed'" th:each="lab, itemIndex : ${course.labs}">
+                <a href="#" th:href="@{/lab/{id}(id=${lab.id})}" >
+                    [[${lab.title}]] [[${#temporals.format(lab.slot.opensAt, 'dd MMMM yyyy HH:mm')}]] - [[${#temporals.format(lab.slot.closesAt, 'dd MMMM yyyy HH:mm')}]] [[${occupation[itemIndex.index]}]]
+                </a>
+                <span class="badge badge-pill badge-success text-white" th:if="${lab.isOpen()}">Open</span>
+                <span class="badge badge-pill badge-warning text-white" th:if="${!lab.isOpen() and lab.slotSelectionIsOpen()}">Slot selection</span>
+                <div class="btn-group pull-right">
+                    <th:block th:if="${@permissionService.canEditLab(#authentication.principal, lab.id)}">
+                        <a th:href="@{/lab/{id}/edit(id=${lab.id})}" class="btn btn-sm btn-secondary">
+                            <i class="fa fa-pencil" aria-hidden="true"></i>
+                        </a>
+                    </th:block>
+                    <th:block th:if="${@permissionService.canRemoveLab(#authentication.principal, lab.id)}">
+                        <a th:href="@{/lab/{id}/remove(id=${lab.id})}" class="btn btn-sm btn-danger">
+                            <i class="fa fa-trash-o" aria-hidden="true"></i>
+                        </a>
+                    </th:block>
+                </div>
+            </li>
+        </ul>
+    </th:block>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/leave.html b/backend/src/main/resources/templates/course/view/leave.html
new file mode 100644
index 000000000..55561ca54
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/leave.html
@@ -0,0 +1,41 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Leave ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Leave</h3>
+    </div>
+
+    <p>Are you sure you want to leave the course <strong th:text="${course}">Programming 101</strong>?</p>
+
+    <form action="#" th:action="@{/course/{id}/leave(id=${course.id})}" class="form-horizontal" method="post">
+        <div class="text-center">
+            <button type="submit" name="enroll" class="btn btn-danger">Leave</button> <small>or <a href="#" th:href="${#mvc.url('CC#list').build()}">go away</a></small>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/participants.html b/backend/src/main/resources/templates/course/view/participants.html
new file mode 100644
index 000000000..1852cbe9c
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/participants.html
@@ -0,0 +1,204 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="course/view">
+<head>
+    <title>Participants ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <div class="pull-right">
+            <a type="submit" class="btn btn-sm btn-danger"
+               th:href="@{/course/{id}/participants(id=${course.id},clean=1)}">
+                Clean TA's and Staff
+            </a>
+        </div>
+        <div class="pull-right" style="margin-right: 10px">
+            <a href="#" th:href="@{/course/{id}/participants/create(id=${course.id})}" class="btn btn-sm btn-secondary">
+                Add participant
+            </a>
+        </div>
+        <h3>Participants</h3>
+    </div>
+
+    <div class="boxed-group">
+        <ul class="nav nav-pills">
+            <li role="presentation" th:unless="${staff}" class="nav-item"><a href="#"
+                                                                             class="nav-link active">Students</a></li>
+            <li role="presentation" th:if="${staff}" class="nav-item"><a class="nav-link"
+                                                                         th:href="@{/course/{id}/participants(id=${course.id})}">Students</a>
+            </li>
+            <li role="presentation" th:if="${staff}" class="nav-item"><a href="#" class="nav-link active">Staff</a></li>
+            <li role="presentation" th:unless="${staff}" class="nav-item"><a class="nav-link"
+                                                                             th:href="@{/course/{id}/participants(id=${course.id},staff=1)}">Staff</a>
+            </li>
+        </ul>
+    </div>
+
+    <div class="boxed-group" th:if="${staff}">
+        <h3>Teachers</h3>
+        <div class="boxed-group-inner"
+             th:if="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Teacher)))}">
+            There are no teachers participating in this course.
+        </div>
+
+        <ul class="list-group" th:unless="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Teacher)))}">
+            <li class="list-group-item" th:each="role : ${course.getRoles(T(nl.tudelft.ewi.queue.model.Teacher))}">
+                [[${role.user}]]
+                <div class="pull-right"
+                     th:if="${@permissionService.canManageTeachers(#authentication.principal, course.id)}">
+                    <a class="btn btn-xs btn-danger"
+                       th:href="@{/course/{courseId}/participants/{id}/remove(courseId=${course.id},id=${role.id})}">
+                        <i class="fa fa-trash-o" aria-hidden="true"></i>
+                    </a>
+                </div>
+            </li>
+        </ul>
+    </div>
+
+    <div class="boxed-group" th:if="${staff}">
+        <h3>Manager</h3>
+        <div class="boxed-group-inner"
+             th:if="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Manager)))}">
+            There are no managers participating in this course.
+        </div>
+
+        <ul class="list-group" th:unless="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Manager)))}">
+            <li class="list-group-item" th:each="role : ${course.getRoles(T(nl.tudelft.ewi.queue.model.Manager))}">
+                <th:block th:if="${@permissionService.canViewFeedback(#authentication.principal, role.user.id)}">
+                    <a th:href="@{/course/{courseId}/participants/feedback/{id}(courseId=${course.id},id=${role.user.id})}">[[${role.user}]]</a>
+                </th:block>
+                <th:block th:unless="${@permissionService.canViewFeedback(#authentication.principal, role.user.id)}">
+                    [[${role.user}]]
+                </th:block>
+                <div class="pull-right">
+                    <a class="btn btn-xs btn-danger"
+                       th:href="@{/course/{courseId}/participants/{id}/remove(courseId=${course.id},id=${role.id})}">
+                        <i class="fa fa-trash-o" aria-hidden="true"></i>
+                    </a>
+                </div>
+            </li>
+        </ul>
+    </div>
+
+    <div class="boxed-group" th:if="${staff}">
+        <h3>Assistants</h3>
+        <div class="boxed-group-inner"
+             th:if="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Assistant)))}">
+            There are no assistants participating in this course.
+        </div>
+
+        <ul class="list-group" th:unless="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Assistant)))}">
+            <li class="list-group-item" th:each="role : ${course.getRoles(T(nl.tudelft.ewi.queue.model.Assistant))}">
+                <th:block th:if="${@permissionService.canViewFeedback(#authentication.principal, role.user.id)}">
+                    <a th:href="@{/course/{courseId}/participants/feedback/{id}(courseId=${course.id},id=${role.user.id})}">[[${role.user}]]</a>
+                    <span th:text="'(' + ${feedbackCount.get(role.user.id)} + ')'" class="small">0</span>
+                </th:block>
+                <th:block th:unless="${@permissionService.canViewFeedback(#authentication.principal, role.user.id)}">
+                    [[${role.user}]]
+                </th:block>
+                <div class="pull-right">
+                    <a class="btn btn-xs btn-danger"
+                       th:href="@{/course/{courseId}/participants/{id}/remove(courseId=${course.id},id=${role.id})}">
+                        <i class="fa fa-trash-o" aria-hidden="true"></i>
+                    </a>
+                </div>
+            </li>
+        </ul>
+    </div>
+
+    <form action="" class="form-horizontal" method="get" th:unless="${staff}">
+        <div class="form-group">
+            <input type="text" id="filter" class="form-control" name="filter" placeholder="Student name"
+                   th:value="${#httpServletRequest.getParameter('filter')}"/>
+        </div>
+    </form>
+
+    <div class="boxed-group" th:unless="${staff}">
+        <h3>Students</h3>
+        <div class="boxed-group-inner"
+             th:if="${#lists.isEmpty(course.getRoles(T(nl.tudelft.ewi.queue.model.Student)))}">
+            There are no students participating in this course.
+        </div>
+
+        <ul class="list-group" th:unless="${students.nrOfElements == 0}">
+            <li class="list-group-item" th:each="student : ${students.pageList}">
+                <a th:href="@{/history/course/{courseId}/student/{id}/(courseId=${course.id},id=${student.id})}">[[${student.user}]]</a>
+                <div class="pull-right">
+                    <a class="btn btn-xs btn-danger"
+                       th:href="@{/course/{courseId}/participants/{id}/remove(courseId=${course.id},id=${student.id})}">
+                        <i class="fa fa-trash-o" aria-hidden="true"></i>
+                    </a>
+                </div>
+            </li>
+        </ul>
+
+        <nav th:unless="${staff}" th:if="${students.pageCount > 1}">
+            <ul class="pagination">
+                <li class="page-item" th:classappend="${not students.firstPage} ? '' : 'disabled'">
+                    <span class="page-link" th:if="${students.firstPage}">&laquo;</span>
+
+                    <a class="page-link" th:if="${!students.firstPage}"
+                       th:href="@{/course/{courseId}/participants(courseId=${course.id},page=0)}">&laquo;</a>
+                </li>
+
+                <li th:if="${students.page &gt; paginationRange}">
+                    <span class="page-link">&#8230;</span>
+                </li>
+
+                <li class="page-item"
+                    th:each="pageNumber : ${#numbers.sequence((students.page - paginationRange gt 0) ? students.page - paginationRange: 0, (students.page + paginationRange lt students.pageCount-1) ? students.page + paginationRange : students.pageCount-1)}"
+                    th:classappend="${pageNumber == students.page} ? 'active' : ''">
+                    <span class="page-link" th:if="${pageNumber == students.page}" th:text="${pageNumber+1}">1</span>
+
+                    <a class="page-link" th:if="${pageNumber != students.page}" th:text="${pageNumber+1}"
+                       th:href="@{/course/{courseId}/participants(courseId=${course.id},page=${pageNumber})}">1</a>
+                </li>
+
+                <li th:if="${(students.pageCount - students.page - 1) &gt; paginationRange}">
+                    <span class="page-link">&#8230;</span>
+                </li>
+
+                <li class="page-item" th:class="${not students.lastPage} ? '' : 'disabled'">
+                    <span class="page-link" th:if="${students.lastPage}">&raquo;</span>
+
+                    <a class="page-link" th:if="${!students.lastPage}"
+                       th:href="@{/course/{courseId}/participants(courseId=${course.id},page=${students.pageCount})}">&raquo;</a>
+                </li>
+            </ul>
+        </nav>
+    </div>
+
+    <div th:if="${cleaned != null}">
+        <script th:inline="javascript">
+            /*<![CDATA[*/
+            var count = /*[[${cleaned}]]*/ '0';
+            var message = "Cleaned " + count + " users";
+            alert(message);
+            /*]]>*/
+        </script>
+    </div>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/settings.html b/backend/src/main/resources/templates/course/view/settings.html
new file mode 100644
index 000000000..49b063abc
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/settings.html
@@ -0,0 +1,85 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Settings ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Settings</h3>
+    </div>
+
+    <form action="#" th:action="@{/course/{id}/settings(id=${course.id})}" th:object="${course}" class="form-horizontal" method="post">
+        <input type="hidden" th:field="*{id}"/>
+
+        <div class="form-group">
+            <label for="inputCode" class="col-sm-2 control-label">Code</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:classappend="${#fields.hasErrors('code')} ? 'is-invalid'" th:field="*{code}" id="inputCode" class="form-control" placeholder="IN4303"/>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('code')}" th:errors="*{code}">Code error</div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputName" class="col-sm-2 control-label">Name</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:classappend="${#fields.hasErrors('name')} ? 'is-invalid'" th:field="*{name}" id="inputName" class="form-control" placeholder="Compiler Construction"/>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name error</div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputName" class="col-sm-2 control-label">Submission URL</label>
+
+            <div class="col-sm-8">
+                <input type="text" th:field="*{submissionUrl}" id="inputSubmissionUrl"
+                       class="form-control" placeholder="https://weblab.tudelft.nl/ti1206/2017-2018/dossier/{netid}"/>
+                ({netid} will be replaced with the actual netid) (e.g. link to CPM or WebLab)
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="archived" class="col-sm-2 control-label">Archived</label>
+
+            <input type="checkbox" th:field="*{isArchived}" id="archived"
+                   class="form-control col-sm-1" />
+            <small class="col-sm-8">Archived courses are hidden from students.</small>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <input type="submit" class="btn btn-primary" value="Save"/>
+            </div>
+        </div>
+    </form>
+
+    <div class="page-sub-header">
+        <h3>Danger zone</h3>
+    </div>
+
+    <a th:href="@{/course/{id}/remove(id=${course.id})}" class="btn btn-danger">Delete course</a>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/course/view/status.html b/backend/src/main/resources/templates/course/view/status.html
new file mode 100644
index 000000000..6ffa67d77
--- /dev/null
+++ b/backend/src/main/resources/templates/course/view/status.html
@@ -0,0 +1,188 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Status ?? [[${course}]]</title>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <script src="/webjars/momentjs/2.20.1/min/moment.min.js"></script>
+    <script src="/webjars/chartjs/2.7.0/Chart.min.js"></script>
+    <script src="/js/labstatus.js"></script>
+
+    <div class="page-sub-header">
+        <h3>Status</h3>
+    </div>
+
+    <ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
+        <li class="nav-item">
+            <a class="nav-link active" id="complete-pill"
+               data-toggle="pill" href="#complete" role="tab" aria-controls="complete-home">Course</a>
+        </li>
+        <li class="nav-item" th:each="lab : ${course.getLabs()}" >
+            <a class="nav-link" th:id="${lab.toSlug()} + '-pill'"
+               data-toggle="pill" th:href="'#' + ${lab.toSlug()}" role="tab" aria-controls="pills-home"
+               th:text="${lab.toString()}">Home</a>
+        </li>
+    </ul>
+
+    <div class="tab-content" id="pills-tabContent">
+
+        <div class="tab-pane fade show active" id="complete">
+            <table class="table table-responsive">
+                <tr>
+                    <td data-toggle="tooltip" data-placement="top" title="Time between creation and processing of a request">
+                        Average waiting time for the last hour:
+                    </td>
+                    <td>
+                        <th:block th:with="waitingTime=${course.averageWaitingFormatted()}">
+                            <span th:text="${waitingTime.get() + ' minutes'}" th:if="${waitingTime.isPresent()}">1 hour</span>
+                            <span th:if="${!waitingTime.isPresent()}">No waiting time.</span>
+                        </th:block>
+                    </td>
+                </tr>
+                <tr>
+                    <td data-toggle="tooltip" data-placement="bottom" title="Time between processing and handling (approved / rejected / forwarded) of a request">
+                        Average processing time for the last hour:
+                    </td>
+                    <td>
+                        <th:block th:with="processing=${course.averageProcessingFormatted()}">
+                            <span th:text="${processing.get() + ' minutes'}" th:if="${processing.isPresent()}">1 hour</span>
+                            <span th:if="${!processing.isPresent()}">No processing time.</span>
+                        </th:block>
+                    </td>
+                </tr>
+                <tr>
+                    <td data-toggle="tooltip" data-placement="bottom" title="Assistants that have approved / rejected / forwarded a request in the past hour">
+                        Active assistants in past hour:
+                    </td>
+                    <td>
+                        <th:block th:text="${course.activeAssistants()}"></th:block>
+                    </td>
+                </tr>
+                <tr>
+                    <td data-toggle="tooltip" data-placement="bottom" title="# of students that have a pending or assigned request in the queue">
+                        Number of students in the queue:
+                    </td>
+                    <td>
+                        <th:block th:text="${course.studentsInTheQueue()}"></th:block>
+                    </td>
+                </tr>
+            </table>
+
+            <span th:text="${course.requestsHandledPerHour()}" style="display:none;" id='complete-handledData'></span>
+            <span th:text="${course.requestsTotalPerHour()}" style="display:none;" id='complete-totalData'></span>
+            <span th:text="${course.requestsApprovedPerHour()}" style="display:none;" id='complete-approvedData'></span>
+            <span th:text="${course.requestsRejectedPerHour()}" style="display:none;" id='complete-rejectedData'></span>
+
+            <div class="row">
+                <div class="col-md-6">
+                    <h5>Handled requests</h5>
+                    <canvas id='complete-handledGraph' width="400" height="400"></canvas>
+                </div>
+                <div class="col-md-6">
+                    <h5>Total requests per hour</h5>
+                    <canvas id='complete-totalGraph' width="400" height="400"></canvas>
+                </div>
+                <div class="col-md-6">
+                    <h5>Approved requests per hour</h5>
+                    <canvas id='complete-approvedGraph' width="400" height="400"></canvas>
+                </div>
+                <div class="col-md-6">
+                    <h5>Rejected requests per hour</h5>
+                    <canvas id='complete-rejectedGraph' width="400" height="400"></canvas>
+                </div>
+            </div>
+        </div>
+
+        <div class="tab-pane fade show labstatus" th:each="lab : ${course.getTodaysLabs()}" th:id="${lab.toSlug()}">
+            <table class="table table-responsive">
+                <tr>
+                    <td data-toggle="tooltip" data-placement="top" title="Time between creation and processing of a request">
+                        Average waiting time for the last hour:
+                    </td>
+                    <td>
+                        <th:block th:with="waitingTime=${lab.averageWaiting()}">
+                            <span th:text="${waitingTime.get() + ' minutes'}" th:if="${waitingTime.isPresent()}">1 hour</span>
+                            <span th:if="${!waitingTime.isPresent()}">No waiting time.</span>
+                        </th:block>
+                    </td>
+                </tr>
+                <tr>
+                    <td data-toggle="tooltip" data-placement="bottom" title="Time between processing and handling (approved / rejected / forwarded) of a request">
+                        Average processing time for the last hour:
+                    </td>
+                    <td>
+                        <th:block th:with="processing=${lab.averageProcessing()}">
+                            <span th:text="${processing.get() + ' minutes'}" th:if="${processing.isPresent()}">1 hour</span>
+                            <span th:if="${!processing.isPresent()}">No processing time.</span>
+                        </th:block>
+                    </td>
+                </tr>
+                <tr>
+                    <td data-toggle="tooltip" data-placement="bottom" title="Assistants that have approved / rejected / forwarded a request in the past hour">
+                        Active assistants in past hour:
+                    </td>
+                    <td>
+                       <th:block th:text="${lab.activeAssistantsCount()}"></th:block>
+                    </td>
+                </tr>
+                <tr>
+                    <td data-toggle="tooltip" data-placement="bottom" title="# of students that have a pending or assigned request in the queue">
+                        Number of students in the queue:
+                    </td>
+                    <td>
+                        <th:block th:text="${lab.nrOfStudentsInQueue()}"></th:block>
+                    </td>
+                </tr>
+            </table>
+
+            <span th:text="${lab.requestsHandledPerHour()}" style="display:none;" th:id="${lab.getId()} + '-handledData'"></span>
+            <span th:text="${lab.requestsTotalPerHour()}" style="display:none;" th:id="${lab.getId()} + '-totalData'"></span>
+            <span th:text="${lab.requestsApprovedPerHour()}" style="display:none;" th:id="${lab.getId()} + '-approvedData'"></span>
+            <span th:text="${lab.requestsRejectedPerHour()}" style="display:none;" th:id="${lab.getId()} + '-rejectedData'"></span>
+
+
+            <div class="row" th:id="${lab.getId()}">
+                <div class="col-md-6">
+                    <h5>Handled requests</h5>
+                    <canvas th:id="${lab.getId()} + '-handledGraph'" width="400" height="400"></canvas>
+                </div>
+                <div class="col-md-6">
+                    <h5>Total requests per hour</h5>
+                    <canvas th:id="${lab.getId()} + '-totalGraph'" width="400" height="400"></canvas>
+                </div>
+                <div class="col-md-6">
+                    <h5>Approved requests per hour</h5>
+                    <canvas th:id="${lab.getId()} + '-approvedGraph'" width="400" height="400"></canvas>
+                </div>
+                <div class="col-md-6">
+                    <h5>Rejected requests per hour</h5>
+                    <canvas th:id="${lab.getId()} + '-rejectedGraph'" width="400" height="400"></canvas>
+                </div>
+            </div>
+        </div>
+    </div>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/error/403.html b/backend/src/main/resources/templates/error/403.html
new file mode 100644
index 000000000..c73a28483
--- /dev/null
+++ b/backend/src/main/resources/templates/error/403.html
@@ -0,0 +1,37 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <div class="page-header">
+        <h1>Error 403</h1>
+    </div>
+
+    <h2>Forbidden</h2>
+
+    <p>You are not authorized to access the requested page.</p>
+</section>
+</body>
+
+</html>
diff --git a/backend/src/main/resources/templates/error/404.html b/backend/src/main/resources/templates/error/404.html
new file mode 100644
index 000000000..a110f9392
--- /dev/null
+++ b/backend/src/main/resources/templates/error/404.html
@@ -0,0 +1,36 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <div class="page-header">
+        <h1>Error 404</h1>
+    </div>
+
+    <h2>Page not found</h2>
+
+    <p>The requested page could not be found.</p>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/error/alreadyEnqueued.html b/backend/src/main/resources/templates/error/alreadyEnqueued.html
new file mode 100644
index 000000000..b2b7c5d28
--- /dev/null
+++ b/backend/src/main/resources/templates/error/alreadyEnqueued.html
@@ -0,0 +1,36 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <div class="page-header">
+        <h1>Error</h1>
+    </div>
+
+    <h2>Already enqueued</h2>
+
+    <p>You are already enqueued for the lab.</p>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/error/entityNotFound.html b/backend/src/main/resources/templates/error/entityNotFound.html
new file mode 100644
index 000000000..633d4c547
--- /dev/null
+++ b/backend/src/main/resources/templates/error/entityNotFound.html
@@ -0,0 +1,36 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <div class="page-header">
+        <h1>Error 404</h1>
+    </div>
+
+    <h2>Object not found</h2>
+
+    <p>The requested object could not be found, stop looking for things that aren't there :)</p>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/error/error.html b/backend/src/main/resources/templates/error/error.html
new file mode 100644
index 000000000..37d5c2358
--- /dev/null
+++ b/backend/src/main/resources/templates/error/error.html
@@ -0,0 +1,40 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.thymeleaf.org" layout:decorate="container">
+<head>
+</head>
+
+<body>
+	<section layout:fragment="container">
+		<div class="page-header">
+			<h1>Error</h1>
+		</div>
+		<div class="jumbotron alert-danger">
+			<h1>Oops. Something went wrong...</h1>
+			<h2 th:text="${message}">Message</h2>
+		</div>
+	</section>
+<!--
+ Failed URL: ${url}
+-->
+</body>
+
+</html>
diff --git a/backend/src/main/resources/templates/error/thouShaltNotPass.html b/backend/src/main/resources/templates/error/thouShaltNotPass.html
new file mode 100644
index 000000000..fa916a9e1
--- /dev/null
+++ b/backend/src/main/resources/templates/error/thouShaltNotPass.html
@@ -0,0 +1,39 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+	layout:decorate="container">
+<head>
+</head>
+
+<body>
+	<section layout:fragment="container">
+		<div class="page-header">
+			<h1>Error</h1>
+		</div>
+
+		<h2>Access Denied</h2>
+
+		<p>You do not have permission to enter. This incident will be reported.</p>
+		<img src="https://imgs.xkcd.com/comics/incident.png" />
+		<p>Source: <a href="http://www.xkcd.org">xkcd</a></p>
+	</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/history/index.html b/backend/src/main/resources/templates/history/index.html
new file mode 100644
index 000000000..9e64fb68c
--- /dev/null
+++ b/backend/src/main/resources/templates/history/index.html
@@ -0,0 +1,52 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.w3.org/1999/xhtml" layout:decorate="layout">
+<head>
+</head>
+
+<body>
+<section layout:fragment="content">
+    <div class="container">
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item"><a href="/">Home</a></li>
+                <li class="breadcrumb-item active">History</li>
+            </ol>
+        </nav>
+
+        <div class="page-header">
+            <h1>My Requests</h1>
+        </div>
+    </div>
+
+    <div class="container-fluid">
+        <div class="row">
+            <div class="col-lg-12 offset-xl-1 col-xl-10">
+                <th:block th:replace="request/list/filters :: filters"></th:block>
+                <th:block th:replace="request/list/requesttable :: requesttable"></th:block>
+                <th:block th:replace="request/list/pagination :: pagination"></th:block>
+            </div>
+        </div>
+    </div>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/history/student.html b/backend/src/main/resources/templates/history/student.html
new file mode 100644
index 000000000..97ab0f5ae
--- /dev/null
+++ b/backend/src/main/resources/templates/history/student.html
@@ -0,0 +1,47 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.w3.org/1999/xhtml" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <script type="text/javascript" src="/js/bootstrap-multiselect.js"></script>
+    <link rel="stylesheet" href="/css/bootstrap-multiselect.css" type="text/css"/>
+
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item">History</li>
+        </ol>
+    </nav>
+
+    <div class="page-header">
+        <h1>Requests of <span th:text="${student}">student</span></h1>
+    </div>
+
+    <th:block th:replace="request/list/filters :: filters(route='/history')"></th:block>
+    <th:block th:replace="request/list/requesttable :: requesttable"></th:block>
+    <th:block th:replace="request/list/pagination :: pagination"></th:block>
+
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/home/dashboard.html b/backend/src/main/resources/templates/home/dashboard.html
new file mode 100644
index 000000000..ff6a86a0a
--- /dev/null
+++ b/backend/src/main/resources/templates/home/dashboard.html
@@ -0,0 +1,80 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="container" xmlns:sec="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+    <section layout:fragment="container">
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item active" aria-current="page">Home</li>
+            </ol>
+        </nav>
+
+        <div class="page-header">
+            <h1>Home</h1>
+        </div>
+
+        <div class="boxed-group">
+            <h3>Courses you participate in</h3>
+            <div class="boxed-group-inner" th:if="${#lists.isEmpty(availableCourses)}">
+                You do not participate in any courses. Why don't you <a href="#" th:href="@{/courses}">enroll for your first course</a>?
+            </div>
+
+            <ul class="list-group">
+                <li class="list-group-item" th:each="role : ${availableCourses}" >
+                    <a href="#" th:href="@{/course/{id}(id=${role.course.id})}">[[${role.course}]]</a>
+                    <span>([[${role}]])</span>
+                    <ul th:unless="${#lists.isEmpty(role.course.todaysLabs)}">
+                        <li th:each="lab : ${role.course.todaysLabs}">
+                            <a href="#" th:href="@{/lab/{id}(id=${lab.id})}">[[${lab}]] from [[${#temporals.format(lab.slot.opensAt, 'dd MMMM yyyy HH:mm')}]] until [[${#temporals.format(lab.slot.closesAt, 'dd MMMM yyyy HH:mm')}]]</a>
+                            <span class="badge badge-pill bg-success text-white" th:if="${lab.isOpen()}">Open</span>
+                            <span class="badge badge-pill bg-warning text-white" th:if="${!lab.isOpen() and lab.slotSelectionIsOpen()}">Slot selection</span>
+                        </li>
+                    </ul>
+                </li>
+            </ul>
+        </div>
+
+        <div class="boxed-group" sec:authorize="hasAnyRole('ADMIN')">
+            <h3>Admin overview - Labs running today:</h3>
+            <div class="boxed-group-inner" th:if="${#lists.isEmpty(runningCourses)}">
+                None.
+            </div>
+
+            <ul class="list-group">
+                <li class="list-group-item" th:each="course : ${runningCourses}" >
+                    <a href="#" th:href="@{/course/{id}(id=${course.id})}">[[${course}]]</a>
+                    <ul th:unless="${#lists.isEmpty(course.todaysLabs)}">
+                        <li th:each="lab : ${course.todaysLabs}">
+                            <a href="#" th:href="@{/lab/{id}(id=${lab.id})}">[[${lab}]] from [[${#temporals.format(lab.slot.opensAt, 'dd MMMM yyyy HH:mm')}]] until [[${#temporals.format(lab.slot.closesAt, 'dd MMMM yyyy HH:mm')}]]</a>
+                            <span class="badge badge-pill bg-success text-white" th:if="${lab.isOpen()}">Open</span>
+                            <span class="badge badge-pill bg-warning text-white" th:if="${!lab.isOpen() and lab.slotSelectionIsOpen()}">Slot selection</span>
+                        </li>
+                    </ul>
+                </li>
+            </ul>
+        </div>
+    </section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/home/index.html b/backend/src/main/resources/templates/home/index.html
new file mode 100644
index 000000000..55611b8ba
--- /dev/null
+++ b/backend/src/main/resources/templates/home/index.html
@@ -0,0 +1,47 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+    <section layout:fragment="container">
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item active">Home</li>
+            </ol>
+        </nav>
+
+        <div class="page-header">
+            <h1>Home</h1>
+        </div>
+
+        Welcome to TU Delft queue system!
+
+        <h2>How to use this system?</h2>
+
+        After you login, you can <em>enroll</em> for a particular course. Once enrolled you can navigate to a specific
+        lab. If the lab is opened, you can <em>enqueue</em> for a specific assignment. The assistants process this
+        queue. You will receive a notification when an assistant is assigned to your request. The notification
+        instructs you to either visit the TA or wait for the TA to visit you.
+    </section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/lab/create.html b/backend/src/main/resources/templates/lab/create.html
new file mode 100644
index 000000000..d6d942b71
--- /dev/null
+++ b/backend/src/main/resources/templates/lab/create.html
@@ -0,0 +1,340 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="course/view">
+<head>
+    <link rel="stylesheet" href="/css/tempusdominus-bootstrap-4.min.css"/>
+    <script src="/webjars/momentjs/2.20.1/min/moment.min.js"></script>
+    <script type="text/javascript" src="/js/tempusdominus-bootstrap-4.min.js"></script>
+    <script type="text/javascript" src="/js/bootstrap-multiselect.js"></script>
+    <link rel="stylesheet" href="/css/bootstrap-multiselect.css" type="text/css"/>
+
+    <script type="text/javascript">
+        function formValidation(event) {
+            console.log("validating form");
+
+            // Check if at least one room is selected
+            var selection = document.getElementById("roomSelect");
+            var ret = false;
+            var selectedOne = false;
+            function helper(value) {
+                selectedOne = selectedOne || value.selected;
+            }
+            selection.childNodes.forEach(helper);
+            if (!selectedOne) {
+                event.preventDefault();
+                alert("You must select at least one room!");
+                return false;
+            }
+
+            selectedOne = false;
+            // check if one request type is selected
+            var reqTypes = document.getElementsByClassName("request-type");
+            for (var i = 0; i < reqTypes.length; i++) {
+                selectedOne = selectedOne || reqTypes[i].checked;
+            }
+            if (!selectedOne) {
+                event.preventDefault();
+                alert("You must select at least one request type");
+                return false
+            }
+
+            return true
+        }
+    </script>
+</head>
+
+
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-header">
+        <h3>Create lab</h3>
+    </div>
+    <form action="#" th:action="@{/course/{id}/lab/create(id=${lab.course.id})}" th:object="${lab}"
+          class="form-horizontal" method="post" onsubmit="return formValidation(event)">
+        <input type="hidden" th:field="*{id}"/>
+
+        <div class="form-group">
+            <label class="col-sm-2 control-label">Title</label>
+            <div class="col-sm-8">
+                <input type="text" class="form-control"
+                       required="required" th:field="*{title}" placeholder="Question session"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputDirection" class="col-sm-2 control-label">Direction</label>
+
+            <div class="col-sm-8">
+                <select class="form-control" id="inputDirection" th:field="*{direction}">
+                    <option th:each="direction : ${T(nl.tudelft.ewi.queue.model.Direction).values()}"
+                            th:value="${direction}" th:text="${direction.displayName}"></option>
+                </select>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="col-sm-2 control-label">Slot</label>
+
+            <div class="col-sm-8">
+                <div class="row">
+                    <div class="col-sm-6 input-group date" id="slotopensAt" data-target-input="nearest">
+                        <div class="input-group-append">
+                            <span class="input-group-text">from</span>
+                        </div>
+                        <input th:classappend="${#fields.hasErrors('slot.opensAt')} ? 'is-invalid'"
+                               type="text" th:field="*{slot.opensAt}" class="form-control datetimepicker-input"
+                               value="2012-04-05" data-target="#slotopensAt"/>
+                        <div class="input-group-append">
+                            <span class="input-group-text" data-target="#slotopensAt" data-toggle="datetimepicker">
+                                <i class="fa fa-calendar"></i>
+                            </span>
+                        </div>
+                        <div class="invalid-feedback" th:if="${#fields.hasErrors('slot.opensAt')}"
+                             th:errors="*{slot.opensAt}">Slot opensAt error
+                        </div>
+                    </div>
+                    <div class="col-sm-6 input-group date" id="slotclosesAt" data-target-input="nearest">
+                        <div class="input-group-append">
+                            <span class="input-group-text">to</span>
+                        </div>
+                        <input th:classappend="${#fields.hasErrors('slot.closesAt')} ? 'is-invalid'"
+                               type="text" th:field="*{slot.closesAt}" class="form-control datetimepicker-input"
+                               value="2012-04-19" data-target="#slotclosesAt"/>
+                        <div class="input-group-append">
+                            <span class="input-group-text" data-target="#slotclosesAt" data-toggle="datetimepicker">
+                                <i class="fa fa-calendar"></i>
+                            </span>
+                        </div>
+                        <div class="invalid-feedback" th:if="${#fields.hasErrors('slot.closesAt')}"
+                             th:errors="*{slot.closesAt}">Slot error
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-8">
+                <div class="checkbox">
+                    <label>
+                        <input type="checkbox" th:field="*{signOffIntervals}"/> Sign off in time intervals
+                    </label>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="col-sm-4 control-label">Intervals of x minutes</label>
+            <div class="col-sm-3">
+                <input type="number" th:unless="${lab.signOffIntervals}" name="intervalTime" id="intervalTime"
+                       class="form-control" required="required" disabled="disabled" value="15"/>
+                <input type="number" th:if="${lab.signOffIntervals}" th:field="*{intervalTime}" class="form-control"
+                       required="required"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="col-sm-4 control-label">Number of TAs working this lab (capacity)</label>
+            <div class="col-sm-3">
+                <input type="number" th:unless="${lab.signOffIntervals}" name="capacity" id="capacity"
+                       class="form-control" disabled="disabled" required="required" value="3"/>
+                <input type="number" th:if="${lab.signOffIntervals}" th:field="*{capacity}" class="form-control"
+                       required="required"/>
+            </div>
+        </div>
+
+        <div class="form-group date">
+            <label class="col-sm-6 control-label">Slot selection opens at</label>
+            <div class="col-sm-6 input-group date" data-target-input="nearest">
+                <input th:unless="${lab.signOffIntervals}" type="text" name="slotSelectionOpensAt"
+                       id="slotSelectionOpensAt" class="form-control"
+                       disabled="disabled" required="required"/>
+                <input th:if="${lab.signOffIntervals}" type="text" th:field="*{slotSelectionOpensAt}"
+                       class="form-control datetimepicker-input"/>
+                <div class="input-group-append">
+                    <span class="input-group-text" data-target="#slotSelectionOpensAt" data-toggle="datetimepicker">
+                                <i class="fa fa-calendar"></i>
+                    </span>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Rooms</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="form-control-label col-sm-2" for="roomSelect">Rooms</label>
+            <div class="row">
+                <div class="col-sm-8">
+                    <select multiple="multiple" class="form-control" id="roomSelect" name="rooms" size="10">
+                        <th:block th:each="room : ${#lists.sort(rooms)}">
+                            <option th:value="${room.id}" th:selected="${lab.hasRoom(room)}">[[${room.name}]]</option>
+                        </th:block>
+                    </select>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+
+                <div class="form-group">
+                    <label>New Room name</label>
+                    <input type="text" name="roomName" class="form-control" placeholder="Room name..." />
+                </div>
+                <button type="submit" class="btn btn-success" name="addRoom"><i class="fa fa-plus"></i> Add room
+                </button>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Assignments in this lab</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <th:block th:each="assignment, i : ${course.assignments}">
+                    <div class="form-check form-check-inline">
+                        <input type="checkbox"
+                               class="form-check-input"
+                               th:field="*{assignments[__${i.index}__]}"
+                               th:value="${assignment.id}"/>
+                        <label class="form-check-label">[[${assignment}]]</label>
+                    </div>
+                </th:block>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Feedback lab</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input"
+                           th:field="*{feedbackLab}"/>
+                    <label class="form-check-label">Create as feedback
+                        lab</label>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Allowed request types in this lab</h4>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <th:block th:each="requestType, i : ${requesttypes}">
+                    <div class="form-check form-check-inline">
+                        <input type="checkbox"
+                               class="form-check-input request-type"
+                               th:field="*{allowedRequestTypes[__${i.index}__]}"
+                               th:value="${requestType.id}"/>
+                        <label class="form-check-label">[[${requestType.name}]]</label>
+                    </div>
+                </th:block>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Allowed mentor groups in this lab</h4>
+                <p>Leave empty if you do not want to use this feature</p>
+                <th:block th:each="group, i : ${mentorgroups}">
+                    <div class="form-check form-check-inline">
+                        <input type="checkbox"
+                               class="form-check-input"
+                               th:field="*{allowedMentorGroups[__${i.index}__]}"
+                               th:value="${group.id}"/>
+                        <label class="form-check-label">[[${group.name}]]</label>
+                    </div>
+                </th:block>
+                <div class="form-check form-check-inline">
+                    <input type="checkbox"
+                           class="form-check-input"
+                           th:field="*{allowWithoutMentorGroup}"/>
+                    <label class="form-check-label">No group</label>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Repeat options</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="col-sm-2 control-label">Repeat for x weeks</label>
+            <div class="col-sm-3">
+                <input type="number" name="weekRepeat" class="form-control" placeholder="1"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <button type="submit" class="btn btn-primary ctrl-enter-submit"
+                        name="storeLab">Create new lab</button>
+                <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script>
+            </div>
+        </div>
+    </form>
+
+    <div class="clearfix"></div>
+
+    <script type="text/javascript">
+        $(function () {
+            $('#slotclosesAt').datetimepicker({format: 'DD/MM/YYYY HH:mm'});
+            $('#slotopensAt').datetimepicker({format: 'DD/MM/YYYY HH:mm'});
+            $('#slotSelectionOpensAt').datetimepicker({format: 'DD/MM/YYYY HH:mm'});
+            $('#slotopensAt').on('change.datetimepicker', function (e) {
+                newDate = e.date.add(225, 'minutes');
+                $('#slotclosesAt').datetimepicker('date', newDate);
+            });
+
+            $('#signOffIntervals1').change(function () {
+                if ($('input[name=signOffIntervals]').is(':checked')) {
+                    $('#intervalTime').prop('disabled', false);
+                    $('#capacity').prop('disabled', false);
+                    $('#slotSelectionOpensAt').prop('disabled', false);
+                } else {
+                    $('#intervalTime').prop('disabled', true);
+                    $('#capacity').prop('disabled', true);
+                    $('#slotSelectionOpensAt').prop('disabled', true);
+                }
+            });
+        });
+    </script>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/lab/edit.html b/backend/src/main/resources/templates/lab/edit.html
new file mode 100644
index 000000000..c4bc81917
--- /dev/null
+++ b/backend/src/main/resources/templates/lab/edit.html
@@ -0,0 +1,255 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="course/view">
+<head>
+    <link rel="stylesheet" href="/css/tempusdominus-bootstrap-4.min.css"/>
+    <script src="/webjars/momentjs/2.20.1/min/moment.min.js"></script>
+    <script type="text/javascript" src="/js/tempusdominus-bootstrap-4.min.js"></script>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Edit lab</h3>
+    </div>
+
+    <form action="#" th:action="@{/lab/{id}(id=${lab.id})}" th:object="${lab}" class="form-horizontal" method="post">
+        <input type="hidden" th:field="*{id}"/>
+
+        <div class="form-group">
+            <label class="col-sm-2 control-label">Title</label>
+            <div class="col-sm-8">
+                <input type="text" th:field="*{title}" class="form-control"
+                       required="required" placeholder="Question session"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputDirection" class="col-sm-2 control-label">Direction</label>
+
+            <div class="col-sm-8">
+                <select class="form-control" id="inputDirection" th:field="*{direction}">
+                    <option th:each="direction : ${T(nl.tudelft.ewi.queue.model.Direction).values()}"
+                            th:value="${direction}" th:text="${direction.displayName}"></option>
+                </select>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="col-sm-2 control-label">Slot</label>
+
+            <div class="col-sm-8">
+                <div class="row">
+                    <div class="col-sm-6 input-group date" id="slotopensAt" data-target-input="nearest">
+                        <div class="input-group-append">
+                            <span class="input-group-text">from</span>
+                        </div>
+                        <input type="text" th:field="*{slot.opensAt}" class="form-control datetimepicker-input"
+                               value="2012-04-05" data-target="#slotopensAt"/>
+                        <div class="input-group-append">
+                            <span class="input-group-text" data-target="#slotopensAt" data-toggle="datetimepicker">
+                                <i class="fa fa-calendar"></i>
+                            </span>
+                        </div>
+                    </div>
+                    <div class="col-sm-6 input-group date" id="slotclosesAt" data-target-input="nearest">
+                        <div class="input-group-append">
+                            <span class="input-group-text">to</span>
+                        </div>
+                        <input type="text" th:field="*{slot.closesAt}" class="form-control datetimepicker-input"
+                               value="2012-04-19" data-target="#slotclosesAt"/>
+                        <div class="input-group-append">
+                            <span class="input-group-text" data-target="#slotclosesAt" data-toggle="datetimepicker">
+                                <i class="fa fa-calendar"></i>
+                            </span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group" th:if="${lab.getSignOffIntervals()}">
+            <label class="col-sm-4 control-label">Intervals of x minutes</label>
+            <div class="col-sm-3">
+                <input type="number" min="1" th:field="*{intervalTime}" class="form-control" placeholder="15"/>
+            </div>
+        </div>
+
+        <div class="form-group" th:if="${lab.getSignOffIntervals()}">
+            <label class="col-sm-4 control-label">Number of TAs working this lab (capacity)</label>
+            <div class="col-sm-3">
+                <input type="number" min="1" th:field="*{capacity}" class="form-control" placeholder="3"/>
+            </div>
+        </div>
+
+        <div class="form-group" th:if="${lab.getSignOffIntervals()}">
+            <label class="col-sm-6 control-label">Slot selection opens at</label>
+            <div class="col-sm-6 input-group date" id="slotSelectionOpensAt" data-target-input="nearest">
+                <input type="text" th:field="*{slotSelectionOpensAt}" class="form-control datetimepicker-input"
+                       value="2012-04-05" data-target="#slotSelectionOpensAt"/>
+                <div class="input-group-append">
+                    <span class="input-group-text" data-target="#slotSelectionOpensAt" data-toggle="datetimepicker">
+                                <i class="fa fa-calendar"></i>
+                    </span>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Rooms</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label class="form-control-label col-sm-2" for="roomSelect">Rooms</label>
+            <div class="row">
+                <div class="col-sm-8">
+                    <select multiple="multiple" class="form-control" id="roomSelect" name="rooms">
+                        <th:block th:each="room : ${rooms}">
+                            <option th:value="${room.id}" th:selected="${lab.hasRoom(room)}">[[${room.name}]]</option>
+                        </th:block>
+                    </select>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+
+                <div class="form-group">
+                    <label>New Room name</label>
+                    <input type="text" name="roomName" class="form-control" placeholder="Room name..." />
+                </div>
+                <button type="submit" class="btn btn-success" name="addRoom"><i class="fa fa-plus"></i> Add room
+                </button>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Assignments</h4>
+            </div>
+        </div>
+
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <th:block th:each="assignment, i : ${course.assignments}">
+                    <div class="form-check form-check-inline">
+                        <input class="form-check-input"
+                               type="checkbox"
+                               th:name="'assignments[' + ${i.index} + ']'"
+                               th:value="${assignment.id}"
+                               th:checked="${lab.containsAssignment(assignment)}"/>
+                        <label class="form-check-label">[[${assignment}]]</label>
+                    </div>
+                </th:block>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Feedback lab</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <div class="form-check form-check-inline">
+                    <th:block>
+                        <input type="checkbox" class="form-check-input"
+                        th:checked="${lab.isFeedbackLab()}"/>
+                        <label class="form-check-label">Set as feedback
+                            lab</label>
+                    </th:block>
+
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Allowed Request Types</h4>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <th:block th:each="requestType, i : ${requesttypes}">
+                    <div class="form-check form-check-inline">
+                        <label class="form-check-label">
+                            <input class="form-check-input" type="checkbox"
+                                   th:name="'allowedRequestTypes[' + ${i.index} + ']'"
+                                   th:checked="${lab.containsAllowedRequestType(requestType)}"
+                                   th:value="${requestType.id}"
+                                   th:text="${requestType.name}"/>
+                        </label>
+                    </div>
+                </th:block>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <h4>Allowed mentor groups in this lab</h4>
+                <p>Leave empty if you do not want to use this feature</p>
+                <th:block th:each="group, i : ${mentorgroups}">
+                    <div class="form-check form-check-inline">
+                        <input type="checkbox"
+                               class="form-check-input"
+                               th:name="'allowedMentorGroups[' + ${i.index} + ']'"
+                               th:value="${group.id}"
+                               th:checked="${lab.containsAllowedMentorGroup(group)}"/>
+                        <label class="form-check-label">[[${group.name}]]</label>
+                    </div>
+                </th:block>
+                <div class="form-check form-check-inline">
+                    <input type="checkbox"
+                           class="form-check-input"
+                           th:field="*{allowWithoutMentorGroup}" />
+                    <label class="form-check-label">No group</label>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-8">
+                <button type="submit" class="btn btn-primary ctrl-enter-submit"
+                        name="storeLab">
+                    <i class="fa fa-cloud"></i> Save changes
+                </button>
+                <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script>
+            </div>
+        </div>
+    </form>
+
+    <script type="text/javascript">
+        $(function () {
+            $('#slotclosesAt').datetimepicker({format: 'DD/MM/YYYY HH:mm'});
+            $('#slotopensAt').datetimepicker({format: 'DD/MM/YYYY HH:mm'});
+            $('#slotSelectionOpensAt').datetimepicker({format: 'DD/MM/YYYY HH:mm'});
+        });
+    </script>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/lab/enqueue.html b/backend/src/main/resources/templates/lab/enqueue.html
new file mode 100644
index 000000000..944ace7ef
--- /dev/null
+++ b/backend/src/main/resources/templates/lab/enqueue.html
@@ -0,0 +1,190 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorator="course/view">
+<head>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Lab #[[${lab.id}]]</h3>
+    </div>
+
+    <div class="alert alert-info mt-md-3" role="alert" th:unless="${#strings.isEmpty(message)}">
+        <span th:text="${message}"></span>
+    </div>
+
+    <form action="#" th:action="@{/lab/{id}/enqueue(id=${lab.id})}" th:object="${request}" method="post"
+          class="form-horizontal">
+        <div th:if="${timeslots}">
+            <label for="inputAssignment" class="col-sm-2 control-label">Choose time slot</label>
+
+            <div class="col-sm-8 mb-3">
+                <div class="row">
+                    <div class="col-sm-4" th:each="slot : ${timeslots}" th:value="${slot.getOpensAt()}">
+                        <a href="#" class="timeslotChoice badge badge-pill badge-primary"
+                           th:attr="data-start=${#temporals.format(slot.getOpensAt(),'dd/MM/yyyy HH:mm')},
+                           data-end=${#temporals.format(slot.getClosesAt(),'dd/MM/yyyy HH:mm')}"
+                           th:if="${slot.getAvailable()}">[[${#temporals.format(slot.getOpensAt(), 'HH:mm')}]] -
+                            [[${#temporals.format(slot.getClosesAt(), 'HH:mm')}]]</a>
+                        <span class="badge badge-pill badge-secondary" th:unless="${slot.getAvailable()}">[[${#temporals.format(slot.getOpensAt(), 'HH:mm')}]] - [[${#temporals.format(slot.getClosesAt(), 'HH:mm')}]]</span>
+                    </div>
+                </div>
+            </div>
+
+            <div class="form-group">
+                <div class="col-sm-8">
+                    Your chosen time slot: <span id="timeslot">No timeslot selected yet</span>
+                    <input type="hidden" th:field="*{slot.opensAt}"/>
+                    <input type="hidden" th:field="*{slot.closesAt}"/>
+                    <span class="badge badge-pill badge-danger"
+                          style="display:none;" id="timeslot-error">Select a time slot to enqueue!</span>
+                </div>
+
+
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputAssignment" class="col-sm-2 control-label">Assignment</label>
+
+            <div class="col-sm-8">
+                <select class="form-control" th:classappend="${#fields.hasErrors('assignment')} ? 'is-invalid'"
+                        id="inputAssignment" th:field="*{assignment}">
+                    <option th:each="assignment : ${lab.assignments}" th:value="${assignment.id}"
+                            th:text="${assignment.name}">Assembly programming
+                    </option>
+                </select>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('assignment')}" th:errors="*{assignment}">
+                    Assignment error
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputRoom" class="col-sm-2 control-label">Room</label>
+
+            <div class="col-sm-8">
+                <select class="form-control" id="inputRoom" th:field="*{room}"
+                        th:classappend="${#fields.hasErrors('room')} ? 'is-invalid'">
+                    <div>
+                        <option value="" th:selected="selected">Choose a room</option>
+                        <option th:each="room : ${lab.rooms}" th:value="${room.id}" th:text="${room}">DW 1.01</option>
+                    </div>
+                </select>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('room')}" th:errors="*{room}">Room error</div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputType" class="col-sm-2 control-label">Type</label>
+
+            <div class="col-sm-8">
+                <select class="form-control" id="inputType" th:field="*{requestType}"
+                        th:classappend="${#fields.hasErrors('requestType')} ? 'is-invalid'">
+                    <option th:each="requestType : ${lab.allowedRequestTypes}" th:value="${requestType.id}"
+                            th:text="${requestType.name}"></option>
+                </select>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('requestType')}" th:errors="*{requestType}">
+                    Type error
+                </div>
+            </div>
+        </div>
+
+        <div id="image-holder" class="mb-3"></div>
+
+        <div class="form-group">
+            <label for="inputComment" id="labelComment" class="control-label">Comment</label>
+
+            <div class="col-sm-8">
+                <textarea maxlength="250" th:classappend="${#fields.hasErrors('comment')} ? 'is-invalid'"
+                          class="form-control" id="inputComment" th:field="*{comment}"
+                          placeholder="You can use this field to specify a location within the room, e.g. your cubicle number."></textarea>
+
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('comment')}" th:errors="*{comment}">Comment
+                    error
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <button type="submit" name="enqueue" id="enqueue"
+                        class="btn btn-success ctrl-enter-submit">Enqueue
+                </button>
+                <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script>
+            </div>
+        </div>
+    </form>
+
+    <script src="/webjars/momentjs/2.20.1/min/moment.min.js"></script>
+    <script src="/js/map_loader.js"></script>
+    <script type="text/javascript" th:if="${timeslots}">
+        //<![CDATA[
+        $(function () {
+            $('.timeslotChoice').click(function () {
+                $('#timeslot').html($(this).html());
+                $('#slot\\.opensAt').val($(this).attr('data-start'));
+                $('#slot\\.closesAt').val($(this).attr('data-end'));
+            });
+
+            $('form').one('submit', function (e) {
+                e.preventDefault();
+                if ($('#timeslot').text() === 'No timeslot selected yet') {
+                    $('#timeslot-error').show();
+                } else {
+                    $(this).submit();
+                }
+            });
+        });
+        //]]>
+    </script>
+    <script type="text/javascript" th:if="${lab.getSignOffIntervals() and lab.isOpen()}">
+        //<![CDATA[
+        $(function () {
+            function updateTimeBadges() {
+                $('.timeslotChoice').each(function (index) {
+                    var timeStart = $(this).attr('data-start');
+                    var start = moment(timeStart, "DD/MM/YYYY HH:mm");
+                    var now = moment();
+                    var difference = moment.duration(start.diff(now));
+                    if ((difference.hours() == 0) && (difference.minutes() < 3)) {
+                        $(this).removeClass('badge-primary');
+                        $(this).addClass('badge-warning');
+                    }
+                    if ((difference.hours() == 0) && (difference.minutes() < 0)) {
+                        $(this).unbind();
+                        $(this).removeClass('timeslotChoice');
+                        $(this).removeClass('badge-warning');
+                        $(this).addClass('badge-secondary');
+                    }
+                });
+            }
+
+            updateTimeBadges();
+            var run = setInterval(updateTimeBadges, 60 * 1000)
+        });
+        //]]>
+    </script>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/lab/remove.html b/backend/src/main/resources/templates/lab/remove.html
new file mode 100644
index 000000000..4c3f55aa2
--- /dev/null
+++ b/backend/src/main/resources/templates/lab/remove.html
@@ -0,0 +1,42 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <h3>Remove lab</h3>
+    </div>
+
+    <form action="#" th:action="@{/lab/{id}/remove(id=${lab.id})}" th:object="${lab}" class="form-horizontal" method="post">
+        <input type="hidden" th:field="*{id}"/>
+
+        Are you sure you want to remove <strong >lab #[[${lab.id}]]</strong>?
+
+        <div class="text-center">
+            <button class="btn btn-danger">Delete this lab</button> <small>or <a href="#" th:href="@{/course/{id}/labs(id=${lab.course.id})}">go back</a></small>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/lab/view.html b/backend/src/main/resources/templates/lab/view.html
new file mode 100644
index 000000000..18b1874b2
--- /dev/null
+++ b/backend/src/main/resources/templates/lab/view.html
@@ -0,0 +1,382 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org"
+      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="course/view">
+<head>
+    <title>Lab #[[${lab.id}]]</title>
+
+    <link rel="stylesheet" href="/css/labview.css">
+    <link rel="stylesheet" href="/css/chosen.min.css">
+    <script src="/js/chosen.jquery.min.js"></script>
+</head>
+
+<body>
+<section layout:fragment="subcontent">
+    <div class="page-sub-header">
+        <div class="pull-right"
+             th:if="${@permissionService.canEnqueueSelfForLab(#authentication.principal, lab.id)}">
+            <th:block
+                    th:unless="${lab.isEnqueued(#authentication.principal.user) || lab.isBeingProcessed(#authentication.principal.user)}">
+                <a href="#" th:href="@{/lab/{id}/enqueue(id=${lab.id})}"
+                   class="btn btn-sm btn-secondary btn-success">Enqueue</a>
+            </th:block>
+
+        </div>
+        <h3>Lab #[[${lab.id}]]: [[${lab.title}]]</h3>
+        <p
+                th:if="${lab.isFeedbackLab() and #authentication.principal.user.isStudent()}">
+            Note: you can only enqueue in this lab by submitting an assignment on CPM.</p>
+    </div>
+
+    <div class="row">
+        <div class="col-12">
+            <div class="card bg-primary text-white"
+                 th:if="${lab.isEnqueued(#authentication.principal.user)}">
+                <h3 class="card-header">You are currently enqueued</h3>
+                <div class="card-body">
+                    <p class="lead">Current position in the queue:</p>
+                    <p class="lead" th:text="${lab.position(#authentication.principal.user)}"></p>
+                    <p>Once your request has been processed, you can <a th:href="@{/history}"
+                                                                        class="text-white">leave
+                        feedback</a>.</p>
+                </div>
+                <div class="card-footer">
+
+                    <th:block th:if="${lab.isEnqueued(#authentication.principal.user)}">
+                        <form th:action="@{/lab/{id}/unenqueue(id=${lab.id})}" method="POST">
+                            <button class="btn btn-sm btn-secondary btn-danger">Unenqueue</button>
+                        </form>
+                    </th:block>
+                </div>
+            </div>
+        </div>
+
+        <div class="col-12 mt-3"
+             th:with="request=${lab.getPendingRequest(#authentication.principal.user).get()}"
+             th:if="${lab.isEnqueued(#authentication.principal.user)}">
+            <div class="card bg-warning text-white">
+                <th:block>
+                    <h3 class="card-header">Update your room and location</h3>
+                    <div class="card-body col-12 col-md-8">
+                        Current room: [[${request.getRoom()}]]
+                        <form class="form" method="post"
+                              th:action="@{/request/{id}/update-room/(id=${request.getId()})}">
+                            <div class="form-group">
+                                <select class="custom-select custom-select-md" name="room">
+                                    <option disabled>Choose your new room</option>
+                                    <option th:each="room : ${lab.getFilteredRooms()}"
+                                            th:value="${room.getId()}"
+                                            th:selected="${request.room.equals(room)}">
+                                        [[${room}]]
+                                    </option>
+                                </select>
+                            </div>
+                            <div class="form-group" id="comment" style="display:none;">
+                                <label>Where are you located?</label>
+                                <input type="text" class="form-control" id="inputLocation"
+                                       name="comment"
+                                       placeholder="Cubicle 1..." th:value="${request.comment}"/>
+                            </div>
+
+                            <div class="form-group">
+                                <button type="submit" class="btn btn-success" name="updateRoom">
+                                    Save
+                                </button>
+                            </div>
+                        </form>
+
+                        <div id="image-holder"></div>
+                    </div>
+                </th:block>
+            </div>
+        </div>
+
+        <div class="col-md-12" style="margin-top: 1em;">
+            <div class="card-deck">
+                <div class="card">
+                    <h3 class="card-header">Lab info</h3>
+
+                    <div class="card-body">
+                        <dl class="dl-horizontal">
+                            <dt>Rooms</dt>
+                            <dd th:text="${#strings.listJoin(lab.getFilteredRooms(), ', ')}">DW
+                                1.01, DW 1.02, DW 1.03
+                            </dd>
+
+                            <dt>Slot</dt>
+                            <dd>[[${#temporals.format(lab.slot.opensAt, 'dd MMMM yyyy HH:mm')}]] -
+                                [[${#temporals.format(lab.slot.closesAt, 'dd MMMM yyyy HH:mm')}]]
+                            </dd>
+
+                            <dt th:if="${!lab.allAllowed()}">For mentor groups</dt>
+                            <dd th:if="${!lab.allAllowed()}">
+                                <span th:text="${#strings.listJoin(lab.getAllowedMentorGroups(), ', ')}">TI-AB</span>
+                                <br/>
+                                <span th:text="${lab.isAllowWithoutMentorGroup() ? 'No group' : ''}"></span>
+                            </dd>
+
+                            <dt>Avg. waiting time</dt>
+                            <th:block th:if="${lab.averageWaiting().isPresent()}">
+                                <dd>[[${lab.averageWaiting().orElse(0)}]] minutes <i
+                                        class="fa fa-question-circle" data-toggle="tooltip"
+                                        data-placement="bottom"
+                                        title="Computed as the average waiting time of archived requests in the past hour."></i>
+                                </dd>
+                            </th:block>
+
+                            <th:block th:if="!${lab.averageWaiting().isPresent()}">
+                                <dd>Unknown</dd>
+                            </th:block>
+
+                            <dt>Current size of queue</dt>
+                            <dd>[[${lab.nrOfStudentsInQueue()}]] student(s) <i
+                                    class="fa fa-question-circle" data-toggle="tooltip"
+                                    data-placement="bottom"
+                                    title="Number of students currently in the queue for this lab"></i>
+                            </dd>
+
+
+                            <th:block th:if="${lab.getSignOffIntervals()}">
+                                <dt>Interval time slots</dt>
+                                <dd>[[${lab.getIntervalTime()}]] minutes</dd>
+                            </th:block>
+
+                            <dt>Open?</dt>
+                            <dd th:if="${lab.isOpen()}">
+                                <span class="badge badge-pill badge-success text-white">Open</span>
+                            </dd>
+
+                            <dd th:if="${!lab.isOpen() and lab.slotSelectionIsOpen()}">
+                                <span class="badge badge-pill badge-warning text-white">Slot selection</span>
+                            </dd>
+
+                            <dd th:if="${!lab.isOpenOrSlotSelection()}">
+                                <span class="badge badge-pill badge-danger">Closed</span>
+                            </dd>
+
+                        </dl>
+                    </div>
+                    <div class="card-footer"></div>
+                </div>
+                <div class="card">
+                    <h3 class="card-header">More</h3>
+
+                    <div class="card-body">
+                        <h4 class="card-title">Submission info</h4>
+                        <ul>
+                            <li th:if="${lab.feedbackLab}">This lab is a feedback lab</li>
+                            <li th:each="assignment : ${lab.assignments}"
+                                th:text="${assignment.name}"></li>
+                            <li th:if="${lab.assignments.size() == 0}">This lab does not have any
+                                assignments.
+                            </li>
+                        </ul>
+
+                        <h4 class="card-title">Lab accepts</h4>
+
+                        <ul>
+                            <li th:each="requesttype : ${lab.allowedRequestTypes}"
+                                th:text="${requesttype.name}"></li>
+                        </ul>
+                    </div>
+                    <div class="card-footer"></div>
+                </div>
+            </div>
+        </div>
+
+        <th:block
+                th:if="${(#authenticated.teaches(course) || #authenticated.assists(course) || #authenticated.manages(course)) and lab.getSignOffIntervals()}">
+            <div class="col-sm-12" style="margin-top:1em;">
+                <h3>Slot overview</h3>
+                Total # of pending requests: <span>[[${requests.size()}]]</span>
+                <div class="col-sm-8 mb-3">
+                    <div class="row">
+                        <div class="col-sm-4" th:each="slot : ${timeslots}"
+                             th:value="${slot.getOpensAt()}">
+                            <span style="cursor:pointer" href="#"
+                                  class="badge badge-pill toggler" data-toggle="tooltip"
+                                  data-placement="right"
+                                  th:title="${lab.getNumberOfRequestsOnSlot(slot) + '/' + lab.getCapacity() + ' enrolled'}"
+                                  th:classappend="${slot.getAvailable()} ? 'badge-primary' : 'badge-secondary'">
+                                [[${#temporals.format(slot.getOpensAt(), 'HH:mm')}]] - [[${#temporals.format(slot.getClosesAt(), 'HH:mm')}]]
+                            </span>
+                            <table style="display:none;"
+                                   class="table table-bordered togglable">
+                                <tr th:each="request : ${lab.getRequestsOnSlot(slot)}">
+                                    <td th:with="entity=${request.getRequestEntity()}, cond=${entity} != null"
+                                        style="white-space:nowrap;">
+                                        <form th:if="${cond}"
+                                              th:action="@{/lab/{id}/unenqueue/{netid}(id=${lab.id},netid=${request.getRequestEntity().getDisplayName()})}"
+                                              class="form-horizontal" method="post">
+                                            <span class="p-1">
+                                                [[${request.getRequestEntity().getDisplayName()}]]
+                                            </span>
+                                            <button type="submit" name="unenroll"
+                                                    class="btn btn-danger btn-sm">unenroll
+                                            </button>
+                                        </form>
+                                    </td>
+                                </tr>
+                                <tr th:if="${slot.getAvailable()}" style="white-space:nowrap;">
+                                    <td>
+                                        <form th:action="@{/lab/{id}/enqueuestudent(id=${lab.id})}"
+                                              class="form-horizontal" method="post">
+                                            <input hidden name="slotStart"
+                                                   th:value="${slot.getOpensAt()}">
+                                            <input hidden name="slotEnd"
+                                                   th:value="${slot.getClosesAt()}">
+                                            <select class="chosen-select" name="assignmentid">
+                                                <option
+                                                        th:each="assignment : ${lab.getAssignments()}"
+                                                        th:value="${assignment.getId()}">
+                                                    [[${assignment.getName()}]]
+                                                </option>
+                                            </select>
+                                            <select class="chosen-select" name="netid">
+                                                <option th:each="student : ${students}"
+                                                        th:value="${student.getUsername()}"
+                                                        th:if="${!lab.isEnqueued(student)}"
+                                                >[[${student
+                                                    .getUsername()}]]
+                                                </option>
+                                            </select>
+                                            <button type="submit" name="enroll"
+                                                    class="btn btn-success btn-sm">enroll
+                                            </button>
+                                        </form>
+                                    </td>
+                                </tr>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </th:block>
+
+        <div class="col-sm-12" style="margin-top:1em;">
+            <h3>Requests for this lab</h3>
+            <p>
+                <th:block th:if="${lab.isEnqueued(#authentication.principal.user)}">
+                    You are waiting to be processed.
+                </th:block>
+
+                <th:block th:if="${!lab.isEnqueued(#authentication.principal.user)}">
+                    You are not enqueued.
+                </th:block>
+
+                <th:block th:if="${lab.isBeingProcessed(#authentication.principal.user)}">
+                    Your request is currently being processed.
+                </th:block>
+            </p>
+
+            <table class="table table-bordered table-striped table-responsive-sm">
+                <thead>
+                <th>Status</th>
+                <th>Request</th>
+                <th>Assigned</th>
+                <th>Course</th>
+                <th>Handled</th>
+                <th>Feedback</th>
+                </thead>
+                <tbody>
+                <tr th:each="request : ${requestsForLab}" class="text-white"
+                    th:classappend="${#filter.mapStatusToBootstrapColor(request.status)}">
+                    <td><span class="badge badge-pill bg-info" th:text="${request.status}"></span>
+                    </td>
+                    <td><a href="#" class="text-white" th:href="@{/request/{id}(id=${request.id})}"
+                           th:text="${request.toSentence()}">
+                        Request
+                    </a><br/>
+                        <small th:text="${#temporals.format(request.createdAt, 'dd-MM HH:mm')}">
+                            March 27, 2016
+                            at 12:34
+                        </small>
+                    </td>
+                    <td>
+                        <span th:if="${request.assistant}">[[${request.assistant}]]</span>
+                    </td>
+                    <td><span class="d-inline-block text-truncate" style="max-width:150px;"
+                              data-toggle="tooltip" data-placement="bottom"
+                              th:title="${request.assignment.course}">[[${request.assignment.course}]]</span>
+                    </td>
+                    <td>
+                        <th:block th:if="${request.handled}">
+                            [[${request.assistant}]] <br/>
+                            <small th:text="${#temporals.format(request.handledAt, 'dd-MM HH:mm')}">
+                                22 May 2016
+                            </small>
+                        </th:block>
+                    </td>
+                    <td>
+                        <th:block
+                                th:if="${(request.isHandled() and request.getLeftFeedback() and request.getRequestEntity().getId().equals(#authenticated.getId()))}">
+                            You have given feedback to your teaching assistant.
+                        </th:block>
+
+                        <th:block
+                                th:if="${request.isHandled() and !(request.getLeftFeedback() and request.getRequestEntity().getId().equals(#authenticated.getId()))}">
+                            <a href="#" class="text-white"
+                               th:href="@{/request/{id}(id=${request.id})}">
+                                Click to add feedback for the Teaching assistant
+                            </a>
+                        </th:block>
+                    </td>
+                </tr>
+                <!--todo: make the line below depend on whether you are teacher or student-->
+                <tr th:if="${lab.countRequestsBy(#authentication.principal.user) == 0}">
+                    <td colspan="5">You don't have any requests yet for this lab.</td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+
+    <script src="/js/map_loader.js"></script>
+    <script type="text/javascript">
+        $(function () {
+            var roomId = $('select').val();
+            if (roomId) {
+                updateRoomInfo(roomId);
+            }
+        });
+    </script>
+    <script type="text/javascript">
+        $(function () {
+            $('.toggler').on('click', function () {
+
+                var sibling = $(this).next();
+
+                if (sibling.is(":visible")) {
+                    $('.togglable').hide();
+                } else {
+                    $('.togglable').hide();
+                    sibling.show();
+                }
+            });
+        });
+    </script>
+    <script type="text/javascript">
+        $(".chosen-select").chosen({disable_search_threshold: 1, width: '33%'});
+    </script>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/layout.html b/backend/src/main/resources/templates/layout.html
new file mode 100644
index 000000000..173105a1a
--- /dev/null
+++ b/backend/src/main/resources/templates/layout.html
@@ -0,0 +1,129 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      xmlns:sec="http://www.w3.org/1999/xhtml">
+<head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+    <meta http-equiv="Content-Language" content="en_GB"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1"/>
+
+    <meta name="description" content="Queue system"/>
+    <meta name="author" content=""/>
+    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
+    <meta name="_csrf" th:content="${_csrf.token}"/>
+
+    <link rel="icon" href="/favicon.ico"/>
+
+    <title>Queue</title>
+
+    <link rel="manifest" href="/manifest.json"/>
+
+    <link rel="stylesheet" href="/webjars/bootstrap/4.0.0/css/bootstrap.min.css"/>
+    <link rel="stylesheet" href="/webjars/fontawesome/4.7.0/css/font-awesome.min.css"/>
+    <link rel="stylesheet" href="/css/global.css"/>
+
+    <script src="/webjars/jquery/3.4.1/jquery.min.js"></script>
+    <script src="/webjars/popper.js/1.13.0/dist/umd/popper.min.js"></script>
+    <script src="/webjars/bootstrap/4.0.0/js/bootstrap.min.js"></script>
+    <script src="/webjars/sockjs-client/1.1.4/dist/sockjs.min.js"></script>
+    <script src="/webjars/stomp-websocket/2.3.3-1/stomp.min.js"></script>
+    <script src="/js/global.js"></script>
+    <script src="/js/push.js" sec:authorize="isAuthenticated()"></script>
+
+</head>
+
+<body>
+
+<nav class="navbar navbar-expand-lg navbar-inverse text-white">
+    <div class="container">
+        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
+                aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
+            <i class="fa fa-bars" style="color:white;" aria-hidden="true"></i>
+        </button>
+        <a class="navbar-brand" href="/">Queue</a>
+        <div class="collapse navbar-collapse" id="navbarNav">
+            <ul class="navbar-nav nav nav-pills mr-auto mt-2 mt-lg-0 block">
+                <th:block sec:authorize="isAuthenticated()">
+                    <li class="nav-item"><a class="nav-link menu-link"
+                                            th:classappend="${page == 'courses' ? 'active text-white' : ''}" href="#"
+                                            th:href="@{/courses}">Courses</a></li>
+                    <li class="nav-item" th:if="${@permissionService.canViewRequests(#authentication.principal)}"><a
+                            href="#" th:classappend="${page == 'requests' ? 'active text-white' : ''}"
+                            class="nav-link menu-link" th:href="@{/requests}">Requests</a></li>
+                    <li class="nav-item" sec:authorize="hasAnyRole('ADMIN')"><a
+                            href="#" th:classappend="${page == 'admin' ? 'active text-white' : ''}"
+                            class="nav-link menu-link" th:href="@{/admin}">Admin</a></li>
+                    <li class="nav-item d-block d-sm-none">
+                        <a href="#" th:href="@{/notifications}" class="nav-link"
+                           th:classappend="${page == 'notifications' ? 'active' : ''}">
+                            Notifications
+                        </a>
+                    </li>
+                </th:block>
+            </ul>
+            <ul class="nav navbar-nav navbar-right">
+                <th:block sec:authorize="isAuthenticated()">
+                    <li class="d-none d-sm-block nav-item">
+                        <a href="#" class="nav-link notifications" th:href="@{/notifications}"
+                           th:unless="${#authenticated.outdated}">
+                            <i class="fa fa-bell"></i>
+                            <span class="badge badge-light notificationCount"></span>
+                        </a>
+                        <a href="#" class="nav-link notifications" style="background-color:purple;"
+                           th:href="@{/notifications}" th:if="${#authenticated.outdated}">
+                            <i class="fa fa-bell"></i>
+                            <span class="badge badge-light notificationCount">[[${#authenticated.notificationCount()}]]</span>
+                        </a>
+                    </li>
+                    <li class="nav-item dropdown">
+                        <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button"
+                           aria-haspopup="true" aria-expanded="false" th:text="${#authenticated.displayName}">
+                            User
+                            <span class="caret"></span>
+                        </a>
+                        <div class="dropdown-menu">
+                            <a class="dropdown-item" href="#" th:href="@{/history}">Request history</a>
+                            <a class="dropdown-item" href="#" th:href="@{/saml/logout}">Logout</a>
+                        </div>
+                    </li>
+                </th:block>
+                <li class="nav-item" sec:authorize="!isAuthenticated()">
+                    <a class="nav-link" href="#" th:href="@{/saml/login}">Login</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</nav>
+
+<th:block layout:fragment="content">
+    <p>Page content goes here</p>
+</th:block>
+
+<footer class="footer mt-md-3">
+    <div class="container">
+        <p class="text-muted">&copy; Delft University of Technology
+
+            <img src="/img/tudelft-logo.png" class="pull-right" height="50px"/></p>
+    </div>
+</footer>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/login.html b/backend/src/main/resources/templates/login.html
new file mode 100644
index 000000000..27ea70fef
--- /dev/null
+++ b/backend/src/main/resources/templates/login.html
@@ -0,0 +1,62 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <div class="page-header">
+        <h1>Login</h1>
+    </div>
+
+    <div class="alert alert-danger" role="alert" th:if="${param.error}">
+        <span>Invalid username and password.</span>
+    </div>
+
+    <div class="alert alert-info" role="alert" th:if="${param.logout}">
+        <span>You have been logged out.</span>
+    </div>
+
+    <form action="#" th:action="@{/login}" class="form-horizontal" method="post">
+        <div class="form-group">
+            <label for="inputUsername" class="col-sm-2 control-label">Username</label>
+
+            <div class="col-sm-8">
+                <input type="text" id="inputUsername" name="username" class="form-control" placeholder="Username"/>
+            </div>
+        </div>
+        <div class="form-group">
+            <label for="inputPassword" class="col-sm-2 control-label">Password</label>
+
+            <div class="col-sm-8">
+                <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password"/>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <input type="submit" class="btn btn-primary" value="Login"/>
+            </div>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/notification/index.html b/backend/src/main/resources/templates/notification/index.html
new file mode 100644
index 000000000..0688b4137
--- /dev/null
+++ b/backend/src/main/resources/templates/notification/index.html
@@ -0,0 +1,84 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+    <section layout:fragment="container">
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item"><a href="/">Home</a></li>
+                <li class="breadcrumb-item active">Notifications</li>
+            </ol>
+        </nav>
+
+        <div class="alert alert-info mt-md-3" role="alert" th:unless="${#strings.isEmpty(message)}">
+            <span th:text="${message}"></span>
+        </div>
+
+        <div class="page-header">
+            <div class="btn-toolbar pull-right">
+                <div class="btn-group">
+                    <a th:href="@{/notifications/clear}" class="btn btn-danger">Clear all notifications</a>
+                </div>
+            </div>
+
+            <h1>Notifications</h1>
+        </div>
+
+        <p th:if="${#lists.isEmpty(notifications)}">
+            There are no notifications.
+        </p>
+
+        <div class="row" th:unless="${#lists.isEmpty(notifications)}">
+            <div class="col-sm-3">
+                <ul class="nav nav-pills nav-stacked">
+                    <li class="nav-item"><a href="#" class="nav-link" th:classappend="${active == null ? 'active' : ''}"  th:href="@{/notifications}">All notifications</a></li>
+                </ul>
+                <hr/>
+                <ul class="nav nav-pills nav-stacked small course-filter">
+                    <li th:each="course : ${notifications.courses}" class="nav-item">
+                        <a class="nav-link d-inline-block truncate" style="max-width:200px;"
+                           th:classappend="${course.id == active ? 'active' : ''}"
+                           th:href="@{/notifications/course/{id}(id=${course.id})}" >
+                            <span class="count" th:text="${notifications.forCourse(course).unread()}">42</span>
+                            [[${course}]]
+                        </a>
+                    </li>
+                </ul>
+            </div>
+
+            <div class="col-sm-9">
+                <div class="list-group">
+                    <a href="#" class="list-group-item item-icon" th:each="notification : ${notifications}" th:href="@{/notification/{id}(id=${notification.id})}">
+                        <span class="icon unread" th:unless="${notification.read}"></span>
+                        <h4 class="list-group-item-heading" th:text="${notification.title}">Assistant incoming</h4>
+                        <p class="list-group-item-text" th:text="${notification.message}">Assistant 'martijn' is on its way!</p>
+                        <p class="time" th:text="${#prettyTime.diffForHumans(notification.createdAt)}"></p>
+                    </a>
+                </div>
+            </div>
+        </div>
+    </section>
+</body>
+
+</html>
diff --git a/backend/src/main/resources/templates/pagination.html b/backend/src/main/resources/templates/pagination.html
new file mode 100644
index 000000000..c507fe2d5
--- /dev/null
+++ b/backend/src/main/resources/templates/pagination.html
@@ -0,0 +1,47 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+</head>
+<body>
+<nav th:fragment="pagination (pager, url)"  aria-label="Page navigation">
+    <ul class="pagination" th:if="${pager.totalPages > 0}">
+        <li th:classappend="${pager.hasPrevious()} ? '' : 'disabled'" class="page-item">
+            <span class="page-link" th:if="${not pager.hasPrevious()}">&laquo;</span>
+
+            <a class="page-link" th:if="${pager.hasPrevious()}" th:href="@{/{url}(url=${url}, page=${pager.number - 1})}">&laquo;</a>
+        </li>
+
+        <li class="page-item" th:each="pageNumber : ${#numbers.sequence(0, (pager.totalPages-1))}" th:classappend="${pageNumber == pager.number} ? 'active' : ''">
+            <span class="page-link" th:if="${pageNumber == pager.number}" th:text="${pageNumber+1}">1</span>
+
+            <a class="page-link" th:if="${pageNumber != pager.number}" th:text="${pageNumber+1}" th:href="@{/{url}(url=${url}, page=${pageNumber})}">1</a>
+        </li>
+
+        <li class="page-item" th:classappend="${pager.hasNext()} ? '' : 'disabled'">
+            <span class="page-link" th:if="${not pager.hasNext()}">&raquo;</span>
+
+            <a class="page-link" th:if="${pager.hasNext()}" th:href="@{/{url}(url=${url}, page=${pager.number})}">&raquo;</a>
+        </li>
+    </ul>
+</nav>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/request/approve.html b/backend/src/main/resources/templates/request/approve.html
new file mode 100644
index 000000000..f461a13a2
--- /dev/null
+++ b/backend/src/main/resources/templates/request/approve.html
@@ -0,0 +1,63 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+
+</head>
+
+<body>
+<section layout:fragment="container">
+
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/course/{id}(id=${request.lab.course.id})}" th:text="${request.lab.course.code}">TI1316</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/lab/{id}(id=${request.lab.id})}" >Lab #[[${request.lab.id}]]</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/request/{id}(id=${request.id})}" >Request #[[${request.id}]]</a></li>
+            <li class="breadcrumb-item active">Approve</li>
+        </ol>
+    </nav>
+
+    <div class="page-header">
+        <h1 >Request #[[${request.id}]]</h1>
+    </div>
+
+    <form action="#" th:action="@{/request/{id}(id=${request.id})}" method="post" class="form-horizontal">
+        <div class="form-group">
+            <label for="inputComment" class="col-sm-2 control-label">Comment</label>
+
+            <div class="col-sm-8">
+                <textarea maxlength="250" class="form-control" id="inputComment" name="comment" placeholder="Optionally leave a comment to explain your approval to other TA's."></textarea>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <button type="submit" name="approve"
+                        class="btn btn-success submit-form ctrl-enter-submit">Approve</button>
+                <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script>
+            </div>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/request/forward.html b/backend/src/main/resources/templates/request/forward.html
new file mode 100644
index 000000000..3c2fcf290
--- /dev/null
+++ b/backend/src/main/resources/templates/request/forward.html
@@ -0,0 +1,81 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/course/{id}(id=${request.lab.course.id})}" th:text="${request.lab.course.code}">TI1316</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/lab/{id}(id=${request.lab.id})}" >Lab #[[${request.lab.id}]]</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/request/{id}(id=${request.id})}" >Request #[[${request.id}]]</a></li>
+            <li class="breadcrumb-item active">Forward</li>
+        </ol>
+    </nav>
+
+    <div class="page-header">
+        <h1 >Request #[[${request.id}]]</h1>
+    </div>
+
+    <th:block th:if="${#lists.isEmpty(assistants)}">
+        <div class="alert alert-danger" role="alert">
+            <span>There are no other assistants in this course.</span>
+        </div>
+    </th:block>
+
+    <th:block th:unless="${#lists.isEmpty(assistants)}">
+        <form action="#" th:action="@{/request/{id}(id=${request.id})}" method="post" class="form-horizontal">
+            <div class="form-group">
+                <label for="inputAssistant" class="col-sm-2 control-label">Assistant</label>
+
+                <div class="col-sm-8">
+                    <select name="assistant" class="form-control" id="inputAssistant">
+                        <option value="-1">anyone but me</option>
+                        <option th:each="assistant : ${assistants}" th:value="${assistant.id}" th:text="${assistant}">assistant</option>
+                    </select>
+                </div>
+            </div>
+
+            <div class="form-group">
+                <label for="inputComment" class="col-sm-2 control-label">Comment</label>
+
+                <div class="col-sm-8">
+                    <textarea maxlength="250" class="form-control" id="inputComment" name="comment" required="required"
+                              placeholder="Leave a comment to explain your forward to other TA's."></textarea>
+                </div>
+            </div>
+
+            <div class="form-group">
+                <div class="col-sm-offset-2 col-sm-10">
+                    <button type="submit" name="forward"
+                            class="btn btn-warning ctrl-enter-submit">Forward</button>
+                    <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script>
+                </div>
+            </div>
+        </form>
+    </th:block>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/request/list.html b/backend/src/main/resources/templates/request/list.html
new file mode 100644
index 000000000..d592271c7
--- /dev/null
+++ b/backend/src/main/resources/templates/request/list.html
@@ -0,0 +1,115 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="layout">
+<head>
+    <title>Requests</title>
+</head>
+
+<body>
+<section layout:fragment="content">
+    <div class="container">
+        <script type="text/javascript" src="/js/bootstrap-multiselect.js"></script>
+        <script type="text/javascript" src="/js/request_table.js"></script>
+        <script type="text/javascript" src="/webjars/handlebars/4.0.6/handlebars.min.js"></script>
+        <link rel="stylesheet" href="/css/bootstrap-multiselect.css" type="text/css"/>
+
+        <nav role="navigation" class="breadcrumbs">
+            <ol class="breadcrumb">
+                <li class="breadcrumb-item"><a href="/">Home</a></li>
+                <li class="breadcrumb-item active">Requests</li>
+            </ol>
+        </nav>
+
+        <div class="page-header">
+            <div class="pull-right btn-toolbar">
+                <form th:each="lab : ${activeLabs}" th:action="${#filter.buildUrlMultiValueMap(state, '/requests/next/' + lab.id)}"
+                      class="pull-right" style="padding-right:2px;" method="POST">
+                    <th:block th:if="${queued.get(lab.id) > 0}">
+                        <button type="submit" class="btn btn-sm btn-primary" >Get next for
+                            [[${lab.course.code}]] [[${lab.toString()}]] (<span th:id="${'span-' + lab.id}">[[${queued.get(lab.id)}]]</span>)
+                        </button>
+                    </th:block>
+                    <th:block th:unless="${queued.get(lab.id) > 0}">
+                        <button th:id="${'lab-' + lab.id}" style="display:none;" type="submit"
+                                class="btn btn-sm btn-primary" >Get next for
+                            [[${lab.course.code}]] [[${lab.toString()}]] (<span th:id="${'span-' + lab.id}">[[${queued.get(lab.id)}]]</span>)
+                        </button>
+                    </th:block>
+                </form>
+                <a class="btn btn-sm btn-secondary text-white pull-right" style="margin-right:2px;"
+                   onclick='location.reload();'>Refresh</a>
+            </div>
+
+            <h1>Requests</h1>
+        </div>
+
+        <div class="row">
+            <div class="col-sm-12">
+                <form class="form-inline" method="get">
+                    <div class="form-group"
+                         th:with="num=${state.get('size') != null ? state.get('size').get(0) : 25} ">
+                        <input type="number" class="form-control" id="pagesize" name="size"
+                               th:placeholder="${num} + ' elements per page'"
+                               required="required"
+                               />
+                    </div>
+                    <button type="submit" value="Submit" class="d-none d-sm-block btn btn-success"
+                            style="margin-left:5px;">Submit
+                    </button>
+                    <div class="col-12 d-block d-sm-none">
+                        <button type="submit" value="Submit" class="btn btn-success">Submit</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+    <div class="container-fluid">
+        <div class="row">
+            <div class="col-lg-12 offset-xl-1 col-xl-10">
+
+                <th:block th:replace="request/list/filters :: filters"></th:block>
+                <th:block th:replace="request/list/requesttable :: requesttable"></th:block>
+                <th:block th:replace="request/list/pagination :: pagination"></th:block>
+            </div>
+            <script id="request-entry-template" type="text/x-handlebars-template">
+                <tr class="bg-primary text-white" id="request-{{id}}">
+                    <td><span class="badge badge-pill bg-danger" id="status-{{id}}">NEW</span></td>
+                    <td><a href="/request/{{id}}" class="text-white">{{sentence}}</a><br/>
+                        <small>Right now</small>
+                    </td>
+                    <td id="assigned-{{id}}">{{assigned}}</td>
+                    <td><span class="d-inline-block text-truncate" style="max-width:150px;"
+                              data-toggle="tooltip" data-placement="bottom" title="{{course}}">
+                        {{course}}
+                    </span>
+                    </td>
+                    <td>{{handled}}</td>
+                    <td>{{room}}</td>
+                </tr>
+            </script>
+        </div>
+    </div>
+</section>
+</body>
+</html>
+
+
diff --git a/backend/src/main/resources/templates/request/list/filters.html b/backend/src/main/resources/templates/request/list/filters.html
new file mode 100644
index 000000000..4f166a5f9
--- /dev/null
+++ b/backend/src/main/resources/templates/request/list/filters.html
@@ -0,0 +1,156 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.w3.org/1999/xhtml" layout:decorate="container">
+<head>
+</head>
+<body>
+<div class="table-list-header" th:fragment="filters">
+    <script type="text/javascript" src="/js/bootstrap-multiselect.js"></script>
+    <link rel="stylesheet" href="/css/bootstrap-multiselect.css" type="text/css"/>
+    <div class="filters" style="padding:5px;">
+        <form method="get" class="form">
+            <div class="form-group filter">
+                <label class="form-control-label" for="courseSelect">Course</label>
+                <select multiple="multiple" class="form-control" id="courseSelect" name="lab.course">
+                    <th:block th:each="course : ${courses}">
+                        <option th:value="${course.id}"
+                                th:unless="${#filter.valueInMultiMap('lab.course', course, state)}"
+                                th:text="${course}">One
+                        </option>
+                        <option th:value="${course.id}"
+                                th:if="${#filter.valueInMultiMap('lab.course', course, state)}"
+                                selected="selected" th:text="${course}">One
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+
+            <div class="form-group filter">
+                <label class="form-control-label" for="labSelect">Lab</label>
+                <select multiple="multiple" class="form-control" id="labSelect" name="lab">
+                    <th:block th:each="lab : ${activeLabs}">
+                        <option
+                                th:value="${lab.id}"
+                                th:unless="${#filter.valueInMultiMap('lab', lab, state)}" th:text="${lab.toReadableString()}">One
+                        </option>
+                        <option th:value="${lab.id}"
+                                th:if="${#filter.valueInMultiMap('lab', lab, state)}" selected="selected"
+                                th:text="${lab.toReadableString()}">One
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+
+            <div class="form-group filter">
+                <label class="form-control-label" for="assignmentSelect">Assignment</label>
+                <select multiple="multiple" class="form-control" id="assignmentSelect" name="assignment">
+                    <th:block th:each="assignment : ${assignments}">
+                        <option th:value="${assignment.id}"
+                                th:unless="${#filter.valueInMultiMap('assignment', assignment, state)}"
+                                th:text="${assignment}">One
+                        </option>
+                        <option th:value="${assignment.id}"
+                                th:if="${#filter.valueInMultiMap('assignment', assignment, state)}"
+                                selected="selected" th:text="${assignment}">One
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+
+            <div class="form-group filter">
+                <label class="form-control-label" for="roomSelect">Room</label>
+                <select multiple="multiple" class="form-control" id="roomSelect" name="room">
+                    <th:block th:each="room : ${rooms}">
+                        <option th:value="${room.id}"
+                                th:unless="${#filter.valueInMultiMap('room', room.id, state)}" th:text="${room}">One
+                        </option>
+                        <option th:value="${room.id}"
+                                th:if="${#filter.valueInMultiMap('room', room.id, state)}" selected="selected"
+                                th:text="${room}">One
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+
+            <div class="form-group filter">
+                <label class="form-control-label" for="assignedSelect">Assigned</label>
+                <select multiple="multiple" class="form-control" id="assignedSelect" name="assistant">
+                    <option th:value="${#authenticated.getId().toString()}">You</option>
+                </select>
+            </div>
+
+            <div class="form-group filter">
+                <label class="form-control-label" for="statusSelect">Status</label>
+                <select multiple="multiple" class="form-control" id="statusSelect" name="status">
+                    <th:block th:each="status : ${T(nl.tudelft.ewi.queue.model.Request.Status).values()}">
+                        <option th:value="${status}" th:text="${status}"
+                                th:unless="${#filter.valueInMultiMap('status', status, state)}">One
+                        </option>
+                        <option th:value="${status}" th:text="${status}"
+                                th:if="${#filter.valueInMultiMap('status', status, state)}" selected="selected">One
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+
+            <div class="form-group filter">
+                <label class="form-control-label" for="statusSelect">Type</label>
+                <select multiple="multiple" class="form-control" id="requestTypeSelect" name="requestType">
+                    <th:block th:each="requestType : ${requestTypes}">
+                        <option th:value="${requestType.id}" th:text="${requestType.name}"
+                                th:unless="${#filter.valueInMultiMap('requestType', requestType.id, state)}">One
+                        </option>
+                        <option th:value="${requestType.id}" th:text="${requestType.name}"
+                                th:if="${#filter.valueInMultiMap('requestType', requestType.id, state)}" selected="selected">One
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+
+            <div class="filter">
+                <button type="submit" class="btn btn-primary">Filter</button>
+
+                <th:block th:if="${state.size() > 0}">
+                    <a href="#" th:href="@{/requests(clear=1)}" class="filters-reset" th:if="${page == 'requests'}">
+                        <i class="fa fa-times" aria-hidden="true"></i> Clear current filters
+                    </a>
+                    <a href="#" th:href="@{/history}" class="filters-reset" th:if="${page == 'history'}">
+                        <i class="fa fa-times" aria-hidden="true"></i> Clear current filters
+                    </a>
+                </th:block>
+
+            </div>
+        </form>
+    </div>
+
+
+    <script type="text/javascript">
+        $(document).ready(function () {
+            $('select').multiselect({
+                buttonWidth: '110px',
+                nonSelectedText: 'Select...',
+                templates: {li: '<li><a class="dropdown-item" tabindex="0"><label style="padding-left:1px;display: block;"></label></a></li>'}
+            });
+        });
+    </script>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/backend/src/main/resources/templates/request/list/pagination.html b/backend/src/main/resources/templates/request/list/pagination.html
new file mode 100644
index 000000000..52379afe6
--- /dev/null
+++ b/backend/src/main/resources/templates/request/list/pagination.html
@@ -0,0 +1,76 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.w3.org/1999/xhtml" layout:decorate="container">
+<head>
+</head>
+<!--
+The body tag declares the types of the variables required from pages using this page.
+When using the entire body of this page, remove the th:with in body.
+-->
+<body th:with="requests = ${org.springframework.data.domain.Page},
+               state = ${java.util.Map}">
+<nav th:if="${requests.getTotalPages() > 1}" aria-label="Page navigation" th:fragment="pagination">
+    <ul class="pagination">
+        <li th:classappend="${not requests.isFirst()} ? '' : 'disabled'" class="page-item">
+            <span th:if="${requests.isFirst()}" class="page-link">&laquo;</span>
+
+            <a th:if="${!requests.isFirst()}" th:href="${#filter.updateUrlMultiValueMap(state, 'page', 0)}" class="page-link">&laquo;</a>
+        </li>
+
+        <th:block th:if="${requests.getTotalPages() &lt; 3}">
+            <li th:each="pageNumber : ${#numbers.sequence(0, requests.getTotalPages()-1)}"
+                th:classappend="${pageNumber == requests.getNumber()} ? 'active' : ''" class="page-item">
+                <span th:if="${pageNumber == requests.getNumber()}" th:text="${pageNumber+1}" class="page-link">1</span>
+                <a th:if="${pageNumber != requests.getNumber()}" th:text="${pageNumber+1}"
+                   th:href="${#filter.updateUrlMultiValueMap(state, 'page', pageNumber)}" class="page-link">1</a>
+            </li>
+        </th:block>
+
+        <th:block th:unless="${requests.getTotalPages() &lt; 3}">
+            <th:block th:if="${requests.getNumber() &gt; 3}">
+                <li class="page-item"><a href="#" class="page-link">...</a></li>
+            </th:block>
+
+            <th:block th:each="pageNumber : ${#numbers.sequence(requests.getNumber()-2, requests.getNumber()+2)}">
+                <th:block th:if="${pageNumber &gt;= 0 and pageNumber &lt;= requests.getTotalPages()-1}">
+                    <li th:classappend="${pageNumber == requests.getNumber()} ? 'active' : ''"  class="page-item">
+                        <span th:if="${pageNumber == requests.getNumber()}" th:text="${pageNumber+1}" class="page-link">1</span>
+                        <a th:if="${pageNumber != requests.getNumber()}" th:text="${pageNumber+1}"
+                           th:href="${#filter.updateUrlMultiValueMap(state, 'page', pageNumber)}" class="page-link">1</a>
+                    </li>
+                </th:block>
+            </th:block>
+
+            <th:block th:if="${requests.getNumber() + 3 <= requests.getTotalPages()-1}">
+                <li class="page-item"><a href="#" class="page-link">...</a></li>
+            </th:block>
+        </th:block>
+
+        <li th:classappend="${not requests.isLast()} ? '' : 'disabled'" class="page-item">
+            <span th:if="${requests.isLast()}" class="page-link">&raquo;</span>
+
+            <a th:if="${!requests.isLast()}"
+               th:href="${#filter.updateUrlMultiValueMap(state, 'page', requests.getTotalPages()-1)}" class="page-link">&raquo;</a>
+        </li>
+    </ul>
+</nav>
+</body>
+</html>
\ No newline at end of file
diff --git a/backend/src/main/resources/templates/request/list/requesttable.html b/backend/src/main/resources/templates/request/list/requesttable.html
new file mode 100644
index 000000000..b4246e7b4
--- /dev/null
+++ b/backend/src/main/resources/templates/request/list/requesttable.html
@@ -0,0 +1,68 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.w3.org/1999/xhtml" layout:decorate="container">
+<head>
+</head>
+<body>
+
+<table id="requesttable" class="table" th:fragment="requesttable">
+    <thead>
+        <tr class="no-border-top">
+            <th>Status</th>
+            <th>Request</th>
+            <th>Assigned</th>
+            <th>Course</th>
+            <th>Handled</th>
+            <th>Room</th>
+        </tr>
+    </thead>
+    <tbody>
+    <tr th:if="${requests.getTotalElements() == 0}">
+        <td colspan="5">There aren't any requests.</td>
+    </tr>
+    <th:block th:each="request : ${requests.content}">
+        <tr class="text-white" th:classappend="${#filter.mapStatusToBootstrapColor(request.status)}" th:id="'request-' + ${request.id}">
+            <td><span class="badge badge-pill bg-info" th:text="${request.status}" th:id="'status-' + ${request.id}"></span></td>
+            <td><a href="#" class="text-white" th:href="@{/request/{id}(id=${request.id})}"
+                   th:text="${request.toSentence()}">
+                Request
+            </a><br/>
+                <small th:text="${#temporals.format(request.createdAt, 'dd-MM HH:mm')}">March 27, 2016
+                    at 12:34
+                </small>
+            </td>
+            <td th:id="'assigned-' + ${request.id}">
+                <span th:if="${request.assistant}" >[[${request.assistant}]]</span>
+            </td>
+            <td><span  class="d-inline-block">[[${request.assignment.course}]]</span></td>
+            <td>
+                <th:block th:if="${request.handled}" >
+                    [[${request.assistant}]] <br/>
+                    <small th:text="${#temporals.format(request.handledAt, 'dd-MM HH:mm')}">22 May 2016</small>
+                </th:block>
+            </td>
+            <td th:text="${request.getRoom().toString()}">Room</td>
+        </tr>
+    </th:block>
+    </tbody>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/backend/src/main/resources/templates/request/reject.html b/backend/src/main/resources/templates/request/reject.html
new file mode 100644
index 000000000..9dff06701
--- /dev/null
+++ b/backend/src/main/resources/templates/request/reject.html
@@ -0,0 +1,71 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="container">
+<head>
+</head>
+
+<body>
+<section layout:fragment="container">
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/course/{id}(id=${request.lab.course.id})}" th:text="${request.lab.course.code}">TI1316</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/lab/{id}(id=${request.lab.id})}" >Lab #[[${request.lab.id}]]</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/request/{id}(id=${request.id})}" >Request #[[${request.id}]]</a></li>
+            <li class="breadcrumb-item active">Reject</li>
+        </ol>
+    </nav>
+
+    <div class="page-header">
+        <h1 >Request #[[${request.id}]]</h1>
+    </div>
+
+    <form action="#" th:action="@{/request/{id}(id=${request.id})}" method="post" class="form-horizontal">
+        <div class="form-group">
+            <label for="inputComment" class="col-sm-4 control-label">Comment for other TAs</label>
+
+            <div class="col-sm-8">
+                <textarea maxlength="250" class="form-control" id="inputComment" name="comment"
+                          placeholder="Comment to explain your rejection to other TAs..." required="required"></textarea>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <label for="inputCommentForStudent" class="col-sm-4 control-label">Comment for student</label>
+            <div class="col-sm-8">
+                <textarea maxlength="250" class="form-control" id="inputCommentForStudent" name="commentForStudent"
+                          placeholder="Comment to explain your rejection to the student..."></textarea>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+                <button type="submit" name="reject"
+                        class="btn btn-danger ctrl-enter-submit">
+                    Reject</button>
+                <script type="text/javascript" src="/js/ctrl_enter_submit.js"></script>
+            </div>
+        </div>
+    </form>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/request/status.html b/backend/src/main/resources/templates/request/status.html
new file mode 100644
index 000000000..c3152729e
--- /dev/null
+++ b/backend/src/main/resources/templates/request/status.html
@@ -0,0 +1,35 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<div th:fragment="status(s)">
+    <th:block th:switch="${s}">
+        <span class="badge badge-pill bg-primary" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).PENDING}">PENDING</span>
+        <span class="badge badge-pill bg-primary" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).ASSIGNED}">ASSIGNED</span>
+        <span class="badge badge-pill bg-secondary" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).CANCELLED}">CANCELLED</span>
+        <span class="badge badge-pill bg-secondary" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).REVOKED}">REVOKED</span>
+        <span class="badge badge-pill bg-info" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).PROCESSING}">PROCESSING</span>
+        <span class="badge badge-pill bg-success" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).APPROVED}">APPROVED</span>
+        <span class="badge badge-pill bg-danger" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).REJECTED}">REJECTED</span>
+        <span class="badge badge-pill bg-warning" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).FORWARDED}">FORWARDED</span>
+        <span class="badge badge-pill bg-danger" th:case="${T(nl.tudelft.ewi.queue.model.Request.Status).NOTFOUND}">NOT FOUND</span>
+    </th:block>
+</div>
+</html>
diff --git a/backend/src/main/resources/templates/request/view.html b/backend/src/main/resources/templates/request/view.html
new file mode 100644
index 000000000..2e8bdc351
--- /dev/null
+++ b/backend/src/main/resources/templates/request/view.html
@@ -0,0 +1,198 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="course/view">
+<head>
+    <title >Request #[[${request.id}]] ?? [[${request.lab.course}]]</title>
+
+</head>
+
+<body>
+<section layout:fragment="breadcrumb">
+
+    <nav role="navigation" class="breadcrumbs">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item"><a href="/">Home</a></li>
+            <li class="breadcrumb-item"><a href="/courses">Courses</a></li>
+            <li class="breadcrumb-item"><a href="#" th:href="@{/course/{id}(id=${course.id})}" th:text="${course.code}">TI1234</a></li>
+            <li class="breadcrumb-item active" >Request #[[${request.id}]]</li>
+        </ol>
+    </nav>
+
+</section>
+
+<section layout:fragment="subcontent">
+
+    <div class="page-sub-header">
+        <div class='btn-toolbar pull-right' th:if="${!submissionUrl.isEmpty()}">
+            <div class='btn-group'>
+                <a th:href="${submissionUrl}" target="_blank" class="btn btn-secondary">View submission</a>
+            </div>
+        </div>
+        <h3  th:unless="${request.getLab().getSignOffIntervals()}">Request #[[${request.id}]]</h3>
+        <h3  th:if="${request.getLab().getSignOffIntervals()}">Request #[[${request.id}]] at [[${request.getSlot()}]]</h3>
+
+    </div>
+
+    <div class="row">
+        <div class="col-sm-12" th:if="${request.isHandled() and !request.getLeftFeedback() and request.getRequestEntity().getId().equals(#authenticated.getId())}">
+            <div class="page-header">
+                <h4>Feedback</h4>
+            </div>
+                <form action="#" th:action="@{/request/{id}/feedback(id=${request.id})}" method="post" class="form-horizontal">
+                    <div class="form-group">
+                        <label for="inputFeedback" class="col-sm-2 control-label">Feedback</label>
+
+                        <div class="col-sm-8">
+                            <textarea maxlength="250" class="form-control" id="inputFeedback" name="feedback" placeholder="Leave feedback about the TA (only teachers can see this feedback)."></textarea>
+                        </div>
+                    </div>
+
+                    <div class="form-group">
+                        <div class="col-sm-offset-2 col-sm-10">
+                            <button type="submit" class="btn btn-primary">Leave feedback</button>
+                        </div>
+                    </div>
+                </form>
+        </div>
+
+        <div class="col-sm-4">
+            <div class="page-header">
+                <h4>History</h4>
+            </div>
+            <ul class="timeline">
+                <li th:each="event : ${request.events}">
+                    <div class="timeline-icon" th:classappend="${event}"></div>
+                    <div class="timeline-item">
+                        <div class="timeline-description" th:text="${event.description}">
+                            Request Created
+                        </div>
+                        <div class="timeline-time">
+                            <span th:text="${#temporals.format(event.timestamp, 'MMM dd, yyyy ''at'' HH:mm')}">March 27, 2016 at 12:34</span>
+                        </div>
+                    </div>
+                </li>
+            </ul>
+            <div id="image-holder"></div>
+        </div>
+
+        <div class="col-sm-8">
+            <div class="card text-white" th:classappend="${request.isApproved()} ? 'bg-success' : 'bg-primary '">
+                <h4 class="card-header">Info</h4>
+
+                <div class="card-body">
+                <dl class="dl-horizontal request">
+                    <th:block >
+                        <dt>Request by</dt>
+                        <dd th:text="${request.requestEntity}"></dd>
+                    </th:block>
+
+                    <th:block th:if="${request.requestEntity.isGroup()}">
+                        <dt>Members</dt>
+                        <dd th:text="${request.requestEntity.membersToString()}"></dd>
+                    </th:block>
+
+                    <dt>Course</dt>
+                    <dd th:text="${request.lab.course}"></dd>
+
+                    <dt>Lab</dt>
+                    <dd th:text="${request.lab}"></dd>
+
+                    <dt>Assignment</dt>
+                    <dd th:text="${request.assignment}"></dd>
+
+                    <dt>Room</dt>
+                    <dd th:text="${request.room}"></dd>
+
+                    <dt>Type</dt>
+                    <dd th:text="${request.requestType.name}"></dd>
+
+                    <th:block th:unless="${#strings.isEmpty(request.comment)}">
+                        <dt>Comment</dt>
+                        <dd th:text="${request.comment}"></dd>
+                    </th:block>
+
+                    <th:block th:if="${request.assistant != null}">
+                        <dt>Assistant</dt>
+                        <dd th:text="${request.assistant}"></dd>
+                    </th:block>
+
+                    <dt>Status</dt>
+                    <dd th:include="request/status :: status(${request.status})"></dd>
+
+                    <th:block th:if="${@permissionService.canViewRequestReason(#authentication.principal, request.id)}">
+                        <th:block th:unless="${#strings.isEmpty(request.reason)}">
+                            <dt>Reason (TA only)</dt>
+                            <dd th:utext="${@markDownParser.parseToHtml(request.reason)}"></dd>
+                        </th:block>
+                    </th:block>
+
+                    <th:block th:unless="${#strings.isEmpty(request.reasonForStudent)}">
+                        <dt>Reason</dt>
+                        <dd th:text="${request.reasonForStudent}"></dd>
+                    </th:block>
+                </dl>
+                </div>
+                <div class="card-footer" th:if="${@permissionService.canFinishRequest(#authentication.principal, request.id)}">
+                    <div class="btn-group d-flex" role="group">
+                        <a href="#" th:href="@{/request/{id}/approve(id=${request.id})}" class="w-100 btn btn-success btn-sm">Approve</a>
+                        <a href="#" th:href="@{/request/{id}/reject(id=${request.id})}" class="w-100 btn btn-danger btn-sm">Reject</a>
+                        <a href="#" th:href="@{/request/{id}/forward(id=${request.id})}" class="w-100 btn btn-secondary btn-sm">Forward</a>
+                        <a href="#" th:href="@{/request/{id}/notfound(id=${request.id})}" class="w-100 btn btn-warning btn-sm">Not found</a>
+                    </div>
+                </div>
+            </div>
+
+            <div class="page-header">
+                <h4>Previous</h4>
+            </div>
+
+            <p th:if="${#lists.isEmpty(request.previous)}">
+                There are no previous requests by <strong th:text="${request.requestEntity}">student</strong> for <strong th:text="${request.assignment}">Assembly programming</strong>.
+            </p>
+
+            <div class="list-group mb-3" th:unless="${#lists.isEmpty(request.previous)}">
+                <a href="#" class="list-group-item list-group-item-action"  th:each="request : ${request.previous}" th:href="@{/request/{id}(id=${request.id})}">
+                    <th:block th:if="${request.revoked}">
+                        Request <strong>#[[${request.id}]]</strong> was <strong>revoked</strong>.
+                    </th:block>
+
+                    <th:block th:unless="${request.revoked}">
+                    	<th:block th:if="${@permissionService.canViewRequestReason(#authentication.principal, request.id)} and ${!request.reason.equals('')}">
+                        	Request <strong>#[[${request.id}]]</strong> was <strong>[[${request.status}]]</strong> by <strong>[[${request.assistant}]]</strong> because &ldquo;[[${request.reason}]]&rdquo;
+                        </th:block>
+                        <th:block th:unless="${@permissionService.canViewRequestReason(#authentication.principal, request.id)} and ${!request.reason.equals('')}">
+                        	Request <strong>#[[${request.id}]]</strong> was <strong>[[${request.status}]]</strong> by <strong>[[${request.assistant}]]</strong>.
+                       	</th:block>
+                    </th:block>
+
+                    <p class="time" th:text="${#prettyTime.diffForHumans(request.handledAt)}" th:if="request.handledAt">5 minutes ago</p>
+                </a>
+            </div>
+        </div>
+    </div>
+    <script src="/js/map_loader.js"></script>
+    <script type="text/javascript" th:inline="javascript">
+        var roomId = /*[[${request.room.id}]]*/ 0;
+        updateRoomInfo(roomId);
+    </script>
+</section>
+</body>
+</html>
diff --git a/backend/src/main/resources/templates/saml/idpselection.html b/backend/src/main/resources/templates/saml/idpselection.html
new file mode 100644
index 000000000..a4839376d
--- /dev/null
+++ b/backend/src/main/resources/templates/saml/idpselection.html
@@ -0,0 +1,38 @@
+<!--
+
+    Queue - A Queueing system that can be used to handle labs in higher education
+    Copyright (C) 2016-2019  Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
+      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
+<head>
+    <title>Select your IdP!</title>
+</head>
+<body>
+<h1>Select your IdP:</h1>
+<form th:action="${idpDiscoReturnURL}" method="get">
+    <div th:each="idp : ${idps}">
+        <input type="radio" th:name="${idpDiscoReturnParam}" th:id="'idp_' + ${idp}" th:value="${idp}" />
+        <label th:for="'idp_' + ${idp}" ><span th:text="${idp}">null</span></label>
+    </div>
+    <p>
+        <input type="submit" value="Login" />
+    </p>
+</form>
+</body>
+</html>
diff --git a/backend/src/test/java/nl/tudelft/ewi/QueueApplicationTests.java b/backend/src/test/java/nl/tudelft/ewi/QueueApplicationTests.java
new file mode 100644
index 000000000..87f95890c
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/QueueApplicationTests.java
@@ -0,0 +1,57 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi;
+
+import nl.tudelft.ewi.queue.QueueApplication;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+//import com.github.fppt.jedismock.RedisServer;
+//import redis.clients.jedis.Jedis;
+import java.io.IOException;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = QueueApplication.class)
+@DataJpaTest
+@WebAppConfiguration
+public class QueueApplicationTests {
+//	private static RedisServer server = null;
+
+	@PostConstruct
+	public void before() throws IOException {
+//		server = RedisServer.newRedisServer(6379);  // bind to default port
+//		server.start();
+	}
+
+	@Test
+	public void contextLoads() {
+//		Jedis jedis = new Jedis(server.getHost(), server.getBindPort());
+	}
+
+	@PreDestroy
+	public void after() {
+//		server.stop();
+//		server = null;
+	}
+}
+
diff --git a/backend/src/test/java/nl/tudelft/ewi/queue/controller/CourseControllerTest.java b/backend/src/test/java/nl/tudelft/ewi/queue/controller/CourseControllerTest.java
new file mode 100644
index 000000000..16bb05fa3
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/queue/controller/CourseControllerTest.java
@@ -0,0 +1,111 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import nl.tudelft.ewi.queue.QueueApplication;
+import nl.tudelft.ewi.queue.model.Course;
+import nl.tudelft.ewi.queue.model.User;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithUserDetails;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+import javax.transaction.Transactional;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = QueueApplication.class)
+@WebAppConfiguration
+@AutoConfigureMockMvc
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+public class CourseControllerTest {
+
+    @Autowired
+    private RequestController controller;
+
+    @Autowired
+    RequestRepository requestRepository;
+
+    @Autowired
+    CourseRepository courseRepository;
+
+    @Autowired
+    UserRepository userRepository;
+
+    @Autowired
+    ObjectMapper objectMapper;
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+    }
+
+    @Test
+    @WithUserDetails("admin@tudelft.nl")
+    public void testCoursesPage() throws Exception {
+        this.mockMvc
+                .perform(get("/courses"))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("courses"));
+    }
+
+    @Test
+    @Transactional
+    @WithUserDetails("teacher3@tudelft.nl")
+    public void testViewCourseSomeoneElse() throws Exception {
+        Course course  = courseRepository.findAll().get(0);
+        User user = userRepository.findByUsername("teacher3");
+        Assert.assertFalse(course.getTeachers().contains(user));
+
+        MvcResult result = this.mockMvc
+                .perform(get("/course/" + course.getId()))
+                .andExpect(status().isOk()).andReturn();
+        String pageContents = result.getResponse().getContentAsString();
+        Assert.assertTrue(pageContents.contains("Access Denied"));
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testViewParticipants() throws Exception {
+        Course course  = courseRepository.findAll().get(0);
+        this.mockMvc
+                .perform(get("/course/" + course.getId() + "/participants"))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("staff"))
+                .andExpect(model().attributeExists("students"));
+    }
+
+}
diff --git a/backend/src/test/java/nl/tudelft/ewi/queue/controller/RequestControllerTest.java b/backend/src/test/java/nl/tudelft/ewi/queue/controller/RequestControllerTest.java
new file mode 100644
index 000000000..89af2d1ef
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/queue/controller/RequestControllerTest.java
@@ -0,0 +1,257 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import nl.tudelft.ewi.queue.QueueApplication;
+import nl.tudelft.ewi.queue.model.*;
+import nl.tudelft.ewi.queue.repository.AssignmentRepository;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithUserDetails;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+import javax.transaction.Transactional;
+import java.time.LocalDateTime;
+
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = QueueApplication.class)
+@WebAppConfiguration
+@AutoConfigureMockMvc
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+public class RequestControllerTest {
+
+    @Autowired
+    private RequestController controller;
+
+    @Autowired
+    AssignmentRepository assignmentRepository;
+
+    @Autowired
+    RequestRepository requestRepository;
+
+    @Autowired
+    CourseRepository courseRepository;
+
+    @Autowired
+    UserRepository userRepository;
+
+    @Autowired
+    ObjectMapper objectMapper;
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    private Request request;
+
+    @Before
+    public void setUp() {
+        Request templateRequest = requestRepository.findAll().iterator().next();
+
+        request = new Request(templateRequest.getRequestEntity(), templateRequest.getAssignment(), templateRequest.getRoom(), templateRequest.getRequestType(), "", templateRequest.getLab());
+        requestRepository.save(request);
+    }
+
+    @Test
+    public void contextLoads() {
+        assert controller != null;
+    }
+
+    @Test
+    @WithUserDetails("admin@tudelft.nl")
+    public void testRequestList() throws Exception {
+        this.mockMvc
+                .perform(get("/requests"))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("requests"));
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testViewRequest() throws Exception {
+        this.mockMvc
+                .perform(get("/request/" + request.getId()))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("request"));
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testApproveARequestGet() throws Exception {
+        this.mockMvc
+                .perform(get("/request/" + request.getId() + "/approve"))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("request"));
+    }
+
+    @Test
+    @Transactional
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testApproveARequestPost() throws Exception {
+        this.mockMvc
+                .perform(
+                        post("/request/" + request.getId() + "?approve=")
+                                .with(csrf())
+                                .param("comment", "Well done")
+                )
+                .andExpect(status().is3xxRedirection())
+                .andExpect(flash().attribute("message", "Request approved."));
+    }
+
+    @Test
+    @Transactional
+    @WithUserDetails("student1@tudelft.nl")
+    public void testStudentCannotApproveOwnRequest() throws Exception {
+        MvcResult result = mockMvc
+                .perform(
+                        post("/request/" + request.getId() + "?approve=")
+                                .with(csrf())
+                                .param("comment", "Well done")
+                )
+                .andExpect(status().is2xxSuccessful()).andReturn();
+        String pageContents = result.getResponse().getContentAsString();
+        Assert.assertTrue(pageContents.contains("Access Denied"));
+        Assert.assertFalse(request.isApproved());
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testRejectARequestGet() throws Exception {
+        this.mockMvc
+                .perform(get("/request/" + request.getId() + "/reject"))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("request")).andReturn();
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testRejectARequestPost() throws Exception {
+        User assistant = userRepository.findByUsername("assistant1@tudelft.nl");
+        RequestProcessingEvent processing = new RequestProcessingEvent(request, LocalDateTime.now());
+        RequestAssignedEvent assigned = new RequestAssignedEvent(request, LocalDateTime.now(), assistant);
+        request.addEvent(processing);
+        request.addEvent(assigned);
+        requestRepository.save(request);
+
+        mockMvc
+                .perform(
+                        post("/request/" + request.getId() + "?reject=")
+                                .with(csrf())
+                                .param("comment", "FAIL FAIL FAIl")
+                                .param("commentForStudent", "FAIL FAIL FAIl")
+                )
+                .andExpect(status().is3xxRedirection()).andReturn();
+        request = requestRepository.findOne(request.getId());
+        Assert.assertTrue(request.isRejected());
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testForwardRequestGet() throws Exception {
+        this.mockMvc
+                .perform(get("/request/" + request.getId() + "/forward"))
+                .andExpect(status().isOk())
+                .andExpect(model().attributeExists("request")).andReturn();
+    }
+
+    @Test
+    @Transactional
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testForwardRequestPost() throws Exception {
+        User assistant = userRepository.findByUsername("assistant2@tudelft.nl");
+        User assistant2 = userRepository.findByUsername("assistant3@tudelft.nl");
+        request.setAssistant(assistant);
+        requestRepository.save(request);
+
+        mockMvc
+                .perform(
+                        post("/request/" + request.getId() + "?forward=")
+                                .with(csrf())
+                                .param("assistant", assistant2.getId().toString())
+                                .param("comment", "What is this, I don't even")
+                )
+                .andExpect(status().is3xxRedirection())
+        .andExpect(flash().attribute("message", "Request forwarded."));
+        request = requestRepository.findOne(request.getId());
+        Assert.assertTrue(request.isAssigned());
+    }
+
+    @Test
+    @WithUserDetails("teacher1@tudelft.nl")
+    public void testExportListRequests() throws Exception {
+        Course course = courseRepository.findAll().iterator().next();
+        MvcResult result = mockMvc
+                .perform(get("/course/" + course.getId() + "/requests/export"))
+                .andExpect(status().isOk())
+                .andReturn();
+        String resultJson = result.getResponse().getContentAsString();
+        Assert.assertTrue(resultJson.contains("student1"));
+        Assert.assertTrue(resultJson.contains("Submission"));
+    }
+
+    @Test
+    @WithUserDetails("student1@tudelft.nl")
+    public void testPostFeedbackOnRequest() throws Exception {
+        mockMvc
+                .perform(
+                        post("/request/" + request.getId() + "/feedback")
+                                .with(csrf())
+                                .param("feedback", "This TA did not even talk to me!")
+                )
+                .andExpect(status().is3xxRedirection())
+                .andExpect(flash().attribute("message", "Feedback successfully saved."))
+                .andReturn();
+        request = requestRepository.findOne(request.getId());
+        Assert.assertNotNull(request.getFeedback());
+    }
+
+    @Test
+    @WithUserDetails("student2@tudelft.nl")
+    public void testCannotPostFeedbackOnNotOwnRequest() throws Exception {
+        request.setFeedback(null);
+        requestRepository.save(request);
+        MvcResult result = mockMvc
+                .perform(
+                        post("/request/" + request.getId() + "/feedback")
+                                .with(csrf())
+                                .param("feedback", "This TA did not even talk to me!")
+                )
+                .andExpect(status().is2xxSuccessful())
+                .andReturn();
+        request = requestRepository.findOne(request.getId());
+        Assert.assertTrue(result.getResponse().getContentAsString().contains("Oops. Something went wrong..."));
+        Assert.assertNull(request.getFeedback());
+    }
+}
diff --git a/backend/src/test/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactoryTest.java b/backend/src/test/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactoryTest.java
new file mode 100644
index 000000000..4abfbbb52
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/queue/dialect/FilterExpressionObjectFactoryTest.java
@@ -0,0 +1,113 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.dialect;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class FilterExpressionObjectFactoryTest {
+    private FilterExpressionObjectFactory factory = new FilterExpressionObjectFactory();
+    private FilterExpressionObjectFactory.Filter filter =
+            (FilterExpressionObjectFactory.Filter) factory.buildObject(null, "filter");
+
+    private MultiValueMap<String, String> emptyState = new LinkedMultiValueMap<String, String>() {{
+    }};
+
+    private MultiValueMap<String, String> page1State = new LinkedMultiValueMap<String, String>() {{
+        add("page", "1");
+    }};
+
+    private MultiValueMap<String, String> page2AssignmentState = new LinkedMultiValueMap<String, String>() {{
+        add("page", "2");
+        add("assignment", "1");
+    }};
+
+    private MultiValueMap<String, String> dupeState = new LinkedMultiValueMap<String, String>() {{
+        add("room", "1");
+        add("room", "2");
+    }};
+
+    private String getPath(String uri) {
+        return URI.create(uri).getPath();
+    }
+
+    private Collection<String> getQueryParams(String uri) {
+        return Arrays.asList(URI.create(uri).getQuery().split("&"));
+    }
+
+    private Collection<String> listOf(String... args) {
+        return Arrays.asList(args);
+    }
+
+    @Test
+    public void buildingUrlShouldContainNoQueryParams() {
+        Assert.assertEquals(
+                filter.buildUrlMultiValueMap(emptyState),
+                ""
+        );
+    }
+
+    @Test
+    public void buildingUrlShouldContainSingleQueryParam() {
+        Assert.assertEquals(
+                filter.buildUrlMultiValueMap(page1State),
+                "?page=1"
+        );
+    }
+
+    @Test
+    public void buildingUrlShouldContainDifferentQueryParams() {
+        String url = filter.buildUrlMultiValueMap(page2AssignmentState);
+        Assert.assertTrue(CollectionUtils.isEqualCollection(
+                getQueryParams(url),
+                listOf("page=2", "assignment=1")
+        ));
+    }
+
+    @Test
+    public void buildingUrlShouldContainDuplicateQueryParams() {
+        String url = filter.buildUrlMultiValueMap(dupeState);
+        Assert.assertTrue(CollectionUtils.isEqualCollection(
+                getQueryParams(url),
+                listOf("room=1", "room=2")
+        ));
+    }
+
+    @Test
+    public void buildingUrlShouldAcceptPaths() {
+        Assert.assertEquals(
+                filter.buildUrlMultiValueMap(emptyState, "some/path"),
+                "some/path"
+        );
+    }
+
+    @Test
+    public void updateUrlShouldChangeExisting() {
+        Assert.assertEquals(
+                filter.updateUrlMultiValueMap(page1State, "page", "2"),
+                "?page=2"
+        );
+    }
+}
diff --git a/backend/src/test/java/nl/tudelft/ewi/queue/model/GroupTest.java b/backend/src/test/java/nl/tudelft/ewi/queue/model/GroupTest.java
new file mode 100644
index 000000000..c0d116e72
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/queue/model/GroupTest.java
@@ -0,0 +1,116 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import nl.tudelft.ewi.queue.QueueApplication;
+import nl.tudelft.ewi.queue.repository.CourseRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = QueueApplication.class)
+@WebAppConfiguration
+@AutoConfigureMockMvc
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+public class GroupTest {
+
+    Group group;
+
+    User student;
+
+    @Autowired
+    CourseRepository courseRepository;
+
+    @Autowired
+    UserRepository userRepository;
+
+    @Before
+    public void setUp() {
+        Course course = courseRepository.findOne(0L);
+        group = new Group("Test Group", course);
+        student = userRepository.findAll().get(0);
+        group.addMember(student);
+    }
+
+    @Test
+    public void testGetMembers() {
+        List<User> members = group.getMembers();
+        Assert.assertEquals(members.size(), 1);
+        User student2 = userRepository.findAll().get(1);
+        group.addMember(student2);
+        Assert.assertEquals(members.size(), 2);
+    }
+
+    @Test
+    public void testSetMembers() {
+        group.setMembers(new ArrayList<>());
+        Assert.assertEquals(0, group.getMembers().size());
+    }
+
+    @Test
+    public void testAddMember() {
+        User student2 = userRepository.findAll().get(1);
+        group.addMember(student2);
+        Assert.assertEquals(2, group.getMembers().size());
+    }
+
+    @Test
+    public void testAddMemberAlreadyAdded() {
+        User student2 = userRepository.findAll().get(1);
+        group.addMember(student2);
+        group.addMember(student2);
+        Assert.assertEquals(2, group.getMembers().size());
+    }
+
+    @Test
+    public void testMembersToString() {
+        Assert.assertNotNull(group.membersToString());
+        Assert.assertTrue(group.membersToString().contains("student"));
+    }
+
+    @Test
+    public void testHasMember() {
+        Assert.assertTrue(group.hasMember(student));
+    }
+
+    @Test
+    public void testGetCourse() {
+        Assert.assertEquals(courseRepository.findOne(0L), group.getCourse());
+    }
+
+    @Test
+    public void testSetCourse() {
+        Course course = courseRepository.findAll().get(1);
+        group.setCourse(course);
+        assertEquals(group.getCourse(), course);
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/nl/tudelft/ewi/queue/model/LabTest.java b/backend/src/test/java/nl/tudelft/ewi/queue/model/LabTest.java
new file mode 100644
index 000000000..3ce0738e6
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/queue/model/LabTest.java
@@ -0,0 +1,252 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import nl.tudelft.ewi.queue.QueueApplication;
+import nl.tudelft.ewi.queue.exception.AlreadyEnqueuedException;
+import nl.tudelft.ewi.queue.repository.LabRepository;
+import nl.tudelft.ewi.queue.repository.RequestRepository;
+import nl.tudelft.ewi.queue.repository.RequestTypeRepository;
+import nl.tudelft.ewi.queue.repository.UserRepository;
+import nl.tudelft.ewi.queue.service.LabService;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import javax.transaction.Transactional;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = QueueApplication.class)
+@WebAppConfiguration
+@AutoConfigureMockMvc
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+public class LabTest {
+
+	@Autowired
+	private RequestTypeRepository requestTypeRepository;
+
+	@Autowired
+	private RequestRepository requestRepository;
+
+	@Autowired
+	private LabRepository labRepository;
+
+	@Autowired
+	private UserRepository userRepository;
+
+	@Autowired
+	private LabService labService;
+
+	private RequestType questionRequestType;
+
+	private RequestType submissionRequestType;
+
+	private Lab lab;
+
+	private User user;
+
+
+	@Before
+	@Transactional
+	public void setUp() {
+		questionRequestType = new RequestType("Question");
+		submissionRequestType = new RequestType("Submission");
+		requestTypeRepository.save(questionRequestType);
+		requestTypeRepository.save(submissionRequestType);
+		lab = labRepository.findFirstByOrderByIdAsc();
+		user = userRepository.findByUsername("student1@tudelft.nl");
+	}
+
+	@Test
+	@Transactional
+	public void testIsEnqueuedSingletonQueue() {
+		lab.addRequest(new Request(user, null, new Room("DW 101"), questionRequestType , "", lab));
+		Assert.assertTrue(lab.isEnqueued(user));
+	}
+
+	@Test
+	@Transactional
+	public void testIsEnqueuedEmptyQueue() {
+		// student chosen by random dice roll (also: above 25)
+		User randomUser = userRepository.findByUsername("student42@tudelft.nl");
+		Assert.assertFalse(lab.isEnqueued(randomUser));
+	}
+
+	@Test
+	@Transactional
+	public void testPositionSingletonQueue() {
+		lab.getQueue().add(new Request(user, null, new Room("DW 101"), submissionRequestType, "", lab));
+		Assert.assertEquals("1", lab.position(user));
+	}
+
+	@Test(expected=AlreadyEnqueuedException.class)
+	@Transactional
+	public void testMultipleEnqueue() {
+		Room room = new Room();
+
+		Course course = new Course();
+
+		Role role = new Student(user, course, LocalDateTime.now());
+		course.addRole(role);
+		user.addRole(role);
+
+		Lab lab = new Lab(course, new LabSlot(LocalDateTime.now().minus(1,
+				ChronoUnit.DAYS), LocalDateTime.now().plus(1,
+				ChronoUnit.DAYS)), Collections.emptyList(), false);
+		course.addLab(lab);
+
+		Assignment assignment = new Assignment(course, "");
+		lab.addAssignment(assignment);
+
+		// Act
+		Request request = new Request(user, assignment, room, submissionRequestType, "", lab);
+		lab.addRequest(request);
+
+		// Assert
+		labService.enqueue(request);
+	}
+
+	@Test
+	@Transactional
+	public void testEnqueueWithoutEnroll() {
+		User randomUser = userRepository.findByUsername("student43@tudelft.nl"); //again... random + 1
+		Assert.assertFalse(lab.isEnqueued(randomUser));
+		Assert.assertFalse(lab.getCourse().isEnrolled(randomUser));
+		Request request = new Request(user, null, new Room("DW 101"), submissionRequestType, "", lab);
+		lab.addRequest(request);
+		Assert.assertEquals("1", lab.position(user));
+	}
+
+	@Test
+	@Transactional
+	public void testGetQueue() {
+		List<Request> queue = lab.getQueue();
+		Assert.assertNotNull(queue);
+		Assert.assertEquals(25, queue.size()); // adapted to state in DatabaseLoader
+	}
+
+	@Test
+	@Transactional
+	public void testGetPending() {
+		List<Request> pending = lab.getPending();
+		Assert.assertNotNull(pending);
+		Assert.assertEquals(25, pending.size());	// adapted to state in DatabaseLoader
+		Assert.assertEquals(pending.get(0).getRequestEntity().getDisplayName(), "student1");
+	}
+
+	@Test
+	@Transactional
+	public void testGetProcessing() {
+		List<Request> processing = lab.getProcessing();
+		Assert.assertNotNull(processing);
+		Assert.assertEquals(0, processing.size());
+		Request request = lab.getQueue().get(0);
+		RequestEvent processingEvent = new RequestProcessingEvent(request, LocalDateTime.now());
+		request.addEvent(processingEvent);
+		requestRepository.save(request);
+		processing = lab.getProcessing();
+		Assert.assertEquals(1, processing.size());
+	}
+
+	@Test
+	@Transactional
+	public void testGetArchived() {
+		List<Request> archived = lab.getArchived();
+		Assert.assertNotNull(archived);
+		Assert.assertEquals(0, archived.size());
+		Request request = lab.getQueue().get(0);
+		User assistant = userRepository.findByUsername("student5@tudelft.nl");
+		RequestEvent handledEvent = new RequestApprovedEvent(request, assistant, LocalDateTime.now(), "Top!");
+		request.addEvent(handledEvent);
+		requestRepository.save(request);
+		archived = lab.getArchived();
+		Assert.assertEquals(1, archived.size());
+	}
+
+	@Test
+	@Transactional
+	public void testGetHandled() {
+		List<Request> handled = lab.getHandled();
+		Assert.assertNotNull(handled);
+		Assert.assertEquals(0, handled.size());
+		Request request = lab.getQueue().get(0);
+		User assistant = userRepository.findByUsername("student5@tudelft.nl");
+		RequestEvent approvedRequest = new RequestApprovedEvent(request, assistant, LocalDateTime.now(), "Top!");
+		request.addEvent(approvedRequest);
+		requestRepository.save(request);
+		handled = lab.getHandled();
+		Assert.assertEquals(1, handled.size());
+	}
+
+	@Test
+	@Transactional
+	public void testGetApproved() {
+		List<Request> approved = lab.getApproved();
+		Assert.assertNotNull(approved);
+		Assert.assertEquals(0, approved.size());
+
+		Request request = lab.getQueue().get(0);
+		User assistant = userRepository.findByUsername("student5@tudelft.nl");
+		RequestEvent approvedEvent = new RequestApprovedEvent(request, assistant, LocalDateTime.now(), "Top!");
+		request.addEvent(approvedEvent);
+		requestRepository.save(request);
+
+		approved = lab.getApproved();
+		Assert.assertEquals(1, approved.size());
+	}
+
+	@Test
+	@Transactional
+	public void testGetRejected() {
+		List<Request> rejected = lab.getRejected();
+		Assert.assertNotNull(rejected);
+		Assert.assertEquals(0, rejected.size());
+
+		User assistant = userRepository.findByUsername("student5@tudelft.nl");
+
+		Request rejectedRequest = lab.getQueue().get(0);
+		requestRepository.save(rejectedRequest);
+		RequestEvent rejectedEvent = new RequestRejectedEvent(rejectedRequest, assistant, LocalDateTime.now(), "Fail!", "Fail!");
+		rejectedRequest.addEvent(rejectedEvent);
+		requestRepository.save(rejectedRequest);
+
+		rejected = lab.getRejected();
+		Assert.assertEquals(1, rejected.size());
+	}
+
+	@Test
+	@Transactional
+	public void testGetPendingRequestForUser() {
+		Optional<Request> pendingForUser = lab.getPendingRequest(user);
+		Assert.assertTrue(pendingForUser.isPresent());
+		Assert.assertEquals(pendingForUser.get().getRequestEntity(), user);
+	}
+
+}
diff --git a/backend/src/test/java/nl/tudelft/ewi/queue/model/RequestTest.java b/backend/src/test/java/nl/tudelft/ewi/queue/model/RequestTest.java
new file mode 100644
index 000000000..a6bdc7c4a
--- /dev/null
+++ b/backend/src/test/java/nl/tudelft/ewi/queue/model/RequestTest.java
@@ -0,0 +1,83 @@
+/**
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2019  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.ewi.queue.model;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.time.LocalDateTime;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class RequestTest {
+
+    private Request request;
+
+    @Before
+    public void init() {
+        RequestEntity requestEntity = Mockito.mock(RequestEntity.class);
+        Assignment assignment = Mockito.mock(Assignment.class);
+        Room room = Mockito.mock(Room.class);
+        RequestType requestType = Mockito.mock(RequestType.class);
+        Lab lab = Mockito.mock(Lab.class);
+        request = new Request(requestEntity, assignment, room, requestType, "", lab);
+    }
+
+    @Test
+    public void calculateWaitingTime() {
+        request.setStatus(Request.Status.APPROVED);
+        request.setCreatedAt(LocalDateTime.now().minusMinutes(1));
+        request.setProcessingAt(LocalDateTime.now());
+
+        Assert.assertEquals(1, request.waitingTime());
+    }
+
+    @Test
+    public void requestWithNoWaitingTime() {
+        request.setStatus(Request.Status.APPROVED);
+        request.setCreatedAt(LocalDateTime.now().minusMinutes(1));
+        request.setApprovedAt(LocalDateTime.now());
+
+        Assert.assertEquals(1, request.waitingTime());
+    }
+
+    @Test
+    public void calculateProcessingTime() {
+        request.setStatus(Request.Status.APPROVED);
+        request.setCreatedAt(LocalDateTime.now().minusMinutes(2));
+        request.setProcessingAt(LocalDateTime.now().minusSeconds(1));
+        request.setApprovedAt(LocalDateTime.now());
+
+        Assert.assertEquals(1, request.waitingTime());
+    }
+
+    @Test
+    public void requestWithNoProcessingTime() {
+        request.setStatus(Request.Status.APPROVED);
+        request.setCreatedAt(LocalDateTime.now().minusMinutes(1));
+        request.setApprovedAt(LocalDateTime.now());
+
+        Assert.assertEquals(1, request.processingTime());
+    }
+
+
+}
diff --git a/frontend/frontend/.gitignore b/frontend/frontend/.gitignore
new file mode 100644
index 000000000..4d29575de
--- /dev/null
+++ b/frontend/frontend/.gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/frontend/frontend/README.md b/frontend/frontend/README.md
new file mode 100644
index 000000000..897dc8366
--- /dev/null
+++ b/frontend/frontend/README.md
@@ -0,0 +1,44 @@
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.<br>
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
+
+The page will reload if you make edits.<br>
+You will also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.<br>
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.<br>
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.<br>
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
+
+If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
+
+You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
diff --git a/frontend/frontend/package.json b/frontend/frontend/package.json
new file mode 100644
index 000000000..0cd84b132
--- /dev/null
+++ b/frontend/frontend/package.json
@@ -0,0 +1,36 @@
+{
+  "name": "frontend",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@types/jest": "24.0.18",
+    "@types/node": "12.7.2",
+    "@types/react": "16.9.2",
+    "@types/react-dom": "16.9.0",
+    "react": "^16.9.0",
+    "react-dom": "^16.9.0",
+    "react-scripts": "3.1.1",
+    "typescript": "3.5.3"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "eject": "react-scripts eject"
+  },
+  "eslintConfig": {
+    "extends": "react-app"
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  }
+}
diff --git a/frontend/frontend/public/favicon.ico b/frontend/frontend/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a
GIT binary patch
literal 3870
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
ZtMWJ#1p_C<F-T5Hu2|;y@Dg%;1puB)gNgtE

literal 0
HcmV?d00001

diff --git a/frontend/frontend/public/index.html b/frontend/frontend/public/index.html
new file mode 100644
index 000000000..a146b6fd7
--- /dev/null
+++ b/frontend/frontend/public/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta
+      name="description"
+      content="Web site created using create-react-app"
+    />
+    <link rel="apple-touch-icon" href="logo192.png" />
+    <!--
+      manifest.json provides metadata used when your web app is installed on a
+      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <title>React App</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>
diff --git a/frontend/frontend/public/logo192.png b/frontend/frontend/public/logo192.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9
GIT binary patch
literal 5347
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

literal 0
HcmV?d00001

diff --git a/frontend/frontend/public/logo512.png b/frontend/frontend/public/logo512.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4e47a6545bc15971f8f63fba70e4013df88a664
GIT binary patch
literal 9664
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
Q-2sVvy85}Sb4q9e0RM_kJ^%m!

literal 0
HcmV?d00001

diff --git a/frontend/frontend/public/manifest.json b/frontend/frontend/public/manifest.json
new file mode 100644
index 000000000..d1c1d1de9
--- /dev/null
+++ b/frontend/frontend/public/manifest.json
@@ -0,0 +1,25 @@
+{
+  "short_name": "React App",
+  "name": "Create React App Sample",
+  "icons": [
+    {
+      "src": "favicon.ico",
+      "sizes": "64x64 32x32 24x24 16x16",
+      "type": "image/x-icon"
+    },
+    {
+      "src": "logo192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    },
+    {
+      "src": "logo512.png",
+      "type": "image/png",
+      "sizes": "512x512"
+    }	 
+  ],
+  "start_url": ".",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}
diff --git a/frontend/frontend/public/robots.txt b/frontend/frontend/public/robots.txt
new file mode 100644
index 000000000..01b0f9a10
--- /dev/null
+++ b/frontend/frontend/public/robots.txt
@@ -0,0 +1,2 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
diff --git a/frontend/frontend/src/App.css b/frontend/frontend/src/App.css
new file mode 100644
index 000000000..b41d297ca
--- /dev/null
+++ b/frontend/frontend/src/App.css
@@ -0,0 +1,33 @@
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  animation: App-logo-spin infinite 20s linear;
+  height: 40vmin;
+  pointer-events: none;
+}
+
+.App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
diff --git a/frontend/frontend/src/App.test.tsx b/frontend/frontend/src/App.test.tsx
new file mode 100644
index 000000000..a754b201b
--- /dev/null
+++ b/frontend/frontend/src/App.test.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+it('renders without crashing', () => {
+  const div = document.createElement('div');
+  ReactDOM.render(<App />, div);
+  ReactDOM.unmountComponentAtNode(div);
+});
diff --git a/frontend/frontend/src/App.tsx b/frontend/frontend/src/App.tsx
new file mode 100644
index 000000000..efdd6c7f0
--- /dev/null
+++ b/frontend/frontend/src/App.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import logo from './logo.svg';
+import './App.css';
+
+const App: React.FC = () => {
+  return (
+    <div className="App">
+      <header className="App-header">
+        <img src={logo} className="App-logo" alt="logo" />
+        <p>
+          Edit <code>src/App.tsx</code> and save to reload.
+          BLAAAAA
+        </p>
+        <a
+          className="App-link"
+          href="https://reactjs.org"
+          target="_blank"
+          rel="noopener noreferrer"
+        >
+          Learn React
+        </a>
+      </header>
+    </div>
+  );
+}
+
+export default App;
diff --git a/frontend/frontend/src/index.css b/frontend/frontend/src/index.css
new file mode 100644
index 000000000..ec2585e8c
--- /dev/null
+++ b/frontend/frontend/src/index.css
@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}
diff --git a/frontend/frontend/src/index.tsx b/frontend/frontend/src/index.tsx
new file mode 100644
index 000000000..87d1be551
--- /dev/null
+++ b/frontend/frontend/src/index.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './App';
+import * as serviceWorker from './serviceWorker';
+
+ReactDOM.render(<App />, document.getElementById('root'));
+
+// If you want your app to work offline and load faster, you can change
+// unregister() to register() below. Note this comes with some pitfalls.
+// Learn more about service workers: https://bit.ly/CRA-PWA
+serviceWorker.unregister();
diff --git a/frontend/frontend/src/logo.svg b/frontend/frontend/src/logo.svg
new file mode 100644
index 000000000..6b60c1042
--- /dev/null
+++ b/frontend/frontend/src/logo.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
+    <g fill="#61DAFB">
+        <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
+        <circle cx="420.9" cy="296.5" r="45.7"/>
+        <path d="M520.5 78.1z"/>
+    </g>
+</svg>
diff --git a/frontend/frontend/src/react-app-env.d.ts b/frontend/frontend/src/react-app-env.d.ts
new file mode 100644
index 000000000..6431bc5fc
--- /dev/null
+++ b/frontend/frontend/src/react-app-env.d.ts
@@ -0,0 +1 @@
+/// <reference types="react-scripts" />
diff --git a/frontend/frontend/src/serviceWorker.ts b/frontend/frontend/src/serviceWorker.ts
new file mode 100644
index 000000000..15d90cb81
--- /dev/null
+++ b/frontend/frontend/src/serviceWorker.ts
@@ -0,0 +1,143 @@
+// This optional code is used to register a service worker.
+// register() is not called by default.
+
+// This lets the app load faster on subsequent visits in production, and gives
+// it offline capabilities. However, it also means that developers (and users)
+// will only see deployed updates on subsequent visits to a page, after all the
+// existing tabs open on the page have been closed, since previously cached
+// resources are updated in the background.
+
+// To learn more about the benefits of this model and instructions on how to
+// opt-in, read https://bit.ly/CRA-PWA
+
+const isLocalhost = Boolean(
+  window.location.hostname === 'localhost' ||
+    // [::1] is the IPv6 localhost address.
+    window.location.hostname === '[::1]' ||
+    // 127.0.0.1/8 is considered localhost for IPv4.
+    window.location.hostname.match(
+      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
+    )
+);
+
+type Config = {
+  onSuccess?: (registration: ServiceWorkerRegistration) => void;
+  onUpdate?: (registration: ServiceWorkerRegistration) => void;
+};
+
+export function register(config?: Config) {
+  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+    // The URL constructor is available in all browsers that support SW.
+    const publicUrl = new URL(
+      (process as { env: { [key: string]: string } }).env.PUBLIC_URL,
+      window.location.href
+    );
+    if (publicUrl.origin !== window.location.origin) {
+      // Our service worker won't work if PUBLIC_URL is on a different origin
+      // from what our page is served on. This might happen if a CDN is used to
+      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
+      return;
+    }
+
+    window.addEventListener('load', () => {
+      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+
+      if (isLocalhost) {
+        // This is running on localhost. Let's check if a service worker still exists or not.
+        checkValidServiceWorker(swUrl, config);
+
+        // Add some additional logging to localhost, pointing developers to the
+        // service worker/PWA documentation.
+        navigator.serviceWorker.ready.then(() => {
+          console.log(
+            'This web app is being served cache-first by a service ' +
+              'worker. To learn more, visit https://bit.ly/CRA-PWA'
+          );
+        });
+      } else {
+        // Is not localhost. Just register service worker
+        registerValidSW(swUrl, config);
+      }
+    });
+  }
+}
+
+function registerValidSW(swUrl: string, config?: Config) {
+  navigator.serviceWorker
+    .register(swUrl)
+    .then(registration => {
+      registration.onupdatefound = () => {
+        const installingWorker = registration.installing;
+        if (installingWorker == null) {
+          return;
+        }
+        installingWorker.onstatechange = () => {
+          if (installingWorker.state === 'installed') {
+            if (navigator.serviceWorker.controller) {
+              // At this point, the updated precached content has been fetched,
+              // but the previous service worker will still serve the older
+              // content until all client tabs are closed.
+              console.log(
+                'New content is available and will be used when all ' +
+                  'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
+              );
+
+              // Execute callback
+              if (config && config.onUpdate) {
+                config.onUpdate(registration);
+              }
+            } else {
+              // At this point, everything has been precached.
+              // It's the perfect time to display a
+              // "Content is cached for offline use." message.
+              console.log('Content is cached for offline use.');
+
+              // Execute callback
+              if (config && config.onSuccess) {
+                config.onSuccess(registration);
+              }
+            }
+          }
+        };
+      };
+    })
+    .catch(error => {
+      console.error('Error during service worker registration:', error);
+    });
+}
+
+function checkValidServiceWorker(swUrl: string, config?: Config) {
+  // Check if the service worker can be found. If it can't reload the page.
+  fetch(swUrl)
+    .then(response => {
+      // Ensure service worker exists, and that we really are getting a JS file.
+      const contentType = response.headers.get('content-type');
+      if (
+        response.status === 404 ||
+        (contentType != null && contentType.indexOf('javascript') === -1)
+      ) {
+        // No service worker found. Probably a different app. Reload the page.
+        navigator.serviceWorker.ready.then(registration => {
+          registration.unregister().then(() => {
+            window.location.reload();
+          });
+        });
+      } else {
+        // Service worker found. Proceed as normal.
+        registerValidSW(swUrl, config);
+      }
+    })
+    .catch(() => {
+      console.log(
+        'No internet connection found. App is running in offline mode.'
+      );
+    });
+}
+
+export function unregister() {
+  if ('serviceWorker' in navigator) {
+    navigator.serviceWorker.ready.then(registration => {
+      registration.unregister();
+    });
+  }
+}
diff --git a/frontend/frontend/tsconfig.json b/frontend/frontend/tsconfig.json
new file mode 100644
index 000000000..f2850b716
--- /dev/null
+++ b/frontend/frontend/tsconfig.json
@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": [
+      "dom",
+      "dom.iterable",
+      "esnext"
+    ],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react"
+  },
+  "include": [
+    "src"
+  ]
+}
diff --git a/frontend/frontend/yarn.lock b/frontend/frontend/yarn.lock
new file mode 100644
index 000000000..52711eb26
--- /dev/null
+++ b/frontend/frontend/yarn.lock
@@ -0,0 +1,10050 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@7.5.5", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
+  integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
+  dependencies:
+    "@babel/highlight" "^7.0.0"
+
+"@babel/core@7.5.5", "@babel/core@^7.1.0", "@babel/core@^7.4.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30"
+  integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==
+  dependencies:
+    "@babel/code-frame" "^7.5.5"
+    "@babel/generator" "^7.5.5"
+    "@babel/helpers" "^7.5.5"
+    "@babel/parser" "^7.5.5"
+    "@babel/template" "^7.4.4"
+    "@babel/traverse" "^7.5.5"
+    "@babel/types" "^7.5.5"
+    convert-source-map "^1.1.0"
+    debug "^4.1.0"
+    json5 "^2.1.0"
+    lodash "^4.17.13"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.4.0", "@babel/generator@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf"
+  integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
+  dependencies:
+    "@babel/types" "^7.5.5"
+    jsesc "^2.5.1"
+    lodash "^4.17.13"
+    source-map "^0.5.0"
+    trim-right "^1.0.1"
+
+"@babel/helper-annotate-as-pure@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
+  integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0":
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f"
+  integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-builder-react-jsx@^7.3.0":
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz#a1ac95a5d2b3e88ae5e54846bf462eeb81b318a4"
+  integrity sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw==
+  dependencies:
+    "@babel/types" "^7.3.0"
+    esutils "^2.0.0"
+
+"@babel/helper-call-delegate@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43"
+  integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.4.4"
+    "@babel/traverse" "^7.4.4"
+    "@babel/types" "^7.4.4"
+
+"@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4"
+  integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg==
+  dependencies:
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-member-expression-to-functions" "^7.5.5"
+    "@babel/helper-optimise-call-expression" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.5.5"
+    "@babel/helper-split-export-declaration" "^7.4.4"
+
+"@babel/helper-define-map@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369"
+  integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg==
+  dependencies:
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/types" "^7.5.5"
+    lodash "^4.17.13"
+
+"@babel/helper-explode-assignable-expression@^7.1.0":
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6"
+  integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==
+  dependencies:
+    "@babel/traverse" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-function-name@^7.1.0":
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
+  integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.0.0"
+    "@babel/template" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-get-function-arity@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
+  integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-hoist-variables@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a"
+  integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==
+  dependencies:
+    "@babel/types" "^7.4.4"
+
+"@babel/helper-member-expression-to-functions@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590"
+  integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==
+  dependencies:
+    "@babel/types" "^7.5.5"
+
+"@babel/helper-module-imports@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d"
+  integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a"
+  integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-simple-access" "^7.1.0"
+    "@babel/helper-split-export-declaration" "^7.4.4"
+    "@babel/template" "^7.4.4"
+    "@babel/types" "^7.5.5"
+    lodash "^4.17.13"
+
+"@babel/helper-optimise-call-expression@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5"
+  integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-plugin-utils@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
+  integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
+
+"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351"
+  integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==
+  dependencies:
+    lodash "^4.17.13"
+
+"@babel/helper-remap-async-to-generator@^7.1.0":
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f"
+  integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.0.0"
+    "@babel/helper-wrap-function" "^7.1.0"
+    "@babel/template" "^7.1.0"
+    "@babel/traverse" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-replace-supers@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2"
+  integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.5.5"
+    "@babel/helper-optimise-call-expression" "^7.0.0"
+    "@babel/traverse" "^7.5.5"
+    "@babel/types" "^7.5.5"
+
+"@babel/helper-simple-access@^7.1.0":
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c"
+  integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==
+  dependencies:
+    "@babel/template" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@babel/helper-split-export-declaration@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
+  integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==
+  dependencies:
+    "@babel/types" "^7.4.4"
+
+"@babel/helper-wrap-function@^7.1.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa"
+  integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/template" "^7.1.0"
+    "@babel/traverse" "^7.1.0"
+    "@babel/types" "^7.2.0"
+
+"@babel/helpers@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e"
+  integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==
+  dependencies:
+    "@babel/template" "^7.4.4"
+    "@babel/traverse" "^7.5.5"
+    "@babel/types" "^7.5.5"
+
+"@babel/highlight@^7.0.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
+  integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
+  dependencies:
+    chalk "^2.0.0"
+    esutils "^2.0.2"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
+  integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
+
+"@babel/plugin-proposal-async-generator-functions@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e"
+  integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.1.0"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+
+"@babel/plugin-proposal-class-properties@7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4"
+  integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.5.5"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-proposal-decorators@7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0"
+  integrity sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.4.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-decorators" "^7.2.0"
+
+"@babel/plugin-proposal-dynamic-import@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506"
+  integrity sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+
+"@babel/plugin-proposal-json-strings@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317"
+  integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-json-strings" "^7.2.0"
+
+"@babel/plugin-proposal-object-rest-spread@7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58"
+  integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5"
+  integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78"
+  integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.5.4"
+
+"@babel/plugin-syntax-async-generators@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f"
+  integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-decorators@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b"
+  integrity sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-dynamic-import@7.2.0", "@babel/plugin-syntax-dynamic-import@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612"
+  integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-flow@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz#a765f061f803bc48f240c26f8747faf97c26bf7c"
+  integrity sha512-r6YMuZDWLtLlu0kqIim5o/3TNRAlWb073HwT3e2nKf9I8IIvOggPrnILYPsrrKilmn/mYEMCf/Z07w3yQJF6dg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-json-strings@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
+  integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-jsx@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7"
+  integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e"
+  integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c"
+  integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-typescript@^7.2.0":
+  version "7.3.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991"
+  integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-arrow-functions@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
+  integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-async-to-generator@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e"
+  integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.1.0"
+
+"@babel/plugin-transform-block-scoped-functions@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190"
+  integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-block-scoping@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce"
+  integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    lodash "^4.17.13"
+
+"@babel/plugin-transform-classes@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9"
+  integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.0.0"
+    "@babel/helper-define-map" "^7.5.5"
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-optimise-call-expression" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.5.5"
+    "@babel/helper-split-export-declaration" "^7.4.4"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da"
+  integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-destructuring@7.5.0", "@babel/plugin-transform-destructuring@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a"
+  integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-dotall-regex@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3"
+  integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.5.4"
+
+"@babel/plugin-transform-duplicate-keys@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853"
+  integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-exponentiation-operator@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008"
+  integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-flow-strip-types@7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.4.tgz#d267a081f49a8705fc9146de0768c6b58dccd8f7"
+  integrity sha512-WyVedfeEIILYEaWGAUWzVNyqG4sfsNooMhXWsu/YzOvVGcsnPb5PguysjJqI3t3qiaYj0BR8T2f5njdjTGe44Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-flow" "^7.2.0"
+
+"@babel/plugin-transform-for-of@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
+  integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-function-name@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad"
+  integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==
+  dependencies:
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-literals@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1"
+  integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-member-expression-literals@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d"
+  integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-amd@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91"
+  integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.1.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74"
+  integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.4.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-simple-access" "^7.1.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249"
+  integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.4.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-umd@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae"
+  integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.1.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5":
+  version "7.4.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106"
+  integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==
+  dependencies:
+    regexp-tree "^0.1.6"
+
+"@babel/plugin-transform-new-target@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5"
+  integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-object-super@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9"
+  integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.5.5"
+
+"@babel/plugin-transform-parameters@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16"
+  integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==
+  dependencies:
+    "@babel/helper-call-delegate" "^7.4.4"
+    "@babel/helper-get-function-arity" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-property-literals@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905"
+  integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-constant-elements@^7.0.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.5.0.tgz#4d6ae4033bc38f8a65dfca2b6235c44522a422fc"
+  integrity sha512-c5Ba8cpybZFp1Izkf2sWGuNjOxoQ32tFgBvvYvwGhi4+9f6vGiSK9Gex4uVuO/Va6YJFu41aAh1MzMjUWkp0IQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-display-name@7.2.0", "@babel/plugin-transform-react-display-name@^7.0.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0"
+  integrity sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-jsx-self@^7.0.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz#461e21ad9478f1031dd5e276108d027f1b5240ba"
+  integrity sha512-v6S5L/myicZEy+jr6ielB0OR8h+EH/1QFx/YJ7c7Ua+7lqsjj/vW6fD5FR9hB/6y7mGbfT4vAURn3xqBxsUcdg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@babel/plugin-transform-react-jsx-source@^7.0.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.5.0.tgz#583b10c49cf057e237085bcbd8cc960bd83bd96b"
+  integrity sha512-58Q+Jsy4IDCZx7kqEZuSDdam/1oW8OdDX8f+Loo6xyxdfg1yF0GE2XNJQSTZCaMol93+FBzpWiPEwtbMloAcPg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@babel/plugin-transform-react-jsx@^7.0.0":
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290"
+  integrity sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg==
+  dependencies:
+    "@babel/helper-builder-react-jsx" "^7.3.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@babel/plugin-transform-regenerator@^7.4.5":
+  version "7.4.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f"
+  integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==
+  dependencies:
+    regenerator-transform "^0.14.0"
+
+"@babel/plugin-transform-reserved-words@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634"
+  integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-runtime@7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc"
+  integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    resolve "^1.8.1"
+    semver "^5.5.1"
+
+"@babel/plugin-transform-shorthand-properties@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0"
+  integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-spread@^7.2.0":
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406"
+  integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-sticky-regex@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1"
+  integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.0.0"
+
+"@babel/plugin-transform-template-literals@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0"
+  integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typeof-symbol@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2"
+  integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typescript@^7.3.2":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz#6d862766f09b2da1cb1f7d505fe2aedab6b7d4b8"
+  integrity sha512-pehKf4m640myZu5B2ZviLaiBlxMCjSZ1qTEO459AXKX5GnPueyulJeCqZFs1nz/Ya2dDzXQ1NxZ/kKNWyD4h6w==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.5.5"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-typescript" "^7.2.0"
+
+"@babel/plugin-transform-unicode-regex@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f"
+  integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.5.4"
+
+"@babel/preset-env@7.5.5", "@babel/preset-env@^7.4.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a"
+  integrity sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A==
+  dependencies:
+    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-async-generator-functions" "^7.2.0"
+    "@babel/plugin-proposal-dynamic-import" "^7.5.0"
+    "@babel/plugin-proposal-json-strings" "^7.2.0"
+    "@babel/plugin-proposal-object-rest-spread" "^7.5.5"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+    "@babel/plugin-syntax-json-strings" "^7.2.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-transform-arrow-functions" "^7.2.0"
+    "@babel/plugin-transform-async-to-generator" "^7.5.0"
+    "@babel/plugin-transform-block-scoped-functions" "^7.2.0"
+    "@babel/plugin-transform-block-scoping" "^7.5.5"
+    "@babel/plugin-transform-classes" "^7.5.5"
+    "@babel/plugin-transform-computed-properties" "^7.2.0"
+    "@babel/plugin-transform-destructuring" "^7.5.0"
+    "@babel/plugin-transform-dotall-regex" "^7.4.4"
+    "@babel/plugin-transform-duplicate-keys" "^7.5.0"
+    "@babel/plugin-transform-exponentiation-operator" "^7.2.0"
+    "@babel/plugin-transform-for-of" "^7.4.4"
+    "@babel/plugin-transform-function-name" "^7.4.4"
+    "@babel/plugin-transform-literals" "^7.2.0"
+    "@babel/plugin-transform-member-expression-literals" "^7.2.0"
+    "@babel/plugin-transform-modules-amd" "^7.5.0"
+    "@babel/plugin-transform-modules-commonjs" "^7.5.0"
+    "@babel/plugin-transform-modules-systemjs" "^7.5.0"
+    "@babel/plugin-transform-modules-umd" "^7.2.0"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5"
+    "@babel/plugin-transform-new-target" "^7.4.4"
+    "@babel/plugin-transform-object-super" "^7.5.5"
+    "@babel/plugin-transform-parameters" "^7.4.4"
+    "@babel/plugin-transform-property-literals" "^7.2.0"
+    "@babel/plugin-transform-regenerator" "^7.4.5"
+    "@babel/plugin-transform-reserved-words" "^7.2.0"
+    "@babel/plugin-transform-shorthand-properties" "^7.2.0"
+    "@babel/plugin-transform-spread" "^7.2.0"
+    "@babel/plugin-transform-sticky-regex" "^7.2.0"
+    "@babel/plugin-transform-template-literals" "^7.4.4"
+    "@babel/plugin-transform-typeof-symbol" "^7.2.0"
+    "@babel/plugin-transform-unicode-regex" "^7.4.4"
+    "@babel/types" "^7.5.5"
+    browserslist "^4.6.0"
+    core-js-compat "^3.1.1"
+    invariant "^2.2.2"
+    js-levenshtein "^1.1.3"
+    semver "^5.5.0"
+
+"@babel/preset-react@7.0.0", "@babel/preset-react@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.0.0.tgz#e86b4b3d99433c7b3e9e91747e2653958bc6b3c0"
+  integrity sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-transform-react-display-name" "^7.0.0"
+    "@babel/plugin-transform-react-jsx" "^7.0.0"
+    "@babel/plugin-transform-react-jsx-self" "^7.0.0"
+    "@babel/plugin-transform-react-jsx-source" "^7.0.0"
+
+"@babel/preset-typescript@7.3.3":
+  version "7.3.3"
+  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a"
+  integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-transform-typescript" "^7.3.2"
+
+"@babel/runtime@7.5.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
+  integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
+  dependencies:
+    regenerator-runtime "^0.13.2"
+
+"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
+  integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.4.4"
+    "@babel/types" "^7.4.4"
+
+"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
+  integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
+  dependencies:
+    "@babel/code-frame" "^7.5.5"
+    "@babel/generator" "^7.5.5"
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-split-export-declaration" "^7.4.4"
+    "@babel/parser" "^7.5.5"
+    "@babel/types" "^7.5.5"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.13"
+
+"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
+  integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.17.13"
+    to-fast-properties "^2.0.0"
+
+"@cnakazawa/watch@^1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef"
+  integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==
+  dependencies:
+    exec-sh "^0.3.2"
+    minimist "^1.2.0"
+
+"@csstools/convert-colors@^1.4.0":
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
+  integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==
+
+"@csstools/normalize.css@^9.0.1":
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-9.0.1.tgz#c27b391d8457d1e893f1eddeaf5e5412d12ffbb5"
+  integrity sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA==
+
+"@hapi/address@2.x.x":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
+  integrity sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw==
+
+"@hapi/hoek@6.x.x":
+  version "6.2.4"
+  resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-6.2.4.tgz#4b95fbaccbfba90185690890bdf1a2fbbda10595"
+  integrity sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A==
+
+"@hapi/hoek@8.x.x":
+  version "8.2.1"
+  resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.2.1.tgz#924af04cbb22e17359c620d2a9c946e63f58eb77"
+  integrity sha512-JPiBy+oSmsq3St7XlipfN5pNA6bDJ1kpa73PrK/zR29CVClDVqy04AanM/M/qx5bSF+I61DdCfAvRrujau+zRg==
+
+"@hapi/joi@^15.0.0":
+  version "15.1.0"
+  resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.0.tgz#940cb749b5c55c26ab3b34ce362e82b6162c8e7a"
+  integrity sha512-n6kaRQO8S+kepUTbXL9O/UOL788Odqs38/VOfoCrATDtTvyfiO3fgjlSRaNkHabpTLgM7qru9ifqXlXbXk8SeQ==
+  dependencies:
+    "@hapi/address" "2.x.x"
+    "@hapi/hoek" "6.x.x"
+    "@hapi/marker" "1.x.x"
+    "@hapi/topo" "3.x.x"
+
+"@hapi/marker@1.x.x":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@hapi/marker/-/marker-1.0.0.tgz#65b0b2b01d1be06304886ce9b4b77b1bfb21a769"
+  integrity sha512-JOfdekTXnJexfE8PyhZFyHvHjt81rBFSAbTIRAhF2vv/2Y1JzoKsGqxH/GpZJoF7aEfYok8JVcAHmSz1gkBieA==
+
+"@hapi/topo@3.x.x":
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.3.tgz#c7a02e0d936596d29f184e6d7fdc07e8b5efce11"
+  integrity sha512-JmS9/vQK6dcUYn7wc2YZTqzIKubAQcJKu2KCKAru6es482U5RT5fP1EXCPtlXpiK7PR0On/kpQKI4fRKkzpZBQ==
+  dependencies:
+    "@hapi/hoek" "8.x.x"
+
+"@jest/console@^24.7.1":
+  version "24.7.1"
+  resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.7.1.tgz#32a9e42535a97aedfe037e725bd67e954b459545"
+  integrity sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==
+  dependencies:
+    "@jest/source-map" "^24.3.0"
+    chalk "^2.0.1"
+    slash "^2.0.0"
+
+"@jest/core@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.8.0.tgz#fbbdcd42a41d0d39cddbc9f520c8bab0c33eed5b"
+  integrity sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==
+  dependencies:
+    "@jest/console" "^24.7.1"
+    "@jest/reporters" "^24.8.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/transform" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    ansi-escapes "^3.0.0"
+    chalk "^2.0.1"
+    exit "^0.1.2"
+    graceful-fs "^4.1.15"
+    jest-changed-files "^24.8.0"
+    jest-config "^24.8.0"
+    jest-haste-map "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-regex-util "^24.3.0"
+    jest-resolve-dependencies "^24.8.0"
+    jest-runner "^24.8.0"
+    jest-runtime "^24.8.0"
+    jest-snapshot "^24.8.0"
+    jest-util "^24.8.0"
+    jest-validate "^24.8.0"
+    jest-watcher "^24.8.0"
+    micromatch "^3.1.10"
+    p-each-series "^1.0.0"
+    pirates "^4.0.1"
+    realpath-native "^1.1.0"
+    rimraf "^2.5.4"
+    strip-ansi "^5.0.0"
+
+"@jest/environment@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.8.0.tgz#0342261383c776bdd652168f68065ef144af0eac"
+  integrity sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==
+  dependencies:
+    "@jest/fake-timers" "^24.8.0"
+    "@jest/transform" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    jest-mock "^24.8.0"
+
+"@jest/fake-timers@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.8.0.tgz#2e5b80a4f78f284bcb4bd5714b8e10dd36a8d3d1"
+  integrity sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-mock "^24.8.0"
+
+"@jest/reporters@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.8.0.tgz#075169cd029bddec54b8f2c0fc489fd0b9e05729"
+  integrity sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==
+  dependencies:
+    "@jest/environment" "^24.8.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/transform" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    chalk "^2.0.1"
+    exit "^0.1.2"
+    glob "^7.1.2"
+    istanbul-lib-coverage "^2.0.2"
+    istanbul-lib-instrument "^3.0.1"
+    istanbul-lib-report "^2.0.4"
+    istanbul-lib-source-maps "^3.0.1"
+    istanbul-reports "^2.1.1"
+    jest-haste-map "^24.8.0"
+    jest-resolve "^24.8.0"
+    jest-runtime "^24.8.0"
+    jest-util "^24.8.0"
+    jest-worker "^24.6.0"
+    node-notifier "^5.2.1"
+    slash "^2.0.0"
+    source-map "^0.6.0"
+    string-length "^2.0.0"
+
+"@jest/source-map@^24.3.0":
+  version "24.3.0"
+  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.3.0.tgz#563be3aa4d224caf65ff77edc95cd1ca4da67f28"
+  integrity sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==
+  dependencies:
+    callsites "^3.0.0"
+    graceful-fs "^4.1.15"
+    source-map "^0.6.0"
+
+"@jest/test-result@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.8.0.tgz#7675d0aaf9d2484caa65e048d9b467d160f8e9d3"
+  integrity sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==
+  dependencies:
+    "@jest/console" "^24.7.1"
+    "@jest/types" "^24.8.0"
+    "@types/istanbul-lib-coverage" "^2.0.0"
+
+"@jest/test-sequencer@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz#2f993bcf6ef5eb4e65e8233a95a3320248cf994b"
+  integrity sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==
+  dependencies:
+    "@jest/test-result" "^24.8.0"
+    jest-haste-map "^24.8.0"
+    jest-runner "^24.8.0"
+    jest-runtime "^24.8.0"
+
+"@jest/transform@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.8.0.tgz#628fb99dce4f9d254c6fd9341e3eea262e06fef5"
+  integrity sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==
+  dependencies:
+    "@babel/core" "^7.1.0"
+    "@jest/types" "^24.8.0"
+    babel-plugin-istanbul "^5.1.0"
+    chalk "^2.0.1"
+    convert-source-map "^1.4.0"
+    fast-json-stable-stringify "^2.0.0"
+    graceful-fs "^4.1.15"
+    jest-haste-map "^24.8.0"
+    jest-regex-util "^24.3.0"
+    jest-util "^24.8.0"
+    micromatch "^3.1.10"
+    realpath-native "^1.1.0"
+    slash "^2.0.0"
+    source-map "^0.6.1"
+    write-file-atomic "2.4.1"
+
+"@jest/types@^24.8.0":
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad"
+  integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==
+  dependencies:
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    "@types/istanbul-reports" "^1.1.1"
+    "@types/yargs" "^12.0.9"
+
+"@mrmlnc/readdir-enhanced@^2.2.1":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
+  integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==
+  dependencies:
+    call-me-maybe "^1.0.1"
+    glob-to-regexp "^0.3.0"
+
+"@nodelib/fs.stat@^1.1.2":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
+  integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
+
+"@svgr/babel-plugin-add-jsx-attribute@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1"
+  integrity sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig==
+
+"@svgr/babel-plugin-remove-jsx-attribute@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz#297550b9a8c0c7337bea12bdfc8a80bb66f85abc"
+  integrity sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ==
+
+"@svgr/babel-plugin-remove-jsx-empty-expression@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz#c196302f3e68eab6a05e98af9ca8570bc13131c7"
+  integrity sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w==
+
+"@svgr/babel-plugin-replace-jsx-attribute-value@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165"
+  integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w==
+
+"@svgr/babel-plugin-svg-dynamic-title@^4.3.1":
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.1.tgz#646c2f5b5770c2fe318d6e51492344c3d62ddb63"
+  integrity sha512-p6z6JJroP989jHWcuraeWpzdejehTmLUpyC9smhTBWyPN0VVGe2phbYxpPTV7Vh8XzmFrcG55idrnfWn/2oQEw==
+
+"@svgr/babel-plugin-svg-em-dimensions@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz#9a94791c9a288108d20a9d2cc64cac820f141391"
+  integrity sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w==
+
+"@svgr/babel-plugin-transform-react-native-svg@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz#151487322843359a1ca86b21a3815fd21a88b717"
+  integrity sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw==
+
+"@svgr/babel-plugin-transform-svg-component@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697"
+  integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw==
+
+"@svgr/babel-preset@^4.3.1":
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.3.1.tgz#62ffcb85d756580e8ce608e9d2ac3b9063be9e28"
+  integrity sha512-rPFKLmyhlh6oeBv3j2vEAj2nd2QbWqpoJLKzBLjwQVt+d9aeXajVaPNEqrES2spjXKR4OxfgSs7U0NtmAEkr0Q==
+  dependencies:
+    "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0"
+    "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0"
+    "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0"
+    "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0"
+    "@svgr/babel-plugin-svg-dynamic-title" "^4.3.1"
+    "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0"
+    "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0"
+    "@svgr/babel-plugin-transform-svg-component" "^4.2.0"
+
+"@svgr/core@^4.3.2":
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.2.tgz#939c89be670ad79b762f4c063f213f0e02535f2e"
+  integrity sha512-N+tP5CLFd1hP9RpO83QJPZY3NL8AtrdqNbuhRgBkjE/49RnMrrRsFm1wY8pueUfAGvzn6tSXUq29o6ah8RuR5w==
+  dependencies:
+    "@svgr/plugin-jsx" "^4.3.2"
+    camelcase "^5.3.1"
+    cosmiconfig "^5.2.1"
+
+"@svgr/hast-util-to-babel-ast@^4.3.2":
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz#1d5a082f7b929ef8f1f578950238f630e14532b8"
+  integrity sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg==
+  dependencies:
+    "@babel/types" "^7.4.4"
+
+"@svgr/plugin-jsx@^4.3.2":
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.2.tgz#ce9ddafc8cdd74da884c9f7af014afcf37f93d3c"
+  integrity sha512-+1GW32RvmNmCsOkMoclA/TppNjHPLMnNZG3/Ecscxawp051XJ2MkO09Hn11VcotdC2EPrDfT8pELGRo+kbZ1Eg==
+  dependencies:
+    "@babel/core" "^7.4.5"
+    "@svgr/babel-preset" "^4.3.1"
+    "@svgr/hast-util-to-babel-ast" "^4.3.2"
+    svg-parser "^2.0.0"
+
+"@svgr/plugin-svgo@^4.3.1":
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz#daac0a3d872e3f55935c6588dd370336865e9e32"
+  integrity sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w==
+  dependencies:
+    cosmiconfig "^5.2.1"
+    merge-deep "^3.0.2"
+    svgo "^1.2.2"
+
+"@svgr/webpack@4.3.2":
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.3.2.tgz#319d4471c8f3d5c3af35059274834d9b5b8fb956"
+  integrity sha512-F3VE5OvyOWBEd2bF7BdtFRyI6E9it3mN7teDw0JQTlVtc4HZEYiiLSl+Uf9Uub6IYHVGc+qIrxxDyeedkQru2w==
+  dependencies:
+    "@babel/core" "^7.4.5"
+    "@babel/plugin-transform-react-constant-elements" "^7.0.0"
+    "@babel/preset-env" "^7.4.5"
+    "@babel/preset-react" "^7.0.0"
+    "@svgr/core" "^4.3.2"
+    "@svgr/plugin-jsx" "^4.3.2"
+    "@svgr/plugin-svgo" "^4.3.1"
+    loader-utils "^1.2.3"
+
+"@types/babel__core@^7.1.0":
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f"
+  integrity sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==
+  dependencies:
+    "@babel/parser" "^7.1.0"
+    "@babel/types" "^7.0.0"
+    "@types/babel__generator" "*"
+    "@types/babel__template" "*"
+    "@types/babel__traverse" "*"
+
+"@types/babel__generator@*":
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc"
+  integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@types/babel__template@*":
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307"
+  integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==
+  dependencies:
+    "@babel/parser" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
+  version "7.0.7"
+  resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.7.tgz#2496e9ff56196cc1429c72034e07eab6121b6f3f"
+  integrity sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==
+  dependencies:
+    "@babel/types" "^7.3.0"
+
+"@types/eslint-visitor-keys@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
+  integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
+
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
+  integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==
+
+"@types/istanbul-lib-report@*":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c"
+  integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==
+  dependencies:
+    "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a"
+  integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==
+  dependencies:
+    "@types/istanbul-lib-coverage" "*"
+    "@types/istanbul-lib-report" "*"
+
+"@types/jest-diff@*":
+  version "20.0.1"
+  resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89"
+  integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==
+
+"@types/jest@24.0.18":
+  version "24.0.18"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.18.tgz#9c7858d450c59e2164a8a9df0905fc5091944498"
+  integrity sha512-jcDDXdjTcrQzdN06+TSVsPPqxvsZA/5QkYfIZlq1JMw7FdP5AZylbOc+6B/cuDurctRe+MziUMtQ3xQdrbjqyQ==
+  dependencies:
+    "@types/jest-diff" "*"
+
+"@types/json-schema@^7.0.3":
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
+  integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
+
+"@types/node@12.7.2":
+  version "12.7.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44"
+  integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==
+
+"@types/prop-types@*":
+  version "15.7.1"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6"
+  integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==
+
+"@types/q@^1.5.1":
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
+  integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
+
+"@types/react-dom@16.9.0":
+  version "16.9.0"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.0.tgz#ba6ddb00bf5de700b0eb91daa452081ffccbfdea"
+  integrity sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react@*", "@types/react@16.9.2":
+  version "16.9.2"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.2.tgz#6d1765431a1ad1877979013906731aae373de268"
+  integrity sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^2.2.0"
+
+"@types/stack-utils@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
+  integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
+
+"@types/yargs@^12.0.2", "@types/yargs@^12.0.9":
+  version "12.0.12"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916"
+  integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==
+
+"@typescript-eslint/eslint-plugin@1.13.0":
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz#22fed9b16ddfeb402fd7bcde56307820f6ebc49f"
+  integrity sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==
+  dependencies:
+    "@typescript-eslint/experimental-utils" "1.13.0"
+    eslint-utils "^1.3.1"
+    functional-red-black-tree "^1.0.1"
+    regexpp "^2.0.1"
+    tsutils "^3.7.0"
+
+"@typescript-eslint/experimental-utils@1.13.0":
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e"
+  integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==
+  dependencies:
+    "@types/json-schema" "^7.0.3"
+    "@typescript-eslint/typescript-estree" "1.13.0"
+    eslint-scope "^4.0.0"
+
+"@typescript-eslint/parser@1.13.0":
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355"
+  integrity sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==
+  dependencies:
+    "@types/eslint-visitor-keys" "^1.0.0"
+    "@typescript-eslint/experimental-utils" "1.13.0"
+    "@typescript-eslint/typescript-estree" "1.13.0"
+    eslint-visitor-keys "^1.0.0"
+
+"@typescript-eslint/typescript-estree@1.13.0":
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e"
+  integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==
+  dependencies:
+    lodash.unescape "4.0.1"
+    semver "5.5.0"
+
+"@webassemblyjs/ast@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
+  integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==
+  dependencies:
+    "@webassemblyjs/helper-module-context" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/wast-parser" "1.8.5"
+
+"@webassemblyjs/floating-point-hex-parser@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721"
+  integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==
+
+"@webassemblyjs/helper-api-error@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7"
+  integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==
+
+"@webassemblyjs/helper-buffer@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204"
+  integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==
+
+"@webassemblyjs/helper-code-frame@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e"
+  integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==
+  dependencies:
+    "@webassemblyjs/wast-printer" "1.8.5"
+
+"@webassemblyjs/helper-fsm@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452"
+  integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==
+
+"@webassemblyjs/helper-module-context@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245"
+  integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    mamacro "^0.0.3"
+
+"@webassemblyjs/helper-wasm-bytecode@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61"
+  integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==
+
+"@webassemblyjs/helper-wasm-section@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf"
+  integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-buffer" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/wasm-gen" "1.8.5"
+
+"@webassemblyjs/ieee754@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e"
+  integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==
+  dependencies:
+    "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10"
+  integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==
+  dependencies:
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc"
+  integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==
+
+"@webassemblyjs/wasm-edit@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a"
+  integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-buffer" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/helper-wasm-section" "1.8.5"
+    "@webassemblyjs/wasm-gen" "1.8.5"
+    "@webassemblyjs/wasm-opt" "1.8.5"
+    "@webassemblyjs/wasm-parser" "1.8.5"
+    "@webassemblyjs/wast-printer" "1.8.5"
+
+"@webassemblyjs/wasm-gen@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc"
+  integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/ieee754" "1.8.5"
+    "@webassemblyjs/leb128" "1.8.5"
+    "@webassemblyjs/utf8" "1.8.5"
+
+"@webassemblyjs/wasm-opt@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264"
+  integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-buffer" "1.8.5"
+    "@webassemblyjs/wasm-gen" "1.8.5"
+    "@webassemblyjs/wasm-parser" "1.8.5"
+
+"@webassemblyjs/wasm-parser@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d"
+  integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-api-error" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/ieee754" "1.8.5"
+    "@webassemblyjs/leb128" "1.8.5"
+    "@webassemblyjs/utf8" "1.8.5"
+
+"@webassemblyjs/wast-parser@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c"
+  integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/floating-point-hex-parser" "1.8.5"
+    "@webassemblyjs/helper-api-error" "1.8.5"
+    "@webassemblyjs/helper-code-frame" "1.8.5"
+    "@webassemblyjs/helper-fsm" "1.8.5"
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/wast-printer@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc"
+  integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/wast-parser" "1.8.5"
+    "@xtuc/long" "4.2.2"
+
+"@xtuc/ieee754@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+  integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+  integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+abab@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
+  integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==
+
+abbrev@1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+  integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
+  integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
+  dependencies:
+    mime-types "~2.1.24"
+    negotiator "0.6.2"
+
+acorn-globals@^4.1.0, acorn-globals@^4.3.0:
+  version "4.3.3"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.3.tgz#a86f75b69680b8780d30edd21eee4e0ea170c05e"
+  integrity sha512-vkR40VwS2SYO98AIeFvzWWh+xyc2qi9s7OoXSFEGIP/rOJKzjnhykaZJNnHdoq4BL2gGxI5EZOU16z896EYnOQ==
+  dependencies:
+    acorn "^6.0.1"
+    acorn-walk "^6.0.1"
+
+acorn-jsx@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e"
+  integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==
+
+acorn-walk@^6.0.1:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
+  integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
+
+acorn@^5.5.3:
+  version "5.7.3"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
+  integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
+
+acorn@^6.0.1, acorn@^6.0.4, acorn@^6.0.7, acorn@^6.2.1:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
+  integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
+
+address@1.1.0, address@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/address/-/address-1.1.0.tgz#ef8e047847fcd2c5b6f50c16965f924fd99fe709"
+  integrity sha512-4diPfzWbLEIElVG4AnqP+00SULlPzNuyJFNnmMrLgyaxG6tZXJ1sn7mjBu4fHrJE+Yp/jgylOweJn2xsLMFggQ==
+
+adjust-sourcemap-loader@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4"
+  integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==
+  dependencies:
+    assert "1.4.1"
+    camelcase "5.0.0"
+    loader-utils "1.2.3"
+    object-path "0.11.4"
+    regex-parser "2.2.10"
+
+ajv-errors@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
+  integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
+
+ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
+  integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
+
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5:
+  version "6.10.2"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
+  integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
+  dependencies:
+    fast-deep-equal "^2.0.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+alphanum-sort@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
+  integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
+
+ansi-colors@^3.0.0:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
+  integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
+
+ansi-escapes@^3.0.0, ansi-escapes@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
+  integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
+
+ansi-escapes@^4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228"
+  integrity sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==
+  dependencies:
+    type-fest "^0.5.2"
+
+ansi-html@0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
+  integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.0.0, ansi-regex@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+  integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+  integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+anymatch@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+  integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
+  dependencies:
+    micromatch "^3.1.4"
+    normalize-path "^2.1.1"
+
+aproba@^1.0.3, aproba@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+  integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+are-we-there-yet@~1.1.2:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
+  integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+aria-query@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
+  integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=
+  dependencies:
+    ast-types-flow "0.0.7"
+    commander "^2.11.0"
+
+arity-n@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745"
+  integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U=
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+  integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
+
+array-filter@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
+  integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw=
+
+array-flatten@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+  integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
+
+array-flatten@^2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
+  integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
+
+array-includes@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
+  integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.7.0"
+
+array-map@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
+  integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=
+
+array-reduce@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
+  integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=
+
+array-union@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+  integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
+  dependencies:
+    array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+  integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+arrify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+  integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
+
+asap@~2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+  integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
+
+asn1.js@^4.0.0:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+asn1@~0.2.3:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+  integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+  dependencies:
+    safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+  integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+assert@1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
+  integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=
+  dependencies:
+    util "0.10.3"
+
+assert@^1.1.1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
+  integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
+  dependencies:
+    object-assign "^4.1.1"
+    util "0.10.3"
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+ast-types-flow@0.0.7, ast-types-flow@^0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
+  integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
+
+astral-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+  integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+
+async-each@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
+  integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
+
+async-limiter@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+  integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+async@^1.5.2:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+  integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+atob@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+autoprefixer@^9.6.1:
+  version "9.6.1"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47"
+  integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==
+  dependencies:
+    browserslist "^4.6.3"
+    caniuse-lite "^1.0.30000980"
+    chalk "^2.4.2"
+    normalize-range "^0.1.2"
+    num2fraction "^1.2.2"
+    postcss "^7.0.17"
+    postcss-value-parser "^4.0.0"
+
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+  integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
+  integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
+
+axobject-query@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
+  integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==
+  dependencies:
+    ast-types-flow "0.0.7"
+
+babel-code-frame@^6.22.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+  integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
+  dependencies:
+    chalk "^1.1.3"
+    esutils "^2.0.2"
+    js-tokens "^3.0.2"
+
+babel-eslint@10.0.2:
+  version "10.0.2"
+  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456"
+  integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.0.0"
+    "@babel/traverse" "^7.0.0"
+    "@babel/types" "^7.0.0"
+    eslint-scope "3.7.1"
+    eslint-visitor-keys "^1.0.0"
+
+babel-extract-comments@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz#0a2aedf81417ed391b85e18b4614e693a0351a21"
+  integrity sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==
+  dependencies:
+    babylon "^6.18.0"
+
+babel-jest@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.8.0.tgz#5c15ff2b28e20b0f45df43fe6b7f2aae93dba589"
+  integrity sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==
+  dependencies:
+    "@jest/transform" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    "@types/babel__core" "^7.1.0"
+    babel-plugin-istanbul "^5.1.0"
+    babel-preset-jest "^24.6.0"
+    chalk "^2.4.2"
+    slash "^2.0.0"
+
+babel-loader@8.0.6:
+  version "8.0.6"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb"
+  integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==
+  dependencies:
+    find-cache-dir "^2.0.0"
+    loader-utils "^1.0.2"
+    mkdirp "^0.5.1"
+    pify "^4.0.1"
+
+babel-plugin-dynamic-import-node@2.3.0, babel-plugin-dynamic-import-node@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
+  integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
+  dependencies:
+    object.assign "^4.1.0"
+
+babel-plugin-istanbul@^5.1.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854"
+  integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    find-up "^3.0.0"
+    istanbul-lib-instrument "^3.3.0"
+    test-exclude "^5.2.3"
+
+babel-plugin-jest-hoist@^24.6.0:
+  version "24.6.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz#f7f7f7ad150ee96d7a5e8e2c5da8319579e78019"
+  integrity sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==
+  dependencies:
+    "@types/babel__traverse" "^7.0.6"
+
+babel-plugin-macros@2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.6.1.tgz#41f7ead616fc36f6a93180e89697f69f51671181"
+  integrity sha512-6W2nwiXme6j1n2erPOnmRiWfObUhWH7Qw1LMi9XZy8cj+KtESu3T6asZvtk5bMQQjX8te35o7CFueiSdL/2NmQ==
+  dependencies:
+    "@babel/runtime" "^7.4.2"
+    cosmiconfig "^5.2.0"
+    resolve "^1.10.0"
+
+babel-plugin-named-asset-import@^0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.3.tgz#9ba2f3ac4dc78b042651654f07e847adfe50667c"
+  integrity sha512-1XDRysF4894BUdMChT+2HHbtJYiO7zx5Be7U6bT8dISy7OdyETMGIAQBMPQCsY1YRf0xcubwnKKaDr5bk15JTA==
+
+babel-plugin-syntax-object-rest-spread@^6.8.0:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
+  integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=
+
+babel-plugin-transform-object-rest-spread@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06"
+  integrity sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=
+  dependencies:
+    babel-plugin-syntax-object-rest-spread "^6.8.0"
+    babel-runtime "^6.26.0"
+
+babel-plugin-transform-react-remove-prop-types@0.4.24:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a"
+  integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==
+
+babel-preset-jest@^24.6.0:
+  version "24.6.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz#66f06136eefce87797539c0d63f1769cc3915984"
+  integrity sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==
+  dependencies:
+    "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
+    babel-plugin-jest-hoist "^24.6.0"
+
+babel-preset-react-app@^9.0.1:
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-9.0.1.tgz#16a2cf84363045b530b6a03460527a5c6eac42ba"
+  integrity sha512-v7MeY+QxdBhM9oU5uOQCIHLsErYkEbbjctXsb10II+KAnttbe0rvprvP785dRxfa9dI4ZbsGXsRU07Qdi5BtOw==
+  dependencies:
+    "@babel/core" "7.5.5"
+    "@babel/plugin-proposal-class-properties" "7.5.5"
+    "@babel/plugin-proposal-decorators" "7.4.4"
+    "@babel/plugin-proposal-object-rest-spread" "7.5.5"
+    "@babel/plugin-syntax-dynamic-import" "7.2.0"
+    "@babel/plugin-transform-destructuring" "7.5.0"
+    "@babel/plugin-transform-flow-strip-types" "7.4.4"
+    "@babel/plugin-transform-react-display-name" "7.2.0"
+    "@babel/plugin-transform-runtime" "7.5.5"
+    "@babel/preset-env" "7.5.5"
+    "@babel/preset-react" "7.0.0"
+    "@babel/preset-typescript" "7.3.3"
+    "@babel/runtime" "7.5.5"
+    babel-plugin-dynamic-import-node "2.3.0"
+    babel-plugin-macros "2.6.1"
+    babel-plugin-transform-react-remove-prop-types "0.4.24"
+
+babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+babylon@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
+  integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base64-js@^1.0.2:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    cache-base "^1.0.1"
+    class-utils "^0.3.5"
+    component-emitter "^1.2.1"
+    define-property "^1.0.0"
+    isobject "^3.0.1"
+    mixin-deep "^1.2.0"
+    pascalcase "^0.1.1"
+
+batch@0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
+  integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+  integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+  dependencies:
+    tweetnacl "^0.14.3"
+
+big.js@^5.2.2:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+  integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
+binary-extensions@^1.0.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
+  integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
+
+bluebird@^3.5.5:
+  version "3.5.5"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
+  integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
+  integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+
+body-parser@1.19.0:
+  version "1.19.0"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
+  integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
+  dependencies:
+    bytes "3.1.0"
+    content-type "~1.0.4"
+    debug "2.6.9"
+    depd "~1.1.2"
+    http-errors "1.7.2"
+    iconv-lite "0.4.24"
+    on-finished "~2.3.0"
+    qs "6.7.0"
+    raw-body "2.4.0"
+    type-is "~1.6.17"
+
+bonjour@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5"
+  integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU=
+  dependencies:
+    array-flatten "^2.1.0"
+    deep-equal "^1.0.1"
+    dns-equal "^1.0.0"
+    dns-txt "^2.0.2"
+    multicast-dns "^6.0.1"
+    multicast-dns-service-types "^1.1.0"
+
+boolbase@^1.0.0, boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1, braces@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    arr-flatten "^1.1.0"
+    array-unique "^0.3.2"
+    extend-shallow "^2.0.1"
+    fill-range "^4.0.0"
+    isobject "^3.0.1"
+    repeat-element "^1.1.2"
+    snapdragon "^0.8.1"
+    snapdragon-node "^2.0.1"
+    split-string "^3.0.2"
+    to-regex "^3.0.1"
+
+brorand@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+  integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
+
+browser-process-hrtime@^0.1.2:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
+  integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==
+
+browser-resolve@^1.11.3:
+  version "1.11.3"
+  resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6"
+  integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==
+  dependencies:
+    resolve "1.1.7"
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+  integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
+  dependencies:
+    buffer-xor "^1.0.3"
+    cipher-base "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.3"
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+browserify-cipher@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
+  integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
+  integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+browserify-rsa@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+  integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
+  dependencies:
+    bn.js "^4.1.0"
+    randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+  integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
+  dependencies:
+    bn.js "^4.1.1"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.2"
+    elliptic "^6.0.0"
+    inherits "^2.0.1"
+    parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+  integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
+  dependencies:
+    pako "~1.0.5"
+
+browserslist@4.6.6, browserslist@^4.0.0, browserslist@^4.1.1, browserslist@^4.6.0, browserslist@^4.6.3, browserslist@^4.6.4, browserslist@^4.6.6:
+  version "4.6.6"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453"
+  integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==
+  dependencies:
+    caniuse-lite "^1.0.30000984"
+    electron-to-chromium "^1.3.191"
+    node-releases "^1.1.25"
+
+bser@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.0.tgz#65fc784bf7f87c009b973c12db6546902fa9c7b5"
+  integrity sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==
+  dependencies:
+    node-int64 "^0.4.0"
+
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+buffer-indexof@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
+  integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==
+
+buffer-xor@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+  integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
+
+buffer@^4.3.0:
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
+  integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+    isarray "^1.0.0"
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+  integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
+
+bytes@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+  integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+bytes@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
+  integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
+
+cacache@^12.0.2:
+  version "12.0.2"
+  resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.2.tgz#8db03205e36089a3df6954c66ce92541441ac46c"
+  integrity sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg==
+  dependencies:
+    bluebird "^3.5.5"
+    chownr "^1.1.1"
+    figgy-pudding "^3.5.1"
+    glob "^7.1.4"
+    graceful-fs "^4.1.15"
+    infer-owner "^1.0.3"
+    lru-cache "^5.1.1"
+    mississippi "^3.0.0"
+    mkdirp "^0.5.1"
+    move-concurrently "^1.0.1"
+    promise-inflight "^1.0.1"
+    rimraf "^2.6.3"
+    ssri "^6.0.1"
+    unique-filename "^1.1.1"
+    y18n "^4.0.0"
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    collection-visit "^1.0.0"
+    component-emitter "^1.2.1"
+    get-value "^2.0.6"
+    has-value "^1.0.0"
+    isobject "^3.0.1"
+    set-value "^2.0.0"
+    to-object-path "^0.3.0"
+    union-value "^1.0.0"
+    unset-value "^1.0.0"
+
+call-me-maybe@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
+  integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
+
+caller-callsite@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+  integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+  dependencies:
+    callsites "^2.0.0"
+
+caller-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+  integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+  dependencies:
+    caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+  integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camel-case@3.0.x:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+  integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
+  dependencies:
+    no-case "^2.2.0"
+    upper-case "^1.1.1"
+
+camelcase@5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
+  integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
+
+camelcase@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
+  integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
+
+camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+caniuse-api@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
+  integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-lite "^1.0.0"
+    lodash.memoize "^4.1.2"
+    lodash.uniq "^4.5.0"
+
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000984:
+  version "1.0.30000989"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9"
+  integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==
+
+capture-exit@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
+  integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==
+  dependencies:
+    rsvp "^4.8.4"
+
+case-sensitive-paths-webpack-plugin@2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz#3371ef6365ef9c25fa4b81c16ace0e9c7dc58c3e"
+  integrity sha512-u5ElzokS8A1pm9vM3/iDgTcI3xqHxuCao94Oz8etI3cf0Tio0p8izkDYbTIn09uP3yUUr6+veaE6IkjnTYS46g==
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+  integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+chardet@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+  integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
+chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4:
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5"
+  integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==
+  dependencies:
+    anymatch "^2.0.0"
+    async-each "^1.0.1"
+    braces "^2.3.2"
+    glob-parent "^3.1.0"
+    inherits "^2.0.3"
+    is-binary-path "^1.0.0"
+    is-glob "^4.0.0"
+    normalize-path "^3.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.2.1"
+    upath "^1.1.1"
+  optionalDependencies:
+    fsevents "^1.2.7"
+
+chownr@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6"
+  integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==
+
+chrome-trace-event@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
+  integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==
+  dependencies:
+    tslib "^1.9.0"
+
+ci-info@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+  integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+  integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+clean-css@4.2.x:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
+  integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
+  dependencies:
+    source-map "~0.6.0"
+
+cli-cursor@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+  dependencies:
+    restore-cursor "^2.0.0"
+
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+  dependencies:
+    restore-cursor "^3.1.0"
+
+cli-width@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
+  integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
+
+cliui@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
+  integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==
+  dependencies:
+    string-width "^2.1.1"
+    strip-ansi "^4.0.0"
+    wrap-ansi "^2.0.0"
+
+clone-deep@^0.2.4:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6"
+  integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=
+  dependencies:
+    for-own "^0.1.3"
+    is-plain-object "^2.0.1"
+    kind-of "^3.0.2"
+    lazy-cache "^1.0.3"
+    shallow-clone "^0.1.2"
+
+clone-deep@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+  integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+  dependencies:
+    is-plain-object "^2.0.4"
+    kind-of "^6.0.2"
+    shallow-clone "^3.0.0"
+
+co@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+  integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
+
+coa@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
+  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
+  dependencies:
+    "@types/q" "^1.5.1"
+    chalk "^2.4.1"
+    q "^1.1.2"
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+color-convert@^1.9.0, color-convert@^1.9.1:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.5.2:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+  integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
+color@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
+  integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
+  dependencies:
+    color-convert "^1.9.1"
+    color-string "^1.5.2"
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@2.17.x:
+  version "2.17.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+  integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
+
+commander@^2.11.0, commander@^2.20.0, commander@~2.20.0:
+  version "2.20.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
+  integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
+
+commander@~2.19.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+  integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+
+common-tags@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
+  integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==
+
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+  integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
+
+component-emitter@^1.2.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+  integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+compose-function@3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f"
+  integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=
+  dependencies:
+    arity-n "^1.0.4"
+
+compressible@~2.0.16:
+  version "2.0.17"
+  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
+  integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
+  dependencies:
+    mime-db ">= 1.40.0 < 2"
+
+compression@^1.5.2:
+  version "1.7.4"
+  resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+  integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+  dependencies:
+    accepts "~1.3.5"
+    bytes "3.0.0"
+    compressible "~2.0.16"
+    debug "2.6.9"
+    on-headers "~1.0.2"
+    safe-buffer "5.1.2"
+    vary "~1.1.2"
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+concat-stream@^1.5.0:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+  integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+  dependencies:
+    buffer-from "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+confusing-browser-globals@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.8.tgz#93ffec1f82a6e2bf2bc36769cc3a92fa20e502f3"
+  integrity sha512-lI7asCibVJ6Qd3FGU7mu4sfG4try4LX3+GVS+Gv8UlrEf2AeW57piecapnog2UHZSbcX/P/1UDWVaTsblowlZg==
+
+connect-history-api-fallback@^1.3.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
+  integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
+
+console-browserify@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
+  integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=
+  dependencies:
+    date-now "^0.1.4"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
+constants-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+  integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
+
+contains-path@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
+  integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
+
+content-disposition@0.5.3:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
+  integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
+  dependencies:
+    safe-buffer "5.1.2"
+
+content-type@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+  integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+convert-source-map@1.6.0, convert-source-map@^1.1.0, convert-source-map@^1.4.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+  integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
+  dependencies:
+    safe-buffer "~5.1.1"
+
+convert-source-map@^0.3.3:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
+  integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=
+
+cookie-signature@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+  integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
+
+cookie@0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
+  integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
+
+copy-concurrently@^1.0.0:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
+  integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==
+  dependencies:
+    aproba "^1.1.1"
+    fs-write-stream-atomic "^1.0.8"
+    iferr "^0.1.5"
+    mkdirp "^0.5.1"
+    rimraf "^2.5.4"
+    run-queue "^1.0.0"
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-js-compat@^3.1.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.2.1.tgz#0cbdbc2e386e8e00d3b85dc81c848effec5b8150"
+  integrity sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A==
+  dependencies:
+    browserslist "^4.6.6"
+    semver "^6.3.0"
+
+core-js@3.1.4:
+  version "3.1.4"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.4.tgz#3a2837fc48e582e1ae25907afcd6cf03b0cc7a07"
+  integrity sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==
+
+core-js@^2.4.0:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
+  integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
+
+core-util-is@1.0.2, core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+cosmiconfig@^5.0.0, cosmiconfig@^5.2.0, cosmiconfig@^5.2.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+  integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+  dependencies:
+    import-fresh "^2.0.0"
+    is-directory "^0.3.1"
+    js-yaml "^3.13.1"
+    parse-json "^4.0.0"
+
+create-ecdh@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
+  integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+  integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
+  dependencies:
+    cipher-base "^1.0.1"
+    inherits "^2.0.1"
+    md5.js "^1.3.4"
+    ripemd160 "^2.0.1"
+    sha.js "^2.4.0"
+
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+  integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
+  dependencies:
+    cipher-base "^1.0.3"
+    create-hash "^1.1.0"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+  dependencies:
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+crypto-browserify@^3.11.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
+  integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
+  dependencies:
+    browserify-cipher "^1.0.0"
+    browserify-sign "^4.0.0"
+    create-ecdh "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.0"
+    diffie-hellman "^5.0.0"
+    inherits "^2.0.1"
+    pbkdf2 "^3.0.3"
+    public-encrypt "^4.0.0"
+    randombytes "^2.0.0"
+    randomfill "^1.0.3"
+
+css-blank-pseudo@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5"
+  integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==
+  dependencies:
+    postcss "^7.0.5"
+
+css-color-names@0.0.4, css-color-names@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
+  integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
+
+css-declaration-sorter@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22"
+  integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==
+  dependencies:
+    postcss "^7.0.1"
+    timsort "^0.3.0"
+
+css-has-pseudo@^0.10.0:
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee"
+  integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==
+  dependencies:
+    postcss "^7.0.6"
+    postcss-selector-parser "^5.0.0-rc.4"
+
+css-loader@2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea"
+  integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==
+  dependencies:
+    camelcase "^5.2.0"
+    icss-utils "^4.1.0"
+    loader-utils "^1.2.3"
+    normalize-path "^3.0.0"
+    postcss "^7.0.14"
+    postcss-modules-extract-imports "^2.0.0"
+    postcss-modules-local-by-default "^2.0.6"
+    postcss-modules-scope "^2.1.0"
+    postcss-modules-values "^2.0.0"
+    postcss-value-parser "^3.3.0"
+    schema-utils "^1.0.0"
+
+css-prefers-color-scheme@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4"
+  integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==
+  dependencies:
+    postcss "^7.0.5"
+
+css-select-base-adapter@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
+  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
+
+css-select@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+  integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
+  dependencies:
+    boolbase "~1.0.0"
+    css-what "2.1"
+    domutils "1.5.1"
+    nth-check "~1.0.1"
+
+css-select@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede"
+  integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^2.1.2"
+    domutils "^1.7.0"
+    nth-check "^1.0.2"
+
+css-tree@1.0.0-alpha.29:
+  version "1.0.0-alpha.29"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39"
+  integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==
+  dependencies:
+    mdn-data "~1.1.0"
+    source-map "^0.5.3"
+
+css-tree@1.0.0-alpha.33:
+  version "1.0.0-alpha.33"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.33.tgz#970e20e5a91f7a378ddd0fc58d0b6c8d4f3be93e"
+  integrity sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w==
+  dependencies:
+    mdn-data "2.0.4"
+    source-map "^0.5.3"
+
+css-unit-converter@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996"
+  integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=
+
+css-what@2.1, css-what@^2.1.2:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
+  integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
+
+css@^2.0.0:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
+  integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
+  dependencies:
+    inherits "^2.0.3"
+    source-map "^0.6.1"
+    source-map-resolve "^0.5.2"
+    urix "^0.1.0"
+
+cssdb@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
+  integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==
+
+cssesc@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
+  integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+cssnano-preset-default@^4.0.7:
+  version "4.0.7"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76"
+  integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==
+  dependencies:
+    css-declaration-sorter "^4.0.1"
+    cssnano-util-raw-cache "^4.0.1"
+    postcss "^7.0.0"
+    postcss-calc "^7.0.1"
+    postcss-colormin "^4.0.3"
+    postcss-convert-values "^4.0.1"
+    postcss-discard-comments "^4.0.2"
+    postcss-discard-duplicates "^4.0.2"
+    postcss-discard-empty "^4.0.1"
+    postcss-discard-overridden "^4.0.1"
+    postcss-merge-longhand "^4.0.11"
+    postcss-merge-rules "^4.0.3"
+    postcss-minify-font-values "^4.0.2"
+    postcss-minify-gradients "^4.0.2"
+    postcss-minify-params "^4.0.2"
+    postcss-minify-selectors "^4.0.2"
+    postcss-normalize-charset "^4.0.1"
+    postcss-normalize-display-values "^4.0.2"
+    postcss-normalize-positions "^4.0.2"
+    postcss-normalize-repeat-style "^4.0.2"
+    postcss-normalize-string "^4.0.2"
+    postcss-normalize-timing-functions "^4.0.2"
+    postcss-normalize-unicode "^4.0.1"
+    postcss-normalize-url "^4.0.1"
+    postcss-normalize-whitespace "^4.0.2"
+    postcss-ordered-values "^4.1.2"
+    postcss-reduce-initial "^4.0.3"
+    postcss-reduce-transforms "^4.0.2"
+    postcss-svgo "^4.0.2"
+    postcss-unique-selectors "^4.0.1"
+
+cssnano-util-get-arguments@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f"
+  integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=
+
+cssnano-util-get-match@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d"
+  integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=
+
+cssnano-util-raw-cache@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282"
+  integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==
+  dependencies:
+    postcss "^7.0.0"
+
+cssnano-util-same-parent@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3"
+  integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==
+
+cssnano@^4.1.10:
+  version "4.1.10"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2"
+  integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==
+  dependencies:
+    cosmiconfig "^5.0.0"
+    cssnano-preset-default "^4.0.7"
+    is-resolvable "^1.0.0"
+    postcss "^7.0.0"
+
+csso@^3.5.1:
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b"
+  integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==
+  dependencies:
+    css-tree "1.0.0-alpha.29"
+
+cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@^0.3.4:
+  version "0.3.8"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+  integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+cssstyle@^1.0.0, cssstyle@^1.1.1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1"
+  integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==
+  dependencies:
+    cssom "0.3.x"
+
+csstype@^2.2.0:
+  version "2.6.6"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41"
+  integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==
+
+cyclist@~0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
+  integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
+
+d@1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
+  integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
+  dependencies:
+    es5-ext "^0.10.50"
+    type "^1.0.1"
+
+damerau-levenshtein@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414"
+  integrity sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+  dependencies:
+    assert-plus "^1.0.0"
+
+data-urls@^1.0.0, data-urls@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
+  integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
+  dependencies:
+    abab "^2.0.0"
+    whatwg-mimetype "^2.2.0"
+    whatwg-url "^7.0.0"
+
+date-now@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+  integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.2.5, debug@^3.2.6:
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+  integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+  dependencies:
+    ms "^2.1.1"
+
+decamelize@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+  integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decamelize@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7"
+  integrity sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==
+  dependencies:
+    xregexp "4.0.0"
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+deep-equal@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
+  integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
+
+deep-extend@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+default-gateway@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
+  integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==
+  dependencies:
+    execa "^1.0.0"
+    ip-regex "^2.1.0"
+
+define-properties@^1.1.2, define-properties@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+  dependencies:
+    object-keys "^1.0.12"
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+del@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
+  integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
+  dependencies:
+    globby "^6.1.0"
+    is-path-cwd "^1.0.0"
+    is-path-in-cwd "^1.0.0"
+    p-map "^1.1.1"
+    pify "^3.0.0"
+    rimraf "^2.2.8"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+depd@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+des.js@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
+  integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+destroy@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+detect-libc@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
+detect-newline@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
+  integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=
+
+detect-node@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
+  integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
+
+detect-port-alt@1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
+  integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==
+  dependencies:
+    address "^1.0.1"
+    debug "^2.6.0"
+
+diff-sequences@^24.3.0:
+  version "24.3.0"
+  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975"
+  integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==
+
+diffie-hellman@^5.0.0:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
+  integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
+dir-glob@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
+  integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==
+  dependencies:
+    arrify "^1.0.1"
+    path-type "^3.0.0"
+
+dns-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
+  integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0=
+
+dns-packet@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a"
+  integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==
+  dependencies:
+    ip "^1.1.0"
+    safe-buffer "^5.0.1"
+
+dns-txt@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6"
+  integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=
+  dependencies:
+    buffer-indexof "^1.0.0"
+
+doctrine@1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+  integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+doctrine@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+  integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
+  dependencies:
+    esutils "^2.0.2"
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
+dom-converter@^0.2:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
+  integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
+  dependencies:
+    utila "~0.4"
+
+dom-serializer@0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.1.tgz#13650c850daffea35d8b626a4cfc4d3a17643fdb"
+  integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==
+  dependencies:
+    domelementtype "^2.0.1"
+    entities "^2.0.0"
+
+domain-browser@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
+  integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
+
+domelementtype@1, domelementtype@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+  integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
+domexception@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
+  integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
+  dependencies:
+    webidl-conversions "^4.0.2"
+
+domhandler@^2.3.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
+  integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
+  dependencies:
+    domelementtype "1"
+
+domutils@1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+  integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+domutils@^1.5.1, domutils@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+  integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+dot-prop@^4.1.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
+  integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
+  dependencies:
+    is-obj "^1.0.0"
+
+dotenv-expand@4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275"
+  integrity sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU=
+
+dotenv@6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
+  integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
+
+duplexer@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
+  integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
+
+duplexify@^3.4.2, duplexify@^3.6.0:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
+  integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
+  dependencies:
+    end-of-stream "^1.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.0.0"
+    stream-shift "^1.0.0"
+
+ecc-jsbn@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+  integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+  dependencies:
+    jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+electron-to-chromium@^1.3.191:
+  version "1.3.225"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.225.tgz#c6786475b5eb5f491ade01a78b82ba2c5bfdf72b"
+  integrity sha512-7W/L3jw7HYE+tUPbcVOGBmnSrlUmyZ/Uyg24QS7Vx0a9KodtNrN0r0Q/LyGHrcYMtw2rv7E49F/vTXwlV/fuaA==
+
+elliptic@^6.0.0:
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca"
+  integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==
+  dependencies:
+    bn.js "^4.4.0"
+    brorand "^1.0.1"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.0"
+
+emoji-regex@^7.0.1, emoji-regex@^7.0.2:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+  integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emojis-list@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
+  integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
+
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+  integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
+  dependencies:
+    once "^1.4.0"
+
+enhanced-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
+  integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.4.0"
+    tapable "^1.0.0"
+
+entities@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
+  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+
+entities@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+  integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
+
+errno@^0.1.3, errno@~0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
+  integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
+  dependencies:
+    prr "~1.0.1"
+
+error-ex@^1.2.0, error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
+  integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==
+  dependencies:
+    es-to-primitive "^1.2.0"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    is-callable "^1.1.4"
+    is-regex "^1.0.4"
+    object-keys "^1.0.12"
+
+es-to-primitive@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
+  integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
+  dependencies:
+    is-callable "^1.1.4"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.2"
+
+es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14:
+  version "0.10.50"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778"
+  integrity sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==
+  dependencies:
+    es6-iterator "~2.0.3"
+    es6-symbol "~3.1.1"
+    next-tick "^1.0.0"
+
+es6-iterator@2.0.3, es6-iterator@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+  integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
+  dependencies:
+    d "1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
+
+es6-symbol@^3.1.1, es6-symbol@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
+  integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escodegen@^1.11.0, escodegen@^1.9.1:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541"
+  integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==
+  dependencies:
+    esprima "^3.1.3"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    optionator "^0.8.1"
+  optionalDependencies:
+    source-map "~0.6.1"
+
+eslint-config-react-app@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.0.1.tgz#5f3d666ba3ee3cb384eb943e260e868f6c72251b"
+  integrity sha512-GYXP3F/0PSHlYfGHhahqnJze8rYKxzXgrzXVqRRd4rDO40ga4NA3aHM7/HKbwceDN0/C1Ij3BoAWFawJgRbXEw==
+  dependencies:
+    confusing-browser-globals "^1.0.8"
+
+eslint-import-resolver-node@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
+  integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==
+  dependencies:
+    debug "^2.6.9"
+    resolve "^1.5.0"
+
+eslint-loader@2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337"
+  integrity sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==
+  dependencies:
+    loader-fs-cache "^1.0.0"
+    loader-utils "^1.0.2"
+    object-assign "^4.0.1"
+    object-hash "^1.1.4"
+    rimraf "^2.6.1"
+
+eslint-module-utils@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c"
+  integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==
+  dependencies:
+    debug "^2.6.8"
+    pkg-dir "^2.0.0"
+
+eslint-plugin-flowtype@3.13.0:
+  version "3.13.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.13.0.tgz#e241ebd39c0ce519345a3f074ec1ebde4cf80f2c"
+  integrity sha512-bhewp36P+t7cEV0b6OdmoRWJCBYRiHFlqPZAG1oS3SF+Y0LQkeDvFSM4oxoxvczD1OdONCXMlJfQFiWLcV9urw==
+  dependencies:
+    lodash "^4.17.15"
+
+eslint-plugin-import@2.18.2:
+  version "2.18.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6"
+  integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==
+  dependencies:
+    array-includes "^3.0.3"
+    contains-path "^0.1.0"
+    debug "^2.6.9"
+    doctrine "1.5.0"
+    eslint-import-resolver-node "^0.3.2"
+    eslint-module-utils "^2.4.0"
+    has "^1.0.3"
+    minimatch "^3.0.4"
+    object.values "^1.1.0"
+    read-pkg-up "^2.0.0"
+    resolve "^1.11.0"
+
+eslint-plugin-jsx-a11y@6.2.3:
+  version "6.2.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa"
+  integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==
+  dependencies:
+    "@babel/runtime" "^7.4.5"
+    aria-query "^3.0.0"
+    array-includes "^3.0.3"
+    ast-types-flow "^0.0.7"
+    axobject-query "^2.0.2"
+    damerau-levenshtein "^1.0.4"
+    emoji-regex "^7.0.2"
+    has "^1.0.3"
+    jsx-ast-utils "^2.2.1"
+
+eslint-plugin-react-hooks@^1.6.1:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"
+  integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==
+
+eslint-plugin-react@7.14.3:
+  version "7.14.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13"
+  integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==
+  dependencies:
+    array-includes "^3.0.3"
+    doctrine "^2.1.0"
+    has "^1.0.3"
+    jsx-ast-utils "^2.1.0"
+    object.entries "^1.1.0"
+    object.fromentries "^2.0.0"
+    object.values "^1.1.0"
+    prop-types "^15.7.2"
+    resolve "^1.10.1"
+
+eslint-scope@3.7.1:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+  integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-scope@^4.0.0, eslint-scope@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
+  integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-scope@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
+  integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-utils@^1.3.1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.0.tgz#e2c3c8dba768425f897cf0f9e51fe2e241485d4c"
+  integrity sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==
+  dependencies:
+    eslint-visitor-keys "^1.0.0"
+
+eslint-visitor-keys@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
+  integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
+
+eslint@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.1.0.tgz#06438a4a278b1d84fb107d24eaaa35471986e646"
+  integrity sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    ajv "^6.10.0"
+    chalk "^2.1.0"
+    cross-spawn "^6.0.5"
+    debug "^4.0.1"
+    doctrine "^3.0.0"
+    eslint-scope "^5.0.0"
+    eslint-utils "^1.3.1"
+    eslint-visitor-keys "^1.0.0"
+    espree "^6.0.0"
+    esquery "^1.0.1"
+    esutils "^2.0.2"
+    file-entry-cache "^5.0.1"
+    functional-red-black-tree "^1.0.1"
+    glob-parent "^5.0.0"
+    globals "^11.7.0"
+    ignore "^4.0.6"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    inquirer "^6.4.1"
+    is-glob "^4.0.0"
+    js-yaml "^3.13.1"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.3.0"
+    lodash "^4.17.14"
+    minimatch "^3.0.4"
+    mkdirp "^0.5.1"
+    natural-compare "^1.4.0"
+    optionator "^0.8.2"
+    progress "^2.0.0"
+    regexpp "^2.0.1"
+    semver "^6.1.2"
+    strip-ansi "^5.2.0"
+    strip-json-comments "^3.0.1"
+    table "^5.2.3"
+    text-table "^0.2.0"
+    v8-compile-cache "^2.0.3"
+
+espree@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-6.0.0.tgz#716fc1f5a245ef5b9a7fdb1d7b0d3f02322e75f6"
+  integrity sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==
+  dependencies:
+    acorn "^6.0.7"
+    acorn-jsx "^5.0.0"
+    eslint-visitor-keys "^1.0.0"
+
+esprima@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+  integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
+
+esprima@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esquery@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
+  integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
+  dependencies:
+    estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+  integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
+  dependencies:
+    estraverse "^4.1.0"
+
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+  integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
+
+esutils@^2.0.0, esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+eventemitter3@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
+  integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
+
+events@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
+  integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
+
+eventsource@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0"
+  integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==
+  dependencies:
+    original "^1.0.0"
+
+evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+  integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
+  dependencies:
+    md5.js "^1.3.4"
+    safe-buffer "^5.1.1"
+
+exec-sh@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b"
+  integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==
+
+execa@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+  integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+  dependencies:
+    cross-spawn "^6.0.0"
+    get-stream "^4.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
+exit@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+  integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    debug "^2.3.3"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    posix-character-classes "^0.1.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+expect@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-24.8.0.tgz#471f8ec256b7b6129ca2524b2a62f030df38718d"
+  integrity sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    ansi-styles "^3.2.0"
+    jest-get-type "^24.8.0"
+    jest-matcher-utils "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-regex-util "^24.3.0"
+
+express@^4.16.2:
+  version "4.17.1"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
+  integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
+  dependencies:
+    accepts "~1.3.7"
+    array-flatten "1.1.1"
+    body-parser "1.19.0"
+    content-disposition "0.5.3"
+    content-type "~1.0.4"
+    cookie "0.4.0"
+    cookie-signature "1.0.6"
+    debug "2.6.9"
+    depd "~1.1.2"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    finalhandler "~1.1.2"
+    fresh "0.5.2"
+    merge-descriptors "1.0.1"
+    methods "~1.1.2"
+    on-finished "~2.3.0"
+    parseurl "~1.3.3"
+    path-to-regexp "0.1.7"
+    proxy-addr "~2.0.5"
+    qs "6.7.0"
+    range-parser "~1.2.1"
+    safe-buffer "5.1.2"
+    send "0.17.1"
+    serve-static "1.14.1"
+    setprototypeof "1.1.1"
+    statuses "~1.5.0"
+    type-is "~1.6.18"
+    utils-merge "1.0.1"
+    vary "~1.1.2"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extend@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+  integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+external-editor@^3.0.3:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+  integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+  dependencies:
+    chardet "^0.7.0"
+    iconv-lite "^0.4.24"
+    tmp "^0.0.33"
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    array-unique "^0.3.2"
+    define-property "^1.0.0"
+    expand-brackets "^2.1.4"
+    extend-shallow "^2.0.1"
+    fragment-cache "^0.2.1"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extsprintf@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+  integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+fast-deep-equal@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
+  integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
+
+fast-glob@^2.0.2:
+  version "2.2.7"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
+  integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
+  dependencies:
+    "@mrmlnc/readdir-enhanced" "^2.2.1"
+    "@nodelib/fs.stat" "^1.1.2"
+    glob-parent "^3.1.0"
+    is-glob "^4.0.0"
+    merge2 "^1.2.3"
+    micromatch "^3.1.10"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
+
+fast-levenshtein@~2.0.4:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+faye-websocket@^0.10.0:
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
+  integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=
+  dependencies:
+    websocket-driver ">=0.5.1"
+
+faye-websocket@~0.11.1:
+  version "0.11.3"
+  resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
+  integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==
+  dependencies:
+    websocket-driver ">=0.5.1"
+
+fb-watchman@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
+  integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=
+  dependencies:
+    bser "^2.0.0"
+
+figgy-pudding@^3.5.1:
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
+  integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
+
+figures@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+  integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+figures@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9"
+  integrity sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+file-entry-cache@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
+  integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
+  dependencies:
+    flat-cache "^2.0.1"
+
+file-loader@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa"
+  integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==
+  dependencies:
+    loader-utils "^1.0.2"
+    schema-utils "^1.0.0"
+
+filesize@3.6.1:
+  version "3.6.1"
+  resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
+  integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+finalhandler@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
+  integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+  dependencies:
+    debug "2.6.9"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    on-finished "~2.3.0"
+    parseurl "~1.3.3"
+    statuses "~1.5.0"
+    unpipe "~1.0.0"
+
+find-cache-dir@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
+  integrity sha1-yN765XyKUqinhPnjHFfHQumToLk=
+  dependencies:
+    commondir "^1.0.1"
+    mkdirp "^0.5.1"
+    pkg-dir "^1.0.0"
+
+find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+  integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^2.0.0"
+    pkg-dir "^3.0.0"
+
+find-up@3.0.0, find-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+  dependencies:
+    locate-path "^3.0.0"
+
+find-up@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+  integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
+  dependencies:
+    path-exists "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+find-up@^2.0.0, find-up@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+  integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+  dependencies:
+    locate-path "^2.0.0"
+
+flat-cache@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
+  integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
+  dependencies:
+    flatted "^2.0.0"
+    rimraf "2.6.3"
+    write "1.0.3"
+
+flatted@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
+  integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
+
+flatten@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
+  integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
+
+flush-write-stream@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
+  integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
+  dependencies:
+    inherits "^2.0.3"
+    readable-stream "^2.3.6"
+
+follow-redirects@^1.0.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
+  integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==
+  dependencies:
+    debug "^3.2.6"
+
+for-in@^0.1.3:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
+  integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=
+
+for-in@^1.0.1, for-in@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+for-own@^0.1.3:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+  integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=
+  dependencies:
+    for-in "^1.0.1"
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+  integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+fork-ts-checker-webpack-plugin@1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz#ce1d77190b44d81a761b10b6284a373795e41f0c"
+  integrity sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA==
+  dependencies:
+    babel-code-frame "^6.22.0"
+    chalk "^2.4.1"
+    chokidar "^2.0.4"
+    micromatch "^3.1.10"
+    minimatch "^3.0.4"
+    semver "^5.6.0"
+    tapable "^1.0.0"
+    worker-rpc "^0.1.0"
+
+form-data@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.6"
+    mime-types "^2.1.12"
+
+forwarded@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
+  integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+from2@^2.1.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
+  integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
+  dependencies:
+    inherits "^2.0.1"
+    readable-stream "^2.0.0"
+
+fs-extra@7.0.1, fs-extra@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+  integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs-extra@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
+  integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs-minipass@^1.2.5:
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
+  integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==
+  dependencies:
+    minipass "^2.2.1"
+
+fs-write-stream-atomic@^1.0.8:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
+  integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=
+  dependencies:
+    graceful-fs "^4.1.2"
+    iferr "^0.1.5"
+    imurmurhash "^0.1.4"
+    readable-stream "1 || 2"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@2.0.7:
+  version "2.0.7"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a"
+  integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
+
+fsevents@^1.2.7:
+  version "1.2.9"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
+  integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
+  dependencies:
+    nan "^2.12.1"
+    node-pre-gyp "^0.12.0"
+
+function-bind@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+functional-red-black-tree@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
+get-caller-file@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
+  integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
+
+get-own-enumerable-property-symbols@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203"
+  integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==
+
+get-stream@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+  integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+  dependencies:
+    pump "^3.0.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-parent@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
+  integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
+  dependencies:
+    is-glob "^3.1.0"
+    path-dirname "^1.0.0"
+
+glob-parent@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
+  integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-to-regexp@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
+  integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+
+glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
+  version "7.1.4"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
+  integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+global-modules@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
+  integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
+  dependencies:
+    global-prefix "^3.0.0"
+
+global-prefix@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+  integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+  dependencies:
+    ini "^1.3.5"
+    kind-of "^6.0.2"
+    which "^1.3.1"
+
+globals@^11.1.0, globals@^11.7.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globby@8.0.2:
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d"
+  integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==
+  dependencies:
+    array-union "^1.0.1"
+    dir-glob "2.0.0"
+    fast-glob "^2.0.2"
+    glob "^7.1.2"
+    ignore "^3.3.5"
+    pify "^3.0.0"
+    slash "^1.0.0"
+
+globby@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
+  integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
+  dependencies:
+    array-union "^1.0.1"
+    glob "^7.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.1.tgz#1c1f0c364882c868f5bff6512146328336a11b1d"
+  integrity sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==
+
+growly@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+  integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+
+gzip-size@5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274"
+  integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==
+  dependencies:
+    duplexer "^0.1.1"
+    pify "^4.0.1"
+
+handle-thing@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
+  integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
+
+handlebars@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
+  integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
+  dependencies:
+    neo-async "^2.6.0"
+    optimist "^0.6.1"
+    source-map "^0.6.1"
+  optionalDependencies:
+    uglify-js "^3.1.4"
+
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.0:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
+  integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+  dependencies:
+    ajv "^6.5.5"
+    har-schema "^2.0.0"
+
+harmony-reflect@^1.4.6:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9"
+  integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
+  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+has@^1.0.0, has@^1.0.1, has@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  dependencies:
+    function-bind "^1.1.1"
+
+hash-base@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+  integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+  integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+  dependencies:
+    inherits "^2.0.3"
+    minimalistic-assert "^1.0.1"
+
+he@1.2.x:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+hex-color-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
+  integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
+
+hmac-drbg@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+  integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
+hosted-git-info@^2.1.4:
+  version "2.8.4"
+  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
+  integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
+
+hpack.js@^2.1.6:
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
+  integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=
+  dependencies:
+    inherits "^2.0.1"
+    obuf "^1.0.0"
+    readable-stream "^2.0.1"
+    wbuf "^1.1.0"
+
+hsl-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e"
+  integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=
+
+hsla-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38"
+  integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
+
+html-comment-regex@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
+  integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
+
+html-encoding-sniffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
+  integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
+  dependencies:
+    whatwg-encoding "^1.0.1"
+
+html-entities@^1.2.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
+  integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
+
+html-minifier@^3.5.20:
+  version "3.5.21"
+  resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
+  integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
+  dependencies:
+    camel-case "3.0.x"
+    clean-css "4.2.x"
+    commander "2.17.x"
+    he "1.2.x"
+    param-case "2.1.x"
+    relateurl "0.2.x"
+    uglify-js "3.4.x"
+
+html-webpack-plugin@4.0.0-beta.5:
+  version "4.0.0-beta.5"
+  resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz#2c53083c1151bfec20479b1f8aaf0039e77b5513"
+  integrity sha512-y5l4lGxOW3pz3xBTFdfB9rnnrWRPVxlAhX6nrBYIcW+2k2zC3mSp/3DxlWVCMBfnO6UAnoF8OcFn0IMy6kaKAQ==
+  dependencies:
+    html-minifier "^3.5.20"
+    loader-utils "^1.1.0"
+    lodash "^4.17.11"
+    pretty-error "^2.1.1"
+    tapable "^1.1.0"
+    util.promisify "1.0.0"
+
+htmlparser2@^3.3.0:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
+  integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+  dependencies:
+    domelementtype "^1.3.1"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^3.1.1"
+
+http-deceiver@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+  integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=
+
+http-errors@1.7.2:
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
+  integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.3"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
+http-errors@~1.6.2:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+  integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.3"
+    setprototypeof "1.1.0"
+    statuses ">= 1.4.0 < 2"
+
+http-errors@~1.7.2:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
+  integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.4"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
+"http-parser-js@>=0.4.0 <0.4.11":
+  version "0.4.10"
+  resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4"
+  integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=
+
+http-proxy-middleware@^0.19.1:
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
+  integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==
+  dependencies:
+    http-proxy "^1.17.0"
+    is-glob "^4.0.0"
+    lodash "^4.17.11"
+    micromatch "^3.1.10"
+
+http-proxy@^1.17.0:
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a"
+  integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==
+  dependencies:
+    eventemitter3 "^3.0.0"
+    follow-redirects "^1.0.0"
+    requires-port "^1.0.0"
+
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+https-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+  integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
+
+iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+icss-replace-symbols@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+  integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
+
+icss-utils@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
+  integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==
+  dependencies:
+    postcss "^7.0.14"
+
+identity-obj-proxy@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14"
+  integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=
+  dependencies:
+    harmony-reflect "^1.4.6"
+
+ieee754@^1.1.4:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+  integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+
+iferr@^0.1.5:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
+  integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
+
+ignore-walk@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
+  integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
+  dependencies:
+    minimatch "^3.0.4"
+
+ignore@^3.3.5:
+  version "3.3.10"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
+  integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
+
+ignore@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+  integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
+immer@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
+  integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
+
+import-cwd@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
+  integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=
+  dependencies:
+    import-from "^2.1.0"
+
+import-fresh@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+  integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+  dependencies:
+    caller-path "^2.0.0"
+    resolve-from "^3.0.0"
+
+import-fresh@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118"
+  integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+import-from@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1"
+  integrity sha1-M1238qev/VOqpHHUuAId7ja387E=
+  dependencies:
+    resolve-from "^3.0.0"
+
+import-local@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
+  integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
+  dependencies:
+    pkg-dir "^3.0.0"
+    resolve-cwd "^2.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+indexes-of@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
+infer-owner@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
+  integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inherits@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+  integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
+
+inherits@2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+ini@^1.3.5, ini@~1.3.0:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+inquirer@6.5.0:
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42"
+  integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==
+  dependencies:
+    ansi-escapes "^3.2.0"
+    chalk "^2.4.2"
+    cli-cursor "^2.1.0"
+    cli-width "^2.0.0"
+    external-editor "^3.0.3"
+    figures "^2.0.0"
+    lodash "^4.17.12"
+    mute-stream "0.0.7"
+    run-async "^2.2.0"
+    rxjs "^6.4.0"
+    string-width "^2.1.0"
+    strip-ansi "^5.1.0"
+    through "^2.3.6"
+
+inquirer@^6.4.1:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.1.tgz#8bfb7a5ac02dac6ff641ac4c5ff17da112fcdb42"
+  integrity sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==
+  dependencies:
+    ansi-escapes "^4.2.1"
+    chalk "^2.4.2"
+    cli-cursor "^3.1.0"
+    cli-width "^2.0.0"
+    external-editor "^3.0.3"
+    figures "^3.0.0"
+    lodash "^4.17.15"
+    mute-stream "0.0.8"
+    run-async "^2.2.0"
+    rxjs "^6.4.0"
+    string-width "^4.1.0"
+    strip-ansi "^5.1.0"
+    through "^2.3.6"
+
+internal-ip@^4.2.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
+  integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==
+  dependencies:
+    default-gateway "^4.2.0"
+    ipaddr.js "^1.9.0"
+
+invariant@^2.2.2, invariant@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+  integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+  dependencies:
+    loose-envify "^1.0.0"
+
+invert-kv@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
+  integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
+
+ip-regex@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+  integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+
+ip@^1.1.0, ip@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
+  integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
+
+ipaddr.js@1.9.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
+  integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
+
+ipaddr.js@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+is-absolute-url@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
+  integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+  integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.0.2, is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+  integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-callable@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
+  integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
+
+is-ci@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+  integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+  dependencies:
+    ci-info "^2.0.0"
+
+is-color-stop@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
+  integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=
+  dependencies:
+    css-color-names "^0.0.4"
+    hex-color-regex "^1.1.0"
+    hsl-regex "^1.0.0"
+    hsla-regex "^1.0.0"
+    rgb-regex "^1.0.1"
+    rgba-regex "^1.0.0"
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-date-object@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-directory@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+  integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.0, is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-generator-fn@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
+  integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
+
+is-glob@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
+  integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
+  dependencies:
+    is-extglob "^2.1.0"
+
+is-glob@^4.0.0, is-glob@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-obj@^1.0.0, is-obj@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+  integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
+
+is-path-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+  integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
+
+is-path-in-cwd@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
+  integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
+  dependencies:
+    is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
+  integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+  dependencies:
+    path-is-inside "^1.0.1"
+
+is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-promise@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+  integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
+
+is-regex@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
+  dependencies:
+    has "^1.0.1"
+
+is-regexp@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+  integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
+
+is-resolvable@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
+  integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
+
+is-root@2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
+  integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==
+
+is-stream@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+  integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-svg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75"
+  integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==
+  dependencies:
+    html-comment-regex "^1.1.0"
+
+is-symbol@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
+  integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
+  dependencies:
+    has-symbols "^1.0.0"
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+  integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-windows@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-wsl@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+  integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49"
+  integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==
+
+istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630"
+  integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==
+  dependencies:
+    "@babel/generator" "^7.4.0"
+    "@babel/parser" "^7.4.3"
+    "@babel/template" "^7.4.0"
+    "@babel/traverse" "^7.4.3"
+    "@babel/types" "^7.4.0"
+    istanbul-lib-coverage "^2.0.5"
+    semver "^6.0.0"
+
+istanbul-lib-report@^2.0.4:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33"
+  integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==
+  dependencies:
+    istanbul-lib-coverage "^2.0.5"
+    make-dir "^2.1.0"
+    supports-color "^6.1.0"
+
+istanbul-lib-source-maps@^3.0.1:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8"
+  integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==
+  dependencies:
+    debug "^4.1.1"
+    istanbul-lib-coverage "^2.0.5"
+    make-dir "^2.1.0"
+    rimraf "^2.6.3"
+    source-map "^0.6.1"
+
+istanbul-reports@^2.1.1:
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af"
+  integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==
+  dependencies:
+    handlebars "^4.1.2"
+
+jest-changed-files@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.8.0.tgz#7e7eb21cf687587a85e50f3d249d1327e15b157b"
+  integrity sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    execa "^1.0.0"
+    throat "^4.0.0"
+
+jest-cli@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.8.0.tgz#b075ac914492ed114fa338ade7362a301693e989"
+  integrity sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==
+  dependencies:
+    "@jest/core" "^24.8.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    chalk "^2.0.1"
+    exit "^0.1.2"
+    import-local "^2.0.0"
+    is-ci "^2.0.0"
+    jest-config "^24.8.0"
+    jest-util "^24.8.0"
+    jest-validate "^24.8.0"
+    prompts "^2.0.1"
+    realpath-native "^1.1.0"
+    yargs "^12.0.2"
+
+jest-config@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.8.0.tgz#77db3d265a6f726294687cbbccc36f8a76ee0f4f"
+  integrity sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==
+  dependencies:
+    "@babel/core" "^7.1.0"
+    "@jest/test-sequencer" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    babel-jest "^24.8.0"
+    chalk "^2.0.1"
+    glob "^7.1.1"
+    jest-environment-jsdom "^24.8.0"
+    jest-environment-node "^24.8.0"
+    jest-get-type "^24.8.0"
+    jest-jasmine2 "^24.8.0"
+    jest-regex-util "^24.3.0"
+    jest-resolve "^24.8.0"
+    jest-util "^24.8.0"
+    jest-validate "^24.8.0"
+    micromatch "^3.1.10"
+    pretty-format "^24.8.0"
+    realpath-native "^1.1.0"
+
+jest-diff@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.8.0.tgz#146435e7d1e3ffdf293d53ff97e193f1d1546172"
+  integrity sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==
+  dependencies:
+    chalk "^2.0.1"
+    diff-sequences "^24.3.0"
+    jest-get-type "^24.8.0"
+    pretty-format "^24.8.0"
+
+jest-docblock@^24.3.0:
+  version "24.3.0"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.3.0.tgz#b9c32dac70f72e4464520d2ba4aec02ab14db5dd"
+  integrity sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==
+  dependencies:
+    detect-newline "^2.1.0"
+
+jest-each@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.8.0.tgz#a05fd2bf94ddc0b1da66c6d13ec2457f35e52775"
+  integrity sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    chalk "^2.0.1"
+    jest-get-type "^24.8.0"
+    jest-util "^24.8.0"
+    pretty-format "^24.8.0"
+
+jest-environment-jsdom-fourteen@0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-0.1.0.tgz#aad6393a9d4b565b69a609109bf469f62bf18ccc"
+  integrity sha512-4vtoRMg7jAstitRzL4nbw83VmGH8Rs13wrND3Ud2o1fczDhMUF32iIrNKwYGgeOPUdfvZU4oy8Bbv+ni1fgVCA==
+  dependencies:
+    jest-mock "^24.5.0"
+    jest-util "^24.5.0"
+    jsdom "^14.0.0"
+
+jest-environment-jsdom@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz#300f6949a146cabe1c9357ad9e9ecf9f43f38857"
+  integrity sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==
+  dependencies:
+    "@jest/environment" "^24.8.0"
+    "@jest/fake-timers" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    jest-mock "^24.8.0"
+    jest-util "^24.8.0"
+    jsdom "^11.5.1"
+
+jest-environment-node@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.8.0.tgz#d3f726ba8bc53087a60e7a84ca08883a4c892231"
+  integrity sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==
+  dependencies:
+    "@jest/environment" "^24.8.0"
+    "@jest/fake-timers" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    jest-mock "^24.8.0"
+    jest-util "^24.8.0"
+
+jest-get-type@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.8.0.tgz#a7440de30b651f5a70ea3ed7ff073a32dfe646fc"
+  integrity sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==
+
+jest-haste-map@^24.8.0:
+  version "24.8.1"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.8.1.tgz#f39cc1d2b1d907e014165b4bd5a957afcb992982"
+  integrity sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    anymatch "^2.0.0"
+    fb-watchman "^2.0.0"
+    graceful-fs "^4.1.15"
+    invariant "^2.2.4"
+    jest-serializer "^24.4.0"
+    jest-util "^24.8.0"
+    jest-worker "^24.6.0"
+    micromatch "^3.1.10"
+    sane "^4.0.3"
+    walker "^1.0.7"
+  optionalDependencies:
+    fsevents "^1.2.7"
+
+jest-jasmine2@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz#a9c7e14c83dd77d8b15e820549ce8987cc8cd898"
+  integrity sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==
+  dependencies:
+    "@babel/traverse" "^7.1.0"
+    "@jest/environment" "^24.8.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    chalk "^2.0.1"
+    co "^4.6.0"
+    expect "^24.8.0"
+    is-generator-fn "^2.0.0"
+    jest-each "^24.8.0"
+    jest-matcher-utils "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-runtime "^24.8.0"
+    jest-snapshot "^24.8.0"
+    jest-util "^24.8.0"
+    pretty-format "^24.8.0"
+    throat "^4.0.0"
+
+jest-leak-detector@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz#c0086384e1f650c2d8348095df769f29b48e6980"
+  integrity sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==
+  dependencies:
+    pretty-format "^24.8.0"
+
+jest-matcher-utils@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz#2bce42204c9af12bde46f83dc839efe8be832495"
+  integrity sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==
+  dependencies:
+    chalk "^2.0.1"
+    jest-diff "^24.8.0"
+    jest-get-type "^24.8.0"
+    pretty-format "^24.8.0"
+
+jest-message-util@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.8.0.tgz#0d6891e72a4beacc0292b638685df42e28d6218b"
+  integrity sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    "@types/stack-utils" "^1.0.1"
+    chalk "^2.0.1"
+    micromatch "^3.1.10"
+    slash "^2.0.0"
+    stack-utils "^1.0.1"
+
+jest-mock@^24.5.0, jest-mock@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.8.0.tgz#2f9d14d37699e863f1febf4e4d5a33b7fdbbde56"
+  integrity sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==
+  dependencies:
+    "@jest/types" "^24.8.0"
+
+jest-pnp-resolver@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a"
+  integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==
+
+jest-regex-util@^24.3.0:
+  version "24.3.0"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.3.0.tgz#d5a65f60be1ae3e310d5214a0307581995227b36"
+  integrity sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==
+
+jest-resolve-dependencies@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz#19eec3241f2045d3f990dba331d0d7526acff8e0"
+  integrity sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    jest-regex-util "^24.3.0"
+    jest-snapshot "^24.8.0"
+
+jest-resolve@24.8.0, jest-resolve@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.8.0.tgz#84b8e5408c1f6a11539793e2b5feb1b6e722439f"
+  integrity sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    browser-resolve "^1.11.3"
+    chalk "^2.0.1"
+    jest-pnp-resolver "^1.2.1"
+    realpath-native "^1.1.0"
+
+jest-runner@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.8.0.tgz#4f9ae07b767db27b740d7deffad0cf67ccb4c5bb"
+  integrity sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==
+  dependencies:
+    "@jest/console" "^24.7.1"
+    "@jest/environment" "^24.8.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    chalk "^2.4.2"
+    exit "^0.1.2"
+    graceful-fs "^4.1.15"
+    jest-config "^24.8.0"
+    jest-docblock "^24.3.0"
+    jest-haste-map "^24.8.0"
+    jest-jasmine2 "^24.8.0"
+    jest-leak-detector "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-resolve "^24.8.0"
+    jest-runtime "^24.8.0"
+    jest-util "^24.8.0"
+    jest-worker "^24.6.0"
+    source-map-support "^0.5.6"
+    throat "^4.0.0"
+
+jest-runtime@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.8.0.tgz#05f94d5b05c21f6dc54e427cd2e4980923350620"
+  integrity sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==
+  dependencies:
+    "@jest/console" "^24.7.1"
+    "@jest/environment" "^24.8.0"
+    "@jest/source-map" "^24.3.0"
+    "@jest/transform" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    "@types/yargs" "^12.0.2"
+    chalk "^2.0.1"
+    exit "^0.1.2"
+    glob "^7.1.3"
+    graceful-fs "^4.1.15"
+    jest-config "^24.8.0"
+    jest-haste-map "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-mock "^24.8.0"
+    jest-regex-util "^24.3.0"
+    jest-resolve "^24.8.0"
+    jest-snapshot "^24.8.0"
+    jest-util "^24.8.0"
+    jest-validate "^24.8.0"
+    realpath-native "^1.1.0"
+    slash "^2.0.0"
+    strip-bom "^3.0.0"
+    yargs "^12.0.2"
+
+jest-serializer@^24.4.0:
+  version "24.4.0"
+  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.4.0.tgz#f70c5918c8ea9235ccb1276d232e459080588db3"
+  integrity sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==
+
+jest-snapshot@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.8.0.tgz#3bec6a59da2ff7bc7d097a853fb67f9d415cb7c6"
+  integrity sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==
+  dependencies:
+    "@babel/types" "^7.0.0"
+    "@jest/types" "^24.8.0"
+    chalk "^2.0.1"
+    expect "^24.8.0"
+    jest-diff "^24.8.0"
+    jest-matcher-utils "^24.8.0"
+    jest-message-util "^24.8.0"
+    jest-resolve "^24.8.0"
+    mkdirp "^0.5.1"
+    natural-compare "^1.4.0"
+    pretty-format "^24.8.0"
+    semver "^5.5.0"
+
+jest-util@^24.5.0, jest-util@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.8.0.tgz#41f0e945da11df44cc76d64ffb915d0716f46cd1"
+  integrity sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==
+  dependencies:
+    "@jest/console" "^24.7.1"
+    "@jest/fake-timers" "^24.8.0"
+    "@jest/source-map" "^24.3.0"
+    "@jest/test-result" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    callsites "^3.0.0"
+    chalk "^2.0.1"
+    graceful-fs "^4.1.15"
+    is-ci "^2.0.0"
+    mkdirp "^0.5.1"
+    slash "^2.0.0"
+    source-map "^0.6.0"
+
+jest-validate@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.8.0.tgz#624c41533e6dfe356ffadc6e2423a35c2d3b4849"
+  integrity sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    camelcase "^5.0.0"
+    chalk "^2.0.1"
+    jest-get-type "^24.8.0"
+    leven "^2.1.0"
+    pretty-format "^24.8.0"
+
+jest-watch-typeahead@0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.3.1.tgz#47701024b64b444aa325d801b4b3a6d61ed70701"
+  integrity sha512-cDIko96c4Yqg/7mfye1eEYZ6Pvugo9mnOOhGQod3Es7/KptNv1b+9gFVaotzdqNqTlwbkA80BnWHtzV4dc+trA==
+  dependencies:
+    ansi-escapes "^3.0.0"
+    chalk "^2.4.1"
+    jest-watcher "^24.3.0"
+    slash "^2.0.0"
+    string-length "^2.0.0"
+    strip-ansi "^5.0.0"
+
+jest-watcher@^24.3.0, jest-watcher@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.8.0.tgz#58d49915ceddd2de85e238f6213cef1c93715de4"
+  integrity sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==
+  dependencies:
+    "@jest/test-result" "^24.8.0"
+    "@jest/types" "^24.8.0"
+    "@types/yargs" "^12.0.9"
+    ansi-escapes "^3.0.0"
+    chalk "^2.0.1"
+    jest-util "^24.8.0"
+    string-length "^2.0.0"
+
+jest-worker@^24.6.0:
+  version "24.6.0"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.6.0.tgz#7f81ceae34b7cde0c9827a6980c35b7cdc0161b3"
+  integrity sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==
+  dependencies:
+    merge-stream "^1.0.1"
+    supports-color "^6.1.0"
+
+jest@24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-24.8.0.tgz#d5dff1984d0d1002196e9b7f12f75af1b2809081"
+  integrity sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==
+  dependencies:
+    import-local "^2.0.0"
+    jest-cli "^24.8.0"
+
+js-levenshtein@^1.1.3:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
+  integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-tokens@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+  integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
+
+js-yaml@^3.13.1:
+  version "3.13.1"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+  integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+  integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+jsdom@^11.5.1:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
+  integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==
+  dependencies:
+    abab "^2.0.0"
+    acorn "^5.5.3"
+    acorn-globals "^4.1.0"
+    array-equal "^1.0.0"
+    cssom ">= 0.3.2 < 0.4.0"
+    cssstyle "^1.0.0"
+    data-urls "^1.0.0"
+    domexception "^1.0.1"
+    escodegen "^1.9.1"
+    html-encoding-sniffer "^1.0.2"
+    left-pad "^1.3.0"
+    nwsapi "^2.0.7"
+    parse5 "4.0.0"
+    pn "^1.1.0"
+    request "^2.87.0"
+    request-promise-native "^1.0.5"
+    sax "^1.2.4"
+    symbol-tree "^3.2.2"
+    tough-cookie "^2.3.4"
+    w3c-hr-time "^1.0.1"
+    webidl-conversions "^4.0.2"
+    whatwg-encoding "^1.0.3"
+    whatwg-mimetype "^2.1.0"
+    whatwg-url "^6.4.1"
+    ws "^5.2.0"
+    xml-name-validator "^3.0.0"
+
+jsdom@^14.0.0:
+  version "14.1.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b"
+  integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==
+  dependencies:
+    abab "^2.0.0"
+    acorn "^6.0.4"
+    acorn-globals "^4.3.0"
+    array-equal "^1.0.0"
+    cssom "^0.3.4"
+    cssstyle "^1.1.1"
+    data-urls "^1.1.0"
+    domexception "^1.0.1"
+    escodegen "^1.11.0"
+    html-encoding-sniffer "^1.0.2"
+    nwsapi "^2.1.3"
+    parse5 "5.1.0"
+    pn "^1.1.0"
+    request "^2.88.0"
+    request-promise-native "^1.0.5"
+    saxes "^3.1.9"
+    symbol-tree "^3.2.2"
+    tough-cookie "^2.5.0"
+    w3c-hr-time "^1.0.1"
+    w3c-xmlserializer "^1.1.2"
+    webidl-conversions "^4.0.2"
+    whatwg-encoding "^1.0.5"
+    whatwg-mimetype "^2.3.0"
+    whatwg-url "^7.0.0"
+    ws "^6.1.2"
+    xml-name-validator "^3.0.0"
+
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+  integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
+json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+  integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+  integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
+json-stable-stringify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+  integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
+  dependencies:
+    jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+json3@^3.3.2:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
+  integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
+
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+  dependencies:
+    minimist "^1.2.0"
+
+json5@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850"
+  integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==
+  dependencies:
+    minimist "^1.2.0"
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jsonify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+  integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
+
+jsprim@^1.2.2:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+  integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.2.3"
+    verror "1.10.0"
+
+jsx-ast-utils@^2.1.0, jsx-ast-utils@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb"
+  integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==
+  dependencies:
+    array-includes "^3.0.3"
+    object.assign "^4.1.0"
+
+killable@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
+  integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==
+
+kind-of@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5"
+  integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=
+  dependencies:
+    is-buffer "^1.0.2"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+  integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+kleur@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
+  integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
+
+last-call-webpack-plugin@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555"
+  integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==
+  dependencies:
+    lodash "^4.17.5"
+    webpack-sources "^1.1.0"
+
+lazy-cache@^0.2.3:
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65"
+  integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=
+
+lazy-cache@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
+  integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4=
+
+lcid@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
+  integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
+  dependencies:
+    invert-kv "^2.0.0"
+
+left-pad@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
+  integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==
+
+leven@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
+  integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
+
+levn@^0.3.0, levn@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+  integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
+  dependencies:
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+
+load-json-file@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+  integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^2.2.0"
+    pify "^2.0.0"
+    strip-bom "^3.0.0"
+
+load-json-file@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
+  integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^4.0.0"
+    pify "^3.0.0"
+    strip-bom "^3.0.0"
+
+loader-fs-cache@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086"
+  integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==
+  dependencies:
+    find-cache-dir "^0.1.1"
+    mkdirp "0.5.1"
+
+loader-runner@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
+  integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
+
+loader-utils@1.2.3, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
+  integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^2.0.0"
+    json5 "^1.0.1"
+
+locate-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+  integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+  dependencies:
+    p-locate "^2.0.0"
+    path-exists "^3.0.0"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
+    path-exists "^3.0.0"
+
+lodash._reinterpolate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
+  integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
+
+lodash.memoize@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+  integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
+
+lodash.sortby@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
+  integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
+
+lodash.template@^4.4.0, lodash.template@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
+  integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==
+  dependencies:
+    lodash._reinterpolate "^3.0.0"
+    lodash.templatesettings "^4.0.0"
+
+lodash.templatesettings@^4.0.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33"
+  integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==
+  dependencies:
+    lodash._reinterpolate "^3.0.0"
+
+lodash.unescape@4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
+  integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
+
+lodash.uniq@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+  integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
+
+"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5:
+  version "4.17.15"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+loglevel@^1.4.1:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280"
+  integrity sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+lower-case@^1.1.1:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+  integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+
+lru-cache@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+  dependencies:
+    yallist "^3.0.2"
+
+make-dir@^2.0.0, make-dir@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+  integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+  dependencies:
+    pify "^4.0.1"
+    semver "^5.6.0"
+
+makeerror@1.0.x:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
+  integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=
+  dependencies:
+    tmpl "1.0.x"
+
+mamacro@^0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4"
+  integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==
+
+map-age-cleaner@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
+  integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
+  dependencies:
+    p-defer "^1.0.0"
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+md5.js@^1.3.4:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+  integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+mdn-data@2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
+  integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+
+mdn-data@~1.1.0:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01"
+  integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==
+
+media-typer@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+  integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
+
+mem@^4.0.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178"
+  integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==
+  dependencies:
+    map-age-cleaner "^0.1.1"
+    mimic-fn "^2.0.0"
+    p-is-promise "^2.0.0"
+
+memory-fs@^0.4.0, memory-fs@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
+  integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=
+  dependencies:
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
+
+merge-deep@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2"
+  integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==
+  dependencies:
+    arr-union "^3.1.0"
+    clone-deep "^0.2.4"
+    kind-of "^3.0.2"
+
+merge-descriptors@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+  integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+
+merge-stream@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"
+  integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=
+  dependencies:
+    readable-stream "^2.0.1"
+
+merge2@^1.2.3:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3"
+  integrity sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==
+
+methods@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+  integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
+
+microevent.ts@~0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0"
+  integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==
+
+micromatch@^3.1.10, micromatch@^3.1.4:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    braces "^2.3.1"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    extglob "^2.0.4"
+    fragment-cache "^0.2.1"
+    kind-of "^6.0.2"
+    nanomatch "^1.2.9"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.2"
+
+miller-rabin@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
+  integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
+mime-db@1.40.0, "mime-db@>= 1.40.0 < 2":
+  version "1.40.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
+  integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
+
+mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
+  version "2.1.24"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
+  integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+  dependencies:
+    mime-db "1.40.0"
+
+mime@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mime@^2.4.2, mime@^2.4.4:
+  version "2.4.4"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
+  integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
+
+mimic-fn@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+  integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+
+mimic-fn@^2.0.0, mimic-fn@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mini-css-extract-plugin@0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0"
+  integrity sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==
+  dependencies:
+    loader-utils "^1.1.0"
+    schema-utils "^1.0.0"
+    webpack-sources "^1.1.0"
+
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+  integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+  integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
+
+minimatch@3.0.4, minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.1.1, minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+minimist@~0.0.1:
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+  integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
+
+minipass@^2.2.1, minipass@^2.3.5:
+  version "2.3.5"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
+  integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
+  dependencies:
+    safe-buffer "^5.1.2"
+    yallist "^3.0.0"
+
+minizlib@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
+  integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+  dependencies:
+    minipass "^2.2.1"
+
+mississippi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
+  integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==
+  dependencies:
+    concat-stream "^1.5.0"
+    duplexify "^3.4.2"
+    end-of-stream "^1.1.0"
+    flush-write-stream "^1.0.0"
+    from2 "^2.1.0"
+    parallel-transform "^1.1.0"
+    pump "^3.0.0"
+    pumpify "^1.3.3"
+    stream-each "^1.1.0"
+    through2 "^2.0.0"
+
+mixin-deep@^1.2.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+  integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+mixin-object@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e"
+  integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=
+  dependencies:
+    for-in "^0.1.3"
+    is-extendable "^0.1.1"
+
+mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+  dependencies:
+    minimist "0.0.8"
+
+move-concurrently@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
+  integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=
+  dependencies:
+    aproba "^1.1.1"
+    copy-concurrently "^1.0.0"
+    fs-write-stream-atomic "^1.0.8"
+    mkdirp "^0.5.1"
+    rimraf "^2.5.4"
+    run-queue "^1.0.3"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+ms@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+multicast-dns-service-types@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
+  integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=
+
+multicast-dns@^6.0.1:
+  version "6.2.3"
+  resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229"
+  integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==
+  dependencies:
+    dns-packet "^1.3.1"
+    thunky "^1.0.2"
+
+mute-stream@0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+  integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
+
+mute-stream@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+  integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+
+nan@^2.12.1:
+  version "2.14.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
+  integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    fragment-cache "^0.2.1"
+    is-windows "^1.0.2"
+    kind-of "^6.0.2"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+needle@^2.2.1:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
+  integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
+  dependencies:
+    debug "^3.2.6"
+    iconv-lite "^0.4.4"
+    sax "^1.2.4"
+
+negotiator@0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
+  integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+
+neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
+  integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
+
+next-tick@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
+  integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
+
+nice-try@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+no-case@^2.2.0:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+  integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+  dependencies:
+    lower-case "^1.1.1"
+
+node-forge@0.7.5:
+  version "0.7.5"
+  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
+  integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==
+
+node-int64@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+  integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
+
+node-libs-browser@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
+  integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
+  dependencies:
+    assert "^1.1.1"
+    browserify-zlib "^0.2.0"
+    buffer "^4.3.0"
+    console-browserify "^1.1.0"
+    constants-browserify "^1.0.0"
+    crypto-browserify "^3.11.0"
+    domain-browser "^1.1.1"
+    events "^3.0.0"
+    https-browserify "^1.0.0"
+    os-browserify "^0.3.0"
+    path-browserify "0.0.1"
+    process "^0.11.10"
+    punycode "^1.2.4"
+    querystring-es3 "^0.2.0"
+    readable-stream "^2.3.3"
+    stream-browserify "^2.0.1"
+    stream-http "^2.7.2"
+    string_decoder "^1.0.0"
+    timers-browserify "^2.0.4"
+    tty-browserify "0.0.0"
+    url "^0.11.0"
+    util "^0.11.0"
+    vm-browserify "^1.0.1"
+
+node-modules-regexp@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
+  integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
+
+node-notifier@^5.2.1:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.1.tgz#7c0192cc63aedb25cd99619174daa27902b10903"
+  integrity sha512-p52B+onAEHKW1OF9MGO/S7k/ahGEHfhP5/tvwYzog/5XLYOd8ZuD6vdNZdUuWMONRnKPneXV43v3s6Snx1wsCQ==
+  dependencies:
+    growly "^1.3.0"
+    is-wsl "^1.1.0"
+    semver "^5.5.0"
+    shellwords "^0.1.1"
+    which "^1.3.0"
+
+node-pre-gyp@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
+  integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
+  dependencies:
+    detect-libc "^1.0.2"
+    mkdirp "^0.5.1"
+    needle "^2.2.1"
+    nopt "^4.0.1"
+    npm-packlist "^1.1.6"
+    npmlog "^4.0.2"
+    rc "^1.2.7"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^4"
+
+node-releases@^1.1.25:
+  version "1.1.27"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.27.tgz#b19ec8add2afe9a826a99dceccc516104c1edaf4"
+  integrity sha512-9iXUqHKSGo6ph/tdXVbHFbhRVQln4ZDTIBJCzsa90HimnBYc5jw8RWYt4wBYFHehGyC3koIz5O4mb2fHrbPOuA==
+  dependencies:
+    semver "^5.3.0"
+
+nopt@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+  integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
+normalize-package-data@^2.3.2:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+  integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+  dependencies:
+    hosted-git-info "^2.1.4"
+    resolve "^1.10.0"
+    semver "2 || 3 || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+  integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+normalize-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+normalize-url@^3.0.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
+  integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
+
+npm-bundled@^1.0.1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
+  integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
+
+npm-packlist@^1.1.6:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44"
+  integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==
+  dependencies:
+    ignore-walk "^3.0.1"
+    npm-bundled "^1.0.1"
+
+npm-run-path@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+  integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
+  dependencies:
+    path-key "^2.0.0"
+
+npmlog@^4.0.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+  integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+nth-check@^1.0.2, nth-check@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+  integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+  dependencies:
+    boolbase "~1.0.0"
+
+num2fraction@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+nwsapi@^2.0.7, nwsapi@^2.1.3:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.4.tgz#e006a878db23636f8e8a67d33ca0e4edf61a842f"
+  integrity sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==
+
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+  integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-hash@^1.1.4:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df"
+  integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==
+
+object-keys@^1.0.11, object-keys@^1.0.12:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object-path@0.11.4:
+  version "0.11.4"
+  resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949"
+  integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.assign@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    has-symbols "^1.0.0"
+    object-keys "^1.0.11"
+
+object.entries@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519"
+  integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.12.0"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+
+object.fromentries@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab"
+  integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.11.0"
+    function-bind "^1.1.1"
+    has "^1.0.1"
+
+object.getownpropertydescriptors@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
+  integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.5.1"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+object.values@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9"
+  integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.12.0"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+
+obuf@^1.0.0, obuf@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
+  integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+  dependencies:
+    ee-first "1.1.1"
+
+on-headers@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+  integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+onetime@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+  dependencies:
+    mimic-fn "^1.0.0"
+
+onetime@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5"
+  integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==
+  dependencies:
+    mimic-fn "^2.1.0"
+
+open@^6.3.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9"
+  integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==
+  dependencies:
+    is-wsl "^1.1.0"
+
+opn@^5.1.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
+  integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
+  dependencies:
+    is-wsl "^1.1.0"
+
+optimist@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+  integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
+  dependencies:
+    minimist "~0.0.1"
+    wordwrap "~0.0.2"
+
+optimize-css-assets-webpack-plugin@5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572"
+  integrity sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==
+  dependencies:
+    cssnano "^4.1.10"
+    last-call-webpack-plugin "^3.0.0"
+
+optionator@^0.8.1, optionator@^0.8.2:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+  integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=
+  dependencies:
+    deep-is "~0.1.3"
+    fast-levenshtein "~2.0.4"
+    levn "~0.3.0"
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+    wordwrap "~1.0.0"
+
+original@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
+  integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
+  dependencies:
+    url-parse "^1.4.3"
+
+os-browserify@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+  integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
+
+os-homedir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-locale@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
+  integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
+  dependencies:
+    execa "^1.0.0"
+    lcid "^2.0.0"
+    mem "^4.0.0"
+
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+osenv@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+  integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
+p-defer@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
+  integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
+
+p-each-series@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
+  integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=
+  dependencies:
+    p-reduce "^1.0.0"
+
+p-finally@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+  integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
+p-is-promise@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
+  integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
+
+p-limit@^1.1.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+  integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
+  dependencies:
+    p-try "^1.0.0"
+
+p-limit@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2"
+  integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+  integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
+  dependencies:
+    p-limit "^1.1.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-map@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
+  integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
+
+p-reduce@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
+  integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=
+
+p-try@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+  integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+pako@~1.0.5:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
+  integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
+
+parallel-transform@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"
+  integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=
+  dependencies:
+    cyclist "~0.2.2"
+    inherits "^2.0.3"
+    readable-stream "^2.1.5"
+
+param-case@2.1.x:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+  integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+  dependencies:
+    no-case "^2.2.0"
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-asn1@^5.0.0:
+  version "5.1.4"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc"
+  integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==
+  dependencies:
+    asn1.js "^4.0.0"
+    browserify-aes "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    pbkdf2 "^3.0.3"
+    safe-buffer "^5.1.1"
+
+parse-json@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+  integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
+  dependencies:
+    error-ex "^1.2.0"
+
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+  dependencies:
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
+parse5@4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
+  integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==
+
+parse5@5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
+  integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==
+
+parseurl@~1.3.2, parseurl@~1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-browserify@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
+  integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
+
+path-dirname@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
+  integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
+
+path-exists@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+  integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
+  dependencies:
+    pinkie-promise "^2.0.0"
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-is-inside@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+  integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
+
+path-key@^2.0.0, path-key@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-to-regexp@0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+  integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+
+path-type@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+  integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
+  dependencies:
+    pify "^2.0.0"
+
+path-type@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
+  integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
+  dependencies:
+    pify "^3.0.0"
+
+pbkdf2@^3.0.3:
+  version "3.0.17"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
+  integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+  integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+pify@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+  integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
+
+pify@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+  integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+
+pify@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+  integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+  integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
+  dependencies:
+    pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+  integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+
+pirates@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
+  integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
+  dependencies:
+    node-modules-regexp "^1.0.0"
+
+pkg-dir@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+  integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
+  dependencies:
+    find-up "^1.0.0"
+
+pkg-dir@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
+  integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
+  dependencies:
+    find-up "^2.1.0"
+
+pkg-dir@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+  integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+  dependencies:
+    find-up "^3.0.0"
+
+pkg-up@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
+  integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
+  dependencies:
+    find-up "^2.1.0"
+
+pn@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
+  integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
+
+pnp-webpack-plugin@1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.5.0.tgz#62a1cd3068f46d564bb33c56eb250e4d586676eb"
+  integrity sha512-jd9olUr9D7do+RN8Wspzhpxhgp1n6Vd0NtQ4SFkmIACZoEL1nkyAdW9Ygrinjec0vgDcWjscFQQ1gDW8rsfKTg==
+  dependencies:
+    ts-pnp "^1.1.2"
+
+portfinder@^1.0.9:
+  version "1.0.21"
+  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.21.tgz#60e1397b95ac170749db70034ece306b9a27e324"
+  integrity sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA==
+  dependencies:
+    async "^1.5.2"
+    debug "^2.2.0"
+    mkdirp "0.5.x"
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+postcss-attribute-case-insensitive@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.1.tgz#b2a721a0d279c2f9103a36331c88981526428cc7"
+  integrity sha512-L2YKB3vF4PetdTIthQVeT+7YiSzMoNMLLYxPXXppOOP7NoazEAy45sh2LvJ8leCQjfBcfkYQs8TtCcQjeZTp8A==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-selector-parser "^5.0.0"
+
+postcss-browser-comments@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-browser-comments/-/postcss-browser-comments-2.0.0.tgz#dc48d6a8ddbff188a80a000b7393436cb18aed88"
+  integrity sha512-xGG0UvoxwBc4Yx4JX3gc0RuDl1kc4bVihCzzk6UC72YPfq5fu3c717Nu8Un3nvnq1BJ31gBnFXIG/OaUTnpHgA==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-calc@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436"
+  integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==
+  dependencies:
+    css-unit-converter "^1.1.1"
+    postcss "^7.0.5"
+    postcss-selector-parser "^5.0.0-rc.4"
+    postcss-value-parser "^3.3.1"
+
+postcss-color-functional-notation@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0"
+  integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-color-gray@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547"
+  integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==
+  dependencies:
+    "@csstools/convert-colors" "^1.4.0"
+    postcss "^7.0.5"
+    postcss-values-parser "^2.0.0"
+
+postcss-color-hex-alpha@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388"
+  integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==
+  dependencies:
+    postcss "^7.0.14"
+    postcss-values-parser "^2.0.1"
+
+postcss-color-mod-function@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d"
+  integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==
+  dependencies:
+    "@csstools/convert-colors" "^1.4.0"
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-color-rebeccapurple@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77"
+  integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-colormin@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381"
+  integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==
+  dependencies:
+    browserslist "^4.0.0"
+    color "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-convert-values@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f"
+  integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-custom-media@^7.0.8:
+  version "7.0.8"
+  resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c"
+  integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==
+  dependencies:
+    postcss "^7.0.14"
+
+postcss-custom-properties@^8.0.11:
+  version "8.0.11"
+  resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97"
+  integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==
+  dependencies:
+    postcss "^7.0.17"
+    postcss-values-parser "^2.0.1"
+
+postcss-custom-selectors@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba"
+  integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-selector-parser "^5.0.0-rc.3"
+
+postcss-dir-pseudo-class@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2"
+  integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-selector-parser "^5.0.0-rc.3"
+
+postcss-discard-comments@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033"
+  integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-duplicates@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb"
+  integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-empty@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765"
+  integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-overridden@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57"
+  integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-double-position-gradients@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e"
+  integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==
+  dependencies:
+    postcss "^7.0.5"
+    postcss-values-parser "^2.0.0"
+
+postcss-env-function@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7"
+  integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-flexbugs-fixes@4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz#e094a9df1783e2200b7b19f875dcad3b3aff8b20"
+  integrity sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-focus-visible@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e"
+  integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-focus-within@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680"
+  integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-font-variant@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz#71dd3c6c10a0d846c5eda07803439617bbbabacc"
+  integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-gap-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715"
+  integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-image-set-function@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288"
+  integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-initial@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.1.tgz#99d319669a13d6c06ef8e70d852f68cb1b399b61"
+  integrity sha512-I2Sz83ZSHybMNh02xQDK609lZ1/QOyYeuizCjzEhlMgeV/HcDJapQiH4yTqLjZss0X6/6VvKFXUeObaHpJoINw==
+  dependencies:
+    lodash.template "^4.5.0"
+    postcss "^7.0.2"
+
+postcss-lab-function@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e"
+  integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==
+  dependencies:
+    "@csstools/convert-colors" "^1.4.0"
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-load-config@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003"
+  integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==
+  dependencies:
+    cosmiconfig "^5.0.0"
+    import-cwd "^2.0.0"
+
+postcss-loader@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d"
+  integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==
+  dependencies:
+    loader-utils "^1.1.0"
+    postcss "^7.0.0"
+    postcss-load-config "^2.0.0"
+    schema-utils "^1.0.0"
+
+postcss-logical@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5"
+  integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-media-minmax@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5"
+  integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-merge-longhand@^4.0.11:
+  version "4.0.11"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24"
+  integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==
+  dependencies:
+    css-color-names "0.0.4"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    stylehacks "^4.0.0"
+
+postcss-merge-rules@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650"
+  integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    cssnano-util-same-parent "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+    vendors "^1.0.0"
+
+postcss-minify-font-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6"
+  integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-minify-gradients@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471"
+  integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    is-color-stop "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-minify-params@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874"
+  integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    browserslist "^4.0.0"
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    uniqs "^2.0.0"
+
+postcss-minify-selectors@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8"
+  integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+
+postcss-modules-extract-imports@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e"
+  integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==
+  dependencies:
+    postcss "^7.0.5"
+
+postcss-modules-local-by-default@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63"
+  integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==
+  dependencies:
+    postcss "^7.0.6"
+    postcss-selector-parser "^6.0.0"
+    postcss-value-parser "^3.3.1"
+
+postcss-modules-scope@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb"
+  integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==
+  dependencies:
+    postcss "^7.0.6"
+    postcss-selector-parser "^6.0.0"
+
+postcss-modules-values@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64"
+  integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==
+  dependencies:
+    icss-replace-symbols "^1.1.0"
+    postcss "^7.0.6"
+
+postcss-nesting@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052"
+  integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-normalize-charset@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4"
+  integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-normalize-display-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a"
+  integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-positions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f"
+  integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-repeat-style@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c"
+  integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-string@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c"
+  integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==
+  dependencies:
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-timing-functions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9"
+  integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-unicode@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb"
+  integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-url@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1"
+  integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==
+  dependencies:
+    is-absolute-url "^2.0.0"
+    normalize-url "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-whitespace@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82"
+  integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize@7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize/-/postcss-normalize-7.0.1.tgz#eb51568d962b8aa61a8318383c8bb7e54332282e"
+  integrity sha512-NOp1fwrG+6kVXWo7P9SizCHX6QvioxFD/hZcI2MLxPmVnFJFC0j0DDpIuNw2tUDeCFMni59gCVgeJ1/hYhj2OQ==
+  dependencies:
+    "@csstools/normalize.css" "^9.0.1"
+    browserslist "^4.1.1"
+    postcss "^7.0.2"
+    postcss-browser-comments "^2.0.0"
+
+postcss-ordered-values@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee"
+  integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-overflow-shorthand@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30"
+  integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-page-break@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf"
+  integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-place@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62"
+  integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-values-parser "^2.0.0"
+
+postcss-preset-env@6.7.0:
+  version "6.7.0"
+  resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5"
+  integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==
+  dependencies:
+    autoprefixer "^9.6.1"
+    browserslist "^4.6.4"
+    caniuse-lite "^1.0.30000981"
+    css-blank-pseudo "^0.1.4"
+    css-has-pseudo "^0.10.0"
+    css-prefers-color-scheme "^3.1.1"
+    cssdb "^4.4.0"
+    postcss "^7.0.17"
+    postcss-attribute-case-insensitive "^4.0.1"
+    postcss-color-functional-notation "^2.0.1"
+    postcss-color-gray "^5.0.0"
+    postcss-color-hex-alpha "^5.0.3"
+    postcss-color-mod-function "^3.0.3"
+    postcss-color-rebeccapurple "^4.0.1"
+    postcss-custom-media "^7.0.8"
+    postcss-custom-properties "^8.0.11"
+    postcss-custom-selectors "^5.1.2"
+    postcss-dir-pseudo-class "^5.0.0"
+    postcss-double-position-gradients "^1.0.0"
+    postcss-env-function "^2.0.2"
+    postcss-focus-visible "^4.0.0"
+    postcss-focus-within "^3.0.0"
+    postcss-font-variant "^4.0.0"
+    postcss-gap-properties "^2.0.0"
+    postcss-image-set-function "^3.0.1"
+    postcss-initial "^3.0.0"
+    postcss-lab-function "^2.0.1"
+    postcss-logical "^3.0.0"
+    postcss-media-minmax "^4.0.0"
+    postcss-nesting "^7.0.0"
+    postcss-overflow-shorthand "^2.0.0"
+    postcss-page-break "^2.0.0"
+    postcss-place "^4.0.1"
+    postcss-pseudo-class-any-link "^6.0.0"
+    postcss-replace-overflow-wrap "^3.0.0"
+    postcss-selector-matches "^4.0.0"
+    postcss-selector-not "^4.0.0"
+
+postcss-pseudo-class-any-link@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1"
+  integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==
+  dependencies:
+    postcss "^7.0.2"
+    postcss-selector-parser "^5.0.0-rc.3"
+
+postcss-reduce-initial@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df"
+  integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+
+postcss-reduce-transforms@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29"
+  integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-replace-overflow-wrap@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c"
+  integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==
+  dependencies:
+    postcss "^7.0.2"
+
+postcss-safe-parser@4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea"
+  integrity sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-selector-matches@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff"
+  integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==
+  dependencies:
+    balanced-match "^1.0.0"
+    postcss "^7.0.2"
+
+postcss-selector-not@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz#c68ff7ba96527499e832724a2674d65603b645c0"
+  integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ==
+  dependencies:
+    balanced-match "^1.0.0"
+    postcss "^7.0.2"
+
+postcss-selector-parser@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
+  integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=
+  dependencies:
+    dot-prop "^4.1.1"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c"
+  integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==
+  dependencies:
+    cssesc "^2.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-selector-parser@^6.0.0:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+  dependencies:
+    cssesc "^3.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-svgo@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258"
+  integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==
+  dependencies:
+    is-svg "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    svgo "^1.0.0"
+
+postcss-unique-selectors@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac"
+  integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    postcss "^7.0.0"
+    uniqs "^2.0.0"
+
+postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+
+postcss-value-parser@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9"
+  integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==
+
+postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f"
+  integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==
+  dependencies:
+    flatten "^1.0.2"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss@7.0.14:
+  version "7.0.14"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5"
+  integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6:
+  version "7.0.17"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f"
+  integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+prelude-ls@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+  integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+pretty-bytes@^5.1.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"
+  integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==
+
+pretty-error@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
+  integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=
+  dependencies:
+    renderkid "^2.0.1"
+    utila "~0.4"
+
+pretty-format@^24.8.0:
+  version "24.8.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
+  integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==
+  dependencies:
+    "@jest/types" "^24.8.0"
+    ansi-regex "^4.0.0"
+    ansi-styles "^3.2.0"
+    react-is "^16.8.4"
+
+private@^0.1.6:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
+
+progress@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
+promise-inflight@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+  integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
+
+promise@8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-8.0.3.tgz#f592e099c6cddc000d538ee7283bb190452b0bf6"
+  integrity sha512-HeRDUL1RJiLhyA0/grn+PTShlBAcLuh/1BJGtrvjwbvRDCTLLMEz9rOGCV+R3vHY4MixIuoMEd9Yq/XvsTPcjw==
+  dependencies:
+    asap "~2.0.6"
+
+prompts@^2.0.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.2.1.tgz#f901dd2a2dfee080359c0e20059b24188d75ad35"
+  integrity sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw==
+  dependencies:
+    kleur "^3.0.3"
+    sisteransi "^1.0.3"
+
+prop-types@^15.6.2, prop-types@^15.7.2:
+  version "15.7.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+  integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.8.1"
+
+proxy-addr@~2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
+  integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
+  dependencies:
+    forwarded "~0.1.2"
+    ipaddr.js "1.9.0"
+
+prr@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+  integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
+
+psl@^1.1.24, psl@^1.1.28:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.3.0.tgz#e1ebf6a3b5564fa8376f3da2275da76d875ca1bd"
+  integrity sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==
+
+public-encrypt@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
+  integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
+  dependencies:
+    bn.js "^4.1.0"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    parse-asn1 "^5.0.0"
+    randombytes "^2.0.1"
+    safe-buffer "^5.1.2"
+
+pump@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
+  integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pump@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+  integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pumpify@^1.3.3:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
+  integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
+  dependencies:
+    duplexify "^3.6.0"
+    inherits "^2.0.3"
+    pump "^2.0.0"
+
+punycode@1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+  integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
+
+punycode@^1.2.4, punycode@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
+
+punycode@^2.1.0, punycode@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+q@^1.1.2:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
+  integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
+
+qs@6.7.0:
+  version "6.7.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
+  integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
+
+qs@~6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+querystring-es3@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+  integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
+
+querystring@0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+  integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
+
+querystringify@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
+  integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
+
+raf@3.4.1:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
+  integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
+  dependencies:
+    performance-now "^2.1.0"
+
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
+randomfill@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+  integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
+  dependencies:
+    randombytes "^2.0.5"
+    safe-buffer "^5.1.0"
+
+range-parser@^1.2.1, range-parser@~1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
+  integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
+  dependencies:
+    bytes "3.1.0"
+    http-errors "1.7.2"
+    iconv-lite "0.4.24"
+    unpipe "1.0.0"
+
+rc@^1.2.7:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+  dependencies:
+    deep-extend "^0.6.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+react-app-polyfill@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.2.tgz#2a51175885c88245a2a356dc46df29f38ec9f060"
+  integrity sha512-yZcpLnIr0FOIzrOOz9JC37NWAWEuCaQWmYn9EWjEzlCW4cOmA5MkT5L3iP8QuUeFnoqVCTJgjIWYbXEJgNXhGA==
+  dependencies:
+    core-js "3.1.4"
+    object-assign "4.1.1"
+    promise "8.0.3"
+    raf "3.4.1"
+    regenerator-runtime "0.13.3"
+    whatwg-fetch "3.0.0"
+
+react-dev-utils@^9.0.3:
+  version "9.0.3"
+  resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.0.3.tgz#7607455587abb84599451460eb37cef0b684131a"
+  integrity sha512-OyInhcwsvycQ3Zr2pQN+HV4gtRXrky5mJXIy4HnqrWa+mI624xfYfqGuC9dYbxp4Qq3YZzP8GSGQjv0AgNU15w==
+  dependencies:
+    "@babel/code-frame" "7.5.5"
+    address "1.1.0"
+    browserslist "4.6.6"
+    chalk "2.4.2"
+    cross-spawn "6.0.5"
+    detect-port-alt "1.1.6"
+    escape-string-regexp "1.0.5"
+    filesize "3.6.1"
+    find-up "3.0.0"
+    fork-ts-checker-webpack-plugin "1.5.0"
+    global-modules "2.0.0"
+    globby "8.0.2"
+    gzip-size "5.1.1"
+    immer "1.10.0"
+    inquirer "6.5.0"
+    is-root "2.1.0"
+    loader-utils "1.2.3"
+    open "^6.3.0"
+    pkg-up "2.0.0"
+    react-error-overlay "^6.0.1"
+    recursive-readdir "2.2.2"
+    shell-quote "1.6.1"
+    sockjs-client "1.3.0"
+    strip-ansi "5.2.0"
+    text-table "0.2.0"
+
+react-dom@16.9.0:
+  version "16.9.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962"
+  integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+    scheduler "^0.15.0"
+
+react-error-overlay@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.1.tgz#b8d3cf9bb991c02883225c48044cb3ee20413e0f"
+  integrity sha512-V9yoTr6MeZXPPd4nV/05eCBvGH9cGzc52FN8fs0O0TVQ3HYYf1n7EgZVtHbldRq5xU9zEzoXIITjYNIfxDDdUw==
+
+react-is@^16.8.1, react-is@^16.8.4:
+  version "16.9.0"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb"
+  integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==
+
+react-scripts@3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.1.1.tgz#1796bc92447f3a2d3072c3b71ca99f88d099c48d"
+  integrity sha512-dbjTG9vJC61OI62hIswQYg5xHvwlxDTH6QXz6ICEuA5AqkFQWk1LKl76sk8fVL2WsyumbBc4FErALwKcEV2vNA==
+  dependencies:
+    "@babel/core" "7.5.5"
+    "@svgr/webpack" "4.3.2"
+    "@typescript-eslint/eslint-plugin" "1.13.0"
+    "@typescript-eslint/parser" "1.13.0"
+    babel-eslint "10.0.2"
+    babel-jest "^24.8.0"
+    babel-loader "8.0.6"
+    babel-plugin-named-asset-import "^0.3.3"
+    babel-preset-react-app "^9.0.1"
+    camelcase "^5.2.0"
+    case-sensitive-paths-webpack-plugin "2.2.0"
+    css-loader "2.1.1"
+    dotenv "6.2.0"
+    dotenv-expand "4.2.0"
+    eslint "^6.1.0"
+    eslint-config-react-app "^5.0.1"
+    eslint-loader "2.2.1"
+    eslint-plugin-flowtype "3.13.0"
+    eslint-plugin-import "2.18.2"
+    eslint-plugin-jsx-a11y "6.2.3"
+    eslint-plugin-react "7.14.3"
+    eslint-plugin-react-hooks "^1.6.1"
+    file-loader "3.0.1"
+    fs-extra "7.0.1"
+    html-webpack-plugin "4.0.0-beta.5"
+    identity-obj-proxy "3.0.0"
+    is-wsl "^1.1.0"
+    jest "24.8.0"
+    jest-environment-jsdom-fourteen "0.1.0"
+    jest-resolve "24.8.0"
+    jest-watch-typeahead "0.3.1"
+    mini-css-extract-plugin "0.5.0"
+    optimize-css-assets-webpack-plugin "5.0.3"
+    pnp-webpack-plugin "1.5.0"
+    postcss-flexbugs-fixes "4.1.0"
+    postcss-loader "3.0.0"
+    postcss-normalize "7.0.1"
+    postcss-preset-env "6.7.0"
+    postcss-safe-parser "4.0.1"
+    react-app-polyfill "^1.0.2"
+    react-dev-utils "^9.0.3"
+    resolve "1.12.0"
+    resolve-url-loader "3.1.0"
+    sass-loader "7.2.0"
+    semver "6.3.0"
+    style-loader "1.0.0"
+    terser-webpack-plugin "1.4.1"
+    ts-pnp "1.1.2"
+    url-loader "2.1.0"
+    webpack "4.39.1"
+    webpack-dev-server "3.2.1"
+    webpack-manifest-plugin "2.0.4"
+    workbox-webpack-plugin "4.3.1"
+  optionalDependencies:
+    fsevents "2.0.7"
+
+react@16.9.0:
+  version "16.9.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa"
+  integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+
+read-pkg-up@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+  integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
+  dependencies:
+    find-up "^2.0.0"
+    read-pkg "^2.0.0"
+
+read-pkg-up@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978"
+  integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==
+  dependencies:
+    find-up "^3.0.0"
+    read-pkg "^3.0.0"
+
+read-pkg@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+  integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
+  dependencies:
+    load-json-file "^2.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^2.0.0"
+
+read-pkg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
+  integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
+  dependencies:
+    load-json-file "^4.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^3.0.0"
+
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+  integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+readable-stream@^3.0.6, readable-stream@^3.1.1:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
+  integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+readdirp@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
+  integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+    micromatch "^3.1.10"
+    readable-stream "^2.0.2"
+
+realpath-native@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
+  integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==
+  dependencies:
+    util.promisify "^1.0.0"
+
+recursive-readdir@2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
+  integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==
+  dependencies:
+    minimatch "3.0.4"
+
+regenerate-unicode-properties@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
+  integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==
+  dependencies:
+    regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+  integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
+regenerator-runtime@0.13.3, regenerator-runtime@^0.13.2:
+  version "0.13.3"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
+  integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regenerator-transform@^0.14.0:
+  version "0.14.1"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"
+  integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==
+  dependencies:
+    private "^0.1.6"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+regex-parser@2.2.10:
+  version "2.2.10"
+  resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37"
+  integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==
+
+regexp-tree@^0.1.6:
+  version "0.1.11"
+  resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.11.tgz#c9c7f00fcf722e0a56c7390983a7a63dd6c272f3"
+  integrity sha512-7/l/DgapVVDzZobwMCCgMlqiqyLFJ0cduo/j+3BcDJIB+yJdsYCfKuI3l/04NV+H/rfNRdPIDbXNZHM9XvQatg==
+
+regexpp@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
+  integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
+
+regexpu-core@^4.5.4:
+  version "4.5.5"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411"
+  integrity sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==
+  dependencies:
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.1.0"
+    regjsgen "^0.5.0"
+    regjsparser "^0.6.0"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.1.0"
+
+regjsgen@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd"
+  integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==
+
+regjsparser@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c"
+  integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==
+  dependencies:
+    jsesc "~0.5.0"
+
+relateurl@0.2.x:
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+  integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
+
+remove-trailing-separator@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+  integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+renderkid@^2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149"
+  integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==
+  dependencies:
+    css-select "^1.1.0"
+    dom-converter "^0.2"
+    htmlparser2 "^3.3.0"
+    strip-ansi "^3.0.0"
+    utila "^0.4.0"
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+  integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+request-promise-core@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346"
+  integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==
+  dependencies:
+    lodash "^4.17.11"
+
+request-promise-native@^1.0.5:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59"
+  integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==
+  dependencies:
+    request-promise-core "1.1.2"
+    stealthy-require "^1.1.1"
+    tough-cookie "^2.3.3"
+
+request@^2.87.0, request@^2.88.0:
+  version "2.88.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
+  integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    har-validator "~5.1.0"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
+    performance-now "^2.1.0"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.4.3"
+    tunnel-agent "^0.6.0"
+    uuid "^3.3.2"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+  integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-main-filename@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+  integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
+
+require-main-filename@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+requires-port@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+  integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+
+resolve-cwd@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
+  integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=
+  dependencies:
+    resolve-from "^3.0.0"
+
+resolve-from@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+  integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-url-loader@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.0.tgz#54d8181d33cd1b66a59544d05cadf8e4aa7d37cc"
+  integrity sha512-2QcrA+2QgVqsMJ1Hn5NnJXIGCX1clQ1F6QJTqOeiaDw9ACo1G2k+8/shq3mtqne03HOFyskAClqfxKyFBriXZg==
+  dependencies:
+    adjust-sourcemap-loader "2.0.0"
+    camelcase "5.0.0"
+    compose-function "3.0.3"
+    convert-source-map "1.6.0"
+    es6-iterator "2.0.3"
+    loader-utils "1.2.3"
+    postcss "7.0.14"
+    rework "1.0.1"
+    rework-visit "1.0.0"
+    source-map "0.6.1"
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+  integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
+
+resolve@1.12.0, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
+  integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
+  dependencies:
+    path-parse "^1.0.6"
+
+restore-cursor@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+  dependencies:
+    onetime "^2.0.0"
+    signal-exit "^3.0.2"
+
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+  integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rework-visit@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a"
+  integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo=
+
+rework@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7"
+  integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=
+  dependencies:
+    convert-source-map "^0.3.3"
+    css "^2.0.0"
+
+rgb-regex@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
+  integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
+
+rgba-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
+  integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
+
+rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+  dependencies:
+    glob "^7.1.3"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+  integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+
+rsvp@^4.8.4:
+  version "4.8.5"
+  resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
+  integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
+
+run-async@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+  integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
+  dependencies:
+    is-promise "^2.1.0"
+
+run-queue@^1.0.0, run-queue@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
+  integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=
+  dependencies:
+    aproba "^1.1.1"
+
+rxjs@^6.4.0:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7"
+  integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==
+  dependencies:
+    tslib "^1.9.0"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
+  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sane@^4.0.3:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded"
+  integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==
+  dependencies:
+    "@cnakazawa/watch" "^1.0.3"
+    anymatch "^2.0.0"
+    capture-exit "^2.0.0"
+    exec-sh "^0.3.2"
+    execa "^1.0.0"
+    fb-watchman "^2.0.0"
+    micromatch "^3.1.4"
+    minimist "^1.1.1"
+    walker "~1.0.5"
+
+sass-loader@7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.2.0.tgz#e34115239309d15b2527cb62b5dfefb62a96ff7f"
+  integrity sha512-h8yUWaWtsbuIiOCgR9fd9c2lRXZ2uG+h8Dzg/AGNj+Hg/3TO8+BBAW9mEP+mh8ei+qBKqSJ0F1FLlYjNBc61OA==
+  dependencies:
+    clone-deep "^4.0.1"
+    loader-utils "^1.0.1"
+    neo-async "^2.5.0"
+    pify "^4.0.1"
+    semver "^5.5.0"
+
+sax@^1.2.4, sax@~1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+saxes@^3.1.9:
+  version "3.1.11"
+  resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"
+  integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==
+  dependencies:
+    xmlchars "^2.1.1"
+
+scheduler@^0.15.0:
+  version "0.15.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e"
+  integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+schema-utils@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+  integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
+  dependencies:
+    ajv "^6.1.0"
+    ajv-errors "^1.0.0"
+    ajv-keywords "^3.1.0"
+
+schema-utils@^2.0.0, schema-utils@^2.0.1:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.1.0.tgz#940363b6b1ec407800a22951bdcc23363c039393"
+  integrity sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw==
+  dependencies:
+    ajv "^6.1.0"
+    ajv-keywords "^3.1.0"
+
+select-hose@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+  integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
+
+selfsigned@^1.9.1:
+  version "1.10.4"
+  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd"
+  integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==
+  dependencies:
+    node-forge "0.7.5"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@5.5.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
+  integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
+
+semver@6.3.0, semver@^6.0.0, semver@^6.1.2, semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+send@0.17.1:
+  version "0.17.1"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
+  integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
+  dependencies:
+    debug "2.6.9"
+    depd "~1.1.2"
+    destroy "~1.0.4"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    fresh "0.5.2"
+    http-errors "~1.7.2"
+    mime "1.6.0"
+    ms "2.1.1"
+    on-finished "~2.3.0"
+    range-parser "~1.2.1"
+    statuses "~1.5.0"
+
+serialize-javascript@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65"
+  integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==
+
+serve-index@^1.7.2:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
+  integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=
+  dependencies:
+    accepts "~1.3.4"
+    batch "0.6.1"
+    debug "2.6.9"
+    escape-html "~1.0.3"
+    http-errors "~1.6.2"
+    mime-types "~2.1.17"
+    parseurl "~1.3.2"
+
+serve-static@1.14.1:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
+  integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.3"
+    send "0.17.1"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^2.0.0, set-value@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
+  integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+setimmediate@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+  integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
+
+setprototypeof@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
+  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
+
+setprototypeof@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
+  integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
+
+sha.js@^2.4.0, sha.js@^2.4.8:
+  version "2.4.11"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+  integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+shallow-clone@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060"
+  integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=
+  dependencies:
+    is-extendable "^0.1.1"
+    kind-of "^2.0.1"
+    lazy-cache "^0.2.3"
+    mixin-object "^2.0.1"
+
+shallow-clone@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+  integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+  dependencies:
+    kind-of "^6.0.2"
+
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+shell-quote@1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
+  integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=
+  dependencies:
+    array-filter "~0.0.0"
+    array-map "~0.0.0"
+    array-reduce "~0.0.0"
+    jsonify "~0.0.0"
+
+shellwords@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+  integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
+  dependencies:
+    is-arrayish "^0.3.1"
+
+sisteransi@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.3.tgz#98168d62b79e3a5e758e27ae63c4a053d748f4eb"
+  integrity sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg==
+
+slash@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+  integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
+
+slash@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+  integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
+slice-ansi@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
+  integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
+  dependencies:
+    ansi-styles "^3.2.0"
+    astral-regex "^1.0.0"
+    is-fullwidth-code-point "^2.0.0"
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    base "^0.11.1"
+    debug "^2.2.0"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    map-cache "^0.2.2"
+    source-map "^0.5.6"
+    source-map-resolve "^0.5.0"
+    use "^3.1.0"
+
+sockjs-client@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177"
+  integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==
+  dependencies:
+    debug "^3.2.5"
+    eventsource "^1.0.7"
+    faye-websocket "~0.11.1"
+    inherits "^2.0.3"
+    json3 "^3.3.2"
+    url-parse "^1.4.3"
+
+sockjs@0.3.19:
+  version "0.3.19"
+  resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
+  integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==
+  dependencies:
+    faye-websocket "^0.10.0"
+    uuid "^3.0.1"
+
+source-list-map@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
+  integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
+
+source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+  integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+  dependencies:
+    atob "^2.1.1"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-support@^0.5.6, source-map-support@~0.5.12:
+  version "0.5.13"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+  integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+spdx-correct@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+  integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+  dependencies:
+    spdx-expression-parse "^3.0.0"
+    spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+  integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
+
+spdx-expression-parse@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+  integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+  dependencies:
+    spdx-exceptions "^2.1.0"
+    spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
+  integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
+
+spdy-transport@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
+  integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
+  dependencies:
+    debug "^4.1.0"
+    detect-node "^2.0.4"
+    hpack.js "^2.1.6"
+    obuf "^1.1.2"
+    readable-stream "^3.0.6"
+    wbuf "^1.7.3"
+
+spdy@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2"
+  integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==
+  dependencies:
+    debug "^4.1.0"
+    handle-thing "^2.0.0"
+    http-deceiver "^1.2.7"
+    select-hose "^2.0.0"
+    spdy-transport "^3.0.0"
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+sshpk@^1.7.0:
+  version "1.16.1"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+  integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
+    ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
+    jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
+    tweetnacl "~0.14.0"
+
+ssri@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
+  integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
+  dependencies:
+    figgy-pudding "^3.5.1"
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+stack-utils@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8"
+  integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+stealthy-require@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+  integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
+
+stream-browserify@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
+  integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "^2.0.2"
+
+stream-each@^1.1.0:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
+  integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==
+  dependencies:
+    end-of-stream "^1.1.0"
+    stream-shift "^1.0.0"
+
+stream-http@^2.7.2:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
+  integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.3.6"
+    to-arraybuffer "^1.0.0"
+    xtend "^4.0.0"
+
+stream-shift@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
+  integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
+
+string-length@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
+  integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=
+  dependencies:
+    astral-regex "^1.0.0"
+    strip-ansi "^4.0.0"
+
+string-width@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+  integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^4.0.0"
+
+string-width@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+  integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+  dependencies:
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+string-width@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff"
+  integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^5.2.0"
+
+string_decoder@^1.0.0, string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+stringify-object@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629"
+  integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==
+  dependencies:
+    get-own-enumerable-property-symbols "^3.0.0"
+    is-obj "^1.0.1"
+    is-regexp "^1.0.0"
+
+strip-ansi@5.2.0, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+  integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+  dependencies:
+    ansi-regex "^4.1.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+  integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-comments@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d"
+  integrity sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==
+  dependencies:
+    babel-extract-comments "^1.0.0"
+    babel-plugin-transform-object-rest-spread "^6.26.0"
+
+strip-eof@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+  integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+strip-json-comments@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
+  integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+style-loader@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82"
+  integrity sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw==
+  dependencies:
+    loader-utils "^1.2.3"
+    schema-utils "^2.0.1"
+
+stylehacks@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
+  integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+  integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+  integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+  dependencies:
+    has-flag "^3.0.0"
+
+svg-parser@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.2.tgz#d134cc396fa2681dc64f518330784e98bd801ec8"
+  integrity sha512-1gtApepKFweigFZj3sGO8KT8LvVZK8io146EzXrpVuWCDAbISz/yMucco3hWTkpZNoPabM+dnMOpy6Swue68Zg==
+
+svgo@^1.0.0, svgo@^1.2.2:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.0.tgz#bae51ba95ded9a33a36b7c46ce9c359ae9154313"
+  integrity sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ==
+  dependencies:
+    chalk "^2.4.1"
+    coa "^2.0.2"
+    css-select "^2.0.0"
+    css-select-base-adapter "^0.1.1"
+    css-tree "1.0.0-alpha.33"
+    csso "^3.5.1"
+    js-yaml "^3.13.1"
+    mkdirp "~0.5.1"
+    object.values "^1.1.0"
+    sax "~1.2.4"
+    stable "^0.1.8"
+    unquote "~1.1.1"
+    util.promisify "~1.0.0"
+
+symbol-tree@^3.2.2:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+  integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
+table@^5.2.3:
+  version "5.4.5"
+  resolved "https://registry.yarnpkg.com/table/-/table-5.4.5.tgz#c8f4ea2d8fee08c0027fac27b0ec0a4fe01dfa42"
+  integrity sha512-oGa2Hl7CQjfoaogtrOHEJroOcYILTx7BZWLGsJIlzoWmB2zmguhNfPJZsWPKYek/MgCxfco54gEi31d1uN2hFA==
+  dependencies:
+    ajv "^6.10.2"
+    lodash "^4.17.14"
+    slice-ansi "^2.1.0"
+    string-width "^3.0.0"
+
+tapable@^1.0.0, tapable@^1.1.0, tapable@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
+  integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
+
+tar@^4:
+  version "4.4.10"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
+  integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==
+  dependencies:
+    chownr "^1.1.1"
+    fs-minipass "^1.2.5"
+    minipass "^2.3.5"
+    minizlib "^1.2.1"
+    mkdirp "^0.5.0"
+    safe-buffer "^5.1.2"
+    yallist "^3.0.3"
+
+terser-webpack-plugin@1.4.1, terser-webpack-plugin@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4"
+  integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==
+  dependencies:
+    cacache "^12.0.2"
+    find-cache-dir "^2.1.0"
+    is-wsl "^1.1.0"
+    schema-utils "^1.0.0"
+    serialize-javascript "^1.7.0"
+    source-map "^0.6.1"
+    terser "^4.1.2"
+    webpack-sources "^1.4.0"
+    worker-farm "^1.7.0"
+
+terser@^4.1.2:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.1.4.tgz#4478b6a08bb096a61e793fea1a4434408bab936c"
+  integrity sha512-+ZwXJvdSwbd60jG0Illav0F06GDJF0R4ydZ21Q3wGAFKoBGyJGo34F63vzJHgvYxc1ukOtIjvwEvl9MkjzM6Pg==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.12"
+
+test-exclude@^5.2.3:
+  version "5.2.3"
+  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0"
+  integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==
+  dependencies:
+    glob "^7.1.3"
+    minimatch "^3.0.4"
+    read-pkg-up "^4.0.0"
+    require-main-filename "^2.0.0"
+
+text-table@0.2.0, text-table@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
+throat@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
+  integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=
+
+through2@^2.0.0:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+  integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+  dependencies:
+    readable-stream "~2.3.6"
+    xtend "~4.0.1"
+
+through@^2.3.6:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
+
+thunky@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826"
+  integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==
+
+timers-browserify@^2.0.4:
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
+  integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
+  dependencies:
+    setimmediate "^1.0.4"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
+
+tmp@^0.0.33:
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+  integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+  dependencies:
+    os-tmpdir "~1.0.2"
+
+tmpl@1.0.x:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
+  integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+  integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+toidentifier@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
+  integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+
+tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+  integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+  dependencies:
+    psl "^1.1.28"
+    punycode "^2.1.1"
+
+tough-cookie@~2.4.3:
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
+  integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
+  dependencies:
+    psl "^1.1.24"
+    punycode "^1.4.1"
+
+tr46@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
+  integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
+  dependencies:
+    punycode "^2.1.0"
+
+trim-right@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+  integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
+
+ts-pnp@1.1.2, ts-pnp@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552"
+  integrity sha512-f5Knjh7XCyRIzoC/z1Su1yLLRrPrFCgtUAh/9fCSP6NKbATwpOL1+idQVXQokK9GRFURn/jYPGPfegIctwunoA==
+
+tslib@^1.8.1, tslib@^1.9.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
+  integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
+
+tsutils@^3.7.0:
+  version "3.17.1"
+  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
+  integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==
+  dependencies:
+    tslib "^1.8.1"
+
+tty-browserify@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+  integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+type-check@~0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+  integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+  dependencies:
+    prelude-ls "~1.1.2"
+
+type-fest@^0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2"
+  integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==
+
+type-is@~1.6.17, type-is@~1.6.18:
+  version "1.6.18"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.24"
+
+type@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179"
+  integrity sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==
+
+typedarray@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript@3.5.3:
+  version "3.5.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
+  integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
+
+uglify-js@3.4.x:
+  version "3.4.10"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
+  integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==
+  dependencies:
+    commander "~2.19.0"
+    source-map "~0.6.1"
+
+uglify-js@^3.1.4:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
+  integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
+  dependencies:
+    commander "~2.20.0"
+    source-map "~0.6.1"
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+  integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+  integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277"
+  integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57"
+  integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==
+
+union-value@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
+  integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^2.0.1"
+
+uniq@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
+
+uniqs@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+  integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
+
+unique-filename@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
+  integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
+  dependencies:
+    unique-slug "^2.0.0"
+
+unique-slug@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c"
+  integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==
+  dependencies:
+    imurmurhash "^0.1.4"
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+  integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
+
+unquote@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
+  integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+upath@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
+  integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
+
+upper-case@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+  integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
+
+uri-js@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  dependencies:
+    punycode "^2.1.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url-loader@2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.1.0.tgz#bcc1ecabbd197e913eca23f5e0378e24b4412961"
+  integrity sha512-kVrp/8VfEm5fUt+fl2E0FQyrpmOYgMEkBsv8+UDP1wFhszECq5JyGF33I7cajlVY90zRZ6MyfgKXngLvHYZX8A==
+  dependencies:
+    loader-utils "^1.2.3"
+    mime "^2.4.4"
+    schema-utils "^2.0.0"
+
+url-parse@^1.4.3:
+  version "1.4.7"
+  resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
+  integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+  dependencies:
+    querystringify "^2.1.1"
+    requires-port "^1.0.0"
+
+url@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+  integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
+  dependencies:
+    punycode "1.3.2"
+    querystring "0.2.0"
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
+  integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
+  dependencies:
+    define-properties "^1.1.2"
+    object.getownpropertydescriptors "^2.0.3"
+
+util@0.10.3:
+  version "0.10.3"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+  integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
+  dependencies:
+    inherits "2.0.1"
+
+util@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
+  integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
+  dependencies:
+    inherits "2.0.3"
+
+utila@^0.4.0, utila@~0.4:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
+  integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
+
+utils-merge@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+  integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+
+uuid@^3.0.1, uuid@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+  integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+
+v8-compile-cache@^2.0.3:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
+  integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
+
+validate-npm-package-license@^3.0.1:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+  integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+  dependencies:
+    spdx-correct "^3.0.0"
+    spdx-expression-parse "^3.0.0"
+
+vary@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+  integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+
+vendors@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0"
+  integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==
+
+verror@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
+vm-browserify@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019"
+  integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==
+
+w3c-hr-time@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"
+  integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=
+  dependencies:
+    browser-process-hrtime "^0.1.2"
+
+w3c-xmlserializer@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794"
+  integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==
+  dependencies:
+    domexception "^1.0.1"
+    webidl-conversions "^4.0.2"
+    xml-name-validator "^3.0.0"
+
+walker@^1.0.7, walker@~1.0.5:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
+  integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=
+  dependencies:
+    makeerror "1.0.x"
+
+watchpack@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
+  integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==
+  dependencies:
+    chokidar "^2.0.2"
+    graceful-fs "^4.1.2"
+    neo-async "^2.5.0"
+
+wbuf@^1.1.0, wbuf@^1.7.3:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
+  integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
+  dependencies:
+    minimalistic-assert "^1.0.0"
+
+webidl-conversions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+  integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
+
+webpack-dev-middleware@^3.5.1:
+  version "3.7.0"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz#ef751d25f4e9a5c8a35da600c5fda3582b5c6cff"
+  integrity sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==
+  dependencies:
+    memory-fs "^0.4.1"
+    mime "^2.4.2"
+    range-parser "^1.2.1"
+    webpack-log "^2.0.0"
+
+webpack-dev-server@3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz#1b45ce3ecfc55b6ebe5e36dab2777c02bc508c4e"
+  integrity sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw==
+  dependencies:
+    ansi-html "0.0.7"
+    bonjour "^3.5.0"
+    chokidar "^2.0.0"
+    compression "^1.5.2"
+    connect-history-api-fallback "^1.3.0"
+    debug "^4.1.1"
+    del "^3.0.0"
+    express "^4.16.2"
+    html-entities "^1.2.0"
+    http-proxy-middleware "^0.19.1"
+    import-local "^2.0.0"
+    internal-ip "^4.2.0"
+    ip "^1.1.5"
+    killable "^1.0.0"
+    loglevel "^1.4.1"
+    opn "^5.1.0"
+    portfinder "^1.0.9"
+    schema-utils "^1.0.0"
+    selfsigned "^1.9.1"
+    semver "^5.6.0"
+    serve-index "^1.7.2"
+    sockjs "0.3.19"
+    sockjs-client "1.3.0"
+    spdy "^4.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^6.1.0"
+    url "^0.11.0"
+    webpack-dev-middleware "^3.5.1"
+    webpack-log "^2.0.0"
+    yargs "12.0.2"
+
+webpack-log@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
+  integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==
+  dependencies:
+    ansi-colors "^3.0.0"
+    uuid "^3.3.2"
+
+webpack-manifest-plugin@2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.0.4.tgz#e4ca2999b09557716b8ba4475fb79fab5986f0cd"
+  integrity sha512-nejhOHexXDBKQOj/5v5IZSfCeTO3x1Dt1RZEcGfBSul891X/eLIcIVH31gwxPDdsi2Z8LKKFGpM4w9+oTBOSCg==
+  dependencies:
+    fs-extra "^7.0.0"
+    lodash ">=3.5 <5"
+    tapable "^1.0.0"
+
+webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
+  integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
+  dependencies:
+    source-list-map "^2.0.0"
+    source-map "~0.6.1"
+
+webpack@4.39.1:
+  version "4.39.1"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.39.1.tgz#60ed9fb2b72cd60f26ea526c404d2a4cc97a1bd8"
+  integrity sha512-/LAb2TJ2z+eVwisldp3dqTEoNhzp/TLCZlmZm3GGGAlnfIWDgOEE758j/9atklNLfRyhKbZTCOIoPqLJXeBLbQ==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-module-context" "1.8.5"
+    "@webassemblyjs/wasm-edit" "1.8.5"
+    "@webassemblyjs/wasm-parser" "1.8.5"
+    acorn "^6.2.1"
+    ajv "^6.10.2"
+    ajv-keywords "^3.4.1"
+    chrome-trace-event "^1.0.2"
+    enhanced-resolve "^4.1.0"
+    eslint-scope "^4.0.3"
+    json-parse-better-errors "^1.0.2"
+    loader-runner "^2.4.0"
+    loader-utils "^1.2.3"
+    memory-fs "^0.4.1"
+    micromatch "^3.1.10"
+    mkdirp "^0.5.1"
+    neo-async "^2.6.1"
+    node-libs-browser "^2.2.1"
+    schema-utils "^1.0.0"
+    tapable "^1.1.3"
+    terser-webpack-plugin "^1.4.1"
+    watchpack "^1.6.0"
+    webpack-sources "^1.4.1"
+
+websocket-driver@>=0.5.1:
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
+  integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==
+  dependencies:
+    http-parser-js ">=0.4.0 <0.4.11"
+    safe-buffer ">=5.1.0"
+    websocket-extensions ">=0.1.1"
+
+websocket-extensions@>=0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
+  integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
+
+whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+  integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+  dependencies:
+    iconv-lite "0.4.24"
+
+whatwg-fetch@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
+  integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
+
+whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+  integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+
+whatwg-url@^6.4.1:
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
+  integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
+  dependencies:
+    lodash.sortby "^4.7.0"
+    tr46 "^1.0.1"
+    webidl-conversions "^4.0.2"
+
+whatwg-url@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd"
+  integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==
+  dependencies:
+    lodash.sortby "^4.7.0"
+    tr46 "^1.0.1"
+    webidl-conversions "^4.0.2"
+
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which@^1.2.9, which@^1.3.0, which@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
+wide-align@^1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+  integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+  dependencies:
+    string-width "^1.0.2 || 2"
+
+wordwrap@~0.0.2:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+  integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
+
+wordwrap@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+  integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
+
+workbox-background-sync@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz#26821b9bf16e9e37fd1d640289edddc08afd1950"
+  integrity sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-broadcast-update@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz#e2c0280b149e3a504983b757606ad041f332c35b"
+  integrity sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-build@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-4.3.1.tgz#414f70fb4d6de47f6538608b80ec52412d233e64"
+  integrity sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw==
+  dependencies:
+    "@babel/runtime" "^7.3.4"
+    "@hapi/joi" "^15.0.0"
+    common-tags "^1.8.0"
+    fs-extra "^4.0.2"
+    glob "^7.1.3"
+    lodash.template "^4.4.0"
+    pretty-bytes "^5.1.0"
+    stringify-object "^3.3.0"
+    strip-comments "^1.0.2"
+    workbox-background-sync "^4.3.1"
+    workbox-broadcast-update "^4.3.1"
+    workbox-cacheable-response "^4.3.1"
+    workbox-core "^4.3.1"
+    workbox-expiration "^4.3.1"
+    workbox-google-analytics "^4.3.1"
+    workbox-navigation-preload "^4.3.1"
+    workbox-precaching "^4.3.1"
+    workbox-range-requests "^4.3.1"
+    workbox-routing "^4.3.1"
+    workbox-strategies "^4.3.1"
+    workbox-streams "^4.3.1"
+    workbox-sw "^4.3.1"
+    workbox-window "^4.3.1"
+
+workbox-cacheable-response@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz#f53e079179c095a3f19e5313b284975c91428c91"
+  integrity sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-core@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-4.3.1.tgz#005d2c6a06a171437afd6ca2904a5727ecd73be6"
+  integrity sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg==
+
+workbox-expiration@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-4.3.1.tgz#d790433562029e56837f341d7f553c4a78ebe921"
+  integrity sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-google-analytics@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz#9eda0183b103890b5c256e6f4ea15a1f1548519a"
+  integrity sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg==
+  dependencies:
+    workbox-background-sync "^4.3.1"
+    workbox-core "^4.3.1"
+    workbox-routing "^4.3.1"
+    workbox-strategies "^4.3.1"
+
+workbox-navigation-preload@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz#29c8e4db5843803b34cd96dc155f9ebd9afa453d"
+  integrity sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-precaching@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-4.3.1.tgz#9fc45ed122d94bbe1f0ea9584ff5940960771cba"
+  integrity sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-range-requests@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz#f8a470188922145cbf0c09a9a2d5e35645244e74"
+  integrity sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-routing@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-4.3.1.tgz#a675841af623e0bb0c67ce4ed8e724ac0bed0cda"
+  integrity sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-strategies@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-4.3.1.tgz#d2be03c4ef214c115e1ab29c9c759c9fe3e9e646"
+  integrity sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-streams@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-4.3.1.tgz#0b57da70e982572de09c8742dd0cb40a6b7c2cc3"
+  integrity sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA==
+  dependencies:
+    workbox-core "^4.3.1"
+
+workbox-sw@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-4.3.1.tgz#df69e395c479ef4d14499372bcd84c0f5e246164"
+  integrity sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w==
+
+workbox-webpack-plugin@4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-4.3.1.tgz#47ff5ea1cc074b6c40fb5a86108863a24120d4bd"
+  integrity sha512-gJ9jd8Mb8wHLbRz9ZvGN57IAmknOipD3W4XNE/Lk/4lqs5Htw4WOQgakQy/o/4CoXQlMCYldaqUg+EJ35l9MEQ==
+  dependencies:
+    "@babel/runtime" "^7.0.0"
+    json-stable-stringify "^1.0.1"
+    workbox-build "^4.3.1"
+
+workbox-window@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-4.3.1.tgz#ee6051bf10f06afa5483c9b8dfa0531994ede0f3"
+  integrity sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg==
+  dependencies:
+    workbox-core "^4.3.1"
+
+worker-farm@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
+  integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
+  dependencies:
+    errno "~0.1.7"
+
+worker-rpc@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5"
+  integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==
+  dependencies:
+    microevent.ts "~0.1.1"
+
+wrap-ansi@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+  integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+write-file-atomic@2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529"
+  integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==
+  dependencies:
+    graceful-fs "^4.1.11"
+    imurmurhash "^0.1.4"
+    signal-exit "^3.0.2"
+
+write@1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
+  integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
+  dependencies:
+    mkdirp "^0.5.1"
+
+ws@^5.2.0:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
+  integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+ws@^6.1.2:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
+  integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+xml-name-validator@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+  integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+
+xmlchars@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.1.1.tgz#ef1a81c05bff629c2280007f12daca21bd6f6c93"
+  integrity sha512-7hew1RPJ1iIuje/Y01bGD/mXokXxegAgVS+e+E0wSi2ILHQkYAH1+JXARwTjZSM4Z4Z+c73aKspEcqj+zPPL/w==
+
+xregexp@4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
+  integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
+
+xtend@^4.0.0, xtend@~4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+
+yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
+  integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
+
+yargs-parser@^10.1.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
+  integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==
+  dependencies:
+    camelcase "^4.1.0"
+
+yargs-parser@^11.1.1:
+  version "11.1.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
+  integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
+yargs@12.0.2:
+  version "12.0.2"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc"
+  integrity sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==
+  dependencies:
+    cliui "^4.0.0"
+    decamelize "^2.0.0"
+    find-up "^3.0.0"
+    get-caller-file "^1.0.1"
+    os-locale "^3.0.0"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^2.0.0"
+    which-module "^2.0.0"
+    y18n "^3.2.1 || ^4.0.0"
+    yargs-parser "^10.1.0"
+
+yargs@^12.0.2:
+  version "12.0.5"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
+  integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
+  dependencies:
+    cliui "^4.0.0"
+    decamelize "^1.2.0"
+    find-up "^3.0.0"
+    get-caller-file "^1.0.1"
+    os-locale "^3.0.0"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^2.0.0"
+    which-module "^2.0.0"
+    y18n "^3.2.1 || ^4.0.0"
+    yargs-parser "^11.1.1"
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..94336fcae912db8a11d55634156fa011f4686124 100644
GIT binary patch
delta 75
zcmeykjrrp?<_YF3{B|p=CR)d^elGf0#5nPQ^v3PRCxzG;0=(Hd{+{0VNQ{AjL4<*U
cVe*4hGLt1vX@k{oc0YAbfCVHyS@X6B0E0Xq6aWAK

delta 75
zcmeykjrrp?<_YF3`^8VXPqdC<{aEy|h;iZp>5bcsPYN+H1bDM^WdGhdRg8gwL4<*U
cVe*4hGLt1vX@k{oc0YAbfCVHyS@X6B0E$8$k^lez

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f4d7b2bf6..290541c73 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew.bat b/gradlew.bat
index f9553162f..e95643d6a 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,84 +1,84 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem  Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
-- 
GitLab