Merge branch 'cis40_2026-02_rc' into cis40_2026-05_ma_rc

This commit is contained in:
Harald Bamberger
2026-05-11 13:54:38 +02:00
11 changed files with 204 additions and 150 deletions
@@ -109,7 +109,7 @@ class LvPlan extends FHCAPI_Controller
$lvplanEvents = $this->getDataOrTerminateWithError($result);
// fetching moodle events
$moodleEvents = $uid ? [] : $this->fetchMoodleEvents($start_date, $end_date);
$moodleEvents = $this->fetchMoodleEvents($start_date, $end_date, $uid);
// fetching ferien events
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date, $uid);
@@ -406,7 +406,7 @@ class LvPlan extends FHCAPI_Controller
*/
public function compactibleEventTypes()
{
$this->terminateWithSuccess(["lehreinheit", "reservierung"]);
$this->terminateWithSuccess(["lehreinheit", "reservierung", "ferien", "moodle"]);
}
/**
@@ -416,7 +416,7 @@ class LvPlan extends FHCAPI_Controller
* @param string $end_date
* @return array
*/
private function fetchMoodleEvents($start_date, $end_date)
private function fetchMoodleEvents($start_date, $end_date, $uid = null)
{
$this->load->config('calendar');
@@ -439,7 +439,7 @@ class LvPlan extends FHCAPI_Controller
[
'start_date' => $start->format('c'),
'end_date' => $end->format('c'),
'username' => getAuthUID()
'username' => $uid ?? getAuthUID()
]
);
+17 -34
View File
@@ -388,12 +388,6 @@ html {
#nav-search > .input-group > * {
border-radius: 0 !important;
}
#nav-search .searchbar_results {
top: 100% !important;
left: 0;
right: 0 !important;
width: 100% !important;
}
/* frame */
.in-frame {
@@ -467,7 +461,14 @@ html {
/* overflow: visible !important; */
}
#cis-header {
z-index: 10;
z-index: 10;
}
#cis-header-bar {
position: fixed;
top: 0;
height: var(--fhc-cis-header-height);
width: 100%;
background-color: var(--fhc-primary);
}
#cis-header nav {
position: initial;
@@ -483,12 +484,7 @@ html {
display: none;
}
#nav-logo {
position: fixed;
top: 0;
left: 0;
height: var(--fhc-cis-header-height);
width: var(--fhc-cis-menu-width);
background-color: var(--fhc-primary);
padding: var(--fhc-cis-header-py) var(--fhc-cis-header-px);
z-index: 2;
}
@@ -503,37 +499,27 @@ html {
top: var(--fhc-cis-header-height);
display: flex;
flex-direction: column;
}
#nav-main-sticky > :not(#nav-main-toggle) {
overflow: auto;
height: 100%;
}
#nav-main-toggle {
width: 0;
z-index: 1;
}
#nav-main-toggle .btn,
#nav-main-toggle .fa-arrow-circle-left {
#nav-main-toggle:hover {
background-color: var(--fhc-secondary-highlight);
}
#nav-main-toggle .div,
#nav-main-toggle .fa-chevron-left {
transition: all 0.5s ease-in-out;
}
#nav-main-toggle .collapsed.btn {
background-color: transparent !important;
}
#nav-main-toggle .collapsed .fa-arrow-circle-left {
#nav-main-toggle .collapsed .fa-chevron-left {
transform: scaleX(-1);
color: var(--fhc-black-40);
}
#nav-search {
position: fixed;
top: 0;
left: var(--fhc-cis-menu-width);
height: var(--fhc-cis-header-height);
right: calc(var(--fhc-cis-header-height) + 2 * var(--fhc-cis-header-px) - 2 * var(--fhc-cis-header-py));
width: auto !important;
}
#nav-user {
position: fixed;
top: 0;
right: 0;
position: relative;
}
#nav-user-btn {
border-width: 0;
@@ -568,7 +554,7 @@ html {
#nav-main-menu {
height: 100%;
background-color: var(--fhc-cis-menu-bg);
background-color: var(--fhc-primary);
}
#nav-main-menu > div {
width: var(--fhc-cis-menu-width);
@@ -610,9 +596,6 @@ html {
}
#nav-user{
position: relative;
}
#nav-user-btn {
}
#nav-user-btn img {
object-fit: cover;
+11 -26
View File
@@ -274,25 +274,12 @@ const app = Vue.createApp({
name: 'CisApp',
data: () => ({
appSideMenuEntries: {},
uid: '',
isStudent: null,
isMitarbeiter: null
windowWidth: 0,
}),
components: {},
computed: {
isMobile() {
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
return smallScreen;// && touchCapable;
},
},
provide() {
return { // provide injectable & watchable language property
language: Vue.computed(() => this.$p.user_language),
isMobile: this.isMobile,
uid: Vue.computed(() => this.uid),
isStudent: Vue.computed(() => this.isStudent),
isMitarbeiter: Vue.computed(() => this.isMitarbeiter)
isMobile: Vue.computed(() => this.windowWidth < 767),
}
},
methods: {
@@ -328,23 +315,21 @@ const app = Vue.createApp({
this.$router.push(route);
}
}
},
handleWindowResize() {
this.windowWidth = window.innerWidth;
},
},
async created(){
await this.$api
.call(ApiAuthinfo.getAuthInfo())
.then(res => {
this.uid = res.data.uid;
this.isMitarbeiter = res.data.isMitarbeiter;
this.isStudent = res.data.isStudent;
});
created() {
this.windowWidth = window.innerWidth;
},
mounted() {
async mounted() {
document.addEventListener('click', this.handleClick);
window.addEventListener("resize", this.handleWindowResize);
},
beforeUnmount() {
document.removeEventListener('click', this.handleClick);
window.removeEventListener("resize", this.handleWindowResize);
},
});
+15 -10
View File
@@ -134,26 +134,27 @@ const app = Vue.createApp({
childactions: []
}
}
}
},
windowWidth: 0,
};
},
computed: {
isMobile() {
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
return smallScreen;// && touchCapable;
},
},
provide() {
return {
isMobile: this.isMobile,
isNarrow: Vue.computed(() => this.windowWidth < 992),
isMobile: Vue.computed(() => this.windowWidth < 767),
}
},
methods: {
searchfunction: function(searchsettings) {
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
}
},
handleWindowResize() {
this.windowWidth = window.innerWidth;
},
},
created() {
this.windowWidth = window.innerWidth;
},
async mounted() {
const openOtherLvPlanAction = {
label: Vue.computed(() => this.$p.t("lehre/stundenplan")),
@@ -183,6 +184,10 @@ const app = Vue.createApp({
openOtherLvPlanAction,
);
}
window.addEventListener("resize", this.handleWindowResize);
},
beforeUnmount() {
window.removeEventListener("resize", this.handleWindowResize);
},
});
@@ -147,12 +147,13 @@ export default {
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
class="d-flex flex-row justify-content-center gap-1 align-items-center"
>
<i
<span
v-for="(subEvent, subEventIndex) in event.events"
:key="subEventIndex"
class="fa-solid fa-circle fa-2xs"
:style="subEvent.farbe ? {color: '#' + subEvent.farbe} : {}"
></i>
:style="subEvent.farbe ? {'background-color': '#' + subEvent.farbe} : {}"
style="height:10px; width:10px;"
class="border border-dark rounded-circle"
></span>
</div>
<div
v-else-if="event.display === 'compactedExtra'"
@@ -54,6 +54,17 @@ export default {
return this.lv.bezeichnung;
}
},
watch: {
async isMobile() {
await this.$nextTick();
this.handleChangeMode(
this.currentMode,
luxon.DateTime.fromISO(this.currentDay, {
zone: this.timezone,
}),
);
},
},
methods: {
handleChangeDate(day, newMode) {
return this.handleChangeMode(newMode, day);
@@ -82,6 +82,17 @@ export default {
];
}
},
watch: {
async isMobile() {
await this.$nextTick();
this.handleChangeMode(
this.currentMode,
luxon.DateTime.fromISO(this.currentDay, {
zone: this.timezone,
}),
);
},
},
methods: {
handleChangeDate(day, newMode) {
return this.handleChangeMode(newMode, day);
@@ -137,6 +137,15 @@ export default {
this.$router.go();
},
},
async isMobile() {
await this.$nextTick();
this.handleChangeMode(
this.currentMode,
luxon.DateTime.fromISO(this.currentDay, {
zone: this.timezone,
}),
);
},
},
methods: {
handleChangeDate(day, newMode) {
+11
View File
@@ -130,6 +130,17 @@ export default {
];
},
},
watch: {
async isMobile() {
await this.$nextTick();
this.handleChangeMode(
this.currentMode,
luxon.DateTime.fromISO(this.currentDay, {
zone: this.timezone,
}),
);
},
},
methods: {
loadLvPlan() {
if (!this.formData.stgkz) {
+87 -57
View File
@@ -30,7 +30,7 @@ export default {
menuOpen:true,
};
},
inject: ["isMobile"],
inject: ["isNarrow", "isMobile"],
provide(){
return{
setActiveEntry: this.setActiveEntry,
@@ -113,71 +113,101 @@ export default {
});
},
template: /*html*/`
<div
id="header-options-collapsible"
class="collapse multi-collapse collapse-horizontal show"
>
<div class="d-flex flex-row align-items-center gap-2 h-100" style="width: 79px">
<button id="nav-main-btn" class="navbar-toggler rounded-0 px-2 border-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<span v-if="isMobile" class="d-flex flex-row align-items-center">
<theme-switch></theme-switch>
</span>
<div id="cis-header-bar" class="d-flex flex-row flex-grow-1">
<div id="nav-logo" class="d-none d-lg-block">
<div class="d-flex h-100 justify-content-between">
<a :href="rootUrl">
<img :src="logoUrl" alt="Corporate Identity Logo">
</a>
</div>
</div>
</div>
<fhc-searchbar
:searchoptions="searchbaroptions"
:searchfunction="searchfunction"
ref="searchbar"
id="nav-search"
class="fhc-searchbar w-100 py-1 py-lg-2"
></fhc-searchbar>
<div id="nav-logo" class="d-none d-lg-block">
<div class="d-flex h-100 justify-content-between">
<a :href="rootUrl">
<img :src="logoUrl" alt="Corporate Identity Logo">
</a>
<theme-switch></theme-switch>
</div>
</div>
<div
id="header-usermenu-collapsible"
class="collapse multi-collapse collapse-horizontal show"
>
<div
:style="!isMobile ? '' : 'width: 51px'"
id="nav-user"
v-if="isNarrow"
:class="{'collapse multi-collapse collapse-horizontal show': isMobile}"
id="navbar-toggler-collapsible"
>
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
</button>
<ul ref="navUserDropdown"
@[\`shown.bs.collapse\`]="handleShowNavUser"
@[\`hide.bs.collapse\`]="handleHideNavUser"
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
<li >
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
</li>
<li><hr class="dropdown-divider m-0 "></li>
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
</ul>
<div class="d-flex flex-row align-items-center h-100" style="width: 35px">
<button id="nav-main-btn" class="navbar-toggler rounded-0 px-2 border-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<fhc-searchbar
:searchoptions="searchbaroptions"
:searchfunction="searchfunction"
ref="searchbar"
id="nav-search"
class="fhc-searchbar flex-grow-1 py-1 py-lg-2"
>
<template #collapseToggler="{ isSearchShownInMobileView }">
<span
v-if="isMobile"
type="button"
data-bs-toggle="collapse"
data-bs-target=".multi-collapse"
aria-controls="searchbar-collapsible navbar-toggler-collapsible options-collapsible"
aria-expanded="false"
class="d-flex flex-row align-items-center pe-1"
style="color: white"
>
<i v-if="isSearchShownInMobileView" class="fa-solid fa-chevron-left ps-3"></i>
<i v-else class="fa-solid fa-magnifying-glass ps-2"></i>
</span>
</template>
</fhc-searchbar>
<div
id="options-collapsible"
:class="{'collapse multi-collapse collapse-horizontal show': isMobile}"
>
<div :style="!isMobile ? '' : 'width: 105px'" class="d-flex flex-row ps-3 justify-content-end">
<span class="d-flex flex-row align-items-center">
<theme-switch></theme-switch>
</span>
<div id="nav-user">
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
</button>
<ul ref="navUserDropdown"
@[\`shown.bs.collapse\`]="handleShowNavUser"
@[\`hide.bs.collapse\`]="handleHideNavUser"
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
<li >
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
</li>
<li><hr class="dropdown-divider m-0 "></li>
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
</ul>
</div>
</div>
</div>
</div>
<nav id="nav-main" class="offcanvas offcanvas-start" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
<div id="nav-main-sticky">
<div id="nav-main-toggle" class="position-static d-none d-lg-block ">
<button :aria-label="menuCollapseAriaLabel" type="button" @click="menuOpen = !menuOpen" class="btn text-light rounded-0 p-1 d-flex align-items-center" data-bs-toggle="collapse" data-bs-target=".nav-menu-collapse" aria-expanded="true" aria-controls="nav-sprachen nav-main-menu">
<i aria-hidden="true" class="fa fa-arrow-circle-left fhc-text"></i>
</button>
</div>
<div class="offcanvas-body p-0">
<div id="nav-main-menu" class="nav-menu-collapse collapse collapse-horizontal show">
<div>
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeEntry" v-for="entry in entries" :key="entry.content_id" :entry="entry" />
<div class="d-flex flex-row h-100">
<div class="offcanvas-body p-0">
<div id="nav-main-menu" class="nav-menu-collapse collapse collapse-horizontal show">
<div class="flex-grow-1">
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeEntry" v-for="entry in entries" :key="entry.content_id" :entry="entry" />
</div>
</div>
</div>
<div id="nav-main-toggle" class="d-none d-lg-block">
<div
@click="menuOpen = !menuOpen"
:aria-label="menuCollapseAriaLabel"
type="button"
class="h-100 d-flex align-items-center px-2"
data-bs-toggle="collapse"
data-bs-target=".nav-menu-collapse"
aria-expanded="true"
aria-controls="nav-sprachen nav-main-menu"
>
<i aria-hidden="true" class="fa-solid fa-chevron-left fhc-text"></i>
</div>
</div>
</div>
+23 -15
View File
@@ -94,22 +94,25 @@ export default {
@focusin="searchfocusin"
@focusout="searchfocusout"
>
<span
v-if="isMobile"
type="button"
data-bs-toggle="collapse"
data-bs-target=".multi-collapse"
aria-controls="header-searchbar-collapsible header-options-collapsible header-usermenu-collapsible"
aria-expanded="false"
class="d-flex flex-row align-items-center ps-3 pe-1"
>
<i v-if="isSearchShownInMobileView" class="fa-solid fa-chevron-left"></i>
<i v-else class="fa-solid fa-magnifying-glass"></i>
</span>
<slot name="collapseToggler" :isSearchShownInMobileView="isSearchShownInMobileView">
<span
v-if="isMobile"
type="button"
data-bs-toggle="collapse"
data-bs-target="#searchbar-collapsible"
aria-controls="searchbar-collapsible"
aria-expanded="false"
class="d-flex flex-row align-items-center pe-1"
style="color: white"
>
<i v-if="isSearchShownInMobileView" class="fa-solid fa-chevron-left ps-3"></i>
<i v-else class="fa-solid fa-magnifying-glass ps-2"></i>
</span>
</slot>
<div
:class="{'flex-grow-1': !isMobile, 'collapse multi-collapse collapse-horizontal': isMobile}"
id="header-searchbar-collapsible"
id="searchbar-collapsible"
@[\`show.bs.collapse\`]="isSearchShownInMobileView = true"
@[\`hidden.bs.collapse\`]="isSearchShownInMobileView = false"
>
@@ -117,7 +120,7 @@ export default {
:class="{open: showresult, closed: showresult, 'px-3': isMobile}"
ref="searchbox"
class="h-100 input-group me-2 searchbar_searchbox"
:style="isMobile ? 'width: ' + getMaxWidthOfSearchbarInMobileView() : ''"
:style="isMobile ? 'width: ' + getMaxWidthOfSearchbarInNarrowView() : ''"
>
<span class="input-group-text">
<i class="fa-solid fa-magnifying-glass color-white"></i>
@@ -242,6 +245,11 @@ export default {
}
this.search();
},
isMobile() {
if (!this.isMobile) {
this.isSearchShownInMobileView = false;
}
},
},
mounted() {
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
@@ -503,7 +511,7 @@ export default {
}
return this.searchoptions.actions[res.type];
},
getMaxWidthOfSearchbarInMobileView() {
getMaxWidthOfSearchbarInNarrowView() {
// body width - hardcoded chevron width; necessary for accurate collapse transition transition
return (
document.querySelector("body").getBoundingClientRect().width -