Implemented hiding of non-enrolled lines in student result views

This commit is contained in:
PMKuipers 2024-03-07 22:44:29 +01:00
parent 079e2f77cc
commit d713e24e32
13 changed files with 238 additions and 17 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -199,7 +199,6 @@ export function init(contextid,categoryid) {
app.loadingstudyplan = false; app.loadingstudyplan = false;
}); });
} }
}, },
showOverview(){ showOverview(){
app.selectedstudent = null; app.selectedstudent = null;

View File

@ -425,8 +425,25 @@ export default {
self.studyplans = plans; self.studyplans = plans;
self.loading = false; self.loading = false;
// load studyplan from hash if applicable
const hash = window.location.hash.replace('#','');
const parts = hash.split("-");
if (!!parts && parts.length > 0) {
for (const k in self.studyplans) {
const list = self.studyplans[k];
for (const idx in list) {
const plan = list[idx];
if (plan.id == parts[0]){
self.selectStudyplan(plan);
return;
}
}
}
}
if (self.studyplans.present.length == 1) { if (self.studyplans.present.length == 1) {
// Directly show the current study plan if it's the only one // Directly show the current study plan if it's the only current one
self.selectStudyplan(self.studyplans.present[0]); self.selectStudyplan(self.studyplans.present[0]);
} else { } else {
// If there is but a single studyplan, select it anyway, even if it is not current... // If there is but a single studyplan, select it anyway, even if it is not current...
@ -451,11 +468,13 @@ export default {
}])[0].then(function(response){ }])[0].then(function(response){
self.selectedstudyplan = ProcessStudyplan(response); self.selectedstudyplan = ProcessStudyplan(response);
self.loadingstudyplan = false; self.loadingstudyplan = false;
window.location.hash = self.selectedstudyplan.id;
}).catch(notification.exception); }).catch(notification.exception);
}, },
deselectStudyplan() { deselectStudyplan() {
this.selectedstudyplan = null; this.selectedstudyplan = null;
this.loadStudyplans(); // Reload the list of studyplans. this.loadStudyplans(); // Reload the list of studyplans.
window.location.hash = '';
} }
}, },
template: ` template: `
@ -1071,7 +1090,7 @@ export default {
href='#' @click.prevent="" href='#' @click.prevent=""
v-b-modal="'r-enrol-'+value.id" v-b-modal="'r-enrol-'+value.id"
:title="text.can_enrol" :title="text.can_enrol"
><i class='fa fa-unlock-alt text-success'></i>&nbsp;{{text.enrol}}</a> ><i class='fa fa-unlock-alt text-info'></i>&nbsp;{{text.enrol}}</a>
<a v-else-if="enrolled" <a v-else-if="enrolled"
href='#' @click.prevent="" href='#' @click.prevent=""
v-b-modal="'r-enrollment-'+value.id" v-b-modal="'r-enrollment-'+value.id"
@ -1174,6 +1193,10 @@ export default {
} else { } else {
return ""; return "";
} }
},
cloud() {
const enrol = this.line.enrol;
return (!this.teachermode) && (enrol.enrollable > 0) && (!enrol.enrolled);
} }
}, },
data() { data() {
@ -1192,6 +1215,7 @@ export default {
><div class="r-slot-item" v-if="item" ><div class="r-slot-item" v-if="item"
><r-item ><r-item
v-model="item" v-model="item"
:cloud="cloud"
:plan="plan" :plan="plan"
:guestmode='guestmode' :guestmode='guestmode'
:teachermode='teachermode'></r-item :teachermode='teachermode'></r-item
@ -1219,6 +1243,10 @@ export default {
teachermode: { teachermode: {
type: Boolean, type: Boolean,
default: false, default: false,
},
cloud: {
type: Boolean,
default: false,
} }
}, },
data() { data() {
@ -1228,10 +1256,11 @@ export default {
}, },
methods: { methods: {
lineColor(){ lineColor(){
if(this.teachermode){ if(this.teachermode) {
return "var(--gray)"; return "var(--gray)";
} } else if (this.cloud) {
else{ return "#ccc";
} else {
switch(this.value.completion){ switch(this.value.completion){
default: // "incomplete" default: // "incomplete"
return "var(--gray)"; return "var(--gray)";
@ -1267,6 +1296,7 @@ export default {
start: LINE_GRAVITY, start: LINE_GRAVITY,
end: LINE_GRAVITY, end: LINE_GRAVITY,
}, },
class: (this.cloud?"r-dummy-line":""),
}); });
} }
}, },
@ -1343,8 +1373,12 @@ export default {
}, },
template: ` template: `
<div class="r-item-base" :id="'studyitem-'+value.id" :data-x='value.type'> <div class="r-item-base" :id="'studyitem-'+value.id" :data-x='value.type'>
<r-item-competency v-if="value.type == 'competency'" <template v-if="cloud">
v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-competency> <r-item-dummy-course v-if="value.type == 'course'"></r-item-dummy-course>
<r-item-dummy-badge v-else-if="value.type == 'badge'"></r-item-dummy-badge>
<r-item-dummy-filter v-else></r-item-dummy-filter>
</template>
<template v-else>
<r-item-course v-if="value.type == 'course' && !teachermode" :plan="plan" <r-item-course v-if="value.type == 'course' && !teachermode" :plan="plan"
v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-course> v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-course>
<r-item-teachercourse v-if="value.type == 'course' && teachermode" :plan="plan" <r-item-teachercourse v-if="value.type == 'course' && teachermode" :plan="plan"
@ -1359,6 +1393,7 @@ export default {
v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-badge> v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-badge>
<r-item-invalid v-if="value.type == 'invalid' && teachermode" <r-item-invalid v-if="value.type == 'invalid' && teachermode"
v-model="value" ></r-item-invalid> v-model="value" ></r-item-invalid>
</template>
</div> </div>
`, `,
}); });
@ -3530,5 +3565,74 @@ export default {
</div> </div>
`, `,
}); });
Vue.component('r-item-dummy-course', {
props: {
'value' :{
type: Object,
default: function(){ return null;},
},
},
data() {
return {
text: strings.invalid,
};
},
methods: {
},
template: `
<div class="r-item-dummy-course">
<b-card no-body class="r-item-course">
<b-row no-gutters>
<b-col md="1">
<span class="r-timing-indicator timing-dummy"></span>
</b-col>
<b-col md="11">
<b-card-body class="align-items-center">
</b-card-body>
</b-col>
</b-row>
</b-card>
</div>
`,
});
Vue.component('r-item-dummy-filter',{
props: {
},
data() {
return {};
},
computed: {
},
created(){
},
methods: {
},
template: `
<div :class="'r-item-dummy-filter'">
<i class="fa fa-circle"></i>
</div>
`,
});
Vue.component('r-item-dummy-badge',{
props: {
},
data() {
return {};
},
computed: {
},
methods: {
},
template: `
<div :class="'r-item-dummy-badge'" >
<i class="fa fa-circle"></i>
</div>
`,
});
}, },
}; };

View File

@ -816,7 +816,7 @@ export default {
class="fa fa-exclamation-triangle t-not-enrolled-alert" class="fa fa-exclamation-triangle t-not-enrolled-alert"
:title="text.student_not_tracked"></i> :title="text.student_not_tracked"></i>
</template> </template>
<template v-else> <template v-else-if="item.lineenrolled" >
<i v-b-popover.top <i v-b-popover.top
:class="'fa fa-'+completion_icon+ :class="'fa fa-'+completion_icon+
' r-completion-'+item.completion" ' r-completion-'+item.completion"
@ -933,7 +933,7 @@ export default {
// TODO: Show actual grades when relevant at all (don;t forget the grade point completion requirement) // TODO: Show actual grades when relevant at all (don;t forget the grade point completion requirement)
template: ` template: `
<span class='q-conditionresult'> <span class='q-conditionresult'>
<fittext maxsize="10pt" singleline dynamic> <fittext v-if="item.lineenrolled" maxsize="10pt" singleline dynamic>
<template v-if="loading"> <template v-if="loading">
<div class="spinner-border spinner-border-sm text-info" role="status"></div> <div class="spinner-border spinner-border-sm text-info" role="status"></div>
</template> </template>

View File

@ -520,7 +520,7 @@ class studyitem {
"completion" => new \external_value(PARAM_TEXT, 'completion state (incomplete|progress|completed|excellent)'), "completion" => new \external_value(PARAM_TEXT, 'completion state (incomplete|progress|completed|excellent)'),
"slot" => new \external_value(PARAM_INT, 'slot in the study plan'), "slot" => new \external_value(PARAM_INT, 'slot in the study plan'),
"layer" => new \external_value(PARAM_INT, 'layer in the slot'), "layer" => new \external_value(PARAM_INT, 'layer in the slot'),
"span" => new \external_value(PARAM_INT, 'how many periods the item spans'), "span" => new \external_value(PARAM_INT, 'how many periods the item spans'),
"course" => courseinfo::user_structure(VALUE_OPTIONAL), "course" => courseinfo::user_structure(VALUE_OPTIONAL),
"badge" => badgeinfo::user_structure(VALUE_OPTIONAL), "badge" => badgeinfo::user_structure(VALUE_OPTIONAL),
"continuation" => self::link_structure(VALUE_OPTIONAL), "continuation" => self::link_structure(VALUE_OPTIONAL),
@ -528,6 +528,7 @@ class studyitem {
'in' => new \external_multiple_structure(studyitemconnection::structure()), 'in' => new \external_multiple_structure(studyitemconnection::structure()),
'out' => new \external_multiple_structure(studyitemconnection::structure()), 'out' => new \external_multiple_structure(studyitemconnection::structure()),
]), ]),
"lineenrolled" => new \external_value(PARAM_BOOL, 'student is enrolled in the line this item is in'),
], 'Study item info', $value); ], 'Study item info', $value);
} }
@ -550,7 +551,8 @@ class studyitem {
'connections' => [ 'connections' => [
"in" => [], "in" => [],
"out" => [], "out" => [],
] ],
"lineenrolled" => $this->studyline()->isenrolled($userid),
]; ];
// Add badge info if available. // Add badge info if available.

View File

@ -606,6 +606,28 @@ class studyline {
return $model; return $model;
} }
/**
* Check if student is enrolled in the line.
* @param int $userid ID of user to check specific info for
* @return array Webservice data model
*/
public function isenrolled($userid) {
global $DB;
if ($this->r->enrollable == self::ENROLLABLE_NONE) {
return true; // If student cannot enrol, the student always is enrolled
} else {
$r = $DB->get_record('local_treestudyplan_lineuser',[
'line_id' => $this->id(),
'user_id' => $userid,
]);
if (empty($r)) {
return false;
} else {
return boolval($r->enrolled);
}
}
}
/** /**
* Enrol student from this line (if line enrollable) * Enrol student from this line (if line enrollable)
* NOTE: This function does not check if the current user should be allowed to do this, * NOTE: This function does not check if the current user should be allowed to do this,

View File

@ -1438,6 +1438,38 @@ body.path-local-treestudyplan .editmode-switch-form > * {
.features-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); background-color: var(--light);
} }
.path-local-treestudyplan .r-item-dummy-course span.r-timing-indicator.timing-dummy,
.features-treestudyplan .r-item-dummy-course span.r-timing-indicator.timing-dummy {
background-color: #ccc;
}
.path-local-treestudyplan .r-item-dummy-course .card,
.features-treestudyplan .r-item-dummy-course .card {
filter: blur(4px);
background-color: #ddd;
border-color: #ddd;
}
.path-local-treestudyplan .r-item-dummy-badge,
.features-treestudyplan .r-item-dummy-badge {
filter: blur(4px);
color: #ddd;
}
.path-local-treestudyplan .r-item-dummy-badge i,
.features-treestudyplan .r-item-dummy-badge i {
font-size: 50px;
}
.path-local-treestudyplan .r-item-dummy-filter,
.features-treestudyplan .r-item-dummy-filter {
filter: blur(4px);
color: #ddd;
}
.path-local-treestudyplan .r-item-dummy-filter i,
.features-treestudyplan .r-item-dummy-filter i {
font-size: 32px;
}
.path-local-treestudyplan svg.r-dummy-line.simpleline,
.features-treestudyplan svg.r-dummy-line.simpleline {
filter: blur(4px);
}
.path-local-treestudyplan .card.s-studyplan-card, .path-local-treestudyplan .card.s-studyplan-card,
.features-treestudyplan .card.s-studyplan-card { .features-treestudyplan .card.s-studyplan-card {

View File

@ -1239,4 +1239,34 @@
} }
} }
.r-item-dummy-course {
span.r-timing-indicator.timing-dummy {
background-color: #ccc;
}
.card {
filter: blur(4px);
background-color: #ddd;
border-color: #ddd;
}
}
.r-item-dummy-badge {
filter: blur(4px);
color: #ddd;
i {
font-size: 50px;
}
}
.r-item-dummy-filter {
filter: blur(4px);
color: #ddd;
i {
font-size: 32px;
}
}
svg.r-dummy-line.simpleline {
filter: blur(4px);
}
} }

View File

@ -1438,6 +1438,38 @@ body.path-local-treestudyplan .editmode-switch-form > * {
.features-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); background-color: var(--light);
} }
.path-local-treestudyplan .r-item-dummy-course span.r-timing-indicator.timing-dummy,
.features-treestudyplan .r-item-dummy-course span.r-timing-indicator.timing-dummy {
background-color: #ccc;
}
.path-local-treestudyplan .r-item-dummy-course .card,
.features-treestudyplan .r-item-dummy-course .card {
filter: blur(4px);
background-color: #ddd;
border-color: #ddd;
}
.path-local-treestudyplan .r-item-dummy-badge,
.features-treestudyplan .r-item-dummy-badge {
filter: blur(4px);
color: #ddd;
}
.path-local-treestudyplan .r-item-dummy-badge i,
.features-treestudyplan .r-item-dummy-badge i {
font-size: 50px;
}
.path-local-treestudyplan .r-item-dummy-filter,
.features-treestudyplan .r-item-dummy-filter {
filter: blur(4px);
color: #ddd;
}
.path-local-treestudyplan .r-item-dummy-filter i,
.features-treestudyplan .r-item-dummy-filter i {
font-size: 32px;
}
.path-local-treestudyplan svg.r-dummy-line.simpleline,
.features-treestudyplan svg.r-dummy-line.simpleline {
filter: blur(4px);
}
.path-local-treestudyplan .card.s-studyplan-card, .path-local-treestudyplan .card.s-studyplan-card,
.features-treestudyplan .card.s-studyplan-card { .features-treestudyplan .card.s-studyplan-card {