feature(Stundenplan Moodle Events Dependency Injection): injects moodle components dependency to the Stundenplan

This commit is contained in:
SimonGschnell
2025-05-28 12:30:38 +02:00
parent a0fb34420e
commit a5bb72baf6
7 changed files with 226 additions and 73 deletions
@@ -0,0 +1,72 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
use CI3_Events as Events;
class RendererLoader extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'GetRenderers' => self::PERM_LOGGED,
]);
$this->load->library('LogLib');
$this->loglib->setConfigs(array(
'classIndex' => 5,
'functionIndex' => 5,
'lineIndex' => 4,
'dbLogType' => 'API', // required
'dbExecuteUser' => 'RESTful API'
));
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* fetches Stundenplan and Moodle events together
* @access public
*
*/
public function GetRenderers(){
$renderer_paths = [];
Events::trigger(
'loadRenderers',
function & () use (&$renderer_paths)
{
return $renderer_paths;
}
);
$this->terminateWithSuccess($renderer_paths);
}
}
+75
View File
@@ -320,4 +320,79 @@
.selectedEvent {
background-color: #00649c !important;
color: white;
}
.dayPageContainer, .weekPageContainer{
container-type: inline-size;
}
.dayPageContainer .lehreinheitEventContent,
.weekPageContainer .lehreinheitEventContent {
display: flex;
flex-direction: column;
}
.dayPageContainer .moodleEventContent,
.weekPageContainer .moodleEventContent{
display: flex;
flex-direction: row;
}
.dayPageContainer .lehreinheitEventContent{
flex-grow:1;
align-items: center;
font-size: 0.875rem;
font-weight: 400;
line-height:1.25;
}
.dayPageContainer .lehreinheitEventHeader {
display: flex;
flex-direction: column;
padding: 1rem;
border-right: 1px solid gray;
}
.dayPageContainer .moodleEventContent {
align-items: center;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.25;
width:100%;
}
.weekPageContainer .moodleEventContent {
align-items: center;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.25;
width: 100%;
}
.weekPageContainer .lehreinheitEventHeader {
display: flex;
flex-direction: column;
padding: 1rem;
border-right: 1px solid gray;
}
.weekPageContainer .lehreinheitEventContent {
flex-grow: 1;
align-items: center;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.25;
}
@container( min-width: 500px){
.dayPageContainer .lehreinheitEventContent,
.weekPageContainer .lehreinheitEventContent {
flex-direction: row;
}
.dayPageContainer .lehreinheitEventContent{
justify-content: space-evenly;
}
}
+12
View File
@@ -0,0 +1,12 @@
export default {
loadRenderers() {
return {
method: 'get',
url: '/api/frontend/v1/RendererLoader/GetRenderers',
params: {
}
};
},
}
+14 -2
View File
@@ -15,6 +15,8 @@ import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMit
import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../../components/Cis/Studium/Studium.js";
import ApiRenderers from '../../api/factory/renderers.js';
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
@@ -234,7 +236,8 @@ const router = VueRouter.createRouter({
const app = Vue.createApp({
name: 'FhcApp',
data: () => ({
appSideMenuEntries: {}
appSideMenuEntries: {},
renderers: {},
}),
components: {},
computed: {
@@ -244,7 +247,8 @@ const app = Vue.createApp({
},
provide() {
return { // provide injectable & watchable language property
language: Vue.computed(() => this.$p.user_language)
language: Vue.computed(() => this.$p.user_language),
renderers: Vue.computed(() => this.renderers),
}
},
methods: {
@@ -282,6 +286,14 @@ const app = Vue.createApp({
}
}
},
async created(){
await this.$api
.call(ApiRenderers.loadRenderers())
.then(res => res.data)
.then(data => {
this.renderers = data;
});
},
mounted() {
document.addEventListener('click', this.handleClick);
},
@@ -0,0 +1,30 @@
export default {
methods:{
convertTime: function ([hour, minute]) {
let date = new Date();
date.setHours(hour);
date.setMinutes(minute);
// returns date string as hh:mm
return date.toLocaleTimeString(this.$p.user_locale, { hour: '2-digit', minute: '2-digit', hour12: false });
},
},
props:{
event: {
type:Object,
required:true,
},
},
template: `
<div class="lehreinheitEventHeader" v-if="!event.allDayEvent && event?.beginn && event?.ende" >
<span class="small">{{convertTime(event.beginn.split(":"))}}</span>
<span class="small">{{convertTime(event.ende.split(":"))}}</span>
</div>
<div class="lehreinheitEventContent">
<span>{{event.topic}}</span>
<span v-for="lektor in event.lektor">{{lektor.kurzbz}}</span>
<span>{{event.ort_kurzbz}}</span>
</div>
`,
}
@@ -3,11 +3,12 @@ import CalendarDate from "../../../composables/CalendarDate.js";
import LvModal from "../Mylv/LvModal.js";
import LvInfo from "../Mylv/LvInfo.js"
import LvMenu from "../Mylv/LvMenu.js"
import moodleSvg from "../../../helpers/moodleSVG.js"
import lehreinheitEvent from "./EventTypes/lehreinheitEvent.js"
import ApiStundenplan from '../../../api/factory/stundenplan.js';
import ApiAuthinfo from '../../../api/factory/authinfo.js';
export const DEFAULT_MODE_STUNDENPLAN = 'Week'
const Stundenplan = {
@@ -46,6 +47,7 @@ const Stundenplan = {
eventMaxHeight: this.eventMaxHeight
}
},
inject:["renderers"],
watch: {
weekFirstDay: {
handler: async function (newValue) {
@@ -69,7 +71,7 @@ const Stundenplan = {
}
},
components: {
FhcCalendar, LvModal, LvMenu, LvInfo, moodleSvg
FhcCalendar, LvModal, LvMenu, LvInfo, lehreinheitEvent,
},
computed:{
downloadLinks: function(){
@@ -105,17 +107,16 @@ const Stundenplan = {
},
},
methods:{
renderComponent(name){
switch(name){
case 'lehreinheitEvent': return lehreinheitEvent;
default: return !this.renderers? null : Vue.defineAsyncComponent(() => import(this.renderers[name]))
}
},
fetchStudiensemesterDetails: async function (date) {
return this.$api.call(ApiStundenplan.studiensemesterDateInterval(date));
},
convertTime: function([hour,minute]){
let date = new Date();
date.setHours(hour);
date.setMinutes(minute);
// returns date string as hh:mm
return date.toLocaleTimeString(this.$p.user_locale, { hour: '2-digit', minute: '2-digit', hour12:false});
},
setSelectedEvent: function (event) {
this.currentlySelectedEvent = event;
},
@@ -248,6 +249,7 @@ const Stundenplan = {
.then(data => {
this.uid = data.uid;
});
// this.loadEvents();
},
beforeUnmount() {
@@ -285,68 +287,23 @@ const Stundenplan = {
</div>
</template>
<template #monthPage="{event,day}">
<div class="p-1" v-if="event.type=='moodle'" @click="showModal(event)">
<div class="d-flex small w-100" >
<moodle-svg></moodle-svg>
<span class="flex-grow-1 text-center "><strong v-html="event.titel"></strong> - {{event.topic}}</span>
</div>
</div>
<div v-else @click="showModal(event)" class="p-1">
<span>{{event.topic}}</span>
<div @click="showModal(event)" class="p-1">
<component :event="event" :is="renderComponent(event.type+'Event')"></component>
</div>
</template>
<template #weekPage="{event,day}">
<div @click="showModal(event)" type="button"
class=" position-relative border border-secondary border d-flex flex-col align-items-center justify-content-evenly h-100"
:class="{'p-1':event.allDayEvent}"
style="overflow: auto;">
<div v-if="!event.allDayEvent && event?.beginn && event?.ende" class="d-none d-xl-block" >
<div class="d-flex flex-column p-4 p-xl-2 border-end border-secondary">
<span class="small">{{convertTime(event.beginn.split(":"))}}</span>
<span class="small">{{convertTime(event.ende.split(":"))}}</span>
</div>
</div>
<div v-if="event.type=='moodle'" class="d-flex small w-100" >
<moodle-svg></moodle-svg>
<span class="flex-grow-1 text-center"><strong v-html="event.titel"></strong> - {{event.topic}}</span>
</div>
<div v-else class="d-flex flex-column flex-grow-1 align-items-center small">
<span>{{event.topic}}</span>
<span v-for="lektor in event.lektor">{{lektor.kurzbz}}</span>
<span>{{event.ort_kurzbz}}</span>
</div>
<div @click="showModal(event)" type = "button"
class="weekPageContainer position-relative border border-secondary border d-flex flex-col align-items-center justify-content-evenly h-100"
:class="{'p-1':event.allDayEvent}"
style = "overflow: auto;" >
<component :event="event" :is="renderComponent(event.type+'Event')"></component>
</div>
</template>
<template #dayPage="{event,day,mobile}">
<div @click="mobile? showModal(event):null" type="button" class="fhc-entry border border-secondary border m-0 h-100 text-center">
<template v-if="event.type=='moodle'">
<div class="d-flex small align-items-center w-100 p-1" >
<moodle-svg></moodle-svg>
<span class="flex-grow-1 text-center"><strong v-html="event.titel"></strong> - {{event.topic}}</span>
</div>
</template>
<template v-else>
<div class="row justify-content-center align-items-center">
<div class="col-auto" v-if="!event.allDayEvent && event?.beginn && event?.ende" >
<div class="d-flex flex-column p-4 border-end border-secondary">
<span class="small">{{convertTime(event.beginn.split(":"))}}</span>
<span class="small">{{convertTime(event.ende.split(":"))}}</span>
</div>
</div>
<div class="col">
<p>{{ $p.t('lehre/lehrveranstaltung') }}:</p>
<p class="m-0">{{event?.topic}}</p>
</div>
<div class="col" :style="'max-height: ' + eventMaxHeight + '; overflow: auto;'">
<p>{{ $p.t('lehre/lektor') }}:</p>
<p class="m-0" v-for="lektor in event?.lektor">{{lektor.kurzbz}}</p>
</div>
<div class="col">
<p>{{ $p.t('profil/Ort') }}: </p>
<p class="m-0">{{event?.ort_kurzbz}}</p>
</div>
</div>
</template>
<div @click="mobile? showModal(event):null" type="button" class="dayPageContainer fhc-entry border border-secondary border m-0 h-100 text-center">
<div class="h-100 d-flex flex-col justify-content-evenly align-items-center">
<component :is="renderComponent(event.type+'Event')" :event="event"></component>
</div>
</div>
</template>
<template #pageMobilContent="{lvMenu, event}">
-5
View File
@@ -1,5 +0,0 @@
export default {
template:`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 20 130 90" style="height:1.5em" ><radialGradient id="moodle-original-a" cx="532.855" cy="-537.557" r="209.76" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FAAF40" /><stop offset=".043" stop-color="#F9A538" /><stop offset=".112" stop-color="#F89D31" /><stop offset=".227" stop-color="#F89A2F" /><stop offset=".528" stop-color="#F7922D" /><stop offset="1" stop-color="#F37B28" /></radialGradient><path fill="white" stroke="black" stroke-width="1" d="M106.259 105.754V66.975c0-8.164-3.397-12.244-10.034-12.244-6.629 0-10.034 4.08-10.034 12.244v38.779H66.294V66.975c0-8.164-3.228-12.244-9.862-12.244-6.633 0-10.036 4.08-10.036 12.244v38.779H26.667V64.768c0-8.504 2.891-14.801 8.844-19.223 5.102-3.91 12.246-5.777 20.92-5.777 9.015 0 15.478 2.207 19.727 6.801 3.57-4.594 10.207-6.801 19.897-6.801 8.844 0 15.819 1.867 20.922 5.777 5.951 4.422 8.843 10.719 8.843 19.223v41.152h-19.563v-.166z" /><path fill="black" d="M28.539 49.627l-2.041 10.207c18.708 6.291 36.395.166 45.751-16.158-13.778-9.522-26.535.17-43.71 5.951" /><linearGradient id="moodle-original-b" gradientUnits="userSpaceOnUse" x1="324.268" y1="-509.952" x2="368.932" y2="-509.952" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#929497" /><stop offset=".124" stop-color="#757578" /><stop offset=".279" stop-color="#575658" /><stop offset=".44" stop-color="#403E3F" /><stop offset=".609" stop-color="#302D2E" /><stop offset=".788" stop-color="#262223" /><stop offset="1" stop-color="#231F20" /></linearGradient><path fill="black" d="M28.539 47.08c-.681 3.91-1.192 7.65-1.872 11.563 17.857 6.125 35.375.85 44.73-15.137-11.909-13.776-25.17-2.383-42.858 3.574" /><linearGradient id="moodle-original-c" gradientUnits="userSpaceOnUse" x1="332.834" y1="-495.051" x2="351.377" y2="-521.534" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#231F20" /><stop offset="1" stop-color="#231F20" stop-opacity="0" /></linearGradient><path fill="none" d="M49.799 51.668c-8.164-1.701-17.009 2.555-23.131 6.975-3.912-28.57 13.777-27.893 36.903-20.75-1.529 6.975-4.083 16.33-8.502 21.941-.169-3.744-1.869-6.293-5.27-8.166" /><linearGradient id="moodle-original-d" gradientUnits="userSpaceOnUse" x1="299.778" y1="-495.802" x2="381.412" y2="-495.802" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#929497" /><stop offset=".124" stop-color="#757578" /><stop offset=".279" stop-color="#575658" /><stop offset=".44" stop-color="#403E3F" /><stop offset=".609" stop-color="#302D2E" /><stop offset=".788" stop-color="#262223" /><stop offset="1" stop-color="#231F20" /></linearGradient><path fill="black" d="M2.178 47.08c29.932-18.031 46.77-21.43 81.634-25-40.478 31.969-41.499 25-81.634 25" /><path stroke="#4A4A4C" stroke-width=".5" fill="black" d="M83.812 22.246L51.667 45.545" /><path opacity=".23" fill="black" d="M45.545 34.66c.34 3.744-.511-3.572 0 0" /><path stroke="#4A4A4C" stroke-width=".5" fill="black" d="M2.178 47.08l49.489-1.535" /><path stroke="#F16922" stroke-width=".5" d="M42.484 35.002C33.98 37.383 6.09 43.506 5.747 47.08c-.849 6.631-.167 17.176-.167 17.176" fill="none" /><path fill="#F16922" d="M8.131 89.596c-3.063-7.652-6.804-16.158-2.384-26.703C8.64 72.756 8.131 80.24 8.131 89.596" /><path fill="#6D6E70" d="M41.076 33.844c.708-.25 1.384-.17 1.509.184.126.355-.344.846-1.052 1.096-.709.256-1.384.172-1.51-.184-.127-.352.344-.844 1.053-1.096z" /></svg>
`
}