Added additional filters to 'classScheduleOverview' component

This commit is contained in:
Ivymaster
2026-04-27 18:09:57 +02:00
parent 7307c1fa44
commit 11f93f0c91
13 changed files with 541 additions and 90 deletions
@@ -61,6 +61,13 @@ class ClassScheduleApi extends FHCAPI_Controller
public function getAllClassTimeValidityPeriods()
{
$entitledOrganizationalUnitsShortCodes = $this->permissionlib->getOE_isEntitledFor('lehre/unterrichtszeiten_gk');
$organizationalUnitShortCode = $this->input->get('organizationalUnitShortCode');
$validityPeriodFrom = $this->input->get('validityPeriodFrom');
$validityPeriodTo = $this->input->get('validityPeriodTo');
$this->ClassTimeSlotValidityPeriodModel->db->where_in('lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', $entitledOrganizationalUnitsShortCodes);
$this->ClassTimeSlotValidityPeriodModel->addSelect(
'lehre.tbl_unterrichtszeiten_gueltigkeit.*,' .
'public.tbl_organisationseinheit.bezeichnung as organisationseinheit_bezeichnung,' .
@@ -72,6 +79,20 @@ class ClassScheduleApi extends FHCAPI_Controller
$this->ClassTimeSlotValidityPeriodModel->addJoin('public.tbl_organisationseinheit', 'public.tbl_organisationseinheit.oe_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_unterrichtszeiten_typ', 'lehre.tbl_unterrichtszeiten_typ.unterrichtszeitentyp_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.unterrichtszeitentyp_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addOrder('gueltig_von', 'DESC');
if ($organizationalUnitShortCode) {
$this->ClassTimeSlotValidityPeriodModel->db->where('lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', $organizationalUnitShortCode);
}
if ($validityPeriodFrom) {
$this->ClassTimeSlotValidityPeriodModel->db
->where('lehre.tbl_unterrichtszeiten_gueltigkeit.gueltig_von >=', date('Y-m-d', strtotime($validityPeriodFrom)));
}
if ($validityPeriodTo) {
$this->ClassTimeSlotValidityPeriodModel->db
->where('lehre.tbl_unterrichtszeiten_gueltigkeit.gueltig_bis <=', date('Y-m-d', strtotime($validityPeriodTo)));
}
$class_time_slot_validity_period_res = $this->ClassTimeSlotValidityPeriodModel->load();
$class_time_slot_validity_period_res = $this->getDataOrTerminateWithError($class_time_slot_validity_period_res);
$this->terminateWithSuccess($class_time_slot_validity_period_res);
@@ -79,6 +100,10 @@ class ClassScheduleApi extends FHCAPI_Controller
public function getAllClassTimeValidityPeriodsPerOrganizationalUnit($organizationUnitId)
{
if (!$this->isUserEntitledForOrganizationalUnit($organizationUnitId, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->ClassTimeSlotValidityPeriodModel->addSelect(
'lehre.tbl_unterrichtszeiten_gueltigkeit.*,' .
'public.tbl_organisationseinheit.bezeichnung as organisationseinheit_bezeichnung,' .
@@ -97,11 +122,24 @@ class ClassScheduleApi extends FHCAPI_Controller
public function getClassTimeValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->ClassTimeSlotValidityPeriodModel->addSelect('lehre.tbl_unterrichtszeiten_gueltigkeit.*, public.tbl_organisationseinheit.oe_kurzbz as oe_kurzbz, lehre.tbl_studienplan.studienplan_id, lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung');
$this->ClassTimeSlotValidityPeriodModel->addSelect('
lehre.tbl_unterrichtszeiten_gueltigkeit.*,
public.tbl_organisationseinheit.oe_kurzbz as oe_kurzbz,
public.tbl_organisationseinheit.bezeichnung as oe_bezeichnung,
public.tbl_organisationseinheit.organisationseinheittyp_kurzbz as oe_organisationseinheittyp_kurzbz,
lehre.tbl_studienplan.studienplan_id,
lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung,
');
$this->ClassTimeSlotValidityPeriodModel->addJoin('public.tbl_organisationseinheit', 'public.tbl_organisationseinheit.oe_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
$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 (!$this->isUserEntitledForOrganizationalUnit($class_time_slot_validity_period_res[0]->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->terminateWithSuccess($class_time_slot_validity_period_res);
}
@@ -126,6 +164,10 @@ class ClassScheduleApi extends FHCAPI_Controller
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
if (!$this->isUserEntitledForOrganizationalUnit($this->input->post('organizationalUnitShortCode'), 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotValidityPeriodModel->insert([
@@ -170,6 +212,10 @@ class ClassScheduleApi extends FHCAPI_Controller
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
if (!$this->isUserEntitledForOrganizationalUnit($this->input->post('organizationalUnitShortCode'), 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotValidityPeriodModel->update($classTimeSlotValidityPeriodId, [
@@ -193,6 +239,12 @@ class ClassScheduleApi extends FHCAPI_Controller
public function deleteClassTimeSlotValidityPeriod($classTimeSlotValidityPeriodId)
{
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriodResult = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriodResult->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId]);
@@ -212,6 +264,12 @@ class ClassScheduleApi extends FHCAPI_Controller
public function getClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriodResult = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriodResult->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->ClassTimeSlotModel->addOrder('insertamum', 'DESC');
$class_time_slots_res = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$class_time_slots_res = $this->getDataOrTerminateWithError($class_time_slots_res);
@@ -223,6 +281,12 @@ class ClassScheduleApi extends FHCAPI_Controller
$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());
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriodResult = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriodResult->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$timeSlotGroupIdentifier = uniqid();
@@ -253,6 +317,12 @@ class ClassScheduleApi extends FHCAPI_Controller
$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());
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriodResult = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriodResult->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$timeSlotGroupIdentifier = uniqid();
@@ -286,6 +356,12 @@ class ClassScheduleApi extends FHCAPI_Controller
}
public function deleteClassTimeSlotsForValidityPeriodPerGroup($classTimeSlotValidityPeriodId, $groupIdentifikator)
{
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriodResult = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriodResult->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId, 'unterrichtszeit_gruppe_identifikator' => $groupIdentifikator]);
@@ -542,5 +618,11 @@ class ClassScheduleApi extends FHCAPI_Controller
return true;
}
private function isUserEntitledForOrganizationalUnit($organizationalUnitShortCode, $requiredPermission)
{
$entitledOrganizationalUnitsShortCodes = $this->permissionlib->getOE_isEntitledFor($requiredPermission);
return in_array($organizationalUnitShortCode, $entitledOrganizationalUnitsShortCodes);
}
}
@@ -30,7 +30,7 @@ class OrganizationalUnitApi extends FHCAPI_Controller
$this->load->library('form_validation');
$this->load->model('education/ClassTimeSlotValidityPeriod_model', "ClassTimeSlotValidityPeriodModel");
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
// Loads phrases system
$this->loadPhrases([
@@ -44,8 +44,11 @@ class OrganizationalUnitApi extends FHCAPI_Controller
public function getAllOrganizationalUnits()
{
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$entitledOrganizationalUnitsShortCodes = $this->permissionlib->getOE_isEntitledFor('basis/organisationseinheit');
$this->OrganisationseinheitModel->db->where_in('oe_kurzbz', $entitledOrganizationalUnitsShortCodes);
$result = $this->OrganisationseinheitModel->load();
$organization_units_result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($organization_units_result);
@@ -22,7 +22,8 @@ class Studienplan extends FHCAPI_Controller
parent::__construct([
'getAllStudyPlans' => self::PERM_LOGGED,
'getStudyPlansByOrganizationalUnitAndSemesterDates' => self::PERM_LOGGED,
'getBySemester' => self::PERM_LOGGED
'getBySemester' => self::PERM_LOGGED,
'getStudyPlan' => self::PERM_LOGGED,
]);
}
@@ -96,4 +97,15 @@ class Studienplan extends FHCAPI_Controller
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function getStudyPlan($id)
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$result = $this->StudienplanModel->loadWhere(['studienplan_id' => $id]);
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
if (!hasData($result)) return $this->terminateWithSuccess(null);
$this->terminateWithSuccess(getData($result)[0]);
}
}
@@ -21,7 +21,8 @@ $includesArray = array(
'public/css/components/verticalsplit.css',
'public/extensions/FHC-Core-Developer/css/FhcMain.css',
'public/css/components/calendar.css',
'public/css/components/classSchedule.css'
'public/css/components/classSchedule.css',
'public/css/components/vue-datepicker.css',
)
);
+2 -1
View File
@@ -16,10 +16,11 @@
*/
export default {
getAllClassTimeValidityPeriods() {
getAllClassTimeValidityPeriods(params) {
return {
method: "get",
url: "/api/frontend/v1/ClassScheduleApi/getAllClassTimeValidityPeriods",
params,
};
},
getAllClassTimeValidityPeriodsPerOrganizationalUnit(organizationUnitId) {
+6
View File
@@ -28,4 +28,10 @@ export default {
},
};
},
getStudyPlan(studienplan_id) {
return {
method: "get",
url: `api/frontend/v1/organisation/studienplan/getStudyPlan/${studienplan_id}`,
};
},
};
+4
View File
@@ -23,6 +23,8 @@ import FhcAlert from "../../plugins/FhcAlert.js";
import Phrasen from "../../plugins/Phrasen.js";
import FhcApi from "../../plugins/Api.js";
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath =
FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, "") +
FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -64,6 +66,8 @@ const app = Vue.createApp({
},
});
app.config.globalProperties.$capitalize = capitalize;
app
.use(router)
.use(primevue.config.default, { zIndex: { overlay: 9999 } })
@@ -115,6 +115,17 @@ 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);
@@ -202,9 +213,14 @@ export default {
return firstTimeSlotFragment + "-" + lastTimeSlotFragment;
},
userLanguage() {
return Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
}
},
methods: {
createOverlay() {
this.hideOverlayClassTimeTypePopover();
let overlayElement;
overlayElement = this.$refs.calendarSelectorContainer.querySelector(
@@ -350,6 +366,8 @@ export default {
this.$refs.calendarSelectorContainer
.querySelector(`#${overlayId}`)
.remove();
this.hideOverlayClassTimeTypePopover();
},
getLineNumberFromSelectedElementNumber(selectedElementNumber) {
let timeSlotsCount = this.timeSlotsInDay.length;
@@ -696,6 +714,7 @@ export default {
this.selected = [];
},
handleOverlayDrop(event) {
this.hideOverlayClassTimeTypePopover();
if (this.$props.isPreviewMode) return;
let dropzoneItem = event.target;
@@ -725,25 +744,22 @@ export default {
`[data-number='${startElementNumber}']`,
);
// get mouse position
const mouseY = event.clientY;
// get delta Y from mouse position to top of the dropzone item
const dropzoneItemRect = dropzoneItem.getBoundingClientRect();
const deltaY = mouseY - dropzoneItemRect.top;
// get top of the start element
const startElementRect = startElement.getBoundingClientRect();
const startElementTop = startElementRect.top;
// add delta Y to top of the start element to get new top for the dropzone item
const newTop = startElementTop + deltaY;
//find which item has the closest top to the new top and get its data-number attribute
const partBodies =
this.$refs.calendarSelectorContainer.querySelectorAll(
"div[data-weekday='" + dropzoneOverlay.weekday + "']",
);
// see which item has the new top in between its top and bottom and get its data-number attribute
let closestPartBody = null;
partBodies.forEach((partBody) => {
const rect = partBody.getBoundingClientRect();
@@ -1439,10 +1455,20 @@ export default {
});
}, 10);
},
hideOverlayClassTimeTypePopover() {
if (this.visiblePopover) {
this.visiblePopover.dispose();
this.visiblePopover = null;
}
},
getClassTimeSlotTypeLabel(classTimeSlotType) {
if (!classTimeSlotType) return "";
return this.userLanguage?.value === 'English' ?
classTimeSlotType.bezeichnung_mehrsprachig[1].value : classTimeSlotType.bezeichnung_mehrsprachig[0].value;
}
},
unmounted() {
this.visiblePopover?.dispose();
this.visiblePopover = null;
this.hideOverlayClassTimeTypePopover();
},
template: /*html*/ `
<div ref="calendarSelectorContainer" >
@@ -1760,7 +1786,7 @@ export default {
:data-type="type.unterrichtszeitentyp_kurzbz"
class="btn btn-sm btn-outline-dark class-schedule-type-selector-option"
>
{{ type.bezeichnung_mehrsprachig[0].value }}
{{ getClassTimeSlotTypeLabel(type) }}
</span>
</div>
</div>
@@ -1,9 +1,13 @@
// Import the Core Filter- and Core RESTClient Component to build your table and handle data
import { CoreFilterCmpt } from "../filter/Filter.js";
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ApiStudienSemester from "../../../js/api/factory/studiensemester.js";
import { formatDate } from "../../helpers/DateHelpers.js";
import ClassScheduleTypeModal from "./ClassScheduleTypeModal.js";
import ClassScheduleValidityPeriodModal from "./ClassScheduleValidityPeriodModal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
import ApiOrganizationalUnit from "../../../js/api/factory/organizationalUnit.js";
export default {
name: "ClassScheduleOverview",
@@ -11,6 +15,8 @@ export default {
CoreFilterCmpt,
ClassScheduleTypeModal,
ClassScheduleValidityPeriodModal,
CoreForm,
FormInput,
},
props: {
permissions: Object,
@@ -22,6 +28,29 @@ export default {
this.permissions["lehre/unterrichtszeiten_typ_w"] || false,
};
},
watch: {
filterData: {
handler(newValue) {
this.$refs.classTimeSlotValidityPeriodsTable.tabulator.setData("/", {
organizationalUnitShortCode:
newValue.selectedOrganizationalUnit?.value,
validityPeriodFrom: newValue.validityPeriodFrom
? formatDate(newValue.validityPeriodFrom, "yyyy-MM-dd")
: null,
validityPeriodTo: newValue.validityPeriodTo
? formatDate(newValue.validityPeriodTo, "yyyy-MM-dd")
: null,
});
},
deep: true,
},
selectedSemester: {
handler(newValue) {
this.filterData.validityPeriodFrom = this.selectedSemester.start;
this.filterData.validityPeriodTo = this.selectedSemester.ende;
},
},
},
data: () => {
return {
phrasesLoaded: false,
@@ -32,14 +61,25 @@ export default {
mondayClassTimeSlots: [],
isClassTimeSlotTypeModalVisible: false,
isClassTimeSlotValidityPeriodModalVisible: false,
organizationalUnits: [],
filteredOrganizationalUnits: [],
allSemesters: [],
filteredSemesters: [],
filterData: {
selectedOrganizationalUnit: null,
validityPeriodFrom: null,
validityPeriodTo: null,
},
selectedSemester: null,
};
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: "dummy",
ajaxRequestFunc: async () =>
await this.getParsedClassTimeSlotValidityPeriodData(),
ajaxRequestFunc: async () => {
return await this.getParsedClassTimeSlotValidityPeriodData();
},
ajaxResponse: (url, params, response) => response,
persistenceID: "core_class_schedule_validity_periods",
selectableRows: true,
@@ -163,8 +203,7 @@ export default {
container.style.width = "100%";
let label = document.createElement("span");
label.textContent =
value + " (" + count + " item" + (count > 1 ? "s" : "") + ")";
label.textContent = value;
container.append(label);
let button = document.createElement("button");
@@ -187,8 +226,7 @@ export default {
container.style.width = "100%";
let label = document.createElement("span");
label.textContent =
value + " (" + count + " item" + (count > 1 ? "s" : "") + ")";
label.textContent = value;
container.append(label);
let button = document.createElement("button");
@@ -250,14 +288,38 @@ export default {
];
return events;
},
dropdownParsedOrganizationalUnits() {
return this.organizationalUnits.map((unit) => {
return {
label: `${unit.bezeichnung} (${unit.organisationseinheittyp_kurzbz})`,
value: unit.oe_kurzbz,
};
});
},
dropdownParsedSemesters() {
return this.allSemesters.map((semester) => {
return {
label: semester.studiensemester_kurzbz,
value: semester.studiensemester_kurzbz,
start: semester.start,
ende: semester.ende,
};
});
}
},
methods: {
test() {
alert("test");
},
async getParsedClassTimeSlotValidityPeriodData() {
let getAllClassTimeValidityPeriodsResponse = await this.$api.call(
ApiClassSchedule.getAllClassTimeValidityPeriods(),
ApiClassSchedule.getAllClassTimeValidityPeriods({
organizationalUnitShortCode:
this.filterData.selectedOrganizationalUnit?.value,
validityPeriodFrom: this.filterData.validityPeriodFrom
? formatDate(this.filterData.validityPeriodFrom, "yyyy-MM-dd")
: null,
validityPeriodTo: this.filterData.validityPeriodTo
? formatDate(this.filterData.validityPeriodTo, "yyyy-MM-dd")
: null,
}),
);
if (getAllClassTimeValidityPeriodsResponse.meta.status === "success") {
@@ -323,6 +385,36 @@ export default {
resetClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = false;
},
filterOrganizationalUnits(event) {
let defaultItem = {
label: '----------',
value: null,
};
const query = event.query.toLowerCase();
if (!query) {
return this.filteredOrganizationalUnits = [defaultItem, ...this.dropdownParsedOrganizationalUnits];
}
return this.filteredOrganizationalUnits = [defaultItem].concat(this.dropdownParsedOrganizationalUnits.filter((unit) => {
return unit.label.toLowerCase().includes(query);
}));
},
filterSemesters(event) {
let defaultItem = {
label: '----------',
value: null,
};
const query = event.query.toLowerCase();
if (!query) {
return this.filteredSemesters = [defaultItem, ...this.dropdownParsedSemesters];
}
return this.filteredSemesters = [defaultItem].concat(this.dropdownParsedSemesters.filter((semester) => {
return semester.label.toLowerCase().includes(query);
}));
}
},
async created() {
let getAllClassTimeValidityPeriodsResponse = await this.$api.call(
@@ -337,6 +429,32 @@ export default {
getAllClassTimeValidityPeriodsResponse.meta.message,
);
}
let getAllOrganizationalUnitsResponse = await this.$api.call(
ApiOrganizationalUnit.getAllOrganizationalUnits(),
);
if (getAllOrganizationalUnitsResponse.meta.status === "success") {
this.organizationalUnits = getAllOrganizationalUnitsResponse.data.sort(
(a, b) => a.bezeichnung.localeCompare(b.bezeichnung),
);
} else {
console.error(
"Error fetching organizational units:",
getAllOrganizationalUnitsResponse.meta.message,
);
}
let getAllSemestersResponse = await this.$api.call(
ApiStudienSemester.getAll('DESC'),
);
if (getAllSemestersResponse.meta.status === "success") {
this.allSemesters = getAllSemestersResponse.data;
} else {
console.error(
"Error fetching semesters:",
getAllSemestersResponse.meta.message,
);
}
},
mounted() {
this.$p
@@ -349,8 +467,9 @@ export default {
<div class="container mt-4">
<h1 class='mb-5'>{{ $p.t("ui", "classScheduleOverviewHeading") }}</h1>
<div class="row mb-3">
<div class="col d-flex justify-content-end">
<a class="btn btn-primary mb-3" @click="showClassTimeSlotTypeModal">{{$p.t('ui', 'addClassTimeSlotTypeButton')}}</a>
<div class="col d-flex justify-content-between">
<a class="btn btn-primary mb-3" @click="showClassTimeSlotValidityPeriodModal">{{$p.t('ui', 'addClassTimeSlotValidityPeriodButton')}}</a>
<a class="btn btn-secondary mb-3" @click="showClassTimeSlotTypeModal">{{$p.t('ui', 'addClassTimeSlotTypeButton')}}</a>
</div>
</div>
<class-schedule-type-modal
@@ -379,11 +498,68 @@ export default {
:side-menu="false"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
:new-btn-label="$p.t('ui', 'addClassTimeSlotValidityPeriodButton')"
new-btn-show
reload
@click:new="showClassTimeSlotValidityPeriodModal"
></core-filter-cmpt>
>
<template #search>
<slot name="filterzuruecksetzen">
<core-form class="d-flex flex-column flex-md-row align-items-md-end gap-3">
<div>
<form-input
v-model="filterData.selectedOrganizationalUnit"
:label="$capitalize($p.t('lehre/organisationseinheit'))"
:suggestions="filteredOrganizationalUnits"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterOrganizationalUnits($event)"
type="autocomplete"
name="organizationalUnitShortCode"
dropdown
forceSelection
>
</form-input>
</div>
<div>
<form-input
v-model="selectedSemester"
:label="$capitalize($p.t('lehre/studiensemester'))"
:suggestions="filteredSemesters"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterSemesters($event)"
type="autocomplete"
name="selectedSemester"
dropdown
forceSelection
>
</form-input>
</div>
<div>
<div class="d-flex align-items-center gap-2">
<form-input
v-model="filterData.validityPeriodFrom"
:label="$p.t('ui', 'validityPeriod') + ' ' + $p.t('ui', 'von')"
:teleport="true"
:enable-time-picker="false"
type="datePicker"
name="validityPeriodFrom"
format="dd.MM.yyyy"
auto-apply
/>
<form-input
v-model="filterData.validityPeriodTo"
:label="$p.t('ui', 'validityPeriod') + ' ' + $p.t('global', 'bis')"
:teleport="true"
:enable-time-picker="false"
type="datePicker"
name="validityPeriodTo"
format="dd.MM.yyyy"
auto-apply
/>
</div>
</div>
</core-form>
</slot>
</template>
</core-filter-cmpt>
</div>
`,
};
@@ -47,9 +47,23 @@ 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,
);
if (!organizationalUnit) {
console.error(
"Organizational unit not found for validity period:",
validityPeriodData,
);
this.$fhcAlert.alertError(this.$p.t("ui", "errorLoadingData"));
return;
}
this.editedClassTimeSlotValidityPeriod = validityPeriodData;
this.classTimeSlotValidityPeriodFormData = {
id: validityPeriodData.unterrichtszeitengueltigkeit_id,
organizationalUnitShortCode: validityPeriodData.oe_kurzbz,
organizationalUnit,
studyPlanId: validityPeriodData.studienplan_id,
classTimeSlotTypeShortcode:
validityPeriodData.unterrichtszeitentyp_kurzbz,
@@ -68,48 +82,29 @@ export default {
this.$fhcAlert.handleSystemError(error);
});
},
async "classTimeSlotValidityPeriodFormData.organizationalUnitShortCode"(
newValue,
) {
if (!newValue) {
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
return;
}
this.refetchFilterableOptions();
"classTimeSlotValidityPeriodFormData.organizationalUnit"() {
this.updateClassTimeSlotValidityPeriodFormDataWatcher();
},
async "classTimeSlotValidityPeriodFormData.validityPeriodFrom"(newValue) {
if (!newValue) {
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
return;
}
this.refetchFilterableOptions();
"classTimeSlotValidityPeriodFormData.validityPeriodFrom"() {
this.updateClassTimeSlotValidityPeriodFormDataWatcher();
},
async "classTimeSlotValidityPeriodFormData.validityPeriodTo"(newValue) {
if (!newValue) {
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
return;
}
this.refetchFilterableOptions();
},
async "classTimeSlotValidityPeriodFormData.studyPlanId"(newValue) {
this.refetchFilterableOptions();
"classTimeSlotValidityPeriodFormData.validityPeriodTo"() {
this.updateClassTimeSlotValidityPeriodFormDataWatcher();
},
},
data: () => {
return {
editedValidityPeriod: null,
isFormVisible: false,
isEditInProgress: false,
organizationalUnits: [],
filteredOrganizationalUnits: [],
studyPlans: [],
studySemesters: [],
studySemestersByNumber: [],
classTimeSlotTypes: [],
classTimeSlotValidityPeriodFormData: {
id: null,
organizationalUnitShortCode: null,
organizationalUnit: null,
studyPlanId: null,
classTimeSlotTypeShortcode: null,
validityPeriodFrom: null,
@@ -120,9 +115,13 @@ export default {
};
},
computed: {
isEditMode() {
return !!this.$props.editedClassTimeSlotValidityPeriodId;
},
isStudyPlanSelectDisabled() {
return (
!this.classTimeSlotValidityPeriodFormData.organizationalUnitShortCode ||
!this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.oe_kurzbz ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodTo
);
@@ -142,23 +141,62 @@ export default {
this.classTimeSlotValidityPeriodFormData.validityPeriodTo,
);
},
userLanguage() {
return Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
},
},
methods: {
updateClassTimeSlotValidityPeriodFormDataWatcher() {
if (
!this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.oe_kurzbz ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodTo
) {
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
return;
}
this.refetchFilterableOptions();
},
async refetchFilterableOptions() {
this.studyPlans = await this.getTargetedStudyPlans(
this.classTimeSlotValidityPeriodFormData.organizationalUnitShortCode,
this.classTimeSlotValidityPeriodFormData.organizationalUnit?.oe_kurzbz,
this.formattedValidityPeriodFrom,
this.formattedValidityPeriodTo,
);
if (this.isEditMode) {
let isStudyPlanStillValid = this.studyPlans.some(
(plan) =>
plan.studienplan_id ===
this.editedClassTimeSlotValidityPeriod.studienplan_id,
);
if (!isStudyPlanStillValid) {
let editedStudyPlan = await this.getStudyPlan(
this.editedClassTimeSlotValidityPeriod.studienplan_id,
);
if (editedStudyPlan) {
this.studyPlans.push(editedStudyPlan);
} else {
console.error(
"Edited study plan not found:",
this.editedClassTimeSlotValidityPeriod.studienplan_id,
);
}
}
}
let studySemestersByDates =
await this.getStudySemestersByOrganizationalUnitAndDates(
this.classTimeSlotValidityPeriodFormData.organizationalUnitShortCode,
this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.oe_kurzbz,
this.formattedValidityPeriodFrom,
this.formattedValidityPeriodTo,
);
let studySemesters = new Array(
let studySemesters = new Array(
...new Set(
studySemestersByDates
.map((s) => s.semester_numbers)
@@ -220,6 +258,24 @@ export default {
? getAllStudyPlansResponse.data
: [];
},
async getStudyPlan(studienplan_id) {
if (!studienplan_id) {
return null;
}
let getStudyPlanResponse = await this.$api.call(
ApiStudienPlan.getStudyPlan(studienplan_id),
);
if (getStudyPlanResponse.meta.status !== "success") {
console.error(
"Error fetching study plan details:",
getStudyPlanResponse.meta.message,
);
return null;
}
return getStudyPlanResponse.data ? getStudyPlanResponse.data : null;
},
async getStudySemestersByOrganizationalUnitAndDates(
organizationalUnitShortCode,
validityPeriodFrom,
@@ -251,13 +307,21 @@ export default {
? getStudySemestersResponse.data
: [];
},
async getStudySemestersByStudyPlanAndDates(studyProgramId, validityPeriodFrom, validityPeriodTo) {
async getStudySemestersByStudyPlanAndDates(
studyProgramId,
validityPeriodFrom,
validityPeriodTo,
) {
if (!studyProgramId || !validityPeriodFrom || !validityPeriodTo) {
return [];
}
let getStudySemestersResponse = await this.$api.call(
ApiStudienSemester.getStudySemestersByStudyPlanAndDates(studyProgramId, validityPeriodFrom, validityPeriodTo),
ApiStudienSemester.getStudySemestersByStudyPlanAndDates(
studyProgramId,
validityPeriodFrom,
validityPeriodTo,
),
);
if (getStudySemestersResponse.meta.status !== "success") {
console.error(
@@ -273,10 +337,12 @@ export default {
createClassTimeSlotValidityPeriod() {
return this.$refs.classTimeSlotValidityPeriodData
.call(
ApiClassSchedule.createClassTimeSlotValidityPeriod(
this.id,
this.classTimeSlotValidityPeriodFormData,
),
ApiClassSchedule.createClassTimeSlotValidityPeriod(this.id, {
...this.classTimeSlotValidityPeriodFormData,
organizationalUnitShortCode:
this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.oe_kurzbz,
}),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
@@ -299,7 +365,12 @@ export default {
ApiClassSchedule.updateClassTimeSlotValidityPeriod(
this.id,
this.classTimeSlotValidityPeriodFormData.id,
this.classTimeSlotValidityPeriodFormData,
{
...this.classTimeSlotValidityPeriodFormData,
organizationalUnitShortCode:
this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.oe_kurzbz,
},
),
)
.then((response) => {
@@ -307,6 +378,9 @@ export default {
this.$refs.classTimeSlotValidityPeriodModal.hide();
this.resetClassTimeSlotValidityPeriodModal();
window.scrollTo(0, 0);
this.editedClassTimeSlotValidityPeriod = null;
this.$emit("classTimeSlotValidityPeriodUpdated");
})
.catch((error) => {
@@ -321,7 +395,7 @@ export default {
this.$refs.classTimeSlotValidityPeriodData?.clearValidation();
this.classTimeSlotValidityPeriodFormData = {
id: null,
organizationalUnitShortCode: null,
organizationalUnit: null,
studyPlanId: null,
classTimeSlotTypeShortcode: null,
validityPeriodFrom: null,
@@ -330,6 +404,26 @@ export default {
description: null,
};
},
filterOrganizationalUnits(event) {
const query = event.query.toLowerCase();
if (!query) {
return (this.filteredOrganizationalUnits = [
...this.organizationalUnits,
]);
}
return (this.filteredOrganizationalUnits =
this.organizationalUnits.filter((unit) => {
let label = `${unit.bezeichnung} (${unit.organisationseinheittyp_kurzbz})`;
return label.toLowerCase().includes(query);
}));
},
getClassTimeSlotTypeLabel(classTimeSlotType) {
if (!classTimeSlotType) return "";
return this.userLanguage?.value === "English"
? classTimeSlotType.bezeichnung_mehrsprachig[1].value
: classTimeSlotType.bezeichnung_mehrsprachig[0].value;
},
},
async created() {
let getAllOrganizationalUnitsResponse = await this.$api.call(
@@ -392,18 +486,17 @@ export default {
<form-validation />
<div class="row mb-3">
<form-input
v-model="classTimeSlotValidityPeriodFormData.organizationalUnitShortCode"
:label="$p.t('lehre/organisationseinheit') + ' *'"
type="select"
v-model="classTimeSlotValidityPeriodFormData.organizationalUnit"
:label="$capitalize($p.t('lehre/organisationseinheit')) + ' *'"
:suggestions="filteredOrganizationalUnits"
:optionValue="(option) => option.kurzbz"
:optionLabel="(option) => option.bezeichnung + ' (' + option.organisationseinheittyp_kurzbz + ')'"
@complete="filterOrganizationalUnits"
dropdown
forceSelection
type="autocomplete"
name="organizationalUnitShortCode"
>
<option
v-for="organizationalUnit in organizationalUnits"
:key="organizationalUnit.oe_kurzbz"
:value="organizationalUnit.oe_kurzbz"
>
{{organizationalUnit.bezeichnung}} - {{ organizationalUnit.organisationseinheittyp_kurzbz }}
</option>
</form-input>
</div>
<div class="row mb-3">
@@ -449,7 +542,7 @@ export default {
:key="studyPlan.studienplan_id"
:value="studyPlan.studienplan_id"
>
{{studyPlan.bezeichnung}} - {{studyPlan.studienplan_id}}
{{studyPlan.bezeichnung}}
</option>
</form-input>
</div>
@@ -478,12 +571,13 @@ export default {
:label="$p.t('ui/classTimeSlotType')"
v-model="classTimeSlotValidityPeriodFormData.classTimeSlotTypeShortcode"
>
<option :value="null"> - </option>
<option
v-for="classTimeSlotType in classTimeSlotTypes"
:key="classTimeSlotType.unterrichtszeitentyp_kurzbz"
:value="classTimeSlotType.unterrichtszeitentyp_kurzbz"
>
{{classTimeSlotType.bezeichnung_mehrsprachig[0].value}} / ({{classTimeSlotType.bezeichnung_mehrsprachig[1].value}})
{{ getClassTimeSlotTypeLabel(classTimeSlotType) }}
</option>
</form-input>
</div>
@@ -248,8 +248,14 @@ export default {
</div>
</div>
<h2>{{ classScheduleValidityPeriodStartDate }} - {{ classScheduleValidityPeriodEndDate }}</h2>
<p>{{ $p.t("lehre", "ausbildungssemester") }}: {{ classTimeSlotValidityPeriod?.ausbildungssemester }}</p>
<p v-html="classTimeSlotValidityPeriod?.anmerkung"></p>
<h4 class="text-capitalize">{{ $p.t("lehre", "organisationseinheit") }}: {{ classTimeSlotValidityPeriod?.oe_bezeichnung }} ({{ classTimeSlotValidityPeriod?.oe_organisationseinheittyp_kurzbz }})</h4>
<h4 class="text-capitalize">{{ $p.t("lehre", "studienplan") }}: {{ classTimeSlotValidityPeriod?.studienplan_bezeichnung }}</h4>
<h5 class="text-capitalize">{{ $p.t("lehre", "ausbildungssemester") }}:
<span class="fw-normal">{{ classTimeSlotValidityPeriod?.ausbildungssemester }}</span>
</h5>
<h5 class="text-capitalize">{{ $p.t("global", "anmerkung") }}:
<span class="fw-normal">{{ classTimeSlotValidityPeriod?.anmerkung }}</span>
</h5>
</div>
<div>
<div v-if='!isClassTimeSlotFormVisible' class="col-12 d-flex justify-content-end">
@@ -290,7 +296,7 @@ export default {
</div>
</div>
<div v-else class="d-flex align-items-center justify-content-center border rounded p-4 mt-4">
<p>{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
<p class="m-0">{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
</div>
</div>
<class-schedule-validity-period-modal
@@ -106,7 +106,7 @@ export default {
</div>
</div>
<div v-else class="d-flex align-items-center justify-content-center border rounded p-2">
<p>{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
<p class="m-0">{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
</div>
</div>
</div>
+40
View File
@@ -58416,6 +58416,46 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'item',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'element',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'item',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'items',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'elemente',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'items',
'description' => '',
'insertvon' => 'system'
)
)
),
);