Implemented line enrolling management panel

This commit is contained in:
PMKuipers 2024-03-06 23:49:50 +01:00
parent 8938facdef
commit 079e2f77cc
11 changed files with 186 additions and 40 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -191,6 +191,7 @@ export default {
can_enrol: 'line_can_enrol',
is_enrolled: 'line_is_enrolled',
enrol: 'line_enrol',
unenrol: 'line_unenrol',
enrolled: 'line_enrolled',
notenrolled: 'line_notenrolled',
enrol_question: 'line_enrol_question',
@ -203,6 +204,12 @@ export default {
enrolled_in: 'line_enrolled_in',
since: 'since@core',
byname: 'byname@core',
students: 'students@core',
firstname: 'firstname@core',
lastname: 'lastname@core',
email: 'email@core',
enrol_student_question: 'line_enrol_student_question',
unenrol_student_question: 'line_unenrol_student_question',
}
@ -797,8 +804,8 @@ export default {
students: null,
can_unenrol: false,
sorting: {
asc: true,
field: 'enrolled'
asc: false,
field: 'enrolled_time'
}
};
},
@ -858,11 +865,11 @@ export default {
if (field == 'enrolled') {
return ((df.enrolled)?1:0) - ((ef.enrolled)?1:0);
} else if (field == "enrolled_time") {
const dvalue = (df[field]?df[field]:0);
const evalue = (df[field]?ef[field]:0);
const dvalue = (df[field] && d.enrol.enrolled)?df[field]:0;
const evalue = (ef[field] && e.enrol.enrolled)?ef[field]:0;
return dvalue - evalue;
} else {
return String(d[this.sorting.header]).localeCompare(String(e[this.sorting.header]));
return String(df[this.sorting.field]).localeCompare(String(ef[this.sorting.field]));
}
});
@ -871,6 +878,7 @@ export default {
},
methods: {
premiumenabled,
format_datetime,
onLineHeightChange(lineid){
// All layers for this line have the first slot send an update message on layer height change.
// When one of those updates is received, record the height and recalculate the total height of the
@ -901,23 +909,47 @@ export default {
}).catch(notification.exception);
},
enrol_student(id) {
enrol_student(student) {
const self=this;
call([{
methodname: 'local_treestudyplan_line_enrol_students',
args: { id: self.value.id, users: [id] },
}])[0].then(function(response){
self.$emit('enrolupdate',response);
}).catch(notification.exception);
const user = student.user;
let question = self.text.enrol_student_question.replace('{$a}',`${user.firstname} ${user.lastname}`);
const options = {
okTitle: self.text.yes,
cancelTitle: self.text.no,
okVariant: "success",
cancelVariant: "danger",
};
this.$bvModal.msgBoxConfirm(question, options).then(reply => {
if(reply) {
call([{
methodname: 'local_treestudyplan_line_enrol_students',
args: { id: self.value.id, users: [user.id] },
}])[0].then(function(response){
student.enrol = response[0].enrol;
}).catch(notification.exception);
}
});
},
unenrol_student(id) {
unenrol_student(student) {
const self=this;
call([{
methodname: 'local_treestudyplan_line_unenrol_students',
args: { id: self.value.id, users: [id] },
}])[0].then(function(response){
self.$emit('enrolupdate',response);
}).catch(notification.exception);
const user = student.user;
let question = self.text.enrol_student_question.replace('{$a}',`${user.firstname} ${user.lastname}`);
const options = {
okTitle: self.text.yes,
cancelTitle: self.text.no,
okVariant: "success",
cancelVariant: "danger",
};
this.$bvModal.msgBoxConfirm(question, options).then(reply => {
if(reply) {
call([{
methodname: 'local_treestudyplan_line_unenrol_students',
args: { id: self.value.id, users: [user.id] },
}])[0].then(function(response){
student.enrol = response[0].enrol;
}).catch(notification.exception);
}
});
},
load_students() {
const self=this;
@ -928,9 +960,15 @@ export default {
self.students = response.userinfo;
self.can_unenrol = response.can_unenrol;
}).catch(notification.exception);
}
},
toggleSort(header) {
if (this.sorting.field == header) {
this.sorting.asc = !this.sorting.asc;
} else {
this.sorting.field = header;
this.sorting.asc = true;
}
},
},
template: `
@ -953,20 +991,73 @@ export default {
><i class='fa fa-unlock-alt text-success'></i>&nbsp;{{text.enrollments}}</a>
<b-modal
:id="'r-enrollments-'+value.id"
@show="load_students"
size="xl"
ok-only
scrollable
:title="value.name"
>
<table>
<tr v-if="students == null"><td>
<table class="r-line-enroll-userlist">
<thead>
<th><a href='#' @click.prevent="toggleSort('firstname')"
>{{text.firstname}}</a>
<i v-if="sorting.field=='firstname' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
><i v-else-if="sorting.field=='firstname' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
</th>
<th><a href='#' @click.prevent="toggleSort('lastname')"
>{{text.lastname}}</a>
<i v-if="sorting.field=='lastname' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
><i v-else-if="sorting.field=='lastname' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
</th>
<th><a href='#' @click.prevent="toggleSort('enrolled')"
>{{text.enrolled}}</a>
<i v-if="sorting.field=='enrolled' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
><i v-else-if="sorting.field=='enrolled' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
</th>
<th><a href='#' @click.prevent="toggleSort('enrolled_time')"
>{{text.since}}</a>
<i v-if="sorting.field=='enrolled_time' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
><i v-else-if="sorting.field=='enrolled_time' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
</th>
<th><a href='#' @click.prevent="toggleSort('enrolled_by')"
>{{by}}</a>
<i v-if="sorting.field=='enrolled_by' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
><i v-else-if="sorting.field=='enrolled_by' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
</th>
<th v-if="can_enrol || can_unenrol">{{text.enrol}}</th>
</thead>
<tbody>
<tr v-if="students == null"><td
:colspan="4+(can_enrol?1:0)">
<div class="spinner-border spinner-border-sm text-info" role="status"></div>
</td></tr>
<template v-if="students && can_enrol">
<tr v-for="student in students">
</template>
<template v-else-if="students">
</template>
<template v-else>
<tr v-for="student in sorted_students">
<td>{{student.user.firstname}}</td>
<td>{{student.user.lastname}}</td>
<td><span v-if="student.enrol.enrolled" class="text-success">{{text.enrolled}}</span
><span v-else class="text-danger">{{text.notenrolled}}</span></td>
<td><span v-if="student.enrol.enrolled"
>{{format_datetime(student.enrol.enrolled_time)}}</span></td>
<td><span v-if="student.enrol.enrolled"
>{{student.enrol.enrolled_by}}</span></td>
<td v-if="can_enrol"><b-button variant="success"
@click="enrol_student(student.user)"
>{{text.enrol}}</b-button></td>
<td ><b-button v-if="!student.enrol.enrolled && (can_enrol || can_unenrol)"
variant="success"
size="sm"
@click="enrol_student(student)"
>{{text.enrol}}</b-button
><b-button v-else-if="student.enrol.enrolled && can_unenrol"
variant="danger"
size="sm"
@click="unenrol_student(student)"
>{{text.unenrol}}</b-button>
</td>
</tr>
</template>
</tbody>
</table>
</b-modal>
</template>

View File

@ -478,10 +478,7 @@ export default {
},
updated() {
},
/* https://css-tricks.com/position-sticky-and-table-headers/ */
/* TODO: Rework below to make use of tables. Use <Thead> as main element. Then create multiple <tr> as needed for the headers.
This should create a much better view than using divs overal.
*/
/* TODO: https://css-tricks.com/position-sticky-and-table-headers/ */
template: `
<thead class='q-header'>
<tr> <!-- period heading -->

View File

@ -2010,8 +2010,10 @@ class studyplanservice extends \external_api {
webservicehelper::require_capabilities(self::CAP_VIEW, $context);
$list = [];
// Unenrol capability also acts as overriding manager capability to register/unregister.
$can_unenrol = \has_capability('local/treestudyplan:lineunenrol',$context);
foreach ($users as $userid) {
if ($o->can_enrol($userid)) {
if ($o->can_enrol($userid) || $can_unenrol) {
$o->enrol($userid);
$list[] = self::student_enrol_status_model($userid,$o);
}

View File

@ -1420,6 +1420,24 @@ body.path-local-treestudyplan .editmode-switch-form > * {
position: relative;
top: -3px;
}
.path-local-treestudyplan table.r-line-enroll-userlist td,
.path-local-treestudyplan table.r-line-enroll-userlist th,
.features-treestudyplan table.r-line-enroll-userlist td,
.features-treestudyplan table.r-line-enroll-userlist th {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.path-local-treestudyplan table.r-line-enroll-userlist th,
.features-treestudyplan table.r-line-enroll-userlist th {
background: white;
position: sticky;
top: 0; /* Don't forget this, required for the stickiness */
border-bottom: 1px solid #ddd;
}
.path-local-treestudyplan table.r-line-enroll-userlist tr:nth-child(odd),
.features-treestudyplan table.r-line-enroll-userlist tr:nth-child(odd) {
background-color: var(--light);
}
.path-local-treestudyplan .card.s-studyplan-card,
.features-treestudyplan .card.s-studyplan-card {

View File

@ -472,9 +472,12 @@ $string["line_enrollable:1"] = 'Registration by students themselves.';
$string["line_enrollable:2"] = 'Registration by user with role';
$string["line_enrollable:3"] = 'Registration by students themeselves or user with role';
$string["line_enrol"] = 'Register';
$string["line_unenrol"] = 'Unregister';
$string["line_enrolled"] = 'Registered';
$string["line_notenrolled"] = 'Not registered';
$string["line_enrol_question"] = 'Do you want to register yourself for {$a}?';
$string["line_enrol_student_question"] = 'Do you want to register {$a} in this line?';
$string["line_unenrol_student_question"] = 'Do you want to unregister {$a} from this line?';
$string["line_enrollments"] = 'Registrations';
$string["line_enrollment"] = 'Registration';
$string["line_cannot_enrol"] = 'You cannot register yourself for this line';

View File

@ -472,9 +472,12 @@ $string["line_enrollable:1"] = 'Inschrijving door student zelf';
$string["line_enrollable:2"] = 'Inschrijving door gebruiker met rol';
$string["line_enrollable:3"] = 'Inschrijving door student zelf of gebruiker met rol';
$string["line_enrol"] = 'Inschrijven';
$string["line_unenrol"] = 'Uitschrijven';
$string["line_enrolled"] = 'Ingeschreven';
$string["line_notenrolled"] = 'Niet ingeschreven';
$string["line_enrol_quetsion"] = 'Wil je jezelf inschrijven voor {$a}?';
$string["line_enrol_student_question"] = 'Wil je {$a} inschrijven voor deze leerlijn?';
$string["line_unenrol_student_question"] = 'Wil je {$a} uitschrijven voor deze leerlijn?';
$string["line_enrollments"] = 'Inschrijvingen';
$string["line_enrollment"] = 'Inschrijving';
$string["line_cannot_enrol"] = 'Je kunt je niet zelf inschrijven voor deze leerlijn';

View File

@ -1223,6 +1223,20 @@
}
}
table.r-line-enroll-userlist {
td,th {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
th {
background: white;
position: sticky;
top: 0; /* Don't forget this, required for the stickiness */
border-bottom: 1px solid #ddd;
}
tr:nth-child(odd) {
background-color: var(--light);
}
}
}

View File

@ -1420,6 +1420,24 @@ body.path-local-treestudyplan .editmode-switch-form > * {
position: relative;
top: -3px;
}
.path-local-treestudyplan table.r-line-enroll-userlist td,
.path-local-treestudyplan table.r-line-enroll-userlist th,
.features-treestudyplan table.r-line-enroll-userlist td,
.features-treestudyplan table.r-line-enroll-userlist th {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.path-local-treestudyplan table.r-line-enroll-userlist th,
.features-treestudyplan table.r-line-enroll-userlist th {
background: white;
position: sticky;
top: 0; /* Don't forget this, required for the stickiness */
border-bottom: 1px solid #ddd;
}
.path-local-treestudyplan table.r-line-enroll-userlist tr:nth-child(odd),
.features-treestudyplan table.r-line-enroll-userlist tr:nth-child(odd) {
background-color: var(--light);
}
.path-local-treestudyplan .card.s-studyplan-card,
.features-treestudyplan .card.s-studyplan-card {