FhcApp uses vueRouter4; CisApp checks for global router instance and routes internally as long as a path/routename/component setup is defined, if that is not the case use the provided href link; WIP moving apps like Profil, MyLv etc into components to be navigated by FhcApp;

This commit is contained in:
Johann Hoffmann
2025-01-15 14:10:08 +01:00
parent bae7b330f8
commit f60f735fa8
11 changed files with 274 additions and 37 deletions
+2
View File
@@ -27,6 +27,8 @@ class MyLv extends Auth_Controller
public function index()
{
$this->load->view('Cis/MyLv');
// $this->load->view('Cis/CisRouterView');
}
public function Info($studien_semester,$lvid)
+1 -1
View File
@@ -36,6 +36,6 @@ class Cis4 extends Auth_Controller
'person_id' => $personData->person_id
);
$this->load->view('CisVue/Dashboard.php',['viewData' => $viewData]);
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData]);
}
}
+2 -2
View File
@@ -36,8 +36,8 @@ class Dashboard extends Auth_Controller
'name' => $personData->vorname,
'person_id' => $personData->person_id
);
$this->load->view('CisVue/Dashboard.php',['viewData' => $viewData]);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData]);
}
}
@@ -0,0 +1,32 @@
<?php
$includesArray = array(
'title' => 'Cis4',
'axios027' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'tabulator5' => true,
'vue3' => true,
'primevue3' => true,
'customCSSs' => array(
'public/css/components/verticalsplit.css',
'public/css/components/searchbar.css',
'public/css/Fhc.css',
'public/css/components/dashboard.css'
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.js',
'vendor/npm-asset/primevue/accordiontab/accordiontab.js'
),
'customJSModules' => array(
'public/js/apps/Dashboard/Fhc.js'
),
);
$this->load->view('templates/CISVUE-Header', $includesArray);
?>
<div id="fhccontent">
<router-view view-data-string='<?php echo json_encode($viewData) ?>'></router-view>
</div>
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
-26
View File
@@ -1,26 +0,0 @@
<?php
$includesArray = array(
'title' => 'Dashboard',
'tabulator5'=>true,
'primevue3' => true,
'customJSModules' => [
'public/js/apps/Dashboard/Fhc.js'
],
'customJSs' => [
'vendor/npm-asset/primevue/accordion/accordion.js',
'vendor/npm-asset/primevue/accordiontab/accordiontab.js'
],
'customCSSs' => [
'public/css/components/dashboard.css'
],
);
$this->load->view('templates/CISVUE-Header', $includesArray);
?>
<div id="content">
<fhc-dashboard dashboard="CIS" view-data-string='<?php echo json_encode($viewData) ?>' />
</div>
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
-1
View File
@@ -119,7 +119,6 @@ const app = Vue.createApp({
}
});
app.use(FhcApi);
//TODO: EVERY View that uses CISVUE-HEADER includes Cis.js and needs to import primevue.js even if they don't use it (might be needed for Vue Router)
app.use(primevue.config.default, {
zIndex: {
overlay: 9000,
+50 -3
View File
@@ -2,6 +2,38 @@ import FhcDashboard from '../../components/Dashboard/Dashboard.js';
import FhcApi from '../../plugin/FhcApi.js';
import Phrasen from '../../plugin/Phrasen.js';
import { setScrollbarWidth } from "../../helpers/CssVarCalcHelpers";
import Stundenplan from "../../components/Cis/Stundenplan/Stundenplan";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(`/${ciPath}`),
routes: [
{
path: `/Cis/Stundenplan`,
name: 'Stundenplan',
component: Stundenplan,
props: true
},
{
path: `/`,
name: 'FhcDashboard',
component: FhcDashboard,
props: {dashboard: 'CIS'},
alias: ['/Cis4']
},
{
path: '/:catchAll(.*)',
redirect: { name: 'FhcDashboard'},
props: true
}
]
})
router.beforeEach((from, to) => {
console.log('from', from)
console.log('to', to)
})
const app = Vue.createApp({
name: 'FhcApp',
@@ -9,11 +41,26 @@ const app = Vue.createApp({
appSideMenuEntries: {}
}),
components: {
FhcDashboard
}
FhcDashboard,
Stundenplan
},
methods: {
tryCis4Navigate(e) {
this.$router.push({ name: e.detail });
},
},
mounted() {
window.addEventListener('fhcnavigate', this.tryCis4Navigate);
this.$router.push({ name: 'FhcDashboard' });
},
beforeUnmount() {
window.removeEventListener('fhcnavigate', this.tryCis4Navigate);
},
});
setScrollbarWidth();
app.use(router);
window.fhcVueRouter = router
app.use(FhcApi);
app.use(primevue.config.default, {
zIndex: {
@@ -22,4 +69,4 @@ app.use(primevue.config.default, {
}
})
app.use(Phrasen);
app.mount('#content');
app.mount('#fhccontent');
+7
View File
@@ -2,6 +2,12 @@ import CisMenuEntry from "./Menu/Entry.js";
import FhcSearchbar from "../searchbar/searchbar.js";
import CisSprachen from "./Sprachen.js"
// TODO: maybe get this from global vue router variable
const routeMap = [
{ routeName: 'FhcDashboard', paths: ['', '/', 'Cis4']},
{ routeName: 'Stundenplan', paths: ['Stundenplan']}
]
export default {
components: {
CisMenuEntry,
@@ -28,6 +34,7 @@ export default {
},
provide(){
return{
routeMap,
setActiveEntry: this.setActiveEntry,
addUrlCount: this.addUrlCount,
makeParentContentActive: this.makeParentContentActive,
+16 -3
View File
@@ -15,7 +15,7 @@ export default {
urlCount:0,
}
},
inject: ['makeParentContentActive', 'setActiveEntry','addUrlCount'],
inject: ['makeParentContentActive', 'setActiveEntry','addUrlCount', 'routeMap'],
watch:{
highestMatchingUrlCount: function(newValue)
{
@@ -77,6 +77,7 @@ export default {
return '';
let xmlDoc = (new DOMParser()).parseFromString(this.entry.content,"text/xml");
let url = xmlDoc.getElementsByTagName('url')[0];
if (!url)
return '';
// TODO(chris): replace get params
@@ -184,7 +185,19 @@ export default {
{
this.setActiveEntry(this.entry.content_id);
}
}
},
handleClick(e) {
// TODO: this needs to be done more resilient
const linkParts = this.link.split('/')
const routePath = linkParts.reverse()[0]
const r = this.routeMap.find(route => route.paths.includes(routePath))
if(window.fhcVueRouter && r) {
const re = new CustomEvent("fhcnavigate", { detail: r.routeName })
window.dispatchEvent(re)
} else {
location.href = this.link
}
}
},
mounted() {
if (this.$refs.children) {
@@ -224,7 +237,7 @@ export default {
</ul>
</template>
<a v-else
:href="link"
@click="handleClick"
:target="target"
:class="{
'btn btn-default rounded-0 w-100 text-start': true,
@@ -0,0 +1,163 @@
import FhcCalendar from "../../Calendar/Calendar.js";
import CalendarDate from "../../../composables/CalendarDate.js";
import LvModal from "../Mylv/LvModal.js";
import LvInfo from "../Mylv/LvInfo.js"
import LvMenu from "../Mylv/LvMenu.js"
export const Stundenplan = {
name: 'Stundenplan',
data() {
return {
events: null,
calendarDate: new CalendarDate(new Date()),
currentlySelectedEvent: null,
currentDay: new Date(),
minimized: false,
}
},
components: {
FhcCalendar, LvModal, LvMenu, LvInfo
},
computed:{
lv_id() { // computed so we can theoretically change path/lva selection and reload without page refresh
const pathParts = window.location.pathname.split('/').filter(Boolean);
const id = pathParts[pathParts.length - 1];
return id && !isNaN(Number(id)) ? id : null; // only return id if it is a number string since the path might contain invalid elements
},
weekFirstDay: function () {
return this.calendarDateToString(this.calendarDate.cdFirstDayOfWeek);
},
weekLastDay: function () {
return this.calendarDateToString(this.calendarDate.cdLastDayOfWeek);
},
monthFirstDay: function () {
return this.calendarDateToString(this.calendarDate.cdFirstDayOfCalendarMonth);
},
monthLastDay: function () {
return this.calendarDateToString(this.calendarDate.cdLastDayOfCalendarMonth);
},
},
methods:{
setSelectedEvent: function (event) {
this.currentlySelectedEvent = event;
},
selectDay: function(day){
this.currentDay = day;
},
showModal: function(event){
this.currentlySelectedEvent = event;
Vue.nextTick(() => {
this.$refs.lvmodal.show();
});
},
updateRange: function ({start,end}) {
let checkDate = (date) => {
return date.m != this.calendarDate.m || date.y != this.calendarDate.y;
}
// only load month data if the month or year has changed
if (checkDate(new CalendarDate(start)) && checkDate(new CalendarDate(end))){
// reset the events before querying the new events to activate the loading spinner
this.events = null;
this.calendarDate = new CalendarDate(end);
Vue.nextTick(() => {
this.loadEvents();
});
}
},
calendarDateToString: function (calendarDate) {
return calendarDate instanceof CalendarDate ?
[calendarDate.y, calendarDate.m + 1, calendarDate.d].join('-') :
null;
},
loadEvents: function(){
Promise.allSettled([
this.$fhcApi.factory.stundenplan.getStundenplan(this.monthFirstDay, this.monthLastDay, this.lv_id),
this.$fhcApi.factory.stundenplan.getStundenplanReservierungen(this.monthFirstDay, this.monthLastDay)
]).then((result) => {
let promise_events = [];
result.forEach((promise_result) => {
if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") {
let data = promise_result.value.data;
// adding additional information to the events
if (data && data.forEach) {
data.forEach((el, i) => {
el.id = i;
if (el.type === 'reservierung') {
el.color = '#' + (el.farbe || 'FFFFFF');
} else {
el.color = '#' + (el.farbe || 'CCCCCC');
}
el.start = new Date(el.datum + ' ' + el.beginn);
el.end = new Date(el.datum + ' ' + el.ende);
});
}
promise_events = promise_events.concat(data);
}
})
this.events = promise_events;
});
},
},
created()
{
this.loadEvents();
},
template:/*html*/`
<h2>{{$p.t('lehre/stundenplan')}}</h2>
<hr>
<lv-modal v-if="currentlySelectedEvent" :event="currentlySelectedEvent" ref="lvmodal" />
<fhc-calendar @selectedEvent="setSelectedEvent" :initial-date="currentDay" @change:range="updateRange" :events="events" initial-mode="week" show-weeks @select:day="selectDay" v-model:minimized="minimized">
<template #monthPage="{event,day,isSelected}">
<span class="fhc-entry" :class="{'selectedEvent':isSelected}" style="color:white" :style="{'background-color': event.color}">
{{event.topic}}
</span>
</template>
<template #weekPage="{event,day,isSelected}">
<div @click="showModal(event?.orig)" type="button" :class="{'selectedEvent':isSelected}"
class="fhc-entry border border-secondary border d-flex flex-column align-items-center
justify-content-evenly h-100" style="max-height: 75px; overflow: auto;">
<span>{{event?.orig.topic}}</span>
<span v-for="lektor in event?.orig.lektor">{{lektor.kurzbz}}</span>
<span>{{event?.orig.ort_kurzbz}}</span>
</div>
</template>
<template #dayPage="{event,day,mobile}">
<div @click="mobile? showModal(event?.orig):null" type="button" class="fhc-entry border border-secondary border row m-0 h-100 justify-content-center align-items-center text-center">
<div class="col ">
<p>Lehrveranstaltung:</p>
<p class="m-0">{{event?.orig.topic}}</p>
</div>
<div class="col ">
<p>Lektor:</p>
<p class="m-0" v-for="lektor in event?.orig.lektor">{{lektor.kurzbz}}</p>
</div>
<div class="col ">
<p>Ort: </p>
<p class="m-0">{{event?.orig.ort_kurzbz}}</p>
</div>
</div>
</template>
<template #pageMobilContent="{lvMenu}">
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
<div class="w-100">
<lv-info :event="currentlySelectedEvent" />
</div>
<h3 >Lehrveranstaltungs Menu</h3>
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-show="lvMenu" :menu="lvMenu" />
</template>
<template #pageMobilContentEmpty >
<h3>Keine Lehrveranstaltungen</h3>
</template>
</fhc-calendar>
`
}
export default Stundenplan
+1 -1
View File
@@ -15,7 +15,7 @@ export default {
return {
sections: [],
widgets: null,
viewData: JSON.parse(this.viewDataString),
viewData: JSON.parse(this.viewDataString ?? '{}'),
editMode: false
}
},