merge C4 cleanup

This commit is contained in:
SimonGschnell
2025-02-26 14:42:52 +01:00
31 changed files with 613 additions and 146 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ $route['api/v1/organisation/[O|o]rganisationseinheit/(:any)'] = 'api/v1/organisa
$route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1';
$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['Cis/Stundenplan/(:any)?'] = 'Cis/Stundenplan/index/$1';
$route['Cis/Stundenplan/.*'] = 'Cis/Stundenplan/index/$1';
// load routes from extensions
$subdir = 'application/config/extensions';
+2 -2
View File
@@ -23,10 +23,10 @@ class Stundenplan extends Auth_Controller
/**
* @return void
*/
public function index($lv_id = null)
public function index()
{
$viewData = array(
'lv_id' => $lv_id,
'uid'=>getAuthUID(),
);
@@ -153,6 +153,11 @@ class Stundenplan extends FHCAPI_Controller
$stundenplan_data = $this->StundenplanModel->getStundenplanLVA($start_date, $end_date, $lv_id);
$stundenplan_data = $this->getDataOrTerminateWithError($stundenplan_data) ?? [];
$this->expand_object_information($stundenplan_data);
// query lv itself in case its Stundenplan is being queried and it has no entries
$this->load->model('education/Lehrveranstaltung_model','LehrveranstaltungModel');
$lv = getData($this->LehrveranstaltungModel->load($lv_id))[0];
$this->addMeta('lv', $lv);
$this->terminateWithSuccess($stundenplan_data);
}
@@ -556,16 +556,6 @@ class Lehrveranstaltung_model extends DB_Model
*/
public function getLvsByStudentWithGrades($student_uid, $studiensemester_kurzbz, $sprache = null, $lvid=null)
{
if ($sprache) {
$sprache_qry = $this->db->compile_binds('SELECT index FROM public.tbl_sprache WHERE sprache = ?', [$sprache]);
$bezeichnung = 'bezeichnung_mehrsprachig[(' . $sprache_qry . ')]';
$sgbezeichnung = $sprache == 'English' ? 'COALESCE(sg.english, sg.bezeichnung)' : 'sg.bezeichnung';
$lvbezeichnung = $sprache == 'English' ? 'COALESCE(v.bezeichnung_english, v.bezeichnung)' : 'v.bezeichnung';
} else {
$bezeichnung = 'bezeichnung';
$sgbezeichnung = 'sg.bezeichnung';
$lvbezeichnung = 'v.bezeichnung';
}
$this->addDistinct();
// TODO(chris): selects
@@ -605,16 +595,20 @@ class Lehrveranstaltung_model extends DB_Model
$this->addSelect('znn.positiv');
#$this->addSelect('splv.module');
$this->addSelect($lvbezeichnung . ' AS bezeichnung');
$this->addSelect($sgbezeichnung . ' AS sg_bezeichnung');
$this->addSelect('v.bezeichnung AS bezeichnung');
$this->addSelect('v.bezeichnung_english AS bezeichnung_eng');
$this->addSelect('sg.bezeichnung AS sg_bezeichnung');
$this->addSelect('sg.english AS sg_bezeichnung_eng');
$this->addSelect('UPPER(sg.typ::VARCHAR(1) || sg.kurzbz) AS studiengang_kuerzel');
//also adds returns the index of the grade
//TODO: ist zeugnissnote immer gleich wie die lvgesamtnote
$this->addSelect('COALESCE(zn.note::numeric,gn.note::numeric) as note_index');
$this->addSelect('COALESCE(znn.positiv,gnn.positiv) as positiv');
$this->addSelect('COALESCE(gnn.' . $bezeichnung . ', gnn.bezeichnung, gn.note::text) AS lvnote');
$this->addSelect('COALESCE(znn.' . $bezeichnung . ', znn.bezeichnung, zn.note::text) AS znote');
$this->addSelect('gnn.bezeichnung_mehrsprachig AS lvnotebez');
$this->addSelect('gnn.note AS lvnote');
$this->addSelect('znn.bezeichnung_mehrsprachig AS znotebez');
$this->addSelect('znn.note AS znote');
// TODO(chris): Potentielle Anpassung "Eine UID"
$this->addJoin('campus.vw_student_lehrveranstaltung v', 'lehrveranstaltung_id');
+3
View File
@@ -402,6 +402,7 @@ function checkZeilenUmbruch()
'id'=>'core_menu_mailanstudierende',
'position'=>'100',
'name'=>$p->t('lehre/mail'),
'phrase'=>'lehre/mail',
'icon'=>'../../../skin/images/button_feedback.png',
'link'=>$mailto,
'link_onclick'=>$link_onclick
@@ -477,6 +478,7 @@ function checkZeilenUmbruch()
'id'=>'core_menu_anerkennungNachgewiesenerKenntnisse',
'position'=>'128',
'name'=>$p->t('lehre/anrechnung'),
'phrase'=>'lehre/anrechnung',
'icon'=>'../../../skin/images/button_listen.png',
'link' => APP_ROOT. 'index.ci.php/lehre/anrechnung/RequestAnrechnung?studiensemester='.urlencode($angezeigtes_stsem).'&lv_id='.urlencode($lvid)
);
@@ -491,6 +493,7 @@ if((!defined('CIS_LEHRVERANSTALTUNG_ANRECHNUNG_ANZEIGEN') || CIS_LEHRVERANSTALTU
'id'=>'core_menu_anerkennungNachgewiesenerKenntnisse_empfehlen',
'position'=>'128',
'name'=>$p->t('lehre/anrechnungen'),
'phrase'=>'lehre/anrechnung',
'icon'=>'../../../skin/images/button_listen.png',
'link' => APP_ROOT. 'index.ci.php/lehre/anrechnung/ReviewAnrechnungUebersicht?studiensemester='.urlencode($angezeigtes_stsem)
);
+140 -10
View File
@@ -3,13 +3,13 @@ import FhcApi from '../../plugin/FhcApi.js';
import Phrasen from '../../plugin/Phrasen.js';
import contrast from '../../directives/contrast.js';
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
import Stundenplan from "../../components/Cis/Stundenplan/Stundenplan.js";
import Stundenplan, {DEFAULT_MODE_STUNDENPLAN} from "../../components/Cis/Stundenplan/Stundenplan.js";
import MylvStudent from "../../components/Cis/Mylv/Student.js";
import Profil from "../../components/Cis/Profil/Profil.js";
import CmsNews from "../../components/Cis/Cms/News.js";
import CmsContent from "../../components/Cis/Cms/Content.js";
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
import RoomInformation from "../../components/Cis/Mylv/RoomInformation.js";
import RoomInformation, { DEFAULT_MODE_RAUMINFO } from "../../components/Cis/Mylv/RoomInformation.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -28,11 +28,65 @@ const router = VueRouter.createRouter({
component: Profil,
props: true
},
// Redirect old links to new format
{
path: `/CisVue/Cms/getRoomInformation/:ort_kurzbz`,
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
name: "RoomInformationOld",
component: RoomInformation,
redirect: (to) => {
return { // redirect to longer Rauminfo url and map params
name: "RoomInformation",
params: { // in this case always populate other params since they are not optional
ort_kurzbz: to.params.ort_kurzbz,
mode: DEFAULT_MODE_RAUMINFO,
focus_date: new Date().toISOString().split("T")[0]
},
};
},
},
{
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
name: 'RoomInformation',
component: RoomInformation,
props: true
props: (route) => { // validate and set mode/focus date if for some reason missing
const validModes = ["Month", "Week", "Day"];
// check mode string
const mode = route.params.mode &&
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
: DEFAULT_MODE_RAUMINFO;
// default to today date if not provided
const d = new Date(route.params.focus_date)
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
// existing viewData declaration written from codeigniter 3 into routerview tag
return {
propsViewData: {
mode,
focus_date,
ort_kurzbz: route.params.ort_kurzbz
}
};
},
beforeEnter: (to, from, next) => {
// missing mode or focus_date -> set defaults
if (!to.params.mode || !to.params.focus_date) {
next({
name: "RoomInformation",
params: {
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
ort_kurzbz: route.params.ort_kurzbz
}
});
} else {
next();
}
}
},
{
path: `/CisVue/Cms/Content/:content_id`,
@@ -58,10 +112,68 @@ const router = VueRouter.createRouter({
component: Info,
props: true
},
// Redirect old links to new format
{
path: `/Cis/Stundenplan/:lv_id?`,
// only trigger on first param being numeric to avoid paths like "Stundenplan/Month" entering here
path: "/Cis/Stundenplan/:lv_id(\\d+)",
name: "StundenplanOld",
component: Stundenplan,
redirect: (to) => {
return { // redirect to longer Stundenplan url and map params
name: "Stundenplan",
params: {
lv_id: to.params.lv_id
},
};
},
},
{
path: `/Cis/Stundenplan/:mode?/:focus_date?/:lv_id?`,
name: 'Stundenplan',
component: Stundenplan
component: Stundenplan,
props: (route) => { // validate and set mode/focus date if for some reason missing
const validModes = ["Month", "Week", "Day"];
// check mode string
const mode = route.params.mode &&
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
: DEFAULT_MODE_STUNDENPLAN;
// default to today date if not provided or string forms invalid date
const d = new Date(route.params.focus_date)
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
// existing viewData declaration written from codeigniter 3 into routerview tag
return {
propsViewData: {
mode,
focus_date,
lv_id: route.params.lv_id
}
};
},
beforeEnter: (to, from, next) => {
// missing mode or focus_date -> set defaults
if (!to.params.mode || !to.params.focus_date) {
next({
name: "Stundenplan",
params: {
mode: to.params.mode || DEFAULT_MODE_STUNDENPLAN,
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
lv_id: to.params.lv_id
}
});
} else {
next();
}
}
},
{
path: `/Cis4`,
name: 'Cis4',
component: FhcDashboard,
props: {dashboard: 'CIS'},
},
{
path: `/`,
@@ -70,14 +182,32 @@ const router = VueRouter.createRouter({
props: {dashboard: 'CIS'},
},
{
path: `/Cis4`,
name: 'Cis4',
path: '/:pathMatch(.*)*',
name: 'Fallback',
component: FhcDashboard,
props: {dashboard: 'CIS'},
}
redirect: () => {
return {
name: "Cis4",
params: {
dashboard: 'CIS'
},
};
},
},
]
})
router.beforeEach((to, from) => {
// this avoids redundant routing navigation in place due to router.replace on a route with param function and
// beforeEnter navigation guard
// TODO: manage the infinite forward navigation issue somehow
if (to.fullPath === from.fullPath) {
return false
}
})
const app = Vue.createApp({
name: 'FhcApp',
data: () => ({
@@ -111,7 +241,7 @@ const app = Vue.createApp({
// let click event propagate normally if we dont route internally
const res = this.$router.resolve(route)
if(!res?.matched?.length) return
if(!res?.matched?.length || res.name === 'Fallback') return
event.preventDefault(); // Prevent browser navigation
+15 -7
View File
@@ -62,6 +62,9 @@ export default {
noMonthView: Boolean
},
watch:{
mode(newVal) {
this.$emit('change:mode', newVal)
},
selectedEvent:{
handler(newSelectedEvent) {
this.$emit('selectedEvent', newSelectedEvent);
@@ -92,7 +95,8 @@ export default {
'select:event',
'change:range',
'update:minimized',
'selectedEvent'
'selectedEvent',
'change:offset'
],
data() {
return {
@@ -161,12 +165,12 @@ export default {
if (this.events && Array.isArray(this.events) && this.events.length > 0) {
let filteredEvents = this.events.filter(event => {
let eventDate = new CalendarDate(new Date(event.datum));
if (this.mode == 'week')
if (this.mode == 'week' || this.mode == 'Week')
{
// week view filters the elements only for the same week
return this.focusDate.w == eventDate.w;
}
else if (this.mode == 'day')
else if (this.mode == 'day' || this.mode == 'Day')
{
// day view filters the elements for the same day and the same week
return this.focusDate.d == eventDate.d && this.focusDate.w == eventDate.w;
@@ -187,17 +191,21 @@ export default {
},
},
methods: {
setMode(mode) {
this.mode = mode
},
handleInput(day) {
this.$emit(day[0], day[1]);
},
},
created() {
const allowedInitialModes = ['years'];
const initMode = this.initialMode.toLowerCase()
const allowedInitialModes = ['day'];
if (!this.noWeekView)
allowedInitialModes.push('week');
if (!this.noMonthView)
allowedInitialModes.push('month');
this.mode = allowedInitialModes[allowedInitialModes.indexOf(this.initialMode)] || allowedInitialModes.pop();
this.mode = allowedInitialModes[allowedInitialModes.indexOf(initMode)] || allowedInitialModes.pop();
this.date.set(new Date(this.initialDate));
this.focusDate.set(this.date);
},
@@ -227,14 +235,14 @@ export default {
}
}).observe(this.$refs.container);
}
},
unmounted(){
CalendarDates.cleanup();
},
template: /*html*/`
<div ref="container" class="fhc-calendar card h-100" :class="sizeClass">
<component :is="'calendar-' + mode" @updateMode="mode = $event" @change:range="$emit('change:range',$event)" @input="handleInput" >
<component :is="'calendar-' + mode" @updateMode="mode = $event" @change:range="$emit('change:range',$event)"
@input="handleInput" @change:offset="$emit('change:offset', $event)">
<template #calendarDownloads>
<slot name="calendarDownloads" ></slot>
</template>
+5 -3
View File
@@ -22,21 +22,23 @@ export default {
this.focusDate.d += dir;
this.emitRangeChanged(previousDate);
},
emitRangeChanged(previousDate) {
emitRangeChanged(previousDate, mounted) {
this.$emit('change:range', { start: previousDate, end:this.focusDate });
},
prev() {
this.$refs.pane.prev();
this.$emit('change:offset', { y: 0, m: 0, d: -1 });
},
next() {
this.$refs.pane.next();
this.$emit('change:offset', { y: 0, m: 0, d: 1 });
},
selectEvent(event) {
this.$emit('input', ['select:event', event]);
}
},
created() {
this.emitRangeChanged();
mounted() {
this.emitRangeChanged(new CalendarDate(this.focusDate.y, this.focusDate.m, this.focusDate.d -1), true);
},
template: /*html*/`
<div class="fhc-calendar-day">
+5 -3
View File
@@ -36,6 +36,7 @@ export default {
'calendarClientHeight',
'setSelectedEvent',
'selectedEvent',
'rowMinHeight'
],
props: {
year: Number,
@@ -398,7 +399,7 @@ export default {
<div >
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">Keine Lehrveranstaltungen</h1>
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">{{ $p.t('lehre/noLvFound') }}</h1>
<div class="events position-relative" :class="{'fhc-calendar-no-events-overlay':noEventsCondition}" ref="events" @mousemove="calcHourPosition" @mouseleave="hourPosition = null">
<Transition>
<div v-if="hourPosition && !noEventsCondition" class="position-absolute border-top small" :style="indicatorStyle">
@@ -412,12 +413,13 @@ export default {
</Transition>
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border" :style="hourGridStyle(hour)"></div>
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
<div v-for="hour in hours" :style="'min-height:' + rowMinHeight " :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="(day,dayindex) in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle(day)">
<div v-if="lookingAtToday && !noEventsCondition" :style="overlayStyle"></div>
<div v-for="event in day.events" :key="event" :style="eventGridStyle(day,event)" v-contrast :selected="event.orig == selectedEvent" class="fhc-entry mx-2 small rounded overflow-hidden " >
<div v-for="event in day.events" :key="event" :style="eventGridStyle(day,event)" v-contrast
:selected="event.orig == selectedEvent" class="fhc-entry mx-2 small rounded overflow-hidden" >
<!-- desktop version of the page template, parent receives slotProp mobile = false -->
<div class="d-none d-xl-block h-100 " @click.prevent="eventClick(event)">
<slot name="dayPage" :event="event.orig" :day="day" :mobile="false">
+4 -4
View File
@@ -3,9 +3,9 @@ export default {
return{
selected: this.mode,
modes:{
day: { mode_bezeichnung: "Tag", icon: "fa-calendar-day" , condition:true},
week: { mode_bezeichnung: "Woche", icon: "fa-calendar-week", condition: !this.noWeekView },
month: { mode_bezeichnung: "Monat", icon: "fa-calendar-days", condition: !this.noMonthView },
day: { mode_bezeichnung: "day", icon: "fa-calendar-day" , condition:true},
week: { mode_bezeichnung: "week", icon: "fa-calendar-week", condition: !this.noWeekView },
month: { mode_bezeichnung: "month", icon: "fa-calendar-days", condition: !this.noMonthView },
},
headerPadding:null,
}
@@ -62,7 +62,7 @@ export default {
<div ref="viewButtons" v-if="!noWeekView && !noMonthView" :class="getHeaderClassSide" class="d-flex justify-content-center justify-content-md-end align-items-center" style="pointer-events: none;">
<div style="pointer-events: all;">
<div role="group" aria-label="Kalender Modus">
<button type="button" :class="{'active':mode_kurzbz === mode}" style="margin-right: 4px;" @click.prevent="$emit('updateMode',mode_kurzbz)" class="btn btn-outline-secondary" v-for="({mode_bezeichnung,icon,condition},mode_kurzbz) in modes">
<button type="button" :class="{'active':mode_kurzbz.toLowerCase() === mode.toLowerCase()}" style="margin-right: 4px;" @click.prevent="$emit('updateMode',mode_kurzbz)" class="btn btn-outline-secondary" v-for="({mode_bezeichnung,icon,condition},mode_kurzbz) in modes">
<i v-if="condition" class="fa" :class="icon" ></i>
</button>
</div>
+11 -7
View File
@@ -28,16 +28,23 @@ export default {
} else {
this.focusDate.moveMonthInDirection(dir)
}
this.emitRangeChanged()
},
emitRangeChanged(mounted = false) {
this.$emit('change:range', {
start: new Date(this.focusDate.y, this.focusDate.m, 1),
end: new Date(this.focusDate.y, this.focusDate.m+1, 0)
start: new Date(this.focusDate.y, this.focusDate.m, 1),
end: new Date(this.focusDate.y, this.focusDate.m+1, 0),
mounted
});
},
prev() {
this.$refs.pane.prev();
this.$emit('change:offset', { y: 0, m: -1, d: 0 });
},
next() {
this.$refs.pane.next();
this.$emit('change:offset', { y: 0, m: 1, d: 0 });
},
selectDay(day) {
let m = day.getMonth();
@@ -53,11 +60,8 @@ export default {
this.$emit('input', ['select:day',day])
}
},
created() {
this.$emit('change:range', {
start: new Date(this.focusDate.y, this.focusDate.m, 1),
end: new Date(this.focusDate.y, this.focusDate.m+1, 0)
});
mounted() {
this.emitRangeChanged(true)
},
template: `
<div class="fhc-calendar-month">
+8 -3
View File
@@ -63,6 +63,10 @@ export default {
},
methods: {
handleEventClick(e, day) {
this.selectDay(day)
e.stopPropagation()
},
getDayClass(week, day) {
let classstring = 'fhc-calendar-month-page-day text-decoration-none overflow-hidden'
const isHighlightedWeek = this.isHighlightedWeek(week)
@@ -150,10 +154,11 @@ export default {
:key="day"
:class="getDayClass(week, day)"
>
<span class="no" :style="getNumberStyle(day)">{{day.getDate()}}</span>
<span @click="clickEvent(day,week)" class="no" :style="getNumberStyle(day)">{{day.getDate()}}</span>
<span v-if="events[day.toDateString()] && events[day.toDateString()].length" class="events">
<div @click="setSelectedEvent(event);" v-for="event in events[day.toDateString()]" :key="event.id"
:style="{'background-color': event.color}" class="fhc-entry" :selected="event == selectedEvent" v-contrast >
<div v-for="event in events[day.toDateString()]" :key="event.id"
:style="{'background-color': event.color}" class="fhc-entry" :selected="event == selectedEvent"
v-contrast @click="handleEventClick($event, day)">
<slot name="monthPage" :event="event" :day="day" >
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
+6 -4
View File
@@ -20,23 +20,25 @@ export default {
this.focusDate.d += dir * 7;
this.emitRangeChanged();
},
emitRangeChanged() {
emitRangeChanged(mounted = false) {
let start = this.focusDate.firstDayOfWeek;
let end = this.focusDate.lastDayOfWeek;
this.$emit('change:range', { start, end });
this.$emit('change:range', { start, end, mounted });
},
prev() {
this.$refs.pane.prev();
this.$emit('change:offset', { y: 0, m: 0, d: -7 });
},
next() {
this.$refs.pane.next();
this.$emit('change:offset', { y: 0, m: 0, d: 7 });
},
selectEvent(event) {
this.$emit('input', ['select:event',event]);
}
},
created() {
this.emitRangeChanged();
mounted() {
this.emitRangeChanged(true);
},
template: /*html*/`
<div class="fhc-calendar-week">
+10 -3
View File
@@ -23,7 +23,8 @@ export default {
'noMonthView',
'isSliding',
'selectedEvent',
'setSelectedEvent'
'setSelectedEvent',
'rowMinHeight'
],
props: {
year: Number,
@@ -49,7 +50,7 @@ export default {
},
getGridStyle() {
return {
'min-height': '100px',
'min-height': this.rowMinHeight,
// this.size is the magic number anyway which directs font-size,
// which in turn influences a lot of layout
width: '42px'
@@ -299,7 +300,13 @@ export default {
this.$emit('updateMode', 'month');
}
},
changeToDay(day) {
this.date.set(day);
this.focusDate.set(day);
this.$emit('updateMode', 'day');
},
dateToMinutesOfDay(day) {
// subtract 7 from the total hours because the hours range from 7 to 24
return Math.floor(((day.getHours()-7) * 60 + day.getMinutes()) / this.smallestTimeFrame) + 1;
},
weekPageClick(event, day) {
@@ -346,7 +353,7 @@ export default {
<div ref="page" class="fhc-calendar-week-page" style="min-width: 700px;">
<div class="d-flex flex-column">
<div class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="pageHeaderStyle" >
<div type="button" v-for="day in days" :key="day" class="flex-grow-1" :title="dayText[day]?.heading" @click.prevent="changeToMonth(day)">
<div type="button" v-for="day in days" :key="day" class="flex-grow-1" :title="dayText[day]?.heading" @click.prevent="changeToDay(day)">
<div class="fw-bold">{{dayText[day]?.tag}}</div>
<a href="#" class="small text-secondary text-decoration-none" >{{dayText[day]?.datum}}</a>
</div>
+1 -3
View File
@@ -8,8 +8,6 @@ export default {
'size',
'focusDate'
],
data() {
},
computed: {
weeks(){
return [...Array(this.focusDate.numWeeks).keys()].map(i => i + 1);
@@ -34,7 +32,7 @@ export default {
},
},
template: `
<div class="fhc-calendar-weeks">
<div class="fhc-calendar-weeks h-100">
<calendar-header :title="title" @prev="prev" @next="next" @click="$emit('updateMode', 'years')" @updateMode="$emit('updateMode', $event)" />
<div class="d-flex flex-wrap">
<div v-for="(week, key) in weeks" :key="key" class="d-grid col-2">
+2
View File
@@ -41,9 +41,11 @@ export default {
},
prev() {
this.$refs.pane.prev();
this.$emit('change:offset', { y: -1, m: 0, d: 0 });
},
next() {
this.$refs.pane.next();
this.$emit('change:offset', { y: 1, m: 0, d: 0 });
}
},
created() {
@@ -18,6 +18,13 @@ export default {
return [...Array(this.end - this.start).keys()].map(i => i + this.start);
}
},
mounted() {
const container = document.getElementById("calendarContainer")
if(container) {
container.style['overflow-y'] = 'scroll'
container.style['overflow-x'] = 'auto'
}
},
template: `
<div class="fhc-calendar-years-page d-flex flex-wrap">
<div v-for="year in years" :key="year" class="d-grid col-4">
+2 -2
View File
@@ -56,11 +56,11 @@ export default {
},
},
template:/*html*/`
<div v-if="!menu">No Menu available</div>
<div v-if="!menu">{{$p.t('lehre','lehrveranstaltungsUnavailable')}}</div>
<div id="cis-menu" v-else>
<div class="container" :class="containerStyles">
<div class="row g-2 justify-content-center" :class="rowStyles">
<div style="min-height:150px; min-width:150px;" class="col-12 col-lg-6 col-xl-4" v-for="(menuItem, index) in menu" :key="index">
<div style="min-height:150px; min-width:150px;" class="col-12 col-lg-6 col-xl-4" v-for="(menuItem, index) in menu" :key="index">
<a :id="menuItem.name" :class="{'dropdown-toggle':menuItem.c4_moodle_links?.length }" role="button" :href="c4_link(menuItem)"
:disabled="c4_disabled(menuItem)" :data-bs-toggle="menuItem.c4_moodle_links?.length?'dropdown':null"
class="menu-entry p-2 w-100 text-wrap border border-1 rounded-3 d-flex flex-column align-items-center justify-content-center text-center text-decoration-none link-dark h-100">
+3 -3
View File
@@ -68,7 +68,7 @@ export default {
return BsModal.popup.bind(this)(null, options);
},
template: /*html*/ `
<bs-modal ref="modalContainer" @showBsModal="onModalShow" @hideBsModal="onModalHide" v-bind="$props" :bodyClass="''" dialogClass='modal-lg' class="bootstrap-alert" backdrop="false" >
<bs-modal ref="modalContainer" @showBsModal="onModalShow" @hideBsModal="onModalHide" v-bind="$props" :bodyClass="''" dialogClass='modal-lg' class="bootstrap-alert" :backdrop="false" >
<template v-slot:title>
<template v-if="event?.type=='moodle'">{{event.titel}}</template>
<template v-else-if="event.titel">{{ event.titel + ' - ' + event.lehrfach_bez + ' [' + event.ort_kurzbz+']'}}</template>
@@ -85,7 +85,7 @@ export default {
</h3>
<lv-info :event="event"></lv-info>
<template v-if="showMenu && this.menu">
<h3 >Lehrveranstaltungs Menu</h3>
<h3>{{$p.t('lehre','lehrveranstaltungsmenue')}}</h3>
<lv-menu :menu="menu"></lv-menu>
</template>
</template>
@@ -95,4 +95,4 @@ export default {
</template>
<!-- end of optional footer -->
</bs-modal>`,
};
};
+108 -17
View File
@@ -3,25 +3,41 @@ import CalendarDate from "../../../composables/CalendarDate.js";
import LvModal from "../../../components/Cis/Mylv/LvModal.js";
import LvInfo from "../../../components/Cis/Mylv/LvInfo.js"
export default{
export const DEFAULT_MODE_RAUMINFO = 'Week'
const RoomInformation = {
name: "RoomInformation",
props:{
ort_kurzbz: {
type: String,
required: true,
}
propsViewData: {
type: Object
},
rowMinHeight: {
type: String,
default: '100px'
},
eventMaxHeight: {
type: String,
default: '125px'
}
},
components: {
FhcCalendar,
LvModal,
LvInfo,
},
provide() {
return {
rowMinHeight: this.rowMinHeight,
eventMaxHeight: this.eventMaxHeight
}
},
data() {
return {
events: null,
calendarMode: DEFAULT_MODE_RAUMINFO,
calendarDate: new CalendarDate(new Date()),
currentlySelectedEvent: null,
currentDay: new Date(),
currentDay: this.propsViewData?.focus_date ? new Date(this.propsViewData.focus_date) : new Date(),
minimized: false,
}
@@ -43,6 +59,17 @@ export default{
return this.calendarDateToString(this.calendarDate.cdLastDayOfCalendarMonth);
},
},
watch: {
'propsViewData.ort_kurzbz'(newVal) {
// relevant if ort_kurzbz can be changed from within this component
},
'propsViewData.mode'(newVal) {
if(this.$refs.calendar) this.$refs.calendar.setMode(newVal)
},
'propsViewData.focus_date'(newVal) {
this.currentDate = new Date(newVal)
}
},
methods:{
setSelectedEvent: function(event){
this.currentlySelectedEvent = event;
@@ -50,9 +77,59 @@ export default{
getLvID: function () {
this.lv_id = window.location.pathname
},
selectDay: function (day) {
selectDay: function(day){
const date = day.getFullYear() + "-" +
String(day.getMonth() + 1).padStart(2, "0") + "-" +
String(day.getDate()).padStart(2, "0");
this.$router.push({
name: "RoomInformation",
params: {
mode: this.calendarMode,
focus_date: date,
ort_kurzbz: this.propsViewData.ort_kurzbz
}
})
this.currentDay = day;
},
handleOffset: function(offset) {
this.currentDay = new Date(
this.currentDay.getFullYear() + offset.y,
this.currentDay.getMonth() + offset.m,
this.currentDay.getDate() + offset.d
)
const date = this.currentDay.getFullYear() + "-" +
String(this.currentDay.getMonth() + 1).padStart(2, "0") + "-" +
String(this.currentDay.getDate()).padStart(2, "0");
this.$router.push({
name: "Stundenplan",
params: {
mode: this.calendarMode,
focus_date: date,
lv_id: this.propsViewData?.lv_id || null
}
})
},
handleChangeMode(mode) {
const modeCapitalized = mode.charAt(0).toUpperCase() + mode.slice(1)
const date = this.currentDay.getFullYear() + "-" +
String(this.currentDay.getMonth() + 1).padStart(2, "0") + "-" +
String(this.currentDay.getDate()).padStart(2, "0");
this.$router.push({
name: "RoomInformation",
params: {
mode: modeCapitalized,
focus_date: date,
ort_kurzbz: this.propsViewData.ort_kurzbz
}
})
this.calendarMode = mode
},
showModal: function (event) {
this.currentlySelectedEvent = event;
Vue.nextTick(() => {
@@ -86,8 +163,8 @@ export default{
// bundles the room_events and the reservierungen together into the this.events array
Promise.allSettled([
this.$fhcApi.factory.stundenplan.getRoomInfo(this.ort_kurzbz, this.monthFirstDay, this.monthLastDay),
this.$fhcApi.factory.stundenplan.getOrtReservierungen(this.ort_kurzbz, this.monthFirstDay, this.monthLastDay)
this.$fhcApi.factory.stundenplan.getRoomInfo(this.propsViewData.ort_kurzbz, this.monthFirstDay, this.monthLastDay),
this.$fhcApi.factory.stundenplan.getOrtReservierungen(this.propsViewData.ort_kurzbz, this.monthFirstDay, this.monthLastDay)
]).then((result) => {
let promise_events = [];
result.forEach((promise_result) => {
@@ -120,10 +197,22 @@ export default{
this.loadEvents();
},
template: /*html*/`
<h2>Room Information {{ ort_kurzbz }}</h2>
<h2>{{ $p.t('rauminfo/rauminfo') }} {{ propsViewData.ort_kurzbz }}</h2>
<hr>
<lv-modal v-if="currentlySelectedEvent" :showMenu="false" :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">
<fhc-calendar
ref="calendar"
@selectedEvent="setSelectedEvent"
:initial-date="currentDay"
@change:range="updateRange"
@change:offset="handleOffset"
:events="events"
:initial-mode="propsViewData.mode"
show-weeks
@select:day="selectDay"
@change:mode="handleChangeMode"
v-model:minimized="minimized"
>
<template #monthPage="{event,day}">
<span >
{{event.topic}}
@@ -139,15 +228,15 @@ export default{
<template #dayPage="{event,day,mobile}">
<div @click="mobile? showModal(event?.orig):null" type="button" class="fhc-entry border border-secondary border row h-100 justify-content-center align-items-center text-center">
<div class="col ">
<p>Lehrveranstaltung:</p>
<p>{{ $p.t('lehre/lehrveranstaltung') }}:</p>
<p class="m-0">{{event?.orig.topic}}</p>
</div>
<div class="col ">
<p>Lektor:</p>
<p>{{ $p.t('lehre/lektor') }}:</p>
<p class="m-0" v-for="lektor in event?.orig.lektor">{{lektor.kurzbz}}</p>
</div>
<div class="col ">
<p>Ort: </p>
<p>{{ $p.t('profil/Ort') }}: </p>
<p class="m-0">{{event?.orig.ort_kurzbz}}</p>
</div>
</div>
@@ -155,12 +244,14 @@ export default{
<template #pageMobilContent>
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
<div class="w-100">
<lv-info :event="currentlySelectedEvent" />
<lv-info :event="currentlySelectedEvent" />
</div>
</template>
<template #pageMobilContentEmpty >
<h3>Keine Raum Reservierung</h3>
<h3>{{$p.t('rauminfo','keineRaumReservierung')}}</h3>
</template>
</fhc-calendar>
`,
};
};
export default RoomInformation
+2 -1
View File
@@ -22,7 +22,8 @@ export default {
lv.studiengang_kz + '#' + lv.semester,
{
studiengang_kz: lv.studiengang_kz,
bezeichnung: lv.sg_bezeichnung,
bezeichnung: lv.sg_bezeichnung,
sg_bezeichnung_eng: lv.sg_bezeichnung_eng,
kuerzel: lv.studiengang_kuerzel,
semester: lv.semester
}
@@ -12,7 +12,8 @@ export default {
bezeichnung: String,
kuerzel: String,
semester: [String,Number],
lvs: Array
lvs: Array,
sg_bezeichnung_eng: String
},
computed: {
lehrveranstaltungen() {
@@ -32,7 +33,7 @@ export default {
},
template: `<div class="card mb-3">
<div class="card-body">
<h4 class="card-title mb-3">{{bezeichnung}} - {{kuerzel}}
<h4 class="card-title mb-3">{{$p.user_language.value === 'English' ? sg_bezeichnung_eng : bezeichnung}} - {{kuerzel}}
<small>{{semester}}.{{$p.t('lehre/semester')}}</small>
</h4>
<div class="row">
@@ -21,7 +21,9 @@ export default {
lvinfo: Boolean,
benotung: Boolean,
lvnote: String,
lvnotebez: Array,
znote: String,
znotebez: Array,
studiengang_kuerzel: String,
semester: [String, Number],
orgform_kurzbz: String,
@@ -29,7 +31,7 @@ export default {
ects: String,
incoming: Number,
positiv: Boolean,
note_index: String,
note_index: String
},
data: () => {
return {
@@ -66,7 +68,12 @@ export default {
return bodyStyle;
},
grade() {
return this.benotung ? this.znote || this.lvnote || null : null;
const languageIndex = this.$p.user_language.value === 'English' ? 1 : 0
if(this.benotung && this.znotebez?.length) {
return this.znotebez[languageIndex]
} else if(this.benotung && this.lvnotebez?.length) {
return this.lvnotebez[languageIndex]
} else return null
},
LvHasPruefungenInformation(){
return this.pruefungenData && this.pruefungenData.length > 0;
@@ -122,6 +129,7 @@ export default {
LvInfo.popup({
lehrveranstaltung_id: this.lehrveranstaltung_id,
bezeichnung: this.bezeichnung,
bezeichnung_eng: this.bezeichnung_eng,
studiengang_kuerzel: this.studiengang_kuerzel,
semester: this.semester,
studien_semester: this.studien_semester,
@@ -159,8 +167,8 @@ export default {
<div class="p-2" :class="is_organisatorische_einheit?'':'card-header'">
<!-- {{module}} if the module of the lv is important then query the module from the api endpoint for LV-->
<h6 class="fw-bold" v-if="is_organisatorische_einheit" >Organisatorische Einheit:</h6>
<h6 class="mb-0">{{bezeichnung}}</h6>
<h6 class="fw-bold" v-if="is_organisatorische_einheit" >{{ $p.t('lehre/organisationseinheit') }}:</h6>
<h6 class="mb-0">{{$p.user_language.value === 'English' ? bezeichnung_eng : bezeichnung}}</h6>
</div>
<div v-if="!emptyMenu" class="card-body " :style="bodyStyle">
<template v-if="menu">
@@ -176,7 +184,7 @@ export default {
:class="{'link-dark':menuItem.c4_link, 'unavailable':!menuItem.c4_link, 'dropdown-toggle':menuItem.c4_moodle_links?.length }"
:target="menuItem.c4_target"
:href="c4_link(menuItem) ? c4_link(menuItem) : null">
{{menuItem.name}}
{{ menuItem.phrase ? $p.t(menuItem.phrase) : menuItem.name}}
</a>
</div>
<ul v-if="menuItem.c4_moodle_links?.length" class="dropdown-menu p-0" :aria-labelledby="'moodle_links_'+lehrveranstaltung_id">
@@ -197,14 +205,14 @@ export default {
<template v-if="LvHasPruefungenInformation">
<a href="#" class="col-auto text-start text-decoration-none" @click.prevent="openPruefungen">
<i class="fa fa-check text-success" v-if="positiv"></i>
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || p.t('lehre/noGrades') }}</span>
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || $p.t('lehre/noGrades') }}</span>
</a>
</template>
<!-- template for the LV with no pruefungen -->
<template v-else>
<span class="col-auto text-start text-decoration-none" >
<i class="fa fa-check text-success" v-if="positiv"></i>
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || p.t('lehre/noGrades') }}</span>
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || $p.t('lehre/noGrades') }}</span>
</span>
</template>
</div>
+2 -2
View File
@@ -103,7 +103,7 @@ export default {
<button class="btn btn-outline-secondary" type="button" :disabled="currentIsFirst" @click="prevSem">
<i class="fa fa-caret-left" aria-hidden="true"></i>
</button>
<select ref="studiensemester" v-model="currentSemester" class="form-select" :aria-label="p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<select ref="studiensemester" v-model="currentSemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<option v-for="semester in studiensemester" :key="semester.studiensemester_kurzbz">{{semester.studiensemester_kurzbz}}</option>
</select>
<button class="btn btn-outline-secondary" type="button" :disabled="currentIsLast" @click="nextSem">
@@ -113,7 +113,7 @@ export default {
</div>
</div>
<div class="alert alert-danger" role="alert" v-else>
{{p.t('lehre/noLvFound')}}
{{$p.t('lehre/noLvFound')}}
</div>
<mylv-semester v-bind="current"/>
</div>
@@ -181,7 +181,7 @@ export default {
return BsModal.popup(null, options);
},
template: /*html*/ `
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" backdrop="false">
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" :backdrop="false">
<template v-if="title" v-slot:title>{{title}}</template>
<template v-slot:default>
<div>
@@ -108,7 +108,7 @@ export default {
},
template: /*html*/ `
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" backdrop="false" >
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" :backdrop="false" >
<template v-slot:title>
{{title}}
@@ -5,25 +5,43 @@ import LvInfo from "../Mylv/LvInfo.js"
import LvMenu from "../Mylv/LvMenu.js"
import moodleSvg from "../../../helpers/moodleSVG.js"
export const Stundenplan = {
export const DEFAULT_MODE_STUNDENPLAN = 'Week'
const Stundenplan = {
name: 'Stundenplan',
data() {
return {
events: null,
calendarMode: this.propsViewData?.mode ?? DEFAULT_MODE_STUNDENPLAN,
calendarDate: new CalendarDate(new Date()),
eventCalendarDate: new CalendarDate(new Date()),
currentlySelectedEvent: null,
currentDay: new Date(),
currentDay: this.propsViewData?.focus_date ? new Date(this.propsViewData.focus_date) : new Date(),
lv: null,
minimized: false,
studiensemester_kurzbz:null,
studiensemester_start:null,
studiensemester_ende:null,
uid:null,
studiensemester_kurzbz: null,
studiensemester_start: null,
studiensemester_ende: null,
uid: null
}
},
props: [
"viewData",
],
props: {
propsViewData: Object,
rowMinHeight: {
type: String,
default: '100px'
},
eventMaxHeight: {
type: String,
default: '125px'
}
},
provide() {
return {
rowMinHeight: this.rowMinHeight,
eventMaxHeight: this.eventMaxHeight
}
},
watch: {
weekFirstDay: {
handler: async function (newValue) {
@@ -34,6 +52,17 @@ export const Stundenplan = {
this.studiensemester_ende = ende;
},
immediate: true,
},
// forward/backward on history entries happening in stundenplan
'propsViewData.lv_id'(newVal) {
// relevant if lv_id can be changed from within this component
},
'propsViewData.mode'(newVal) {
if(this.$refs.calendar) this.$refs.calendar.setMode(newVal)
},
'propsViewData.focus_date'(newVal) {
// todo: navigate around with date in current mode
this.currentDate = new Date(newVal)
}
},
components: {
@@ -47,13 +76,17 @@ export const Stundenplan = {
let ende = new Date(this.studiensemester_ende);
ende = Math.floor(ende.getTime() / 1000);
let download_link = (format, version = "", target = "") => `${FHC_JS_DATA_STORAGE_OBJECT.app_root}cis/private/lvplan/stpl_kalender.php?type=student&pers_uid=${this.uid}&begin=${start}&ende=${ende}&format=${format}${version ? '&version=' + version : ''}${target ? '&target=' + target : ''}`;
return [{ title: "excel", icon: 'fa-solid fa-file-excel', link: download_link('excel') }, { title: "csv", icon: 'fa-solid fa-file-csv', link: download_link('csv') }, { title: "ical1", icon: 'fa-regular fa-calendar', link: download_link('ical', '1', 'ical') }, { title: "ical2", icon: 'fa-regular fa-calendar', link: download_link('ical', '2', 'ical') }];
},
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
let download_link =
(format, version = "", target = "") =>
`${FHC_JS_DATA_STORAGE_OBJECT.app_root}cis/private/lvplan/stpl_kalender.php?type=student&pers_uid=
${this.uid}&begin=${start}&ende=${ende}&format=${format}
${version ? '&version=' + version : ''}${target ? '&target=' + target : ''}`;
return [
{ title: "excel", icon: 'fa-solid fa-file-excel', link: download_link('excel') },
{ title: "csv", icon: 'fa-solid fa-file-csv', link: download_link('csv') },
{ title: "ical1", icon: 'fa-regular fa-calendar', link: download_link('ical', '1', 'ical') },
{ title: "ical2", icon: 'fa-regular fa-calendar', link: download_link('ical', '2', 'ical') }
];
},
weekFirstDay: function () {
return this.calendarDateToString(this.calendarDate.cdFirstDayOfWeek);
@@ -67,7 +100,6 @@ export const Stundenplan = {
monthLastDay: function () {
return this.calendarDateToString(this.eventCalendarDate.cdLastDayOfCalendarMonth);
},
},
methods:{
fetchStudiensemesterDetails: async function (date) {
@@ -85,23 +117,88 @@ export const Stundenplan = {
this.currentlySelectedEvent = event;
},
selectDay: function(day){
const date = day.getFullYear() + "-" +
String(day.getMonth() + 1).padStart(2, "0") + "-" +
String(day.getDate()).padStart(2, "0");
const capitalizedMode = this.calendarMode[0].toUpperCase() + this.calendarMode.slice(1);
const isMonthMode = capitalizedMode === 'Month'
const isInCurrentMonth = day.getMonth() == this.currentDay.getMonth()
if(isMonthMode && isInCurrentMonth) {
this.$router.replace({
name: "Stundenplan",
params: {
mode: capitalizedMode,
focus_date: date,
lv_id: this.propsViewData?.lv_id || null
}
})
} else {
this.$router.push({
name: "Stundenplan",
params: {
mode: capitalizedMode,
focus_date: date,
lv_id: this.propsViewData?.lv_id || null
}
})
}
this.currentDay = day;
},
handleOffset: function(offset) {
this.currentDay = new Date(
this.currentDay.getFullYear() + offset.y,
this.currentDay.getMonth() + offset.m,
this.currentDay.getDate() + offset.d
)
const date = this.currentDay.getFullYear() + "-" +
String(this.currentDay.getMonth() + 1).padStart(2, "0") + "-" +
String(this.currentDay.getDate()).padStart(2, "0");
this.$router.push({
name: "Stundenplan",
params: {
mode: this.calendarMode[0].toUpperCase() + this.calendarMode.slice(1),
focus_date: date,
lv_id: this.propsViewData?.lv_id || null
}
})
},
handleChangeMode(mode) {
let m = mode[0].toUpperCase() + mode.slice(1)
if(m === this.calendarMode) return
const date = this.currentDay.getFullYear() + "-" +
String(this.currentDay.getMonth() + 1).padStart(2, "0") + "-" +
String(this.currentDay.getDate()).padStart(2, "0");
this.$router.push({
name: "Stundenplan",
params: {
mode: m,
focus_date: date,
lv_id: this.propsViewData?.lv_id ?? null
}
})
this.calendarMode = m
},
showModal: function(event){
this.currentlySelectedEvent = event;
Vue.nextTick(() => {
this.$refs.lvmodal.show();
});
},
updateRange: function ({start,end}) {
updateRange: function ({start,end, mounted}) {
let checkDate = (date) => {
return date.m != this.eventCalendarDate.m || date.y != this.eventCalendarDate.y;
}
this.calendarDate = new CalendarDate(end);
// only load month data if the month or year has changed
if (checkDate(new CalendarDate(start)) && checkDate(new CalendarDate(end))){
// or we receive a reload flag from the mounted routine of the components
// or this handler is being called from the mounted lifecycle of a component
if (mounted || (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.eventCalendarDate = new CalendarDate(end);
@@ -118,14 +215,16 @@ export const Stundenplan = {
},
loadEvents: function(){
Promise.allSettled([
this.$fhcApi.factory.stundenplan.getStundenplan(this.monthFirstDay, this.monthLastDay, this.viewData.lv_id),
this.$fhcApi.factory.stundenplan.getStundenplan(this.monthFirstDay, this.monthLastDay, this.propsViewData.lv_id),
this.$fhcApi.factory.stundenplan.getStundenplanReservierungen(this.monthFirstDay, this.monthLastDay),
this.loadMoodleEvents(this.monthFirstDay, this.monthLastDay)
]).then((result) => {
let promise_events = [];
result.forEach((promise_result) => {
if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") {
if(promise_result.value.meta?.lv) this.lv = promise_result.value.meta.lv
let data = promise_result.value.data;
// adding additional information to the events
if (data && data.forEach) {
@@ -213,16 +312,32 @@ export const Stundenplan = {
.then(data=>{
this.uid = data.uid;
})
this.loadEvents();
},
beforeUnmount() {
if(this.$refs.lvmodal) this.$refs.lvmodal.hide()
},
template:/*html*/`
<h2>{{$p.t('lehre/stundenplan')}}</h2>
<h2>
{{$p.t('lehre/stundenplan')}}
<span style="padding-left: 0.4em;" v-show="studiensemester_kurzbz">{{studiensemester_kurzbz}}</span>
<span style="padding-left: 0.5em;" v-show="propsViewData?.lv_id && lv"> {{ $p.user_language.value === 'German' ? lv?.bezeichnung : lv?.bezeichnung_english}}</span>
</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">
<fhc-calendar
ref="calendar"
@selectedEvent="setSelectedEvent"
:initial-date="currentDay"
@change:range="updateRange"
@change:offset="handleOffset"
:events="events"
:initial-mode="propsViewData.mode"
show-weeks
@select:day="selectDay"
@change:mode="handleChangeMode"
v-model:minimized="minimized"
>
<template #calendarDownloads>
<div v-for="{title,icon,link} in downloadLinks">
<a :href="link" :title="title" class="py-1 px-2 m-1 btn btn-outline-secondary">
@@ -240,7 +355,7 @@ export const Stundenplan = {
<span class="flex-grow-1 text-center ">{{event.topic}}</span>
</div>
</div>
<div v-else class="p-1">
<div v-else @click="showModal($event, event)" class="p-1">
<span>{{event.topic}}</span>
</div>
</template>
@@ -282,17 +397,17 @@ export const Stundenplan = {
<span class="small">{{convertTime(event.ende.split(":"))}}</span>
</div>
</div>
<div class="col ">
<p>Lehrveranstaltung:</p>
<p class="m-0">{{event?.topic}}</p>
<div class="col">
<p>{{ $p.t('lehre/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?.lektor">{{lektor.kurzbz}}</p>
<div class="col" :style="'max-height: ' + eventMaxHeight + '; overflow: auto;'">
<p>{{ $p.t('lehre/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?.ort_kurzbz}}</p>
<div class="col">
<p>{{ $p.t('profil/Ort') }}: </p>
<p class="m-0">{{event?.orig.ort_kurzbz}}</p>
</div>
</div>
</template>
@@ -304,12 +419,14 @@ export const Stundenplan = {
<lv-info :event="event" />
</div>
<template v-if="event.type != 'moodle'">
<h3 >Lehrveranstaltungs Menu</h3>
<h3 >{{$p.t('lehre','lehrveranstaltungsmenue')}}</h3>
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-show="lvMenu" :menu="lvMenu" />
</template>
</template>
</fhc-calendar>
`
<template #pageMobilContentEmpty >
<h3>{{ $p.t('lehre/noLvFound') }}</h3>
</template>
</fhc-calendar>`
}
export default Stundenplan
@@ -265,11 +265,11 @@ export default {
</div>
<div v-if="!value.length" class="list-group-item small text-center">
{{ p.t('lehre/noLvFound') }}
{{ $p.t('lehre/noLvFound') }}
</div>
</template>
<div v-else class="d-flex h-100 justify-content-center align-items-center fst-italic text-center">
{{ p.t('lehre/noLvFound') }}
{{ $p.t('lehre/noLvFound') }}
</div>
</div>
</template>
+3 -3
View File
@@ -159,11 +159,11 @@ export default {
template: /*html*/ `
<div class="widgets-url w-100 h-100 overflow-scroll" style="padding: 1rem 1rem;">
<div class="d-flex flex-column justify-content-between">
<!-- todo: widgetTag ?? -->
<button v-if="editModeIsActive" class="btn btn-outline-secondary btn-sm w-100 mt-2" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
<template v-if="shared">
<template v-if="!emptyBookmarks">
<button v-if="editModeIsActive" class="btn btn-outline-secondary btn-sm w-100 mt-2" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
<div v-for="link in shared" :key="link.id" class="d-flex mt-2">
<a target="_blank" :href="link.url">
<i class="fa fa-solid fa-arrow-up-right-from-square me-1"></i>{{ link.title }}
@@ -208,7 +208,7 @@ export default {
<form-input id="editUrl" v-model="url_input" name="url"></form-input>
</template>
<template #footer>
<button @click="editBookmark" class="btn btn-primary">{{$p.t('bookmark','editLink')}}</button>
<button @click="editBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
</template>
</bs-modal>
</core-form>
+3 -3
View File
@@ -71,9 +71,9 @@ class CalendarDate {
}
get wYear() {
if( this.w === 1 ) {
return this.cdLastDayOfWeek.format({ year: 'numeric' });
return this.cdLastDayOfWeek.y;
}
return this.cdFirstDayOfWeek.format({ year: 'numeric' });
return this.cdFirstDayOfWeek.y;
}
get wd() {
if (this._wd === null) {
@@ -271,4 +271,4 @@ CalendarDate.getWeekStart = function(locale) {
}
}
export default CalendarDate
export default CalendarDate
+80
View File
@@ -20184,6 +20184,46 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'rauminfo',
'phrase' => 'rauminfo',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Raum Informationen",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "Room Information",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'rauminfo',
'phrase' => 'keineRaumReservierung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Keine Raum Reservierungen gefunden",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "No Room Reservations found",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
@@ -37257,6 +37297,46 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'lehrveranstaltungsmenue',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lehrveranstaltungsmenü',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Course Menu',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'lehrveranstaltungsmenueUnavailable',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kein Lehrveranstaltungsmenü verfügbar',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No Course Menu available',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',