From 6560b27de928f24d55535635d677f01f9ecbc667 Mon Sep 17 00:00:00 2001 From: Ivymaster Date: Mon, 11 May 2026 18:45:44 +0200 Subject: [PATCH] Add removal of multiple weeks per validation period, and other minor changes --- .../api/frontend/v1/ClassScheduleApi.php | 70 ++++++--- .../api/frontend/v1/ClassroomHourApi.php | 43 +++++ .../v1/organisation/Studiensemester.php | 9 +- .../controllers/lehre/ClassSchedule.php | 2 - public/css/components/classSchedule.css | 10 ++ public/js/api/factory/classSchedule.js | 13 +- public/js/api/factory/classroomHour.js | 25 +++ .../ClassScheduleCalendarSelector.js | 147 +++++++++--------- ...leOrgUnitGroupedValidityPeriodsOverview.js | 13 +- .../ClassSchedule/ClassScheduleOverview.js | 4 +- .../ClassSchedule/ClassScheduleTypeModal.js | 27 ++-- .../ClassScheduleValidityPeriodForm.js | 61 ++++---- .../ClassScheduleValidityPeriodModal.js | 62 ++++---- .../ClassScheduleValidityPeriodOverview.js | 145 +++++++++-------- .../ClassScheduleValidityPeriodPreview.js | 55 ++++--- ...031_unterrichtszeiten_der_studiengänge.php | 14 +- system/phrasesupdate.php | 24 ++- 17 files changed, 432 insertions(+), 292 deletions(-) create mode 100644 application/controllers/api/frontend/v1/ClassroomHourApi.php create mode 100644 public/js/api/factory/classroomHour.js diff --git a/application/controllers/api/frontend/v1/ClassScheduleApi.php b/application/controllers/api/frontend/v1/ClassScheduleApi.php index 3f6c3a8ae..dcfd6f50d 100644 --- a/application/controllers/api/frontend/v1/ClassScheduleApi.php +++ b/application/controllers/api/frontend/v1/ClassScheduleApi.php @@ -34,8 +34,8 @@ class ClassScheduleApi extends FHCAPI_Controller 'deleteClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'), 'getClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'), 'createClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'), - 'editClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'), - 'deleteClassTimeSlotsForValidityPeriodPerGroup' => array('lehre/unterrichtszeiten_gk:r'), + 'updateClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'), + 'deleteClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'), 'getAllClassScheduleTypes' => array('lehre/unterrichtszeiten_typ:r'), 'createClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'), 'updateClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'), @@ -135,6 +135,9 @@ class ClassScheduleApi extends FHCAPI_Controller $class_time_slot_validity_period_res = $this->ClassTimeSlotValidityPeriodModel->load($classTimeSlotValidityPeriodId); $class_time_slot_validity_period_res = $this->getDataOrTerminateWithError($class_time_slot_validity_period_res); + if (!$class_time_slot_validity_period_res || count($class_time_slot_validity_period_res) === 0) { + $this->terminateWithError($this->p->t('ui', 'classTimeSlotValidityPeriodNotFound')); + } if (!$this->isUserEntitledForOrganizationalUnit($class_time_slot_validity_period_res[0]->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) { $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); @@ -154,8 +157,7 @@ class ClassScheduleApi extends FHCAPI_Controller 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_organizationalUnit')]), 'max_length' => $this->p->t('ui', 'error_fieldMaxLength', ['field' => $this->p->t('ui', 'field_organizationalUnit'), 'max' => 32]) ]); - $this->form_validation->set_rules('semester', 'Semester', 'required|is_natural_no_zero|less_than_equal_to[8]', [ - 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_semester')]), + $this->form_validation->set_rules('semester', 'Semester', 'is_natural_no_zero|less_than_equal_to[8]', [ 'is_natural_no_zero' => $this->p->t('ui', 'error_fieldInvalid', ['field' => $this->p->t('ui', 'field_semester')]), 'less_than_equal_to' => $this->p->t('ui', 'error_fieldMaxValue', ['field' => $this->p->t('ui', 'field_semester'), 'max' => 8]) ]); @@ -202,8 +204,7 @@ class ClassScheduleApi extends FHCAPI_Controller 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_organizationalUnit')]), 'max_length' => $this->p->t('ui', 'error_fieldMaxLength', ['field' => $this->p->t('ui', 'field_organizationalUnit'), 'max' => 32]) ]); - $this->form_validation->set_rules('semester', 'Semester', 'required|is_natural_no_zero|less_than_equal_to[8]', [ - 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_semester')]), + $this->form_validation->set_rules('semester', 'Semester', 'is_natural_no_zero|less_than_equal_to[8]', [ 'is_natural_no_zero' => $this->p->t('ui', 'error_fieldInvalid', ['field' => $this->p->t('ui', 'field_semester')]), 'less_than_equal_to' => $this->p->t('ui', 'error_fieldMaxValue', ['field' => $this->p->t('ui', 'field_semester'), 'max' => 8]) ]); @@ -265,7 +266,13 @@ class ClassScheduleApi extends FHCAPI_Controller public function getClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId) { $validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]); - $validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult)[0]; + $validityPeriodData = $this->getDataOrTerminateWithError($validityPeriodResult); + if (!$validityPeriodData || count($validityPeriodData) === 0) { + $this->terminateWithError($this->p->t('ui', 'classTimeSlotValidityPeriodNotFound')); + } + + $validityPeriod = $validityPeriodData[0]; + if (!$this->isUserEntitledForOrganizationalUnit($validityPeriod->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) { $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); } @@ -289,11 +296,8 @@ class ClassScheduleApi extends FHCAPI_Controller $this->db->trans_start(); - $timeSlotGroupIdentifier = uniqid(); - foreach ($this->input->post('unterrichtszeiten') as $timeSlot) { $result = $this->ClassTimeSlotModel->insert([ - 'unterrichtszeit_gruppe_identifikator' => $timeSlotGroupIdentifier, 'wochentag' => $timeSlot['wochentag'], 'uhrzeit_von' => $timeSlot['startTime'], 'uhrzeit_bis' => $timeSlot['endTime'], @@ -312,7 +316,7 @@ class ClassScheduleApi extends FHCAPI_Controller $this->terminateWithSuccess(true); } - public function editClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId) + public function updateClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId) { $this->form_validation->set_rules('unterrichtszeiten', 'Class Time Slots', 'callback_validate_items_in_class_time_slots'); if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); @@ -325,11 +329,19 @@ class ClassScheduleApi extends FHCAPI_Controller $this->db->trans_start(); - $timeSlotGroupIdentifier = uniqid(); + $currentTimeSlotsResult = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]); + $currentTimeSlots = $this->getDataOrTerminateWithError($currentTimeSlotsResult); + $currentTimeSlotIds = array_column($currentTimeSlots, 'unterrichtszeit_id'); + + $removedTimeSlotIds = array_values(array_diff($currentTimeSlotIds, array_column($this->input->post('unterrichtszeiten'), 'id'))); + + if (count($removedTimeSlotIds) > 0) { + $query = 'DELETE FROM lehre.tbl_unterrichtszeiten WHERE unterrichtszeit_id IN ?'; + $result = $this->db->query($query, [ $removedTimeSlotIds ]); + } foreach ($this->input->post('unterrichtszeiten') as $timeSlot) { $data = [ - 'unterrichtszeit_gruppe_identifikator' => $timeSlotGroupIdentifier, 'wochentag' => $timeSlot['wochentag'], 'uhrzeit_von' => $timeSlot['startTime'], 'uhrzeit_bis' => $timeSlot['endTime'], @@ -354,7 +366,7 @@ class ClassScheduleApi extends FHCAPI_Controller $this->terminateWithSuccess(true); } - public function deleteClassTimeSlotsForValidityPeriodPerGroup($classTimeSlotValidityPeriodId, $groupIdentifikator) + public function deleteClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId) { $validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]); $validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult)[0]; @@ -364,7 +376,7 @@ class ClassScheduleApi extends FHCAPI_Controller $this->db->trans_start(); - $result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId, 'unterrichtszeit_gruppe_identifikator' => $groupIdentifikator]); + $result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId]); if (isError($result)) { $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); } @@ -439,7 +451,8 @@ class ClassScheduleApi extends FHCAPI_Controller if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); - $this->db->trans_start(); + + $parsedClassTimeSlotTypeId = urldecode($classTimeSlotTypeId); $descriptions = $this->input->post('descriptions'); $pgArray = $this->arrayToPgArray($descriptions); @@ -451,7 +464,7 @@ class ClassScheduleApi extends FHCAPI_Controller $this->input->post('isActive'), date('c'), getAuthUid(), - $classTimeSlotTypeId, + $parsedClassTimeSlotTypeId, ]); $this->db->trans_complete(); @@ -461,14 +474,15 @@ class ClassScheduleApi extends FHCAPI_Controller public function deleteClassTimeSlotType($classTimeSlotTypeId) { $isClassTimeSlotDeletable = true; - - $validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitentyp_kurzbz' => $classTimeSlotTypeId]); + $parsedClassTimeSlotTypeId = urldecode($classTimeSlotTypeId); + + $validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitentyp_kurzbz' => $parsedClassTimeSlotTypeId]); $validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult); if ($validityPeriod && count($validityPeriod) > 0) { $isClassTimeSlotDeletable = false; } - $classTimeSlotResult = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitentyp_kurzbz' => $classTimeSlotTypeId]); + $classTimeSlotResult = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitentyp_kurzbz' => $parsedClassTimeSlotTypeId]); $classTimeSlot = $this->getDataOrTerminateWithError($classTimeSlotResult); if ($classTimeSlot && count($classTimeSlot) > 0) { $isClassTimeSlotDeletable = false; @@ -477,9 +491,9 @@ class ClassScheduleApi extends FHCAPI_Controller $this->db->trans_start(); if (!$isClassTimeSlotDeletable) { - $result = $this->ClassTimeSlotTypeModel->update($classTimeSlotTypeId, ['aktiv' => false, 'updateamum' => date('c'), 'updatevon' => getAuthUid()]); + $result = $this->ClassTimeSlotTypeModel->update($parsedClassTimeSlotTypeId, ['aktiv' => false, 'updateamum' => date('c'), 'updatevon' => getAuthUid()]); } else { - $result = $this->ClassTimeSlotTypeModel->delete(['unterrichtszeitentyp_kurzbz' => $classTimeSlotTypeId]); + $result = $this->ClassTimeSlotTypeModel->delete(['unterrichtszeitentyp_kurzbz' => $parsedClassTimeSlotTypeId]); } if (isError($result)) { $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); @@ -492,10 +506,16 @@ class ClassScheduleApi extends FHCAPI_Controller //------------------------------------------------------------------------------------------------------------------ // Private methods private function arrayToPgArray(array $assoc) { - $flat = []; + $flat = [null, null]; foreach ($assoc as $assocItem) { - $flat[] = $assocItem['lang'] . ':' . $assocItem['value']; + if ($assocItem['lang'] === 'de') { + $flat[0] = $assocItem['value']; + } else if ($assocItem['lang'] === 'en') { + $flat[1] = $assocItem['value']; + } else { + $flat[] = $assocItem['value']; + } } $escaped = array_map(function ($v) { @@ -554,7 +574,7 @@ class ClassScheduleApi extends FHCAPI_Controller return false; } - if (!in_array($timeSlot['wochentag'], [1, 2, 3, 4, 5, 6, 7])) { + if (!in_array($timeSlot['wochentag'], [1, 2, 3, 4, 5, 6, 0])) { $this->form_validation->set_message( 'validate_items_in_class_time_slots', $this->p->t('ui', 'error_fieldWeekdayInvalid', ['field' => $this->p->t('ui', 'field_classTimeSlot')]) diff --git a/application/controllers/api/frontend/v1/ClassroomHourApi.php b/application/controllers/api/frontend/v1/ClassroomHourApi.php new file mode 100644 index 000000000..41956f849 --- /dev/null +++ b/application/controllers/api/frontend/v1/ClassroomHourApi.php @@ -0,0 +1,43 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); +class ClassroomHourApi extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'getAllClassroomHours' => self::PERM_LOGGED, + ]); + + $this->load->model('ressource/Stunde_model', "ClassroomHourModel"); + + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getAllClassroomHours() + { + $this->ClassroomHourModel->addOrder('stunde', 'ASC'); + $result = $this->ClassroomHourModel->load(); + + $this->terminateWithSuccess($this->getDataOrTerminateWithError($result)); + } +} + diff --git a/application/controllers/api/frontend/v1/organisation/Studiensemester.php b/application/controllers/api/frontend/v1/organisation/Studiensemester.php index 0ede3e00b..f7f39d436 100644 --- a/application/controllers/api/frontend/v1/organisation/Studiensemester.php +++ b/application/controllers/api/frontend/v1/organisation/Studiensemester.php @@ -31,8 +31,11 @@ class Studiensemester extends FHCAPI_Controller 'getStudySemestersByStudyPlanAndDates' => self::PERM_LOGGED, ) ); - // Load model StudiensemesterModel + $this->load->model('organisation/studiensemester_model', 'StudiensemesterModel'); + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $this->load->model('organisation/Studienordnung_model', 'StudienordnungModel'); + $this->load->model('organisation/Studienplan_model', 'StudienplanModel'); } /** @@ -171,10 +174,6 @@ class Studiensemester extends FHCAPI_Controller public function getStudySemestersByOrganizationalUnitAndDates($organizationalUnitShortCode) { - $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); - $this->load->model('organisation/Studienordnung_model', 'StudienordnungModel'); - $this->load->model('organisation/Studienplan_model', 'StudienplanModel'); - $startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]'))); $endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]'))); if (!$startDate || !$endDate) { diff --git a/application/controllers/lehre/ClassSchedule.php b/application/controllers/lehre/ClassSchedule.php index d96551bf5..f1581acb3 100644 --- a/application/controllers/lehre/ClassSchedule.php +++ b/application/controllers/lehre/ClassSchedule.php @@ -34,8 +34,6 @@ class ClassSchedule extends Auth_Controller ); $this->_setAuthUID(); - - $this->setControllerId(); } // ----------------------------------------------------------------------------------------------------------------- diff --git a/public/css/components/classSchedule.css b/public/css/components/classSchedule.css index 80f854541..4decf6531 100644 --- a/public/css/components/classSchedule.css +++ b/public/css/components/classSchedule.css @@ -42,3 +42,13 @@ div[role="row"] { background-color: white; } + +.v-enter-active, +.v-leave-active { + transition: opacity 0.5s ease; +} + +.v-enter-from, +.v-leave-to { + opacity: 0; +} \ No newline at end of file diff --git a/public/js/api/factory/classSchedule.js b/public/js/api/factory/classSchedule.js index a0a037f80..42af32034 100644 --- a/public/js/api/factory/classSchedule.js +++ b/public/js/api/factory/classSchedule.js @@ -65,14 +65,14 @@ export default { params, }; }, - editClassTimeSlotsForValidityPeriod( + updateClassTimeSlotsForValidityPeriod( userId, classTimeSlotValidityPeriodId, params, ) { return { method: "post", - url: `api/frontend/v1/ClassScheduleApi/editClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`, + url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`, params, }; }, @@ -87,14 +87,13 @@ export default { params, }; }, - deleteClassTimeSlotsForValidityPeriodPerGroup( + deleteClassTimeSlotsForValidityPeriod( userId, classTimeSlotValidityPeriodId, - groupIdentifikator, ) { return { method: "post", - url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotsForValidityPeriodPerGroup/${classTimeSlotValidityPeriodId}/${groupIdentifikator}`, + url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`, }; }, getAllClassScheduleTypes(queryParams) { @@ -115,14 +114,14 @@ export default { updateClassTimeSlotType(classTimeSlotTypeId, params) { return { method: "post", - url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotType/${classTimeSlotTypeId}`, + url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotType/${encodeURIComponent(classTimeSlotTypeId)}`, params, }; }, deleteClassTimeSlotType(userId, classTimeSlotTypeId) { return { method: "post", - url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotType/${classTimeSlotTypeId}`, + url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotType/${encodeURIComponent(classTimeSlotTypeId)}`, }; }, }; diff --git a/public/js/api/factory/classroomHour.js b/public/js/api/factory/classroomHour.js new file mode 100644 index 000000000..4cd5ffda1 --- /dev/null +++ b/public/js/api/factory/classroomHour.js @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2025 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 . + */ + +export default { + getAllClassroomHours() { + return { + method: "get", + url: "/api/frontend/v1/ClassroomHourApi/getAllClassroomHours", + }; + } +}; diff --git a/public/js/components/ClassSchedule/ClassScheduleCalendarSelector.js b/public/js/components/ClassSchedule/ClassScheduleCalendarSelector.js index 7406e01a0..83703efd2 100644 --- a/public/js/components/ClassSchedule/ClassScheduleCalendarSelector.js +++ b/public/js/components/ClassSchedule/ClassScheduleCalendarSelector.js @@ -13,6 +13,10 @@ export default { required: false, default: false, }, + classroomHours: { + type: [Array, null], + required: true, + }, classTimeSlotTypes: { type: [Array, null], required: true, @@ -22,21 +26,28 @@ export default { required: false, default: () => [], }, + defaultClassTimeSlotType: { + type: String, + required: false, + default: null, + }, }, emits: ["overlaysChanged"], watch: { editedOverlays: { async handler(newVal) { - await this.$nextTick(); - this.overlays = []; - await this.$nextTick(); + this.$refs.calendarContainer - .querySelectorAll("div[id^='overlay-']") + ?.querySelectorAll("div[id^='overlay-']") .forEach((element) => { element.remove(); }); - newVal + + await this.$nextTick(); + + + await newVal .map((slot) => { return { ...slot, @@ -48,7 +59,7 @@ export default { ), }; }) - .forEach((overlay) => { + .forEach(async (overlay) => { let firstElementDataNumber = (overlay.weekday - 1) * this.timeSlotsInDay.length + this.timeSlotsInDay.indexOf(overlay.startingTimeSlot); @@ -64,6 +75,7 @@ export default { this.$refs.calendarSelectorContainer.querySelector( "div[data-number='" + lastElementDataNumber + "']", ); + if (!firstSelectedElement || !lastSelectedElement) { this.$fhcAlert.alertError( this.$p.t("ui", "classTimeSlotLoadingErrorMessage"), @@ -84,7 +96,7 @@ export default { ? parseInt(lastSelectedElement.getAttribute("data-number")) : null; - this.createOverlay(); + await this.createOverlay(1); this.$refs.calendarSelectorContainer .querySelectorAll("div[class*='part-body']") @@ -98,15 +110,16 @@ export default { this.overlays = this.overlays.map((existingOverlay, index) => { let existingOverlayInNewVal = newVal[index]; + let selectedClassTimeSlotType = this.$props.classTimeSlotTypes.find( + (type) => + type.unterrichtszeitentyp_kurzbz === existingOverlayInNewVal.type, + ); existingOverlay.databaseId = existingOverlayInNewVal.databaseId; existingOverlay.type = existingOverlayInNewVal.type; - existingOverlay.hexColor = - this.classTimeSlotTypes.find( - (type) => - type.unterrichtszeitentyp_kurzbz === - existingOverlayInNewVal.type, - )?.hintergrundfarbe || null; + existingOverlay.backgroundColor = + selectedClassTimeSlotType?.hintergrundfarbe || null; + return { ...existingOverlay, }; @@ -115,17 +128,6 @@ export default { deep: true, immediate: true, }, - classTimeSlotTypes() { - this.overlays = this.overlays.map((overlay) => { - let type = this.classTimeSlotTypes.find( - (type) => type.unterrichtszeitentyp_kurzbz === overlay.type, - ); - if (type) { - overlay.hexColor = type.hintergrundfarbe; - } - return overlay; - }); - }, overlays: { handler(newVal) { this.$emit("overlaysChanged", newVal); @@ -144,24 +146,6 @@ export default { this.$p.t("ui", "saturday"), this.$p.t("ui", "sunday"), ], - timeSlotsInDay: [ - "08:00-08:45", - "08:45-09:30", - "09:40-10:25", - "10:25-11:10", - "11:20-12:05", - "12:05-12:50", - "12:50-13:35", - "13:35-14:20", - "14:30-15:15", - "15:15-16:00", - "16:10-16:55", - "16:55-17:40", - "17:50-18:35", - "18:35-19:20", - "19:30-20:15", - "20:15-21:00", - ], isTimeElementCreationInProgress: false, overlays: [], defaultTimeSlotLabelColor: "var(--fhc-calendar-bg-body)", @@ -216,16 +200,30 @@ export default { userLanguage() { return Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language); }, + timeSlotsInDay() { + if (!this.$props.classroomHours) return ["08:00-08:45"]; + + return this.$props.classroomHours; + }, + trackOverlayPool() { + let overlaysInPool = this.$refs.overlaysContainer.children; + if (!overlaysInPool < 5) { + alert("Could not get overlays in pool"); + return []; + } + }, }, methods: { - createOverlay() { + async createOverlay(test) { this.hideOverlayClassTimeTypePopover(); let overlayElement; - overlayElement = this.$refs.calendarSelectorContainer.querySelector( - "#overlays-container", - ).children[0]; + overlayElement = this.$refs.overlaysContainer.children[0]; + if (!overlayElement) { + console.error("Could not find overlay element"); + return; + } let firstSelectedChild = this.$refs.calendarSelectorContainer.querySelector( @@ -252,7 +250,6 @@ export default { firstSelectedElementNumber === null || lastSelectedElementNumber === null ) { - //console.error("Selected elements do not have data-number attribute"); return; } @@ -269,7 +266,7 @@ export default { this.$fhcAlert.alertError( this.$p.t("ui", "classTimeSlotMultipleWeeksSelectedErrorMessage"), ); - + console.error(error); return; } @@ -347,11 +344,19 @@ export default { this.timeSlotsInDay[ this.currentLastSelectedElementNumber % this.timeSlotsInDay.length ], - type: null, - hexColor: null, + type: + this.$props.defaultClassTimeSlotType?.unterrichtszeitentyp_kurzbz || + null, + backgroundColor: + this.$props.defaultClassTimeSlotType?.hintergrundfarbe || null, weekday: gridLineNumber + 1, }); } + this.$refs.calendarSelectorContainer + .querySelectorAll("div[class*='part-body']") + .forEach((child) => { + child.style.backgroundColor = this.defaultTimeSlotLabelColor; + }); }, deleteOverlay(overlayId) { let confirm = window.confirm( @@ -557,7 +562,7 @@ export default { }); } }, - handleMouseUp(event) { + async handleMouseUp(event) { if (this.$props.isPreviewMode) return; let isLeftMouseButton = event.buttons === 0; @@ -637,7 +642,7 @@ export default { }); } - this.createOverlay(); + await this.createOverlay(2); this.$refs.calendarSelectorContainer .querySelectorAll("div[class*='part-body']") @@ -725,16 +730,12 @@ export default { (overlay) => overlay.id === dropzoneItem.id, ); if (!dropzoneOverlay) { - // console.error( - // "Could not find overlay for dropzone item with id " + - // dropzoneItem.id, - // ); return; } if (dropzoneOverlay.id !== this.selected[0].id) { this.$fhcAlert.alertError( - this.$p.t("ui", "classTimeSlotOverlapErrorMessage"), + this.$p.t("ui", "classTimeSlotOverlapErrorMessage2"), ); return; } @@ -901,7 +902,7 @@ export default { if (firstSkippedOverElement) { this.$fhcAlert.alertError( - this.$p.t("ui", "classTimeSlotOverlapErrorMessage"), + this.$p.t("ui", "classTimeSlotOverlapErrorMessage3"), ); let elementBeforeFirstSkippedOverElement = this.$refs.calendarSelectorContainer.querySelector( @@ -1152,7 +1153,7 @@ export default { if (firstSkippedOverElement) { this.$fhcAlert.alertError( - this.$p.t("ui", "classTimeSlotOverlapErrorMessage"), + this.$p.t("ui", "classTimeSlotOverlapErrorMessage4"), ); let elementBeforeFirstSkippedOverElement = this.$refs.calendarSelectorContainer.querySelector( @@ -1224,7 +1225,7 @@ export default { overlayId: null, }; }, - handleMouseOverOnOverlay(overlayId) { + async handleMouseOverOnOverlay(overlayId) { if (this.$props.isPreviewMode) return; if (!this.isTimeElementCreationInProgress) return; @@ -1247,7 +1248,9 @@ export default { this.currentFirstSelectedElementNumber = hitOverlayStartingTimeSlotElementNumber + 1; } - this.createOverlay(); + + await this.createOverlay(3); + this.isTimeElementCreationInProgress = false; this.$refs.calendarSelectorContainer .querySelectorAll("div[class*='part-body']") @@ -1368,7 +1371,7 @@ export default { if (!overlay) return ""; let typeDescriptions = - this.classTimeSlotTypes.find( + this.$props.classTimeSlotTypes.find( (type) => type.unterrichtszeitentyp_kurzbz === overlay.type, )?.bezeichnung_mehrsprachig || ""; if (!typeDescriptions) return ""; @@ -1378,7 +1381,7 @@ export default { : typeDescriptions[0].value; }, handleChangeClassTimeSlotTypeForOverlay(newType) { - let classTimeSlotType = this.classTimeSlotTypes.find( + let classTimeSlotType = this.$props.classTimeSlotTypes.find( (type) => type.unterrichtszeitentyp_kurzbz === newType, ); if (!classTimeSlotType) { @@ -1390,7 +1393,7 @@ export default { return { ...overlay, type: classTimeSlotType.unterrichtszeitentyp_kurzbz, - hexColor: classTimeSlotType.hintergrundfarbe, + backgroundColor: classTimeSlotType.hintergrundfarbe, }; } return overlay; @@ -1423,7 +1426,7 @@ export default { option.addEventListener("click", (event) => { let selectedTypeDescription = event.currentTarget.innerText; - let selectedType = this.classTimeSlotTypes.find((type) => + let selectedType = this.$props.classTimeSlotTypes.find((type) => type.bezeichnung_mehrsprachig.some( (desc) => desc.value === selectedTypeDescription, ), @@ -1437,7 +1440,7 @@ export default { return { ...overlay, type: selectedType.unterrichtszeitentyp_kurzbz, - hexColor: selectedType.hintergrundfarbe, + backgroundColor: selectedType.hintergrundfarbe, }; } return overlay; @@ -1466,7 +1469,7 @@ export default { this.hideOverlayClassTimeTypePopover(); }, template: /*html*/ ` -
+
-
+
- { - let descriptions = []; - for (let item of type.bezeichnung_mehrsprachig) { - let [lang, value] = item.split(":"); - descriptions.push({ lang, value }); - } + let descriptions = [{ + lang: "de", + value: type.bezeichnung_mehrsprachig[0] || "", + }, { + lang: "en", + value: type.bezeichnung_mehrsprachig[1] || "", + }]; + return { ...type, bezeichnung_mehrsprachig: descriptions, diff --git a/public/js/components/ClassSchedule/ClassScheduleOverview.js b/public/js/components/ClassSchedule/ClassScheduleOverview.js index 05db598ad..6cea0e708 100644 --- a/public/js/components/ClassSchedule/ClassScheduleOverview.js +++ b/public/js/components/ClassSchedule/ClassScheduleOverview.js @@ -280,7 +280,9 @@ export default { return events; }, dropdownParsedOrganizationalUnits() { - return this.organizationalUnits.map((unit) => { + return this.organizationalUnits + .filter((unit) => unit.aktiv) + .map((unit) => { return { label: `${unit.bezeichnung} (${unit.organisationseinheittyp_kurzbz})`, value: unit.oe_kurzbz, diff --git a/public/js/components/ClassSchedule/ClassScheduleTypeModal.js b/public/js/components/ClassSchedule/ClassScheduleTypeModal.js index 7a59dbf47..cc1c6cae8 100644 --- a/public/js/components/ClassSchedule/ClassScheduleTypeModal.js +++ b/public/js/components/ClassSchedule/ClassScheduleTypeModal.js @@ -60,11 +60,14 @@ export default { if (getAllClassScheduleTypeResponse.meta.status === "success") { this.classScheduleTypes = getAllClassScheduleTypeResponse.data.map( (type) => { - let descriptions = []; - for (let item of type.bezeichnung_mehrsprachig) { - let [lang, value] = item.split(":"); - descriptions.push({ lang, value }); - } + let descriptions = [{ + lang: "de", + value: type.bezeichnung_mehrsprachig[0] || "", + }, { + lang: "en", + value: type.bezeichnung_mehrsprachig[1] || "", + }]; + return { ...type, bezeichnung_mehrsprachig: descriptions, @@ -190,10 +193,10 @@ export default {
@@ -214,20 +217,20 @@ export default {
diff --git a/public/js/components/ClassSchedule/ClassScheduleValidityPeriodForm.js b/public/js/components/ClassSchedule/ClassScheduleValidityPeriodForm.js index 4c09a995b..0dfc2dfd1 100644 --- a/public/js/components/ClassSchedule/ClassScheduleValidityPeriodForm.js +++ b/public/js/components/ClassSchedule/ClassScheduleValidityPeriodForm.js @@ -1,4 +1,5 @@ import ApiClassSchedule from "../../../js/api/factory/classSchedule.js"; +import ApiClassroomHour from "../../../js/api/factory/classroomHour.js"; import ClassScheduleCalendarSelector from "./ClassScheduleCalendarSelector.js"; @@ -8,6 +9,10 @@ export default { ClassScheduleCalendarSelector, }, props: { + classTimeSlotTypes: { + type: [Array, null], + required: true, + }, classTimeSlotValidityPeriod: { type: Object, required: true, @@ -17,6 +22,11 @@ export default { required: false, default: () => [], }, + classroomHours: { + type: Array, + required: false, + default: () => [], + }, }, emits: ["hideForm", "classTimeSlotsCreated", "classTimeSlotsEdited"], watch: { @@ -26,7 +36,7 @@ export default { return { databaseId: slot.id, id: slot.identifier, - weekday: slot.wochentag, + weekday: slot.wochentag === 0 ? 7 : slot.wochentag, type: slot.unterrichtszeitentyp_kurzbz, startTime: slot.startTime, endTime: slot.endTime, @@ -40,7 +50,6 @@ export default { return { editedOverlays: [], classTimeSlots: [], - classTimeSlotTypes: [], }; }, computed: { @@ -78,7 +87,10 @@ export default { this.$props.classTimeSlotValidityPeriod .unterrichtszeitengueltigkeit_id, { - unterrichtszeiten: this.classTimeSlots, + unterrichtszeiten: this.classTimeSlots.map((slot) => { + slot.wochentag = parseInt(slot.wochentag) === 7 ? 0 : slot.wochentag; + return slot; + }), }, ), ); @@ -87,17 +99,20 @@ export default { this.classTimeSlots = []; this.$emit("classTimeSlotsCreated"); } else { - this.$fhcAlert.handleSystemError(error); + this.$fhcAlert.handleSystemError(response.meta.message); } }, - async editClassTimeSlots() { + async updateClassTimeSlots() { let response = await this.$api.call( - ApiClassSchedule.editClassTimeSlotsForValidityPeriod( + ApiClassSchedule.updateClassTimeSlotsForValidityPeriod( this.id, this.$props.classTimeSlotValidityPeriod .unterrichtszeitengueltigkeit_id, { - unterrichtszeiten: this.classTimeSlots, + unterrichtszeiten: this.classTimeSlots.map((slot) => { + slot.wochentag = parseInt(slot.wochentag) === 7 ? 0 : slot.wochentag; + return slot; + }), }, ), ); @@ -134,35 +149,13 @@ export default { }); }, }, - async created() { - let getAllClassTimeSlotTypesResponse = await this.$api.call( - ApiClassSchedule.getAllClassScheduleTypes("filter[aktiv]=true"), - ); - if (getAllClassTimeSlotTypesResponse.meta.status === "success") { - this.classTimeSlotTypes = getAllClassTimeSlotTypesResponse.data.map( - (type) => { - let descriptions = []; - for (let item of type.bezeichnung_mehrsprachig) { - let [lang, value] = item.split(":"); - descriptions.push({ lang, value }); - } - return { - ...type, - bezeichnung_mehrsprachig: descriptions, - }; - }, - ); - } else { - this.$fhcAlert.alertError( - this.$p.t("ui", "errorFetchingClassScheduleTimeSlotTypes"), - ); - } - }, template: `
- @@ -170,7 +163,7 @@ export default {
- +
`, diff --git a/public/js/components/ClassSchedule/ClassScheduleValidityPeriodModal.js b/public/js/components/ClassSchedule/ClassScheduleValidityPeriodModal.js index ecfb9db38..319ea74bd 100644 --- a/public/js/components/ClassSchedule/ClassScheduleValidityPeriodModal.js +++ b/public/js/components/ClassSchedule/ClassScheduleValidityPeriodModal.js @@ -47,19 +47,21 @@ export default { .call(ApiClassSchedule.getClassTimeValidityPeriod(newValue)) .then((response) => { let validityPeriodData = response.data[0]; - let organizationalUnit = this.organizationalUnits.find( - (unit) => unit.oe_kurzbz === validityPeriodData.oe_kurzbz, + let parsedOrganizationalUnit = this.dropdownParsedOrganizationalUnits.find( + (unit) => unit.value === validityPeriodData.oe_kurzbz, ); - if (!organizationalUnit) { - this.$fhcAlert.alertError(this.$p.t("ui", "errorLoadingData")); - return; + if (!parsedOrganizationalUnit) { + parsedOrganizationalUnit = { + value: validityPeriodData.oe_kurzbz, + label: validityPeriodData.oe_bezeichnung , + }; } this.editedClassTimeSlotValidityPeriod = validityPeriodData; this.classTimeSlotValidityPeriodFormData = { id: validityPeriodData.unterrichtszeitengueltigkeit_id, - organizationalUnit, + organizationalUnit: parsedOrganizationalUnit, studyPlanId: validityPeriodData.studienplan_id, classTimeSlotTypeShortcode: validityPeriodData.unterrichtszeitentyp_kurzbz, @@ -136,12 +138,22 @@ export default { userLanguage() { return Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language); }, + dropdownParsedOrganizationalUnits() { + return this.organizationalUnits + .filter((unit) => unit.aktiv) + .map((unit) => { + return { + label: `${unit.bezeichnung} (${unit.organisationseinheittyp_kurzbz})`, + value: unit.oe_kurzbz, + }; + }); + }, }, methods: { updateClassTimeSlotValidityPeriodFormDataWatcher() { if ( !this.classTimeSlotValidityPeriodFormData.organizationalUnit - ?.oe_kurzbz || + ?.value || !this.classTimeSlotValidityPeriodFormData.validityPeriodFrom || !this.classTimeSlotValidityPeriodFormData.validityPeriodTo ) { @@ -153,7 +165,7 @@ export default { }, async refetchFilterableOptions() { this.studyPlans = await this.getTargetedStudyPlans( - this.classTimeSlotValidityPeriodFormData.organizationalUnit?.oe_kurzbz, + this.classTimeSlotValidityPeriodFormData.organizationalUnit?.value, this.formattedValidityPeriodFrom, this.formattedValidityPeriodTo, ); @@ -171,11 +183,6 @@ export default { ); if (editedStudyPlan) { this.studyPlans.push(editedStudyPlan); - } else { - // console.error( - // "Edited study plan not found:", - // this.editedClassTimeSlotValidityPeriod.studienplan_id, - // ); } } } @@ -183,7 +190,7 @@ export default { let studySemestersByDates = await this.getStudySemestersByOrganizationalUnitAndDates( this.classTimeSlotValidityPeriodFormData.organizationalUnit - ?.oe_kurzbz, + ?.value, this.formattedValidityPeriodFrom, this.formattedValidityPeriodTo, ); @@ -325,7 +332,7 @@ export default { ...this.classTimeSlotValidityPeriodFormData, organizationalUnitShortCode: this.classTimeSlotValidityPeriodFormData.organizationalUnit - ?.oe_kurzbz, + ?.value, }), ) .then((response) => { @@ -349,7 +356,7 @@ export default { ...this.classTimeSlotValidityPeriodFormData, organizationalUnitShortCode: this.classTimeSlotValidityPeriodFormData.organizationalUnit - ?.oe_kurzbz, + ?.value, }, ), ) @@ -384,12 +391,12 @@ export default { const query = event.query.toLowerCase(); if (!query) { return (this.filteredOrganizationalUnits = [ - ...this.organizationalUnits, + ...this.dropdownParsedOrganizationalUnits, ]); } return (this.filteredOrganizationalUnits = - this.organizationalUnits.filter((unit) => { + this.dropdownParsedOrganizationalUnits.filter((unit) => { let label = `${unit.bezeichnung} (${unit.organisationseinheittyp_kurzbz})`; return label.toLowerCase().includes(query); })); @@ -430,11 +437,14 @@ export default { if (getAllClassTimeSlotTypesResponse.meta.status === "success") { this.classTimeSlotTypes = getAllClassTimeSlotTypesResponse.data.map( (type) => { - let descriptions = []; - for (let item of type.bezeichnung_mehrsprachig) { - let [lang, value] = item.split(":"); - descriptions.push({ lang, value }); - } + let descriptions = [{ + lang: "de", + value: type.bezeichnung_mehrsprachig[0] || "", + }, { + lang: "en", + value: type.bezeichnung_mehrsprachig[1] || "", + }]; + return { ...type, bezeichnung_mehrsprachig: descriptions, @@ -460,8 +470,8 @@ export default { v-model="classTimeSlotValidityPeriodFormData.organizationalUnit" :label="$capitalize($p.t('lehre/organisationseinheit')) + ' *'" :suggestions="filteredOrganizationalUnits" - :optionValue="(option) => option.kurzbz" - :optionLabel="(option) => option.bezeichnung + ' (' + option.organisationseinheittyp_kurzbz + ')'" + :optionValue="(option) => option.value" + :optionLabel="(option) => option.label" @complete="filterOrganizationalUnits" dropdown forceSelection @@ -523,7 +533,7 @@ export default { id="ausbildungssemester" name="semester" :disabled="isStudyPlanSelectDisabled" - :label="$p.t('lehre', 'ausbildungssemester')+ '*'" + :label="$p.t('lehre', 'ausbildungssemester')" v-model="classTimeSlotValidityPeriodFormData.semester" >
-
+

{{ $p.t("ui", "classScheduleValidityPeriodTimeSlots") }}

-
-
+ +
- - + + +
+
+
-
-
-
+ +

{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}

diff --git a/public/js/components/ClassSchedule/ClassScheduleValidityPeriodPreview.js b/public/js/components/ClassSchedule/ClassScheduleValidityPeriodPreview.js index 24b3d1f4f..1215a7e55 100644 --- a/public/js/components/ClassSchedule/ClassScheduleValidityPeriodPreview.js +++ b/public/js/components/ClassSchedule/ClassScheduleValidityPeriodPreview.js @@ -1,4 +1,5 @@ import ClassScheduleCalendarSelector from "./ClassScheduleCalendarSelector.js"; +import ApiClassroomHour from "../../../js/api/factory/classroomHour.js"; export default { name: "ClassScheduleValidityPeriodPreview", @@ -20,7 +21,9 @@ export default { }, }, data: () => { - return {}; + return { + classRoomHours: [], + }; }, computed: { classScheduleValidityPeriodStartDate() { @@ -29,38 +32,18 @@ export default { let dateParts = this.$props.classScheduleValidityPeriod.gueltig_von .split("-") .reverse(); - return dateParts.join("/"); + return dateParts.join("."); }, classScheduleValidityPeriodEndDate() { if (!this.$props.classScheduleValidityPeriod) return null; let dateParts = this.$props.classScheduleValidityPeriod.gueltig_bis .split("-") .reverse(); - return dateParts.join("/"); + return dateParts.join("."); }, classScheduleValidityPeriodStudyPlan() { return this.$props.classScheduleValidityPeriod.studienplan_bezeichnung; }, - parsedClassTimeSlots() { - let classTimeSlotsGroupedByWeek = []; - - this.$props.classTimeSlots.forEach((slot) => { - let groupIdentifikator = slot["unterrichtszeit_gruppe_identifikator"]; - let existingGroup = classTimeSlotsGroupedByWeek.find( - (group) => group.groupIdentifikator === groupIdentifikator, - ); - if (existingGroup) { - existingGroup.slots.push(slot); - } else { - classTimeSlotsGroupedByWeek.push({ - groupIdentifikator, - slots: [slot], - }); - } - }); - - return classTimeSlotsGroupedByWeek; - }, }, methods: { showClassScheduleValidityPeriod(classScheduleValidityPeriodId) { @@ -72,7 +55,22 @@ export default { }); }, }, - async created() {}, + async created() { + let getAllClassroomHoursResponse = await this.$api.call( + ApiClassroomHour.getAllClassroomHours(), + ); + if (getAllClassroomHoursResponse.meta.status === "success") { + this.classroomHours = getAllClassroomHoursResponse.data.map((hour) => { + return { + ...hour, + beginn: hour.beginn.substring(0, 5), + ende: hour.ende.substring(0, 5), + }; + }); + } else { + this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingClassroomHours")); + } + }, template: /* html */ `
@@ -86,15 +84,16 @@ export default { >
-
-
+
+
{ return { databaseId: slot.id, id: slot.identifier, - weekday: slot.wochentag, + weekday: parseInt(slot.wochentag) === 0 ? 7 : slot.wochentag, type: slot.unterrichtszeitentyp_kurzbz, startTime: slot.uhrzeit_von, endTime: slot.uhrzeit_bis, diff --git a/system/dbupdate_3.4/76031_unterrichtszeiten_der_studiengänge.php b/system/dbupdate_3.4/76031_unterrichtszeiten_der_studiengänge.php index 210e277d1..4382fd858 100644 --- a/system/dbupdate_3.4/76031_unterrichtszeiten_der_studiengänge.php +++ b/system/dbupdate_3.4/76031_unterrichtszeiten_der_studiengänge.php @@ -37,7 +37,6 @@ if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_unterrichtszeiten LIMIT 1" $qry = ' CREATE TABLE lehre.tbl_unterrichtszeiten ( "unterrichtszeit_id" INTEGER NOT NULL, - "unterrichtszeit_gruppe_identifikator" VARCHAR(32) NOT NULL, "wochentag" INTEGER NOT NULL, "uhrzeit_von" TIME NOT NULL, "uhrzeit_bis" TIME NOT NULL, @@ -104,6 +103,7 @@ if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_unterrichtszeiten_typ LIMI "bezeichnung_mehrsprachig" TEXT[] NOT NULL, "aktiv" BOOLEAN DEFAULT true, "hintergrundfarbe" VARCHAR(7), + "ist_standard" BOOLEAN DEFAULT false, "insertamum" TIMESTAMP WITH TIME ZONE DEFAULT now(), "insertvon" VARCHAR(32), "updateamum" TIMESTAMP WITH TIME ZONE DEFAULT now(), @@ -156,12 +156,12 @@ if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_unterrichtszeiten_typ LIMI $qry = " - INSERT INTO lehre.tbl_unterrichtszeiten_typ (unterrichtszeitentyp_kurzbz, bezeichnung_mehrsprachig, aktiv, hintergrundfarbe) VALUES - ('unterrichtszeiten', ARRAY['de:unterrichtszeiten', 'en:teaching times'], 't', '#FFFFFF'), - ('vorlesungen', ARRAY['de:Vorlesung', 'en:Lecture'], 't', '#FF8A8A'), - ('backuptage', ARRAY['de:Übung', 'en:Exercise'], 't', '#8AFF8A'), - ('ausgleichswochen', ARRAY['de:Ausgleichswochen', 'en:Compensation Weeks'], 't', '#8A8AFF'), - ('prüfungswochen', ARRAY['de:Prüfungswochen', 'en:Exam Weeks'], 't', '#FFFF8A'); + INSERT INTO lehre.tbl_unterrichtszeiten_typ (unterrichtszeitentyp_kurzbz, bezeichnung_mehrsprachig, aktiv, hintergrundfarbe, ist_standard) VALUES + ('unterrichtszeiten', ARRAY['Unterrichtszeiten', 'Teaching Times'], 't', '#FFFFFF', 'f'), + ('vorlesungen', ARRAY['Vorlesung', 'Lecture'], 't', '#FF8A8A', 't'), + ('backuptage', ARRAY['Übung', 'Exercise'], 't', '#8AFF8A', 'f'), + ('ausgleichswochen', ARRAY['Ausgleichswochen', 'Compensation Weeks'], 't', '#8A8AFF', 'f'), + ('prüfungswochen', ARRAY['Prüfungswochen', 'Exam Weeks'], 't', '#FFFF8A', 'f'); "; if(!$db->db_query($qry)) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index e35f6aa36..cfd6a28b2 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -57844,13 +57844,13 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Kein Gültigkeitszeitraum für diese Unterrichtszeit gefunden', + 'text' => 'Kein Gültigkeitszeitraum gefunden', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'No validity period found for this class time slot', + 'text' => 'No validity period found', 'description' => '', 'insertvon' => 'system' ), @@ -58636,6 +58636,26 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'errorFetchingClassroomHours', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Fehler beim Abrufen der Unterrichtsstunden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error fetching classroom hours', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), );