Skip to content

Commit

Permalink
Merge pull request #538 from matematikk-mooc/aj/DIT-235
Browse files Browse the repository at this point in the history
DIT-235: Added custom modal for self unenrollment
  • Loading branch information
madsenandreas authored Jun 26, 2024
2 parents a3878e1 + e435d7c commit b07227c
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 37 deletions.
8 changes: 6 additions & 2 deletions src/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ jQuery(function($) {
});

routes.addRouteForPath(/\/courses\/\d+/, function() {
let courseUnenrollmentUuid = util.getCourseUnenrollmentUuid();

document.body.classList.add('course-menu-expanded');
document.getElementById('left-side').setAttribute('style', 'display: block !important');
coursepagebanner.insertCourseBanner();
coursepagebanner.insertCourseBanner(util.course.id, courseUnenrollmentUuid);
let authenticated = util.isAuthenticated();
informationBanner.updateInformationBanner();
if(!authenticated) {
Expand All @@ -123,8 +125,10 @@ jQuery(function($) {

//The logic below should be refactored and cleaned up.
routes.addRouteForPath(/\/courses\/\d+$/, function () {
let courseUnenrollmentUuid = util.getCourseUnenrollmentUuid();

coursepage.hideElementsFromUsers();
coursepagebanner.insertCourseBanner();
coursepagebanner.insertCourseBanner(util.course.id, courseUnenrollmentUuid);
renderCourseModules("left-side");
util.updateRightMenuButtons();
util.removeRecentFeedback();
Expand Down
6 changes: 4 additions & 2 deletions src/js/modules/coursepagebanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kpasApi from "../api/kpas-api";

export default (function() {
return {
insertCourseBanner: function () {
insertCourseBanner: function (courseId, courseUnenrollmentUuid) {
let currentCourseId = ENV.COURSE_ID? ENV.COURSE_ID : ENV.course_id ? ENV.course_id : ENV.COURSE.id;
let isEnrolled = ENV.current_user_is_student? ENV.current_user_is_student || ENV.COURSE.is_student : false;
let isFrontPage = false;
Expand All @@ -25,7 +25,9 @@ export default (function() {
title: course.name,
isEnrolled: isEnrolled,
isFrontPage: isFrontPage,
languages: settings.multilang.toLowerCase()
languages: settings.multilang.toLowerCase(),
courseId: courseId,
unenrollmentUuid: courseUnenrollmentUuid
});
let coursePageBannerWrapper = document.createElement("div");
coursePageBannerWrapper.id = "course-page-banner-wrapper";
Expand Down
68 changes: 39 additions & 29 deletions src/js/modules/util.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import api from '../api/api.js'
import api from '../api/api.js'
import settings from "../settings";

export default (function () {
Expand All @@ -14,7 +14,7 @@ export default (function () {
mapCourseSettings: function (courses, courseSettings) {
courses.forEach(course => {
var cc = courseSettings.find(x => x.course_id === course.id)
if(cc) {
if (cc) {
course.course_settings = cc
}
});
Expand All @@ -28,19 +28,19 @@ export default (function () {
},


isMobileOrTablet: function() {
isMobileOrTablet: function () {
if (navigator.userAgent.match(/Android/i)
|| navigator.userAgent.match(/webOS/i)
|| navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)
|| navigator.userAgent.match(/BlackBerry/i)
|| navigator.userAgent.match(/Windows Phone/i)) {
|| navigator.userAgent.match(/webOS/i)
|| navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)
|| navigator.userAgent.match(/BlackBerry/i)
|| navigator.userAgent.match(/Windows Phone/i)) {
return true;
}
else {
return false;
}
return false;
}
},

getPageTitleBeforeColon: function () {
Expand Down Expand Up @@ -214,13 +214,13 @@ export default (function () {
return false;
},

isMultilangCourse() { return this.course.kpas.multilang != "NONE"},
isNynorskCourse() { return this.course.kpas.multilang == "NN"},
isSamiskCourse() { return this.course.kpas.multilang == "SE"},
isMultilangCourse() { return this.course.kpas.multilang != "NONE" },
isNynorskCourse() { return this.course.kpas.multilang == "NN" },
isSamiskCourse() { return this.course.kpas.multilang == "SE" },
isPrincipal() {
return (this.isTeacherOrAdmin() || this.isEnrolledWithRole(this.course, settings.principalRoleType));
},
isRoleBasedCourse() {return this.course.kpas.role_support == 1},
isRoleBasedCourse() { return this.course.kpas.role_support == 1 },
isMMOOCLicense() {
return this.course.kpas.licence == 1;
},
Expand All @@ -239,46 +239,46 @@ export default (function () {

getBannerType() {
console.log(this.course)
if(this.course && this.course.kpas && this.course.kpas.banner_type){
if (this.course && this.course.kpas && this.course.kpas.banner_type) {
return this.course.kpas.banner_type;
}
return "NONE";
},
getAlertMsg() {
if (this.course) {
if(this.course.kpas.banner_type == "ALERT"){
if (this.course.kpas.banner_type == "ALERT") {
return this.course.kpas.banner_text;
}
}
return "";
},
getUnmaintainedMsg() {
if (this.course) {
if(this.course.kpas.banner_type == "UNMAINTAINED"){
if (this.course.kpas.banner_type == "UNMAINTAINED") {
return this.course.kpas.banner_text;
}
}
return "";
},
getUnmaintainedSinceDate() {
if(this.course){
if(this.course.kpas.banner_type == "UNMAINTAINED"){
if (this.course) {
if (this.course.kpas.banner_type == "UNMAINTAINED") {
return this.course.kpas.unmaintained_since;
}
}
return null;
},
getNotificationMsg() {
if (this.course) {
if(this.course.kpas.banner_type == "NOTIFICATION"){
if (this.course.kpas.banner_type == "NOTIFICATION") {
return this.course.kpas.banner_text;
}
}
return "";
},
getFeedbackMsg() {
if (this.course) {
if(this.course.kpas.banner_type == "FEEDBACK"){
if (this.course.kpas.banner_type == "FEEDBACK") {
return this.course.kpas.banner_text;
}
}
Expand All @@ -300,7 +300,7 @@ export default (function () {
},
isMemberOfExpiredCommunity(course, callback) {
let self = this
if(!course) {
if (!course) {
return;
}
api.getUserGroupsForCourse(course.id, function (groups) {
Expand All @@ -327,6 +327,16 @@ export default (function () {
return api.getRoles() !== null;
},

getCourseUnenrollmentUuid() {
const unenrollmentLink = document.querySelector("#self_unenrollment_dialog a.btn-primary.action");
if (!unenrollmentLink) return null;

const hrefValue = unenrollmentLink.getAttribute("href") ?? "";
const match = hrefValue.match(/\/self_unenrollment\/([^/]+)/);

const unenrollmentUuid = match ? match[1] : null;
return unenrollmentUuid;
},
getGroupsInfo(groups) {
var groupsInfo = {};
for (var i = 0; i < groups.length; i++) {
Expand Down Expand Up @@ -467,20 +477,20 @@ export default (function () {
(key, value) => key === "" ? value : decodeURIComponent(value)
);
},
updateRightMenuButtons: function() {
$("#course_show_secondary > a").each(function() {
updateRightMenuButtons: function () {
$("#course_show_secondary > a").each(function () {
var $this = $(this);
var _href = $this.attr("href");
if(_href.includes("/calendar?")){
if (_href.includes("/calendar?")) {
$this.remove();
}
else{
else {
$this.attr("href", _href);
}
});
},
removeRecentFeedback: function() {
$(".recent_feedback").each(function() {
removeRecentFeedback: function () {
$(".recent_feedback").each(function () {
var $this = $(this);
$this.remove()
});
Expand Down
35 changes: 31 additions & 4 deletions src/vue/components/course-page-banner/CoursePageBanner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,24 @@
{{ title }}
</h1>
<div class="course-page__banner__actions">
<Button v-if="isEnrolled && isFrontPage" type="outlined" class="self_unenrollment_link">Meld deg av</Button>
<LanguageSelectorContainer :languages="languages"></LanguageSelectorContainer>
<Button v-if="unenrollmentUuid != null" type="outlined" id="actions_self_unenrollment" @click="openUnenrollment">Meld deg av</Button>
<LanguageSelectorContainer :languages="languages"></LanguageSelectorContainer>
</div>
</div>

<UnenrollCourse
:courseId="courseId"
:unenrollmentUuid="unenrollmentUuid"
:isModalOpen="isUnenrollmentOpen"
:closeModal="closeUnenrollment"
/>
</div>
</template>

<script>
import Button from '../Button.vue';
import LanguageSelectorContainer from '../language-selector-container/LanguageSelectorContainer.vue';
import UnenrollCourse from '../unenroll/UnenrollCourse.vue';
export default {
props: {
Expand All @@ -27,6 +35,8 @@ export default {
isEnrolled: Boolean,
isFrontPage: Boolean,
languages: String,
courseId: String,
unenrollmentUuid: String
},
setup(props) {
return {
Expand All @@ -35,12 +45,28 @@ export default {
title: props.title,
isEnrolled: props.isEnrolled,
isFrontPage: props.isFrontPage,
languages: props.languages
languages: props.languages,
courseId: props.courseId,
unenrollmentUuid: props.unenrollmentUuid,
};
},
data() {
return {
isUnenrollmentOpen: false
};
},
methods: {
openUnenrollment() {
this.isUnenrollmentOpen = true;
},
closeUnenrollment() {
this.isUnenrollmentOpen = false;
}
},
components: {
Button,
LanguageSelectorContainer
LanguageSelectorContainer,
UnenrollCourse
},
};
</script>
Expand All @@ -49,6 +75,7 @@ export default {
@import '../../design/card-themes';
.course-page__banner-container {
margin-top: 0px !important;
width: 100%;
height: 12rem;
box-sizing: border-box;
Expand Down
4 changes: 4 additions & 0 deletions src/vue/components/modal/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,8 @@ const closeModal = () => {
> * { margin: 0 0.125rem; }
}
}
.course-page__banner-container .modal-box{
max-width: 40rem;
}
</style>
82 changes: 82 additions & 0 deletions src/vue/components/unenroll/UnenrollCourse.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<Modal :is-open="isModalOpen" @close="closeModal">
<template v-slot:header>
<h1>Bekreft avmelding</h1>
</template>

<template v-slot:main>
<p>
Er du sikker på at du vil melde deg av dette emnet? Du vil ikke lenger se emnedeltagere eller kommunisere direkte med foreleserne, og vil ikke kunne se hendelsene fra emnet i strømmen din og som varslinger.
</p>
</template>

<template v-slot:actions>
<Button class="btn--lg" type="outlined" @click="unenrollCourse">Meld deg av emnet</Button>
</template>
</Modal>
</template>


<script>
import Modal from '../modal/Modal.vue'
import Button from '../Button.vue'
import {getCookie} from '../../utils/url-utils';
export default {
name: 'UnenrollCourse',
components: {
Modal,
Button,
},
props: {
courseId: String,
unenrollmentUuid: String,
isModalOpen: Boolean,
closeModal: Function
},
methods: {
unenrollCourse() {
const url = `/courses/${this.courseId}/self_unenrollment/${this.unenrollmentUuid}`
const csrfToken = getCookie('_csrf_token');
const formData = new URLSearchParams();
formData.append('_method', 'POST');
formData.append('authenticity_token', csrfToken);
fetch(url, {
method: 'POST',
body: formData,
headers: {
'Accept': 'application/json, text/javascript, application/json+canvas-string-ids, */*; q=0.01',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Csrf-Token': decodeURIComponent(csrfToken)
},
})
.then(response => {
if (!response.ok) throw new Error(`Network response was not ok for unenrollment (courseId: ${this.courseId}, unenrollmentUuid: ${this.unenrollmentUuid})`);
window.location.href = '/search/all_courses';
})
.catch(() => {
this.closeModal();
})
}
}
}
</script>

<style scoped>
.backdrop{
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(153, 153, 153, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal{
opacity: 1 !important;
}
</style>
12 changes: 12 additions & 0 deletions src/vue/utils/url-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ export function shallowUpdateUrlParameter(param, value) {
window.history.pushState({ path: newUrl }, '', newUrl);
return newUrl;
}

/**
* Retrieves the value of a specified cookie.
* @param {string} name - The name of the cookie to retrieve.
* @returns {string|null} The value of the cookie if found, otherwise `null`.
*/
export function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}

0 comments on commit b07227c

Please sign in to comment.