diff --git a/amd/build/page-studyplan-report.min.js b/amd/build/page-studyplan-report.min.js
index 9202709..227a718 100644
--- a/amd/build/page-studyplan-report.min.js
+++ b/amd/build/page-studyplan-report.min.js
@@ -1,3 +1,3 @@
-define("local_treestudyplan/page-studyplan-report",["exports","core/ajax","core/notification","./vue/vue","./util/debugger","./util/string-helper","./studyplan-report-components","./modedit-modal","./portal-vue/portal-vue.esm","./bootstrap-vue/bootstrap-vue"],(function(_exports,_ajax,_notification,_vue,_debugger,_stringHelper,_studyplanReportComponents,_modeditModal,_portalVue,_bootstrapVue){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(studyplanid,pageid,firstperiod,lastperiod){if(void 0===pageid||!Number.isInteger(Number(pageid))||void 0===studyplanid||!Number.isInteger(Number(studyplanid)))return void debug.error("Did Error: studyplan id and page id not provided as integer numbers to script.",studyplanid,pageid,firstperiod,lastperiod);studyplanid=Number(studyplanid),pageid=Number(pageid);new _vue.default({el:"#root",data:{structure:null},async mounted(){},created(){this.loadStructure()},computed:{},methods:{loadStructure(){const self=this;this.structure=null,(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_structure",args:{pageid:pageid}}])[0].then((function(response){self.structure=response})).catch(_notification.default.exception)}}})},_notification=_interopRequireDefault(_notification),_vue=_interopRequireDefault(_vue),_debugger=_interopRequireDefault(_debugger),_studyplanReportComponents=_interopRequireDefault(_studyplanReportComponents),_modeditModal=_interopRequireDefault(_modeditModal),_portalVue=_interopRequireDefault(_portalVue),_bootstrapVue=_interopRequireDefault(_bootstrapVue),_vue.default.use(_studyplanReportComponents.default),_vue.default.use(_modeditModal.default),_vue.default.use(_portalVue.default),_vue.default.use(_bootstrapVue.default);let debug=new _debugger.default("treestudyplanviewer");(0,_stringHelper.load_strings)({studyplan:{studyplan_select_placeholder:"studyplan_select_placeholder"}})}));
+define("local_treestudyplan/page-studyplan-report",["exports","core/ajax","core/notification","./vue/vue","./util/debugger","./util/string-helper","./studyplan-report-components","./report-viewer-components","./modedit-modal","./portal-vue/portal-vue.esm","./bootstrap-vue/bootstrap-vue"],(function(_exports,_ajax,_notification,_vue,_debugger,_stringHelper,_studyplanReportComponents,_reportViewerComponents,_modeditModal,_portalVue,_bootstrapVue){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(studyplanid,pageid,firstperiod,lastperiod){if(void 0===pageid||!Number.isInteger(Number(pageid))||void 0===studyplanid||!Number.isInteger(Number(studyplanid)))return void debug.error("Did Error: studyplan id and page id not provided as integer numbers to script.",studyplanid,pageid,firstperiod,lastperiod);studyplanid=Number(studyplanid),pageid=Number(pageid);new _vue.default({el:"#root",data:{structure:null},async mounted(){},created(){this.loadStructure()},computed:{},methods:{loadStructure(){const self=this;this.structure=null,(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_structure",args:{pageid:pageid,firstperiod:firstperiod,lastperiod:lastperiod}}])[0].then((function(response){self.structure=response})).catch(_notification.default.exception)}}})},_notification=_interopRequireDefault(_notification),_vue=_interopRequireDefault(_vue),_debugger=_interopRequireDefault(_debugger),_studyplanReportComponents=_interopRequireDefault(_studyplanReportComponents),_reportViewerComponents=_interopRequireDefault(_reportViewerComponents),_modeditModal=_interopRequireDefault(_modeditModal),_portalVue=_interopRequireDefault(_portalVue),_bootstrapVue=_interopRequireDefault(_bootstrapVue),_vue.default.use(_studyplanReportComponents.default),_vue.default.use(_reportViewerComponents.default),_vue.default.use(_modeditModal.default),_vue.default.use(_portalVue.default),_vue.default.use(_bootstrapVue.default);let debug=new _debugger.default("treestudyplanviewer");(0,_stringHelper.load_strings)({studyplan:{studyplan_select_placeholder:"studyplan_select_placeholder"}})}));
//# sourceMappingURL=page-studyplan-report.min.js.map
\ No newline at end of file
diff --git a/amd/build/page-studyplan-report.min.js.map b/amd/build/page-studyplan-report.min.js.map
index 3ba1f69..7d1e071 100644
--- a/amd/build/page-studyplan-report.min.js.map
+++ b/amd/build/page-studyplan-report.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"page-studyplan-report.min.js","sources":["../src/page-studyplan-report.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport Debugger from './util/debugger';\nimport {load_strings} from './util/string-helper';\n\nimport SRComponents from './studyplan-report-components';\nVue.use(SRComponents);\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\n\nlet debug = new Debugger(\"treestudyplanviewer\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {Number} studyplanid The id of the studyplan we need to view \n * @param {Number} pageid The id of the studyplan page we need to view \n * @param {Number} firstperiod The number of the first period to view\n * @param {Number} lastperiod The number of the last period to view \n */\nexport function init(studyplanid, pageid, firstperiod, lastperiod) {\n if (undefined === pageid || !Number.isInteger(Number(pageid)) ||\n undefined === studyplanid || !Number.isInteger(Number(studyplanid))) {\n debug.error(\"Did Error: studyplan id and page id not provided as integer numbers to script.\",\n studyplanid, pageid, firstperiod, lastperiod);\n return; // Do not continue if plan and page are not proper integers\n }\n // Ensure a numeric value instead of string.\n studyplanid = Number(studyplanid);\n pageid = Number(pageid);\n\n // Startup app.\n const app = new Vue({\n el: '#root',\n data: {\n structure: null,\n },\n async mounted() {\n \n },\n created() {\n this.loadStructure();\n },\n computed: {\n \n },\n methods: {\n loadStructure() {\n const self = this;\n this.structure = null; // Starts loading icon. Hides old data.\n call([{\n methodname: 'local_treestudyplan_get_report_structure',\n args: { pageid: pageid}\n }])[0].then(function(response){\n self.structure = response;\n }).catch(notification.exception);\n }\n },\n });\n}\n"],"names":["studyplanid","pageid","firstperiod","lastperiod","undefined","Number","isInteger","debug","error","Vue","el","data","structure","created","loadStructure","computed","methods","self","this","methodname","args","then","response","catch","notification","exception","use","SRComponents","ModalComponents","PortalVue","BootstrapVue","Debugger","studyplan","studyplan_select_placeholder"],"mappings":"4iBAwCqBA,YAAaC,OAAQC,YAAaC,oBAC/CC,IAAcH,SAAWI,OAAOC,UAAUD,OAAOJ,eACjDG,IAAcJ,cAAgBK,OAAOC,UAAUD,OAAOL,0BACtDO,MAAMC,MAAM,iFACRR,YAAaC,OAAQC,YAAaC,YAI1CH,YAAcK,OAAOL,aACrBC,OAASI,OAAOJ,QAGJ,IAAIQ,aAAI,CAChBC,GAAI,QACJC,KAAM,CACFC,UAAW,wBAKfC,eACSC,iBAETC,SAAU,GAGVC,QAAS,CACLF,sBACUG,KAAOC,UACRN,UAAY,oBACZ,CAAC,CACFO,WAAY,2CACZC,KAAM,CAAEnB,OAAQA,WAChB,GAAGoB,MAAK,SAASC,UACjBL,KAAKL,UAAYU,YAClBC,MAAMC,sBAAaC,mYA5DlCC,IAAIC,iDAEJD,IAAIE,oCAGJF,IAAIG,iCAEJH,IAAII,2BAGJvB,MAAQ,IAAIwB,kBAAS,wBAEX,8BAAa,CACvBC,UAAW,CACPC,6BAA8B"}
\ No newline at end of file
+{"version":3,"file":"page-studyplan-report.min.js","sources":["../src/page-studyplan-report.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport Debugger from './util/debugger';\nimport {load_strings} from './util/string-helper';\n\nimport SRComponents from './studyplan-report-components';\nVue.use(SRComponents);\nimport RVComponents from './report-viewer-components';\nVue.use(RVComponents);\n\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\n\nlet debug = new Debugger(\"treestudyplanviewer\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {Number} studyplanid The id of the studyplan we need to view \n * @param {Number} pageid The id of the studyplan page we need to view \n * @param {Number} firstperiod The number of the first period to view\n * @param {Number} lastperiod The number of the last period to view \n */\nexport function init(studyplanid, pageid, firstperiod, lastperiod) {\n if (undefined === pageid || !Number.isInteger(Number(pageid)) ||\n undefined === studyplanid || !Number.isInteger(Number(studyplanid))) {\n debug.error(\"Did Error: studyplan id and page id not provided as integer numbers to script.\",\n studyplanid, pageid, firstperiod, lastperiod);\n return; // Do not continue if plan and page are not proper integers\n }\n // Ensure a numeric value instead of string.\n studyplanid = Number(studyplanid);\n pageid = Number(pageid);\n\n // Startup app.\n const app = new Vue({\n el: '#root',\n data: {\n structure: null,\n },\n async mounted() {\n \n },\n created() {\n this.loadStructure();\n },\n computed: {\n \n },\n methods: {\n loadStructure() {\n const self = this;\n this.structure = null; // Starts loading icon. Hides old data.\n call([{\n methodname: 'local_treestudyplan_get_report_structure',\n args: { pageid: pageid,\n firstperiod: firstperiod,\n lastperiod: lastperiod\n }\n }])[0].then(function(response){\n self.structure = response;\n }).catch(notification.exception);\n }\n },\n });\n}\n"],"names":["studyplanid","pageid","firstperiod","lastperiod","undefined","Number","isInteger","debug","error","Vue","el","data","structure","created","loadStructure","computed","methods","self","this","methodname","args","then","response","catch","notification","exception","use","SRComponents","RVComponents","ModalComponents","PortalVue","BootstrapVue","Debugger","studyplan","studyplan_select_placeholder"],"mappings":"imBA2CqBA,YAAaC,OAAQC,YAAaC,oBAC/CC,IAAcH,SAAWI,OAAOC,UAAUD,OAAOJ,eACjDG,IAAcJ,cAAgBK,OAAOC,UAAUD,OAAOL,0BACtDO,MAAMC,MAAM,iFACRR,YAAaC,OAAQC,YAAaC,YAI1CH,YAAcK,OAAOL,aACrBC,OAASI,OAAOJ,QAGJ,IAAIQ,aAAI,CAChBC,GAAI,QACJC,KAAM,CACFC,UAAW,wBAKfC,eACSC,iBAETC,SAAU,GAGVC,QAAS,CACLF,sBACUG,KAAOC,UACRN,UAAY,oBACZ,CAAC,CACFO,WAAY,2CACZC,KAAM,CAAEnB,OAAQA,OACZC,YAAaA,YACbC,WAAYA,eAEhB,GAAGkB,MAAK,SAASC,UACjBL,KAAKL,UAAYU,YAClBC,MAAMC,sBAAaC,2cAlElCC,IAAIC,iDAEJD,IAAIE,8CAGJF,IAAIG,oCAGJH,IAAII,iCAEJJ,IAAIK,2BAGJxB,MAAQ,IAAIyB,kBAAS,wBAEX,8BAAa,CACvBC,UAAW,CACPC,6BAA8B"}
\ No newline at end of file
diff --git a/amd/build/studyplan-report-components.min.js b/amd/build/studyplan-report-components.min.js
index c309654..b6157bb 100644
--- a/amd/build/studyplan-report-components.min.js
+++ b/amd/build/studyplan-report-components.min.js
@@ -1,3 +1,3 @@
-define("local_treestudyplan/studyplan-report-components",["exports","./simpleline/simpleline","core/str","./util/string-helper","./util/date-helper","core/ajax","core/notification","./util/svgarc","./util/debugger","core/config","./studyplan-processor","./treestudyplan-components","core/edit_switch"],(function(_exports,_simpleline,_str,_stringHelper,_dateHelper,_ajax,_notification,_svgarc,_debugger,_config,_studyplanProcessor,_treestudyplanComponents,_edit_switch){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger),_config=_interopRequireDefault(_config),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents);Math.PI;var _default={install(Vue){Vue.use(_treestudyplanComponents.default);new _debugger.default("treestudyplan-viewer"),(0,_stringHelper.load_strings)({report:{loading:"loadinghelp@core",studyplan_past:"studyplan_past",studyplan_present:"studyplan_present",studyplan_future:"studyplan_future",back:"back"},invalid:{error:"error"},grading:{ungraded:"ungraded",graded:"graded",allgraded:"allgraded",unsubmitted:"unsubmitted",nogrades:"nogrades",unknown:"unknown"},completion:{completed:"completion_completed",incomplete:"completion_incomplete",completed_pass:"completion_passed",completed_fail:"completion_failed",ungraded:"ungraded",aggregation_all:"aggregation_all",aggregation_any:"aggregation_any",aggregation_one:"aggregation_one",aggregation_overall_all:"aggregation_overall_all",aggregation_overall_any:"aggregation_overall_any",aggregation_overall_one:"aggregation_overall_one",completion_not_configured:"completion_not_configured",configure_completion:"configure_completion",view_completion_report:"view_completion_report",completion_incomplete:"completion_incomplete",completion_failed:"completion_failed",completion_pending:"completion_pending",completion_progress:"completion_progress",completion_completed:"completion_completed",completion_good:"completion_good",completion_excellent:"completion_excellent",view_feedback:"view_feedback",coursetiming_past:"coursetiming_past",coursetiming_present:"coursetiming_present",coursetiming_future:"coursetiming_future",required_goal:"required_goal",student_not_tracked:"student_not_tracked",completion_not_enabled:"completion_not_enabled"},badge:{share_badge:"share_badge",dateissued:"dateissued",dateexpire:"dateexpire",badgeinfo:"badgeinfo",badgeissuedstats:"badgeissuedstats",completion_incomplete:"completion_incomplete_badge",completion_completed:"completion_completed_badge",completioninfo:"completioninfo",badgedisabled:"badgedisabled"},course:{completion_incomplete:"completion_incomplete",completion_failed:"completion_failed",completion_pending:"completion_pending",completion_progress:"completion_progress",completion_completed:"completion_completed",completion_good:"completion_good",completion_excellent:"completion_excellent",view_feedback:"view_feedback",coursetiming_past:"coursetiming_past",coursetiming_present:"coursetiming_present",coursetiming_future:"coursetiming_future",required_goal:"required_goal",student_not_tracked:"student_not_tracked",not_enrolled:"not_enrolled"},competency:{competency_not_configured:"competency_not_configured",configure_competency:"configure_competency",when:"when",required:"required",points:"points@core_grades",heading:"competency_heading",details:"competency_details",results:"results",unrated:"unrated",progress:"completion_progress",view_feedback:"view_feedback"},pageinfo:{edit:"period_edit",fullname:"studyplan_name",shortname:"studyplan_shortname",startdate:"studyplan_startdate",enddate:"studyplan_enddate",description:"studyplan_description",duration:"studyplan_duration",details:"studyplan_details"}});Vue.component("q-studyplanreport",{props:{structure:{type:Object}},data:()=>({students:[],studentresults:{},sorting:{name:"asc"}}),created(){this.loadStudents()},computed:{},methods:{loadStudents(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_all_associated_grouped",args:{studyplan_id:this.structure.studyplan.id}}])[0].then((function(response){self.students=response;for(const group of self.students)for(const student of group.users)self.studentresults[student.id]={loading:!0,results:[]},(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_data",args:{pageid:self.structure.page.id,userid:student.id,firstperiod:self.structure.firstperiod,lastperiod:self.structure.lastperiod}}])[0].then((function(response){self.studentresults[student.id].loading=!1,self.studentresults[student.id].results=response})).catch(_notification.default.exception)})).catch(_notification.default.exception)}},mounted(){},updated(){},template:"\n
\n \n
\n "})}};return _exports.default=_default,_exports.default}));
+define("local_treestudyplan/studyplan-report-components",["exports","./simpleline/simpleline","core/str","./util/string-helper","./util/date-helper","core/ajax","core/notification","./util/svgarc","./util/debugger","core/config","./studyplan-processor","./treestudyplan-components","core/edit_switch"],(function(_exports,_simpleline,_str,_stringHelper,_dateHelper,_ajax,_notification,_svgarc,_debugger,_config,_studyplanProcessor,_treestudyplanComponents,_edit_switch){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger),_config=_interopRequireDefault(_config),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents);Math.PI;var _default={install(Vue){Vue.use(_treestudyplanComponents.default);let debug=new _debugger.default("treestudyplan-viewer"),strings=(0,_stringHelper.load_strings)({report:{loading:"loadinghelp@core",studyplan_past:"studyplan_past",studyplan_present:"studyplan_present",studyplan_future:"studyplan_future",back:"back"},invalid:{error:"error"},header:{overall:"overall",students:"students@core"},studentresults:{completion_incomplete:"completion_incomplete",completion_failed:"completion_failed",completion_pending:"completion_pending",completion_progress:"completion_progress",completion_completed:"completion_completed",completion_good:"completion_good",completion_excellent:"completion_excellent",student_not_tracked:"student_not_tracked"}});Vue.component("q-studyplanreport",{props:{structure:{type:Object}},data:()=>({students:[],studentresults:{},expansioninfo:{periods:{},lines:{},items:{}},groupinfo:{},sorting:{name:"asc"}}),created(){this.loadStudents()},computed:{sortedstudents(){return this.students}},watch:{structure:{immediate:!0,handler(structure){for(const period of structure.periods){const pid=period.period.id;this.expansioninfo.periods[pid]||(this.$set(this.expansioninfo.periods,pid,{expanded:!1}),this.$set(this.expansioninfo.lines,period.period.id,{}));for(const line of period.lines){const lid=line.line.id;this.expansioninfo.lines[lid]||this.$set(this.expansioninfo.lines[pid],lid,{expanded:!0});for(const item of line.items)this.expansioninfo.items[item.id]||this.$set(this.expansioninfo.items,item.id,{expanded:!1})}}}}},methods:{loadStudents(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_all_associated_grouped",args:{studyplan_id:this.structure.studyplan.id}}])[0].then((function(response){self.students=response;for(const group of self.students){self.$set(self.groupinfo,group.label,{expand:!0});for(const student of group.users)self.studentresults[student.id]={loading:!0,results:[]},(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_data",args:{pageid:self.structure.page.id,userid:student.id,firstperiod:self.structure.firstperiod,lastperiod:self.structure.lastperiod}}])[0].then((function(response){self.studentresults[student.id].loading=!1,self.studentresults[student.id].results=response})).catch(_notification.default.exception)}})).catch(_notification.default.exception)},expansionChanged(parm,id,val){parm="p"==parm[0]?"periods":"l"==parm[0]?"lines":"items",debug.info("Expansion Changed",parm,id,val),"lines"==parm?this.expansioninfo[parm][id[0]][id[1]].expanded=val:this.expansioninfo[parm][id].expanded=val}},template:"\n \n
\n
\n \n \n \n \n \n \n
\n
\n "}),Vue.component("q-header",{props:{structure:{type:Object},sorting:{type:Object},expansion:{type:Object}},data:()=>({text:strings.header}),computed:{},methods:{conditions(item){const course=item.course,list=[];if(debug.info("Determining conditions",course),course.completion){debug.info("Has Competencies");for(const cmp of course.competencies)list.push({name:cmp.title})}else if(course.completion){debug.info("Has Core completion");for(const cnd of course.completion.conditions)for(const itm of cnd.items)list.push({name:itm.title})}else if(course.grades){debug.info("Has selected grades");for(const g of course.grades)list.push({name:g.name})}return list}},mounted(){},updated(){},template:"\n \n "}),Vue.component("q-groupheading",{props:{structure:{type:Object}},data:()=>({}),computed:{},methods:{},template:"\n \n\n
\n "}),Vue.component("q-studentresults",{props:{student:{type:Object},structure:{type:Object},results:{type:Object},expansion:{type:Object}},data:()=>({text:strings.studentresults}),computed:{},methods:{hasprogressinfo:item=>!!item.course.enrolled&&(item.course.completion||item.course.competency||item.course.grades),completion_icon(completion){switch(completion){default:return"circle-o";case"pending":return"question-circle";case"failed":return"times-circle";case"progress":return"exclamation-circle";case"completed":case"good":case"excellent":return"check-circle"}},circle_icon(completion){switch(completion){default:return null;case"failed":return"times";case"progress":return"";case"completed":case"good":case"excellent":return"check"}},courseprogress(item){return item.course.enrolled?item.course.completion?item.course.completion.progress/item.course.completion.count:item.course.competency?item.course.competency.progress/item.course.competency.count:item.course.grades?this.gradeprogress(item.course.grades)/item.course.grades.length:0:0},gradeprogress(grades){let progress=0;for(const ix in grades){const g=grades[ix];["completed","excellent","good"].includes(g.completion)&&progress++}return progress},conditions(item){},useritems(line){const list=[];for(const item of line.items){let newitm=item;for(const itm of this.results.results)if(item.id==itm.id){newitm=itm;break}list.push(newitm)}return list}},template:"\n \n
{{student.firstname}} {{student.lastname}}
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n\n\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n "})}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=studyplan-report-components.min.js.map
\ No newline at end of file
diff --git a/amd/build/studyplan-report-components.min.js.map b/amd/build/studyplan-report-components.min.js.map
index 373b818..9a468d6 100644
--- a/amd/build/studyplan-report-components.min.js.map
+++ b/amd/build/studyplan-report-components.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"studyplan-report-components.min.js","sources":["../src/studyplan-report-components.js"],"sourcesContent":["/*eslint no-var: \"error\"*/\n/*eslint no-console: \"off\"*/\n/*eslint no-unused-vars: warn */\n/*eslint max-len: [\"error\", { \"code\": 160 }] */\n/*eslint-disable no-trailing-spaces */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n\nimport {SimpleLine} from './simpleline/simpleline';\nimport {get_strings} from 'core/str';\nimport {load_strings} from './util/string-helper';\nimport {format_date,studyplanPageTiming,studyplanTiming} from './util/date-helper';\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\nimport {svgarcpath} from './util/svgarc';\nimport Debugger from './util/debugger';\nimport Config from 'core/config';\nimport {ProcessStudyplan, ProcessStudyplanPage, objCopy} from './studyplan-processor';\nimport TSComponents from './treestudyplan-components';\nimport {eventTypes as editSwEventTypes} from 'core/edit_switch';\n\n// Make π available as a constant\nconst π = Math.PI;\n// Gravity value for arrow lines - determines how much a line is pulled in the direction of the start/end before changing direction\nconst LINE_GRAVITY = 1.3;\n\n\nexport default {\n install(Vue/*,options*/){\n Vue.use(TSComponents);\n let debug = new Debugger(\"treestudyplan-viewer\");\n\n let strings = load_strings({\n report: {\n loading: \"loadinghelp@core\",\n studyplan_past: \"studyplan_past\",\n studyplan_present: \"studyplan_present\",\n studyplan_future: \"studyplan_future\",\n back: \"back\",\n },\n invalid: {\n error: 'error',\n },\n grading: {\n ungraded: \"ungraded\",\n graded: \"graded\",\n allgraded: \"allgraded\",\n unsubmitted: \"unsubmitted\",\n nogrades: \"nogrades\",\n unknown: \"unknown\",\n },\n completion: {\n completed: \"completion_completed\",\n incomplete: \"completion_incomplete\",\n completed_pass: \"completion_passed\",\n completed_fail: \"completion_failed\",\n ungraded: \"ungraded\",\n aggregation_all: \"aggregation_all\",\n aggregation_any: \"aggregation_any\",\n aggregation_one: \"aggregation_one\",\n aggregation_overall_all: \"aggregation_overall_all\",\n aggregation_overall_any: \"aggregation_overall_any\",\n aggregation_overall_one: \"aggregation_overall_one\",\n completion_not_configured: \"completion_not_configured\",\n configure_completion: \"configure_completion\",\n view_completion_report: \"view_completion_report\",\n completion_incomplete: \"completion_incomplete\",\n completion_failed: \"completion_failed\",\n completion_pending: \"completion_pending\",\n completion_progress: \"completion_progress\",\n completion_completed: \"completion_completed\",\n completion_good: \"completion_good\",\n completion_excellent: \"completion_excellent\",\n view_feedback: \"view_feedback\",\n coursetiming_past: \"coursetiming_past\",\n coursetiming_present: \"coursetiming_present\",\n coursetiming_future: \"coursetiming_future\",\n required_goal: \"required_goal\",\n student_not_tracked: \"student_not_tracked\",\n completion_not_enabled: \"completion_not_enabled\",\n },\n badge: {\n share_badge: \"share_badge\",\n dateissued: \"dateissued\",\n dateexpire: \"dateexpire\",\n badgeinfo: \"badgeinfo\",\n badgeissuedstats: \"badgeissuedstats\",\n completion_incomplete: \"completion_incomplete_badge\",\n completion_completed: \"completion_completed_badge\",\n completioninfo: \"completioninfo\",\n badgedisabled: \"badgedisabled\"\n },\n course: {\n completion_incomplete: \"completion_incomplete\",\n completion_failed: \"completion_failed\",\n completion_pending: \"completion_pending\",\n completion_progress: \"completion_progress\",\n completion_completed: \"completion_completed\",\n completion_good: \"completion_good\",\n completion_excellent: \"completion_excellent\",\n view_feedback: \"view_feedback\",\n coursetiming_past: \"coursetiming_past\",\n coursetiming_present: \"coursetiming_present\",\n coursetiming_future: \"coursetiming_future\",\n required_goal: \"required_goal\",\n student_not_tracked: \"student_not_tracked\",\n not_enrolled: \"not_enrolled\",\n },\n competency: {\n competency_not_configured: \"competency_not_configured\",\n configure_competency: \"configure_competency\",\n when: \"when\",\n required: \"required\",\n points: \"points@core_grades\",\n heading: \"competency_heading\",\n details: \"competency_details\",\n results: \"results\",\n unrated: \"unrated\",\n progress: \"completion_progress\",\n view_feedback: \"view_feedback\",\n }, \n pageinfo: {\n edit: 'period_edit',\n fullname: 'studyplan_name',\n shortname: 'studyplan_shortname',\n startdate: 'studyplan_startdate',\n enddate: 'studyplan_enddate',\n description: 'studyplan_description',\n duration: 'studyplan_duration',\n details: 'studyplan_details',\n }\n\n });\n\n /************************************\n * *\n * Treestudyplan Viewer components *\n * *\n ************************************/\n\n Vue.component('q-studyplanreport', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n students: [],\n studentresults: {},\n\n sorting: {\n name: \"asc\",\n }\n };\n },\n created() {\n this.loadStudents();\n },\n computed: {\n\n },\n methods: {\n loadStudents() {\n const self = this;\n call([{\n methodname: 'local_treestudyplan_all_associated_grouped',\n args: { studyplan_id: this.structure.studyplan.id}\n }])[0].then(function(response){\n self.students = response;\n for(const group of self.students) {\n for(const student of group.users){\n self.studentresults[student.id] = {\n loading: true,\n results: [],\n };\n call([{\n methodname: 'local_treestudyplan_get_report_data',\n args: { pageid: self.structure.page.id, \n userid: student.id,\n firstperiod: self.structure.firstperiod,\n lastperiod: self.structure.lastperiod,\n }\n }])[0].then(function(response){\n self.studentresults[student.id].loading = false;\n self.studentresults[student.id].results = response;\n }).catch(notification.exception);\n }\n }\n }).catch(notification.exception);\n }\n },\n mounted() {\n },\n updated() {\n },\n template: `\n \n \n
\n `,\n });\n\n\n\n },\n};"],"names":["Math","PI","install","Vue","use","TSComponents","Debugger","report","loading","studyplan_past","studyplan_present","studyplan_future","back","invalid","error","grading","ungraded","graded","allgraded","unsubmitted","nogrades","unknown","completion","completed","incomplete","completed_pass","completed_fail","aggregation_all","aggregation_any","aggregation_one","aggregation_overall_all","aggregation_overall_any","aggregation_overall_one","completion_not_configured","configure_completion","view_completion_report","completion_incomplete","completion_failed","completion_pending","completion_progress","completion_completed","completion_good","completion_excellent","view_feedback","coursetiming_past","coursetiming_present","coursetiming_future","required_goal","student_not_tracked","completion_not_enabled","badge","share_badge","dateissued","dateexpire","badgeinfo","badgeissuedstats","completioninfo","badgedisabled","course","not_enrolled","competency","competency_not_configured","configure_competency","when","required","points","heading","details","results","unrated","progress","pageinfo","edit","fullname","shortname","startdate","enddate","description","duration","component","props","structure","type","Object","data","students","studentresults","sorting","name","created","loadStudents","computed","methods","self","this","methodname","args","studyplan_id","studyplan","id","then","response","group","student","users","pageid","page","userid","firstperiod","lastperiod","catch","notification","exception","mounted","updated","template"],"mappings":"y0BAsBUA,KAAKC,gBAKA,CACXC,QAAQC,KACJA,IAAIC,IAAIC,kCACI,IAAIC,kBAAS,yBAEX,8BAAa,CACvBC,OAAQ,CACJC,QAAS,mBACTC,eAAgB,iBAChBC,kBAAmB,oBACnBC,iBAAkB,mBAClBC,KAAM,QAEVC,QAAS,CACLC,MAAO,SAEXC,QAAS,CACLC,SAAU,WACVC,OAAQ,SACRC,UAAW,YACXC,YAAa,cACbC,SAAU,WACVC,QAAS,WAEbC,WAAY,CACRC,UAAW,uBACXC,WAAY,wBACZC,eAAgB,oBAChBC,eAAgB,oBAChBV,SAAU,WACVW,gBAAiB,kBACjBC,gBAAiB,kBACjBC,gBAAiB,kBACjBC,wBAAyB,0BACzBC,wBAAyB,0BACzBC,wBAAyB,0BACzBC,0BAA2B,4BAC3BC,qBAAsB,uBACtBC,uBAAwB,yBACxBC,sBAAuB,wBACvBC,kBAAmB,oBACnBC,mBAAoB,qBACpBC,oBAAqB,sBACrBC,qBAAsB,uBACtBC,gBAAiB,kBACjBC,qBAAsB,uBACtBC,cAAe,gBACfC,kBAAmB,oBACnBC,qBAAsB,uBACtBC,oBAAqB,sBACrBC,cAAe,gBACfC,oBAAqB,sBACrBC,uBAAwB,0BAE5BC,MAAO,CACHC,YAAa,cACbC,WAAY,aACZC,WAAY,aACZC,UAAW,YACXC,iBAAkB,mBAClBnB,sBAAuB,8BACvBI,qBAAsB,6BACtBgB,eAAgB,iBAChBC,cAAe,iBAEnBC,OAAQ,CACJtB,sBAAuB,wBACvBC,kBAAmB,oBACnBC,mBAAoB,qBACpBC,oBAAqB,sBACrBC,qBAAsB,uBACtBC,gBAAiB,kBACjBC,qBAAsB,uBACtBC,cAAe,gBACfC,kBAAmB,oBACnBC,qBAAsB,uBACtBC,oBAAqB,sBACrBC,cAAe,gBACfC,oBAAqB,sBACrBW,aAAc,gBAElBC,WAAY,CACRC,0BAA2B,4BAC3BC,qBAAsB,uBACtBC,KAAM,OACNC,SAAU,WACVC,OAAQ,qBACRC,QAAS,qBACTC,QAAS,qBACTC,QAAS,UACTC,QAAS,UACTC,SAAU,sBACV3B,cAAe,iBAEnB4B,SAAU,CACNC,KAAM,cACNC,SAAU,iBACVC,UAAW,sBACXC,UAAW,sBACXC,QAAS,oBACTC,YAAa,wBACbC,SAAU,qBACVX,QAAS,uBAWjBhE,IAAI4E,UAAU,oBAAqB,CAC/BC,MAAO,CACHC,UAAW,CACPC,KAAMC,SAGdC,KAAI,KACO,CACHC,SAAU,GACVC,eAAgB,GAEhBC,QAAS,CACLC,KAAM,SAIlBC,eACSC,gBAETC,SAAU,GAGVC,QAAS,CACLF,qBACUG,KAAOC,oBACR,CAAC,CACFC,WAAY,6CACZC,KAAM,CAAEC,aAAcH,KAAKb,UAAUiB,UAAUC,OAC/C,GAAGC,MAAK,SAASC,UACjBR,KAAKR,SAAWgB,aACZ,MAAMC,SAAST,KAAKR,aAChB,MAAMkB,WAAWD,MAAME,MACvBX,KAAKP,eAAeiB,QAAQJ,IAAM,CAC9B3F,SAAS,EACT4D,QAAS,mBAER,CAAC,CACF2B,WAAY,sCACZC,KAAM,CAAES,OAAQZ,KAAKZ,UAAUyB,KAAKP,GAC5BQ,OAAQJ,QAAQJ,GAChBS,YAAaf,KAAKZ,UAAU2B,YAC5BC,WAAYhB,KAAKZ,UAAU4B,eAEnC,GAAGT,MAAK,SAASC,UACjBR,KAAKP,eAAeiB,QAAQJ,IAAI3F,SAAU,EAC1CqF,KAAKP,eAAeiB,QAAQJ,IAAI/B,QAAUiC,YAC3CS,MAAMC,sBAAaC,cAG/BF,MAAMC,sBAAaC,aAG9BC,YAEAC,YAEAC,SAAW"}
\ No newline at end of file
+{"version":3,"file":"studyplan-report-components.min.js","sources":["../src/studyplan-report-components.js"],"sourcesContent":["/*eslint no-var: \"error\"*/\n/*eslint no-console: \"off\"*/\n/*eslint no-unused-vars: warn */\n/*eslint max-len: [\"error\", { \"code\": 160 }] */\n/*eslint-disable no-trailing-spaces */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n\nimport {SimpleLine} from './simpleline/simpleline';\nimport {get_strings} from 'core/str';\nimport {load_strings} from './util/string-helper';\nimport {format_date,studyplanPageTiming,studyplanTiming} from './util/date-helper';\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\nimport {svgarcpath} from './util/svgarc';\nimport Debugger from './util/debugger';\nimport Config from 'core/config';\nimport {ProcessStudyplan, ProcessStudyplanPage, objCopy} from './studyplan-processor';\nimport TSComponents from './treestudyplan-components';\nimport {eventTypes as editSwEventTypes} from 'core/edit_switch';\n\n// Make π available as a constant\nconst π = Math.PI;\n// Gravity value for arrow lines - determines how much a line is pulled in the direction of the start/end before changing direction\nconst LINE_GRAVITY = 1.3;\n\n\nexport default {\n install(Vue/*,options*/){\n Vue.use(TSComponents);\n let debug = new Debugger(\"treestudyplan-viewer\");\n\n let strings = load_strings({\n report: {\n loading: \"loadinghelp@core\",\n studyplan_past: \"studyplan_past\",\n studyplan_present: \"studyplan_present\",\n studyplan_future: \"studyplan_future\",\n back: \"back\",\n },\n \n invalid: {\n error: 'error',\n },\n header: {\n overall: 'overall',\n students: 'students@core'\n },\n studentresults: {\n completion_incomplete: \"completion_incomplete\",\n completion_failed: \"completion_failed\",\n completion_pending: \"completion_pending\",\n completion_progress: \"completion_progress\",\n completion_completed: \"completion_completed\",\n completion_good: \"completion_good\",\n completion_excellent: \"completion_excellent\",\n student_not_tracked: \"student_not_tracked\",\n }\n });\n\n /************************************\n * *\n * Treestudyplan Viewer components *\n * *\n ************************************/\n\n Vue.component('q-studyplanreport', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n students: [],\n studentresults: {},\n expansioninfo: {\n periods: {},\n lines: {},\n items: {},\n },\n groupinfo: {},\n\n sorting: {\n name: \"asc\",\n }\n };\n },\n created() {\n this.loadStudents();\n },\n computed: {\n sortedstudents(){\n return this.students;\n }\n },\n watch:{\n structure: {\n immediate: true,\n handler (structure) {\n // (Re)build expansion info structure\n for (const period of structure.periods) {\n const pid = period.period.id;\n if (!this.expansioninfo.periods[pid]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.periods, \n pid, \n {\n expanded: false,\n }\n );\n this.$set(\n this.expansioninfo.lines, \n period.period.id, \n {}\n );\n }\n for (const line of period.lines) {\n const lid = line.line.id;\n if (!this.expansioninfo.lines[lid]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.lines[pid],\n lid, \n {\n expanded: true,\n }\n );\n }\n for (const item of line.items) {\n if (!this.expansioninfo.items[item.id]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.items,\n item.id,\n {\n expanded: false,\n }\n );\n }\n }\n }\n }\n }\n }\n },\n methods: {\n loadStudents() {\n const self = this;\n call([{\n methodname: 'local_treestudyplan_all_associated_grouped',\n args: { studyplan_id: this.structure.studyplan.id}\n }])[0].then(function(response){\n self.students = response;\n for(const group of self.students) {\n self.$set(\n self.groupinfo,\n group.label,\n {\n expand: true,\n }\n );\n\n for(const student of group.users){\n self.studentresults[student.id] = {\n loading: true,\n results: [],\n };\n call([{\n methodname: 'local_treestudyplan_get_report_data',\n args: { pageid: self.structure.page.id, \n userid: student.id,\n firstperiod: self.structure.firstperiod,\n lastperiod: self.structure.lastperiod,\n }\n }])[0].then(function(response){\n self.studentresults[student.id].loading = false;\n self.studentresults[student.id].results = response;\n }).catch(notification.exception);\n }\n }\n }).catch(notification.exception);\n },\n expansionChanged(parm, id, val) {\n if(parm[0] == 'p') {\n parm = 'periods';\n } else if(parm[0] == 'l') {\n parm = 'lines';\n } else {\n parm = 'items';\n }\n debug.info('Expansion Changed',parm,id,val);\n\n if (parm == 'lines') {\n this.expansioninfo[parm][id[0]][id[1]].expanded = val;\n } else {\n this.expansioninfo[parm][id].expanded = val;\n }\n\n }\n },\n template: `\n \n
\n
\n \n \n \n \n \n \n
\n
\n `,\n });\n\n Vue.component('q-header', {\n props: {\n structure: {\n type: Object,\n },\n sorting: {\n type: Object,\n },\n expansion: {\n type: Object\n },\n },\n data() {\n return {\n text: strings.header,\n };\n },\n computed: {\n },\n methods: {\n conditions(item) {\n const course = item.course;\n const list = [];\n debug.info(\"Determining conditions\", course);\n if (course.completion) {\n debug.info(\"Has Competencies\");\n for (const cmp of course.competencies) {\n list.push({\n name: cmp.title,\n });\n }\n } else if(course.completion) {\n debug.info(\"Has Core completion\");\n for (const cnd of course.completion.conditions) {\n for (const itm of cnd.items) {\n list.push({\n name: itm.title,\n });\n }\n }\n } else if(course.grades) {\n debug.info(\"Has selected grades\");\n for (const g of course.grades) {\n list.push({\n name: g.name,\n });\n }\n }\n\n return list;\n },\n },\n mounted() {\n \n },\n updated() {\n },\n template: `\n \n `,\n });\n\n Vue.component('q-groupheading', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n\n };\n },\n computed: {\n },\n methods: {\n },\n template: `\n \n\n
\n `,\n });\n\n Vue.component('q-studentresults', {\n props: {\n student: {\n type: Object,\n },\n structure: {\n type: Object,\n },\n results: {\n type: Object\n }, \n expansion: {\n type: Object\n },\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n },\n methods: {\n hasprogressinfo(item) {\n if (!item.course.enrolled) {\n return false;\n } else {\n return (item.course.completion || item.course.competency || item.course.grades);\n }\n },\n completion_icon(completion) {\n switch(completion){\n default: // case \"incomplete\"\n return \"circle-o\";\n case \"pending\":\n return \"question-circle\";\n case \"failed\":\n return \"times-circle\";\n case \"progress\":\n return \"exclamation-circle\";\n case \"completed\":\n return \"check-circle\";\n case \"good\":\n return \"check-circle\";\n case \"excellent\":\n return \"check-circle\";\n }\n },\n circle_icon(completion) {\n switch(completion){\n default: // case \"incomplete\"\n return null;\n case \"failed\":\n return \"times\";\n case \"progress\":\n return \"\";\n case \"completed\":\n return \"check\";\n case \"good\":\n return \"check\";\n case \"excellent\":\n return \"check\";\n }\n },\n courseprogress(item) {\n if (!item.course.enrolled) {\n return 0;\n } else if(item.course.completion) {\n return (item.course.completion.progress / item.course.completion.count); \n } else if(item.course.competency) {\n return (item.course.competency.progress / item.course.competency.count); \n } else if(item.course.grades) {\n return (this.gradeprogress(item.course.grades) / item.course.grades.length); \n } else {\n return 0;\n }\n },\n gradeprogress(grades) {\n let progress = 0;\n for (const ix in grades) {\n const g = grades[ix];\n if ([\"completed\",\"excellent\",\"good\"].includes(g.completion)) {\n progress++;\n }\n }\n return progress;\n },\n conditions(item) {\n\n },\n useritems(line) {\n const list = [];\n for (const item of line.items) {\n let newitm = item;\n for (const itm of this.results.results) {\n if (item.id == itm.id) {\n newitm = itm;\n break;\n }\n }\n list.push(newitm);\n }\n return list;\n }\n },\n template: `\n \n
{{student.firstname}} {{student.lastname}}
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n\n\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n `,\n });\n\n },\n};"],"names":["Math","PI","install","Vue","use","TSComponents","debug","Debugger","strings","report","loading","studyplan_past","studyplan_present","studyplan_future","back","invalid","error","header","overall","students","studentresults","completion_incomplete","completion_failed","completion_pending","completion_progress","completion_completed","completion_good","completion_excellent","student_not_tracked","component","props","structure","type","Object","data","expansioninfo","periods","lines","items","groupinfo","sorting","name","created","loadStudents","computed","sortedstudents","this","watch","immediate","handler","period","pid","id","$set","expanded","line","lid","item","methods","self","methodname","args","studyplan_id","studyplan","then","response","group","label","expand","student","users","results","pageid","page","userid","firstperiod","lastperiod","catch","notification","exception","expansionChanged","parm","val","info","template","expansion","text","conditions","course","list","completion","cmp","competencies","push","title","cnd","itm","grades","g","mounted","updated","hasprogressinfo","enrolled","competency","completion_icon","circle_icon","courseprogress","progress","count","gradeprogress","length","ix","includes","useritems","newitm"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/src/page-studyplan-report.js b/amd/src/page-studyplan-report.js
index b431261..6bef211 100644
--- a/amd/src/page-studyplan-report.js
+++ b/amd/src/page-studyplan-report.js
@@ -14,6 +14,9 @@ import {load_strings} from './util/string-helper';
import SRComponents from './studyplan-report-components';
Vue.use(SRComponents);
+import RVComponents from './report-viewer-components';
+Vue.use(RVComponents);
+
import ModalComponents from './modedit-modal';
Vue.use(ModalComponents);
@@ -70,7 +73,10 @@ export function init(studyplanid, pageid, firstperiod, lastperiod) {
this.structure = null; // Starts loading icon. Hides old data.
call([{
methodname: 'local_treestudyplan_get_report_structure',
- args: { pageid: pageid}
+ args: { pageid: pageid,
+ firstperiod: firstperiod,
+ lastperiod: lastperiod
+ }
}])[0].then(function(response){
self.structure = response;
}).catch(notification.exception);
diff --git a/amd/src/studyplan-report-components.js b/amd/src/studyplan-report-components.js
index fa74211..c3aeb21 100644
--- a/amd/src/studyplan-report-components.js
+++ b/amd/src/studyplan-report-components.js
@@ -38,32 +38,15 @@ export default {
studyplan_future: "studyplan_future",
back: "back",
},
+
invalid: {
error: 'error',
},
- grading: {
- ungraded: "ungraded",
- graded: "graded",
- allgraded: "allgraded",
- unsubmitted: "unsubmitted",
- nogrades: "nogrades",
- unknown: "unknown",
+ header: {
+ overall: 'overall',
+ students: 'students@core'
},
- completion: {
- completed: "completion_completed",
- incomplete: "completion_incomplete",
- completed_pass: "completion_passed",
- completed_fail: "completion_failed",
- ungraded: "ungraded",
- aggregation_all: "aggregation_all",
- aggregation_any: "aggregation_any",
- aggregation_one: "aggregation_one",
- aggregation_overall_all: "aggregation_overall_all",
- aggregation_overall_any: "aggregation_overall_any",
- aggregation_overall_one: "aggregation_overall_one",
- completion_not_configured: "completion_not_configured",
- configure_completion: "configure_completion",
- view_completion_report: "view_completion_report",
+ studentresults: {
completion_incomplete: "completion_incomplete",
completion_failed: "completion_failed",
completion_pending: "completion_pending",
@@ -71,65 +54,8 @@ export default {
completion_completed: "completion_completed",
completion_good: "completion_good",
completion_excellent: "completion_excellent",
- view_feedback: "view_feedback",
- coursetiming_past: "coursetiming_past",
- coursetiming_present: "coursetiming_present",
- coursetiming_future: "coursetiming_future",
- required_goal: "required_goal",
student_not_tracked: "student_not_tracked",
- completion_not_enabled: "completion_not_enabled",
- },
- badge: {
- share_badge: "share_badge",
- dateissued: "dateissued",
- dateexpire: "dateexpire",
- badgeinfo: "badgeinfo",
- badgeissuedstats: "badgeissuedstats",
- completion_incomplete: "completion_incomplete_badge",
- completion_completed: "completion_completed_badge",
- completioninfo: "completioninfo",
- badgedisabled: "badgedisabled"
- },
- course: {
- completion_incomplete: "completion_incomplete",
- completion_failed: "completion_failed",
- completion_pending: "completion_pending",
- completion_progress: "completion_progress",
- completion_completed: "completion_completed",
- completion_good: "completion_good",
- completion_excellent: "completion_excellent",
- view_feedback: "view_feedback",
- coursetiming_past: "coursetiming_past",
- coursetiming_present: "coursetiming_present",
- coursetiming_future: "coursetiming_future",
- required_goal: "required_goal",
- student_not_tracked: "student_not_tracked",
- not_enrolled: "not_enrolled",
- },
- competency: {
- competency_not_configured: "competency_not_configured",
- configure_competency: "configure_competency",
- when: "when",
- required: "required",
- points: "points@core_grades",
- heading: "competency_heading",
- details: "competency_details",
- results: "results",
- unrated: "unrated",
- progress: "completion_progress",
- view_feedback: "view_feedback",
- },
- pageinfo: {
- edit: 'period_edit',
- fullname: 'studyplan_name',
- shortname: 'studyplan_shortname',
- startdate: 'studyplan_startdate',
- enddate: 'studyplan_enddate',
- description: 'studyplan_description',
- duration: 'studyplan_duration',
- details: 'studyplan_details',
}
-
});
/************************************
@@ -148,6 +74,12 @@ export default {
return {
students: [],
studentresults: {},
+ expansioninfo: {
+ periods: {},
+ lines: {},
+ items: {},
+ },
+ groupinfo: {},
sorting: {
name: "asc",
@@ -158,7 +90,60 @@ export default {
this.loadStudents();
},
computed: {
-
+ sortedstudents(){
+ return this.students;
+ }
+ },
+ watch:{
+ structure: {
+ immediate: true,
+ handler (structure) {
+ // (Re)build expansion info structure
+ for (const period of structure.periods) {
+ const pid = period.period.id;
+ if (!this.expansioninfo.periods[pid]) {
+ // Use this.$set to make sure the properties are reactive.
+ this.$set(
+ this.expansioninfo.periods,
+ pid,
+ {
+ expanded: false,
+ }
+ );
+ this.$set(
+ this.expansioninfo.lines,
+ period.period.id,
+ {}
+ );
+ }
+ for (const line of period.lines) {
+ const lid = line.line.id;
+ if (!this.expansioninfo.lines[lid]) {
+ // Use this.$set to make sure the properties are reactive.
+ this.$set(
+ this.expansioninfo.lines[pid],
+ lid,
+ {
+ expanded: true,
+ }
+ );
+ }
+ for (const item of line.items) {
+ if (!this.expansioninfo.items[item.id]) {
+ // Use this.$set to make sure the properties are reactive.
+ this.$set(
+ this.expansioninfo.items,
+ item.id,
+ {
+ expanded: false,
+ }
+ );
+ }
+ }
+ }
+ }
+ }
+ }
},
methods: {
loadStudents() {
@@ -169,6 +154,14 @@ export default {
}])[0].then(function(response){
self.students = response;
for(const group of self.students) {
+ self.$set(
+ self.groupinfo,
+ group.label,
+ {
+ expand: true,
+ }
+ );
+
for(const student of group.users){
self.studentresults[student.id] = {
loading: true,
@@ -188,20 +181,353 @@ export default {
}
}
}).catch(notification.exception);
+ },
+ expansionChanged(parm, id, val) {
+ if(parm[0] == 'p') {
+ parm = 'periods';
+ } else if(parm[0] == 'l') {
+ parm = 'lines';
+ } else {
+ parm = 'items';
+ }
+ debug.info('Expansion Changed',parm,id,val);
+
+ if (parm == 'lines') {
+ this.expansioninfo[parm][id[0]][id[1]].expanded = val;
+ } else {
+ this.expansioninfo[parm][id].expanded = val;
+ }
+
}
},
- mounted() {
- },
- updated() {
- },
template: `
`,
});
+ Vue.component('q-header', {
+ props: {
+ structure: {
+ type: Object,
+ },
+ sorting: {
+ type: Object,
+ },
+ expansion: {
+ type: Object
+ },
+ },
+ data() {
+ return {
+ text: strings.header,
+ };
+ },
+ computed: {
+ },
+ methods: {
+ conditions(item) {
+ const course = item.course;
+ const list = [];
+ debug.info("Determining conditions", course);
+ if (course.completion) {
+ debug.info("Has Competencies");
+ for (const cmp of course.competencies) {
+ list.push({
+ name: cmp.title,
+ });
+ }
+ } else if(course.completion) {
+ debug.info("Has Core completion");
+ for (const cnd of course.completion.conditions) {
+ for (const itm of cnd.items) {
+ list.push({
+ name: itm.title,
+ });
+ }
+ }
+ } else if(course.grades) {
+ debug.info("Has selected grades");
+ for (const g of course.grades) {
+ list.push({
+ name: g.name,
+ });
+ }
+ }
+ return list;
+ },
+ },
+ mounted() {
+
+ },
+ updated() {
+ },
+ template: `
+
+ `,
+ });
+
+ Vue.component('q-groupheading', {
+ props: {
+ structure: {
+ type: Object,
+ },
+ },
+ data() {
+ return {
+
+ };
+ },
+ computed: {
+ },
+ methods: {
+ },
+ template: `
+
+
+
+ `,
+ });
+
+ Vue.component('q-studentresults', {
+ props: {
+ student: {
+ type: Object,
+ },
+ structure: {
+ type: Object,
+ },
+ results: {
+ type: Object
+ },
+ expansion: {
+ type: Object
+ },
+ },
+ data() {
+ return {
+ text: strings.studentresults,
+ };
+ },
+ computed: {
+ },
+ methods: {
+ hasprogressinfo(item) {
+ if (!item.course.enrolled) {
+ return false;
+ } else {
+ return (item.course.completion || item.course.competency || item.course.grades);
+ }
+ },
+ completion_icon(completion) {
+ switch(completion){
+ default: // case "incomplete"
+ return "circle-o";
+ case "pending":
+ return "question-circle";
+ case "failed":
+ return "times-circle";
+ case "progress":
+ return "exclamation-circle";
+ case "completed":
+ return "check-circle";
+ case "good":
+ return "check-circle";
+ case "excellent":
+ return "check-circle";
+ }
+ },
+ circle_icon(completion) {
+ switch(completion){
+ default: // case "incomplete"
+ return null;
+ case "failed":
+ return "times";
+ case "progress":
+ return "";
+ case "completed":
+ return "check";
+ case "good":
+ return "check";
+ case "excellent":
+ return "check";
+ }
+ },
+ courseprogress(item) {
+ if (!item.course.enrolled) {
+ return 0;
+ } else if(item.course.completion) {
+ return (item.course.completion.progress / item.course.completion.count);
+ } else if(item.course.competency) {
+ return (item.course.competency.progress / item.course.competency.count);
+ } else if(item.course.grades) {
+ return (this.gradeprogress(item.course.grades) / item.course.grades.length);
+ } else {
+ return 0;
+ }
+ },
+ gradeprogress(grades) {
+ let progress = 0;
+ for (const ix in grades) {
+ const g = grades[ix];
+ if (["completed","excellent","good"].includes(g.completion)) {
+ progress++;
+ }
+ }
+ return progress;
+ },
+ conditions(item) {
+
+ },
+ useritems(line) {
+ const list = [];
+ for (const item of line.items) {
+ let newitm = item;
+ for (const itm of this.results.results) {
+ if (item.id == itm.id) {
+ newitm = itm;
+ break;
+ }
+ }
+ list.push(newitm);
+ }
+ return list;
+ }
+ },
+ template: `
+
+
{{student.firstname}} {{student.lastname}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ });
},
};
\ No newline at end of file
diff --git a/css/devstyles.css b/css/devstyles.css
index d9a6716..1570dc0 100644
--- a/css/devstyles.css
+++ b/css/devstyles.css
@@ -1575,9 +1575,157 @@ body.path-local-treestudyplan .editmode-switch-form > * {
color: var(--danger);
}
-.path-local-treestudyplan-studyplan-report {
+.path-local-treestudyplan {
font: inherit;
}
+.path-local-treestudyplan .q-header,
+.path-local-treestudyplan .q-student-results {
+ background-color: white;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+}
+.path-local-treestudyplan .q-header .q-period-heading,
+.path-local-treestudyplan .q-header .q-line-heading,
+.path-local-treestudyplan .q-header .q-item-heading,
+.path-local-treestudyplan .q-header .q-condition-heading,
+.path-local-treestudyplan .q-header .q-period-results,
+.path-local-treestudyplan .q-header .q-line-results,
+.path-local-treestudyplan .q-header .q-item-results,
+.path-local-treestudyplan .q-header .q-condition-results,
+.path-local-treestudyplan .q-student-results .q-period-heading,
+.path-local-treestudyplan .q-student-results .q-line-heading,
+.path-local-treestudyplan .q-student-results .q-item-heading,
+.path-local-treestudyplan .q-student-results .q-condition-heading,
+.path-local-treestudyplan .q-student-results .q-period-results,
+.path-local-treestudyplan .q-student-results .q-line-results,
+.path-local-treestudyplan .q-student-results .q-item-results,
+.path-local-treestudyplan .q-student-results .q-condition-results {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+}
+.path-local-treestudyplan .q-header .q-period-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-line-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-item-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-condition-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-period-results:not(:last-child),
+.path-local-treestudyplan .q-header .q-line-results:not(:last-child),
+.path-local-treestudyplan .q-header .q-item-results:not(:last-child),
+.path-local-treestudyplan .q-header .q-condition-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-period-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-line-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-item-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-condition-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-period-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-line-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-item-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-condition-results:not(:last-child) {
+ border-right: 1px solid gray;
+}
+.path-local-treestudyplan .q-header .q-period-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-line-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-item-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-condition-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-period-results .q-header-title,
+.path-local-treestudyplan .q-header .q-line-results .q-header-title,
+.path-local-treestudyplan .q-header .q-item-results .q-header-title,
+.path-local-treestudyplan .q-header .q-condition-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-period-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-line-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-item-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-period-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-line-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-item-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-condition-results .q-header-title {
+ padding: 0.5rem;
+}
+.path-local-treestudyplan .q-header .q-period-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-period-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-line-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-line-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-item-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-item-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-condition-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-condition-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-period-results .q-header-details,
+.path-local-treestudyplan .q-header .q-period-results .q-result-details,
+.path-local-treestudyplan .q-header .q-line-results .q-header-details,
+.path-local-treestudyplan .q-header .q-line-results .q-result-details,
+.path-local-treestudyplan .q-header .q-item-results .q-header-details,
+.path-local-treestudyplan .q-header .q-item-results .q-result-details,
+.path-local-treestudyplan .q-header .q-condition-results .q-header-details,
+.path-local-treestudyplan .q-header .q-condition-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-period-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-period-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-line-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-line-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-item-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-item-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-condition-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-period-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-period-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-line-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-line-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-item-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-item-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-condition-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-condition-results .q-result-details {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ border-top: 1px solid green;
+}
+.path-local-treestudyplan .q-header .q-result,
+.path-local-treestudyplan .q-student-results .q-result {
+ height: 2rem;
+}
+.path-local-treestudyplan .q-header,
+.path-local-treestudyplan .q-student-results {
+ /*.q-line-heading > .q-header-title,*/
+}
+.path-local-treestudyplan .q-header .q-item-heading > .q-header-title,
+.path-local-treestudyplan .q-header .q-condition-heading,
+.path-local-treestudyplan .q-student-results .q-item-heading > .q-header-title,
+.path-local-treestudyplan .q-student-results .q-condition-heading {
+ width: 2rem;
+ writing-mode: vertical-lr;
+ text-orientation: sideways;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.path-local-treestudyplan .q-header .q-line-heading > .q-header-title,
+.path-local-treestudyplan .q-student-results .q-line-heading > .q-header-title {
+ height: fit-content;
+}
+.path-local-treestudyplan .q-header .q-item-heading > .q-item-heading,
+.path-local-treestudyplan .q-student-results .q-item-heading > .q-item-heading {
+ height: 6rem;
+}
+.path-local-treestudyplan .q-header .q-condition-heading,
+.path-local-treestudyplan .q-student-results .q-condition-heading {
+ height: 8rem;
+}
+.path-local-treestudyplan .q-studentname {
+ padding: 0.5em;
+ border-right: 2px solid grey;
+ width: 20rem;
+}
+.path-local-treestudyplan .q-studentname.heading {
+ font-weight: bold;
+ vertical-align: bottom;
+ display: flex;
+}
+.path-local-treestudyplan .q-studentname.heading span {
+ align-self: flex-end;
+}
+.path-local-treestudyplan .q-scrolly {
+ overflow-y: auto;
+ max-height: 300rem;
+}
.path-local-treestudyplan .b-modal-justify-footer-between .modal-footer,
.features-treestudyplan .b-modal-justify-footer-between .modal-footer {
diff --git a/scss/studyplan-report.scss b/scss/studyplan-report.scss
index 44f2255..832c9da 100644
--- a/scss/studyplan-report.scss
+++ b/scss/studyplan-report.scss
@@ -1,5 +1,91 @@
-.path-local-treestudyplan-studyplan-report {
+.path-local-treestudyplan {
font: inherit;
+ .q-header, .q-student-results {
+ background-color: white;
+ display: flex;
+ flex-direction: row;;
+ flex-wrap: nowrap;
+
+
+ .q-period-heading, .q-line-heading, .q-item-heading, .q-condition-heading,
+ .q-period-results, .q-line-results, .q-item-results, .q-condition-results,
+ {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+
+ &:not(:last-child) {
+ border-right: 1px solid gray;
+ }
+
+ .q-header-title {
+ padding: 0.5rem;
+
+ }
+
+ .q-header-details,
+ .q-result-details {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ border-top: 1px solid green;
+
+ }
+
+ }
+
+ .q-result {
+ height: 2rem;
+
+ }
+
+ }
+
+ .q-header, .q-student-results {
+ /*.q-line-heading > .q-header-title,*/
+ .q-item-heading > .q-header-title,
+ .q-condition-heading {
+ width: 2rem;
+ writing-mode: vertical-lr;
+ text-orientation: sideways;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .q-line-heading > .q-header-title {
+ height: fit-content;
+ }
+ .q-item-heading > .q-item-heading {
+ height: 6rem;
+ }
+ .q-condition-heading {
+ height: 8rem;
+ }
+
+ }
+
+ .q-studentname {
+ padding: 0.5em;
+
+ &.heading {
+ font-weight: bold;
+ vertical-align: bottom;
+ display: flex;
+
+ span {
+ align-self:flex-end;
+ }
+ }
+
+ border-right: 2px solid grey;
+ width: 20rem;
+ }
+ .q-scrolly {
+ overflow-y: auto;
+ max-height: 300rem;
+ }
+
}
\ No newline at end of file
diff --git a/studyplan-report.php b/studyplan-report.php
index ea0bbf5..0efdb15 100644
--- a/studyplan-report.php
+++ b/studyplan-report.php
@@ -38,17 +38,17 @@ $pageid = required_param('page', PARAM_INT); // Category id.
$page = studyplanpage::find_by_id($pageid);
$studyplan = $page->studyplan();
$context = $studyplan->context();
-$contexname = (new contextinfo($context))->pathstr();
+$ci = new contextinfo($context);
+$contextname = $ci->pathstr();
$firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show
$lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show
-$PAGE->set_pagelayout('base');
-//$PAGE->set_context($studyplancontext);
-$PAGE->set_title(get_string('studyplan_report', 'local_treestudyplan')." - ".$contextname);
-$PAGE->set_heading(get_string('studyplan_report', 'local_treestudyplan')." - ".$contextname);
+$PAGE->set_pagelayout('report');
+$PAGE->set_title(get_string('studyplan_report', 'local_treestudyplan'));
+$PAGE->set_heading(get_string('studyplan_report', 'local_treestudyplan'));
if (!premium::enabled()) {
throw new \moodle_exception("error:nopremiumaccess","local_treestudyplan","",premium::statusdescription());
@@ -79,6 +79,10 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
}
print $OUTPUT->header();
+print "{$contextname} / {$studyplan->name()}
";
+if($studyplan->name() != $page->fullname()) {
+ print "{$page->fullname()}
";
+}
?>
diff --git a/styles.css b/styles.css
index d9a6716..1570dc0 100644
--- a/styles.css
+++ b/styles.css
@@ -1575,9 +1575,157 @@ body.path-local-treestudyplan .editmode-switch-form > * {
color: var(--danger);
}
-.path-local-treestudyplan-studyplan-report {
+.path-local-treestudyplan {
font: inherit;
}
+.path-local-treestudyplan .q-header,
+.path-local-treestudyplan .q-student-results {
+ background-color: white;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+}
+.path-local-treestudyplan .q-header .q-period-heading,
+.path-local-treestudyplan .q-header .q-line-heading,
+.path-local-treestudyplan .q-header .q-item-heading,
+.path-local-treestudyplan .q-header .q-condition-heading,
+.path-local-treestudyplan .q-header .q-period-results,
+.path-local-treestudyplan .q-header .q-line-results,
+.path-local-treestudyplan .q-header .q-item-results,
+.path-local-treestudyplan .q-header .q-condition-results,
+.path-local-treestudyplan .q-student-results .q-period-heading,
+.path-local-treestudyplan .q-student-results .q-line-heading,
+.path-local-treestudyplan .q-student-results .q-item-heading,
+.path-local-treestudyplan .q-student-results .q-condition-heading,
+.path-local-treestudyplan .q-student-results .q-period-results,
+.path-local-treestudyplan .q-student-results .q-line-results,
+.path-local-treestudyplan .q-student-results .q-item-results,
+.path-local-treestudyplan .q-student-results .q-condition-results {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+}
+.path-local-treestudyplan .q-header .q-period-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-line-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-item-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-condition-heading:not(:last-child),
+.path-local-treestudyplan .q-header .q-period-results:not(:last-child),
+.path-local-treestudyplan .q-header .q-line-results:not(:last-child),
+.path-local-treestudyplan .q-header .q-item-results:not(:last-child),
+.path-local-treestudyplan .q-header .q-condition-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-period-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-line-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-item-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-condition-heading:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-period-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-line-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-item-results:not(:last-child),
+.path-local-treestudyplan .q-student-results .q-condition-results:not(:last-child) {
+ border-right: 1px solid gray;
+}
+.path-local-treestudyplan .q-header .q-period-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-line-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-item-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-condition-heading .q-header-title,
+.path-local-treestudyplan .q-header .q-period-results .q-header-title,
+.path-local-treestudyplan .q-header .q-line-results .q-header-title,
+.path-local-treestudyplan .q-header .q-item-results .q-header-title,
+.path-local-treestudyplan .q-header .q-condition-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-period-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-line-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-item-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-title,
+.path-local-treestudyplan .q-student-results .q-period-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-line-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-item-results .q-header-title,
+.path-local-treestudyplan .q-student-results .q-condition-results .q-header-title {
+ padding: 0.5rem;
+}
+.path-local-treestudyplan .q-header .q-period-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-period-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-line-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-line-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-item-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-item-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-condition-heading .q-header-details,
+.path-local-treestudyplan .q-header .q-condition-heading .q-result-details,
+.path-local-treestudyplan .q-header .q-period-results .q-header-details,
+.path-local-treestudyplan .q-header .q-period-results .q-result-details,
+.path-local-treestudyplan .q-header .q-line-results .q-header-details,
+.path-local-treestudyplan .q-header .q-line-results .q-result-details,
+.path-local-treestudyplan .q-header .q-item-results .q-header-details,
+.path-local-treestudyplan .q-header .q-item-results .q-result-details,
+.path-local-treestudyplan .q-header .q-condition-results .q-header-details,
+.path-local-treestudyplan .q-header .q-condition-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-period-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-period-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-line-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-line-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-item-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-item-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-details,
+.path-local-treestudyplan .q-student-results .q-condition-heading .q-result-details,
+.path-local-treestudyplan .q-student-results .q-period-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-period-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-line-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-line-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-item-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-item-results .q-result-details,
+.path-local-treestudyplan .q-student-results .q-condition-results .q-header-details,
+.path-local-treestudyplan .q-student-results .q-condition-results .q-result-details {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ border-top: 1px solid green;
+}
+.path-local-treestudyplan .q-header .q-result,
+.path-local-treestudyplan .q-student-results .q-result {
+ height: 2rem;
+}
+.path-local-treestudyplan .q-header,
+.path-local-treestudyplan .q-student-results {
+ /*.q-line-heading > .q-header-title,*/
+}
+.path-local-treestudyplan .q-header .q-item-heading > .q-header-title,
+.path-local-treestudyplan .q-header .q-condition-heading,
+.path-local-treestudyplan .q-student-results .q-item-heading > .q-header-title,
+.path-local-treestudyplan .q-student-results .q-condition-heading {
+ width: 2rem;
+ writing-mode: vertical-lr;
+ text-orientation: sideways;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.path-local-treestudyplan .q-header .q-line-heading > .q-header-title,
+.path-local-treestudyplan .q-student-results .q-line-heading > .q-header-title {
+ height: fit-content;
+}
+.path-local-treestudyplan .q-header .q-item-heading > .q-item-heading,
+.path-local-treestudyplan .q-student-results .q-item-heading > .q-item-heading {
+ height: 6rem;
+}
+.path-local-treestudyplan .q-header .q-condition-heading,
+.path-local-treestudyplan .q-student-results .q-condition-heading {
+ height: 8rem;
+}
+.path-local-treestudyplan .q-studentname {
+ padding: 0.5em;
+ border-right: 2px solid grey;
+ width: 20rem;
+}
+.path-local-treestudyplan .q-studentname.heading {
+ font-weight: bold;
+ vertical-align: bottom;
+ display: flex;
+}
+.path-local-treestudyplan .q-studentname.heading span {
+ align-self: flex-end;
+}
+.path-local-treestudyplan .q-scrolly {
+ overflow-y: auto;
+ max-height: 300rem;
+}
.path-local-treestudyplan .b-modal-justify-footer-between .modal-footer,
.features-treestudyplan .b-modal-justify-footer-between .modal-footer {