Merge remote-tracking branch 'origin/feature-78288/tempus_usability' into feature-68298/Tempus_Grundstruktur

# Conflicts:
#	public/css/components/horizontalsplit.css
#	public/js/components/Tempus/Filters/searchbarOptions.js
This commit is contained in:
ma0048
2026-09-01 12:48:07 +02:00
8 changed files with 124 additions and 18 deletions
+16 -3
View File
@@ -86,9 +86,18 @@ class KalenderLib
tbl_lehreinheitgruppe.verband as le_verband,
tbl_lehreinheitgruppe.gruppe as le_gruppe,
le_gruppe.direktinskription as le_direktinskription,
');
CASE
WHEN vonstunde.stunde IS NOT NULL AND bisstunde.stunde IS NOT NULL
THEN bisstunde.stunde - vonstunde.stunde + 1
ELSE ROUND(
COALESCE(
(
SELECT SUM(EXTRACT(EPOCH FROM (LEAST(stunde.ende, tbl_kalender.bis::time) - GREATEST(stunde.beginn, tbl_kalender.von::time))) / 60) / 45
FROM lehre.tbl_stunde stunde
WHERE stunde.beginn < tbl_kalender.bis::time AND stunde.ende > tbl_kalender.von::time)
,0)
,2
) END AS verplante_stunden');
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id', 'LEFT');
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event', 'tbl_kalender.kalender_id = tbl_kalender_event.kalender_id', 'LEFT');
@@ -134,6 +143,9 @@ class KalenderLib
);
$this->_ci->KalenderModel->addJoin('public.tbl_studiengang le_studiengang', 'le_lehrverband.studiengang_kz = le_studiengang.studiengang_kz', 'LEFT');
$this->_ci->KalenderModel->db->join('lehre.tbl_stunde vonstunde', 'tbl_kalender.von::time = vonstunde.beginn', 'LEFT', FALSE);
$this->_ci->KalenderModel->db->join('lehre.tbl_stunde bisstunde', 'tbl_kalender.bis::time = bisstunde.ende', 'LEFT', FALSE);
$this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date);
$this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date);
@@ -174,6 +186,7 @@ class KalenderLib
'lehrfach_bez' => isset($row->lehrfach_bezeichnung) ? $row->lehrfach_bezeichnung : '',
'farbe' => isset($row->farbe) ? $row->farbe : '',
'lehrveranstaltung_id' => $row->lehrveranstaltung_id,
'verplante_stunden' => $row->verplante_stunden,
'organisationseinheit' => isset($row->oe_kurzbz) ? $row->oe_kurzbz : '',
'kalender_id' => $id,
'lehreinheit_id' => [],
+2
View File
@@ -8,6 +8,8 @@
@import './components/Notiz.css';
@import './components/Messages.css';
@import './components/AppMenu.css';
@import './components/horizontalsplit.css';
html {
font-size: .875em;
+7
View File
@@ -68,9 +68,16 @@
/* Grid */
.fhc-calendar-base-grid {
--fhc-calendar-axis-collapsible: .2fr;
--fhc-calendar-axis-collapsible-manual: .1fr;
background-color: var(--fhc-calendar-bg-gap);
grid-gap: var(--fhc-calendar-gap-header, calc(var(--fhc-calendar-gap, 1px) * 2));
}
.fhc-calendar-base-grid-line-collapsed {
overflow: hidden;
}
.fhc-calendar-base-grid .grid-header {
background-color: var(--fhc-calendar-bg-header-gap, var(--fhc-calendar-bg-gap));
}
+28 -3
View File
@@ -61,7 +61,11 @@ export default {
flipAxis: Boolean,
allDayEvents: Boolean,
axisMainCollapsible: Boolean,
snapToGrid: Boolean
snapToGrid: Boolean,
dayVisibility: {
type: Array,
default: null
}
},
data() {
return {
@@ -160,7 +164,17 @@ export default {
hasValidEvents() {
return this.events.find(e => e.length);
},
styleGridCols() {
styleGridCols()
{
if (this.dayVisibility)
{
let anyVisible = this.dayVisibility.some(day => day);
if (anyVisible)
{
return this.axisMain.map((day, i) => this.isDayCollapsed(i) ? 'var(--fhc-calendar-axis-collapsible-manual, 0.1fr)' : '1fr').join(' ');
}
}
let cols = 'repeat(' + this.axisMain.length + ', 1fr)';
if (this.axisMainCollapsible) {
if (this.hasValidEvents)
@@ -479,6 +493,16 @@ export default {
}
});
},
isDayCollapsed(index)
{
if (this.dayVisibility)
{
let anyVisible = this.dayVisibility.some(day => day);
if (anyVisible)
return !this.dayVisibility[index];
}
return this.axisMainCollapsible && this.hasValidEvents && !this.events[index].length;
}
},
setup()
{
@@ -504,7 +528,7 @@ export default {
v-for="(date, index) in axisMain"
:key="index"
class="main-header"
:class="{'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length}"
:class="{'collapsed-header': isDayCollapsed(index)}"
:style="'grid-' + axisCol + ':' + (2+index)"
>
<slot name="main-header" v-bind="{ index, date }" />
@@ -592,6 +616,7 @@ export default {
:date="date"
:events="eventsNormal[index]"
:backgrounds="backgrounds[index]"
:class="{ 'fhc-calendar-base-grid-line-collapsed': isDayCollapsed(index) }"
style="position:relative"
@resize-start="handleResizeStart"
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
@@ -2,6 +2,8 @@ import CalendarGrid from '../../Base/Grid.js';
import LabelDay from '../../Base/Label/Day.js';
import LabelDow from '../../Base/Label/Dow.js';
import LabelTime from '../../Base/Label/Time.js';
import FormInput from "../../../Form/Input.js";
export default {
name: "WeekView",
@@ -9,7 +11,8 @@ export default {
CalendarGrid,
LabelDay,
LabelDow,
LabelTime
LabelTime,
FormInput
},
inject: {
timeGrid: "timeGrid",
@@ -23,6 +26,11 @@ export default {
},
collapseEmptyDays: Boolean
},
data() {
return {
visibleDays: JSON.parse(localStorage.getItem('fhc-calender-visible-days')) ?? [true, true, true, true, true, true, true]
}
},
computed: {
start() {
return this.day.startOf('week', { useLocaleWeeks: true });
@@ -44,11 +52,19 @@ export default {
const end = this.hoursPlan?.end ?? 23;
return Array.from({ length: end - start + 1 }, (e, i) => luxon.Duration.fromObject({ hours: i + start }));
}
},
dayVisibility() {
return this.axisMain.map(date => this.visibleDays[date.weekday -1])
}
},
methods: {
isToday(date) {
return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day');
},
toggleDay(day)
{
this.visibleDays.splice(day, 1, !this.visibleDays[day])
localStorage.setItem('fhc-calender-visible-days', JSON.stringify(this.visibleDays))
}
},
template: /* html */`
@@ -60,9 +76,10 @@ export default {
:axis-main-collapsible="collapseEmptyDays"
:snap-to-grid="!!timeGrid"
all-day-events
:day-visibility="dayVisibility"
>
<template #main-header="{ date }">
<div :class="{ today: isToday(date) }">
<template #main-header="{ date, index }">
<div :class="{ today: isToday(date) }" class="d-flex flex-column align-items-center">
<label-dow
v-bind="{ date }"
@cal-click="evt => evt.detail.source = 'day'"
@@ -70,6 +87,13 @@ export default {
<label-day
v-bind="{ date }"
/>
<form-input
type="checkbox"
:checked="visibleDays[date.weekday - 1]"
@change="toggleDay(date.weekday - 1)"
@click.stop
>
</form-input>
</div>
</template>
<template #part-header="{ part }">
@@ -51,6 +51,17 @@ export function getTempusSearchbarOptions(self)
self.addToCoursePicker(data);
}
},
{
label: "LVVerwaltung",
icon: "fas fa-up-right-from-square",
type: "function",
action: (data) => {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
"/LVVerwaltung/emp/" + self.selectedStudiensemester + "/" + data.uid;
window.open(link, "_blank");
}
},
]
},
}
@@ -15,6 +15,15 @@ export default {
return classes;
},
statusIcon()
{
if (['planning', 'sync_preview'].includes(this.event.status_kurzbz))
return 'fa-solid fa-pen-ruler text-muted';
else if (['preview', 'sync_live', 'to_delete_preview'].includes(this.event.status_kurzbz))
return 'fa-solid fa-chalkboard-user text-muted';
else if (['live', 'to_delete_live'].includes(this.event.status_kurzbz))
return 'fa-solid fa-user-graduate text-muted'
},
topicString() {
return Array.isArray(this.event.topic) ? this.event.topic.join(', ') : this.event.topic;
},
@@ -85,10 +94,12 @@ export default {
class="position-relative"
@wheel.stop
>
<div v-if="event.has_assigned_resources" class="position-absolute top-0 start-0 m-1" >
<i
class="fa-solid fa-table-list text-muted"
></i>
<div class="position-absolute top-0 start-0 m-1">
<i :class="statusIcon"></i>
<i class="fa-solid fa-table-list text-muted" v-if="event.has_assigned_resources"></i>
</div>
<div class="position-absolute bottom-0 start-0 m-1">
{{event.verplante_stunden}}
</div>
<div
v-if="!event.allDayEvent && event?.beginn && event?.ende"
+18 -5
View File
@@ -48,6 +48,7 @@ import MultiWeekPlanModal from "./MultiWeekPlanModal.js";
import { getTempusSearchbarOptions } from "./Filters/searchbarOptions.js";
import RaumauswahlModal from "./RaumauswahlModal.js";
import LehreinheitModal from "./LehreinheitModal.js";
import HorizontalSplit from "../horizontalsplit/horizontalsplit.js";
export default {
name: "Tempus",
@@ -72,7 +73,8 @@ export default {
KeyboardShortcuts,
MultiWeekPlanModal,
RaumauswahlModal,
LehreinheitModal
LehreinheitModal,
HorizontalSplit
},
props: {
defaultSemester: String,
@@ -996,6 +998,13 @@ export default {
this.resourcesAssignmentModal.areFormButtonsDisplayed = true;
},
sidebarCollapsed(newVal)
{
if(newVal)
this.$refs.hSplit.collapseLeft()
else
this.$refs.hSplit.showBoth()
},
},
mounted() {
this.reservierungPending = false;
@@ -1167,7 +1176,10 @@ export default {
<app-menu app-identifier="tempus" />
</div>
</aside>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100 d-flex flex-column">
<horizontal-split ref="hSplit" :defaultRatio="[15, 85]">
<template #left>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100 w-100">
<div class="sidebar-icons d-flex flex-row align-items-start py-2 gap-1 ps-2">
<button
class="btn btn-outline-secondary"
@@ -1257,9 +1269,9 @@ export default {
</div>
<stv-studiensemester v-model:studiensemester-kurzbz="selectedStudiensemester"></stv-studiensemester>
</nav>
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10">
</template>
<template #right>
<fhc-calendar
ref="calendar"
:timezone="config.timezone"
@@ -1279,7 +1291,8 @@ export default {
@update:range="handleRange"
class="responsive-calendar"
/>
</main>
</template>
</horizontal-split>
</div>
</div>
<app-config ref="config" v-model="appconfig" :endpoints="configEndpoints"></app-config>