add class schedule controllers, components, and updated phrases

This commit is contained in:
Ivymaster
2026-04-14 16:21:46 +02:00
parent a29e0aeb62
commit 010b370914
13 changed files with 2888 additions and 0 deletions
+3
View File
@@ -119,6 +119,9 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/prestudent/(:num)'] = 'a
// // (studiensemester_kurzbz)/person/(person_id)
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/person/(:num)'] = 'api/frontend/v1/stv/Students/getPerson/$1/$2';
$route['lehre/ClassSchedule/(:any)/(:any)'] = 'lehre/ClassSchedule/index';
// load routes from extensions, also look for environment-specific configs
$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions'];
@@ -0,0 +1,477 @@
<?php
/**
* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class ClassScheduleApi extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getAllClassTimeValidityPeriods'=> array('lehre/unterrichtszeiten_gk:r'),
'getClassTimeValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
'createClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
'updateClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
'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'),
'getAllClassScheduleTypes' => array('lehre/unterrichtszeiten_typ:r'),
'createClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'),
'updateClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'),
'deleteClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'),
]);
$this->load->library('form_validation');
$this->load->model('education/ClassTimeSlotValidityPeriod_model', "ClassTimeSlotValidityPeriodModel");
$this->load->model('education/ClassTimeSlot_model', "ClassTimeSlotModel");
$this->load->model('education/ClassTimeSlotType_model', "ClassTimeSlotTypeModel");
// Loads phrases system
$this->loadPhrases([
'global'
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getAllClassTimeValidityPeriods()
{
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addOrder('gueltig_von', 'DESC');
$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);
}
public function getClassTimeValidityPeriod($classTimeSlotValidityPeriodId)
{
$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);
$this->terminateWithSuccess($class_time_slot_validity_period_res);
}
public function createClassTimeSlotValidityPeriod()
{
$this->form_validation->set_rules('validityPeriodFrom', 'Validity Period From', 'required|is_valid_date[Y-m-d]');
$this->form_validation->set_rules('validityPeriodTo', 'Validity Period To', 'required|is_valid_date[Y-m-d]|callback_date_greater_equal[validityPeriodFrom]');
$this->form_validation->set_rules('degreeProgramShortcode', 'Degree Program Shortcode', 'required|max_length[32]');
$this->form_validation->set_rules('semester', 'Semester', 'required|is_natural_no_zero|less_than_equal_to[8]');
$this->form_validation->set_rules('classTimeSlotTypeShortcode', 'Class Time Slot Type Shortcode', 'max_length[32]');
$this->form_validation->set_rules('studyPlanId', 'Study Plan ID', 'is_natural_no_zero');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->db->trans_start();
$result = $this->ClassTimeSlotValidityPeriodModel->insert([
'gueltig_von' => $this->input->post('validityPeriodFrom'),
'gueltig_bis' => $this->input->post('validityPeriodTo'),
'oe_kurzbz' => $this->input->post('degreeProgramShortcode'),
'ausbildungssemester' => $this->input->post('semester'),
'anmerkung' => $this->input->post('description'),
'unterrichtszeitentyp_kurzbz' => $this->input->post('classTimeSlotTypeShortcode') ?? '',
'studienplan_id' => $this->input->post('studyPlanId'),
'insertamum' => date('c'),
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
]);
$data = $this->getDataOrTerminateWithError($result);
$this->db->trans_complete();
$this->terminateWithSuccess(true);
$this->terminateWithSuccess($this->input->post());
}
public function updateClassTimeSlotValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->form_validation->set_rules('validityPeriodFrom', 'Validity Period From', 'required|is_valid_date[Y-m-d]');
$this->form_validation->set_rules('validityPeriodTo', 'Validity Period To', 'required|is_valid_date[Y-m-d]|callback_date_greater_equal[validityPeriodFrom]');
$this->form_validation->set_rules('degreeProgramShortcode', 'Degree Program Shortcode', 'required|max_length[32]');
$this->form_validation->set_rules('semester', 'Semester', 'required|is_natural_no_zero|less_than_equal_to[8]');
$this->form_validation->set_rules('classTimeSlotTypeShortcode', 'Class Time Slot Type Shortcode', 'max_length[32]');
$this->form_validation->set_rules('studyPlanId', 'Study Plan ID', 'is_natural_no_zero');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->ClassTimeSlotValidityPeriodModel->update($classTimeSlotValidityPeriodId, [
'gueltig_von' => $this->input->post('validityPeriodFrom'),
'gueltig_bis' => $this->input->post('validityPeriodTo'),
'oe_kurzbz' => $this->input->post('degreeProgramShortcode'),
'ausbildungssemester' => $this->input->post('semester'),
'anmerkung' => $this->input->post('description'),
'unterrichtszeitentyp_kurzbz' => $this->input->post('classTimeSlotTypeShortcode'),
'studienplan_id' => $this->input->post('studyPlanId'),
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(true);
}
public function deleteClassTimeSlotValidityPeriod($classTimeSlotValidityPeriodId)
{
$result = $this->ClassTimeSlotValidityPeriodModel->delete($classTimeSlotValidityPeriodId);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId]);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess(true);
}
public function getClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$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);
$this->terminateWithSuccess($class_time_slots_res);
}
public function createClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->form_validation->set_rules('classTimeSlots', 'Validity Period From', 'callback_validate_items_in_class_time_slots');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->db->trans_start();
$timeSlotGroupIdentifier = uniqid();
foreach ($this->input->post('classTimeSlots') as $timeSlot) {
$result = $this->ClassTimeSlotModel->insert([
'unterrichtszeit_gruppe_identifikator' => $timeSlotGroupIdentifier,
'wochentag' => $timeSlot['wochentag'],
'uhrzeit_von' => $timeSlot['startTime'],
'uhrzeit_bis' => $timeSlot['endTime'],
'unterrichtszeitentyp_kurzbz' => $timeSlot['classTimeSlotTypeShortcode'],
'unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId,
'insertamum' => date('c'),
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
]);
$this->getDataOrTerminateWithError($result);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function editClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->form_validation->set_rules('classTimeSlots', 'Validity Period From', 'callback_validate_items_in_class_time_slots');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->db->trans_start();
$timeSlotGroupIdentifier = uniqid();
foreach ($this->input->post('classTimeSlots') as $timeSlot) {
$data = [
'unterrichtszeit_gruppe_identifikator' => $timeSlotGroupIdentifier,
'wochentag' => $timeSlot['wochentag'],
'uhrzeit_von' => $timeSlot['startTime'],
'uhrzeit_bis' => $timeSlot['endTime'],
'unterrichtszeitentyp_kurzbz' => $timeSlot['classTimeSlotTypeShortcode'],
'unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId,
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
];
if (isset($timeSlot['id'])) {
$result = $this->ClassTimeSlotModel->update($timeSlot['id'], $data);
} else {
$result = $this->ClassTimeSlotModel->insert(array_merge($data, [
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
]));
}
$this->getDataOrTerminateWithError($result);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function deleteClassTimeSlotsForValidityPeriodPerGroup($classTimeSlotValidityPeriodId, $groupIdentifikator)
{
$result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId, 'unterrichtszeit_gruppe_identifikator' => $groupIdentifikator]);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess(true);
}
public function getAllClassScheduleTypes()
{
$filter = $this->input->get('filter');
if ($filter) {
if (isset($filter['aktiv'])) {
$this->ClassTimeSlotTypeModel->db->where('aktiv', $filter['aktiv']);
}
}
$class_schedule_types_res = $this->ClassTimeSlotTypeModel->load();
$class_schedule_types_res = $this->getDataOrTerminateWithError($class_schedule_types_res);
$this->terminateWithSuccess($class_schedule_types_res);
}
public function createClassTimeSlotType()
{
$this->form_validation->set_rules('shortCode', 'Short Code', 'required|max_length[32]');
$this->form_validation->set_rules('descriptions', 'Descriptions', 'callback_validate_descriptions_array');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->db->trans_start();
$descriptions = $this->input->post('descriptions');
$pgArray = $this->arrayToPgArray($descriptions);
$query = 'INSERT INTO lehre.tbl_unterrichtszeiten_typ (
unterrichtszeitentyp_kurzbz,
bezeichnung_mehrsprachig,
aktiv,
insertamum,
insertvon,
updateamum,
updatevon) VALUES (?, ?, ?, ?, ?, ?, ?)';
$result = $this->db->query($query, [
$this->input->post('shortCode'),
$pgArray,
$this->input->post('isActive'),
date('c'),
getAuthUid(),
date('c'),
getAuthUid(),
]);
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function updateClassTimeSlotType($classTimeSlotTypeId)
{
$this->form_validation->set_rules('descriptions', 'Descriptions', 'callback_validate_descriptions_array');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->db->trans_start();
$descriptions = $this->input->post('descriptions');
$pgArray = $this->arrayToPgArray($descriptions);
$query = 'UPDATE lehre.tbl_unterrichtszeiten_typ SET bezeichnung_mehrsprachig = ?, aktiv = ?, updateamum = ?, updatevon = ? WHERE unterrichtszeitentyp_kurzbz = ?';
$result = $this->db->query($query, [
$pgArray,
$this->input->post('isActive'),
date('c'),
getAuthUid(),
$classTimeSlotTypeId,
]);
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function deleteClassTimeSlotType($classTimeSlotTypeId)
{
$result = $this->ClassTimeSlotTypeModel->delete(['unterrichtszeitentyp_kurzbz' => $classTimeSlotTypeId]);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess(true);
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
private function arrayToPgArray(array $assoc) {
$flat = [];
foreach ($assoc as $assocItem) {
$flat[] = $assocItem['lang'] . ':' . $assocItem['value'];
}
$escaped = array_map(function ($v) {
return '"' . addslashes($v) . '"';
}, $flat);
return '{' . implode(',', $escaped) . '}';
}
public function date_greater_equal($toDate, $fromField)
{
$fromDate = $this->input->post($fromField);
// Check if "from" exists
if (!$fromDate) {
$this->form_validation->set_message(
'date_greater_equal',
'Validity Period From is required.'
);
return false;
}
// Validate both dates
if (!strtotime($toDate) || !strtotime($fromDate)) {
$this->form_validation->set_message(
'date_greater_equal',
'Both dates must be valid.'
);
return false;
}
// Compare dates
if (strtotime($toDate) < strtotime($fromDate)) {
$this->form_validation->set_message(
'date_greater_equal',
'The {field} must be greater than or equal to Validity Period From.'
);
return false;
}
return true;
}
public function validate_items_in_class_time_slots($classTimeSlots)
{
// see if $classTimeSlots is an array and has at least one item
if (!is_array($this->input->post('classTimeSlots')) || count($this->input->post('classTimeSlots')) === 0) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
'At least one class time slot is required.'
);
return false;
}
foreach ($this->input->post('classTimeSlots') as $index => $timeSlot) {
if (!isset($timeSlot['wochentag'], $timeSlot['startTime'], $timeSlot['endTime'], $timeSlot['classTimeSlotTypeShortcode'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
'Each class time slot must have a weekday, start time, end time and class time slot type shortcode.'
);
return false;
}
if (!in_array($timeSlot['wochentag'], [1, 2, 3, 4, 5])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
'Weekday must be an integer between 1 (Monday) and 5 (Friday).'
);
return false;
}
log_message('error', 'Validating class time slots: ' . print_r($timeSlot['startTime'], true));
if (!strtotime($timeSlot['startTime']) || !strtotime($timeSlot['endTime'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
'Start time and end time must be valid time strings.'
);
return false;
}
if (strtotime($timeSlot['endTime']) <= strtotime($timeSlot['startTime'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
'End time must be greater than start time.'
);
return false;
}
}
$slotsByDay = [];
foreach ($this->input->post('classTimeSlots') as $timeSlot) {
$slotsByDay[$timeSlot['wochentag']][] = $timeSlot;
}
foreach ($slotsByDay as $day => $slots) {
usort($slots, function ($a, $b) {
return strtotime($a['startTime']) - strtotime($b['startTime']);
});
for ($i = 1; $i < count($slots); $i++) {
if (strtotime($slots[$i]['startTime']) < strtotime($slots[$i - 1]['endTime'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
'Class time slots for each day must not overlap.'
);
return false;
}
}
}
return true;
}
public function validate_descriptions_array($descriptions)
{
$descriptions = $this->input->post('descriptions');
if (!is_array($descriptions) || count($descriptions) === 0) {
$this->form_validation->set_message(
'validate_descriptions_array',
'Descriptions must be a non-empty array.'
);
return false;
}
foreach ($descriptions as $index => $description) {
if (!isset($description['lang'], $description['value'])) {
$this->form_validation->set_message(
'validate_descriptions_array',
'Each description must have a language and a value.'
);
return false;
}
if (empty($description['lang']) || empty($description['value'])) {
$this->form_validation->set_message(
'validate_descriptions_array',
'Language and value in each description must not be empty.'
);
return false;
}
}
return true;
}
}
@@ -0,0 +1,64 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*/
class ClassSchedule extends Auth_Controller
{
private $_uid; // uid of the logged user
/**
* Constructor
*/
public function __construct()
{
parent::__construct(
array(
'index' => array('lehre/unterrichtszeiten_gk:r'),
)
);
$this->load->library('PermissionLib');
$this->load->library('AuthLib');
$this->loadPhrases(
array(
'ui',
'global',
'person',
'abschlusspruefung',
'password',
'lehre'
)
);
$this->_setAuthUID();
$this->setControllerId();
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function index()
{
return $this->load->view('lehre/class_schedule/index.php', [
'permissions' => [
'lehre/unterrichtszeiten_typ_w' => $this->permissionlib->isBerechtigt('lehre/unterrichtszeiten_typ', 'suid'),
],
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Retrieve the UID of the logged user and checks if it is valid
*/
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid) show_error('User authentification failed');
}
}
@@ -0,0 +1,40 @@
<?php
$includesArray = array(
'title' => 'Unterrichtszeiten der Studiengänge',
'vue3' => true,
'axios027' => true,
'bootstrap5' => true,
'tabulator5' => true,
'fontawesome6' => true,
'primevue3' => true,
'navigationcomponent' => true,
'filtercomponent' => true,
'customJSs' => array(
'vendor/vuejs/vuedatepicker_js/vue-datepicker.iife.js',
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/lehre/ClassScheduleApp.js'
),
'customCSSs' => array(
'public/css/components/vue-datepicker.css',
'public/css/components/primevue.css',
'public/css/components/verticalsplit.css',
'public/extensions/FHC-Core-Developer/css/FhcMain.css'
)
);
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="main">
<router-view
cis-root="<?= CIS_ROOT; ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
>
</router-view>
</div>
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
+121
View File
@@ -0,0 +1,121 @@
/**
* 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 <https://www.gnu.org/licenses/>.
*/
export default {
getAllClassTimeValidityPeriods() {
return {
method: "get",
url: "/api/frontend/v1/ClassScheduleApi/getAllClassTimeValidityPeriods",
};
},
getClassTimeValidityPeriod(classTimeSlotValidityPeriodId) {
return {
method: "get",
url: `/api/frontend/v1/ClassScheduleApi/getClassTimeValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
createClassTimeSlotValidityPeriod(userId, params) {
return {
method: "post",
url: "api/frontend/v1/ClassScheduleApi/createClassTimeSlotValidityPeriod/",
params,
};
},
deleteClassTimeSlotValidityPeriod(userId, classTimeSlotValidityPeriodId) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
getClassTimeSlotsForValidityPeriod(classTimeSlotValidityPeriodId) {
return {
method: "get",
url: `api/frontend/v1/ClassScheduleApi/getClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
createClassTimeSlotsForValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
params,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/createClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
params,
};
},
editClassTimeSlotsForValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
params,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/editClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
params,
};
},
updateClassTimeSlotValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
params,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotValidityPeriod/${classTimeSlotValidityPeriodId}`,
params,
};
},
deleteClassTimeSlotsForValidityPeriodPerGroup(
userId,
classTimeSlotValidityPeriodId,
groupIdentifikator,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotsForValidityPeriodPerGroup/${classTimeSlotValidityPeriodId}/${groupIdentifikator}`,
};
},
getAllClassScheduleTypes(queryParams) {
return {
method: "get",
url:
"/api/frontend/v1/ClassScheduleApi/getAllClassScheduleTypes?" +
new URLSearchParams(queryParams).toString(),
};
},
createClassTimeSlotType(params) {
return {
method: "post",
url: "api/frontend/v1/ClassScheduleApi/createClassTimeSlotType/",
params,
};
},
updateClassTimeSlotType(classTimeSlotTypeId, params) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotType/${classTimeSlotTypeId}`,
params,
};
},
deleteClassTimeSlotType(userId, classTimeSlotTypeId) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotType/${classTimeSlotTypeId}`,
};
},
};
+59
View File
@@ -0,0 +1,59 @@
/**
* Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
*/
import ClassScheduleOverview from "../../components/ClassSchedule/ClassScheduleOverview.js";
import ClassScheduleValidityPeriodOverview from "../../components/ClassSchedule/ClassScheduleValidityPeriodOverview.js";
import FhcAlert from "../../plugins/FhcAlert.js";
import Phrasen from "../../plugins/Phrasen.js";
import FhcApi from "../../plugins/Api.js";
const ciPath =
FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, "") +
FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes: [
{
name: "overview",
path: `/${ciPath}/lehre/ClassSchedule`,
component: ClassScheduleOverview,
},
{
name: "validityPeriodOverview",
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/:classTimeSlotValidityPeriodId`,
component: ClassScheduleValidityPeriodOverview,
params: true,
},
],
});
const app = Vue.createApp({
components: {
ClassScheduleOverview,
ClassScheduleValidityPeriodOverview,
},
});
app
.use(router)
.use(primevue.config.default, { zIndex: { overlay: 9999 } })
.use(FhcAlert)
.use(Phrasen)
.use(FhcApi)
.mount("#main");
@@ -0,0 +1,276 @@
// 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 ClassScheduleTypeModal from "./ClassScheduleTypeModal.js";
import ClassScheduleValidityPeriodModal from "./ClassScheduleValidityPeriodModal.js";
export default {
name: "ClassScheduleOverview",
components: {
CoreFilterCmpt,
ClassScheduleTypeModal,
ClassScheduleValidityPeriodModal,
},
props: {
permissions: Object,
},
provide() {
return {
cisRoot: this.cisRoot,
hasLehreUnterrichtszeitenTypWPermission:
this.permissions["lehre/unterrichtszeiten_typ_w"] || false,
};
},
data: () => {
return {
phrasesLoaded: false,
editorParams: null,
classTimeSlotValidityPeriods: [],
classTimeSlotValidityPeriodId: null,
editedClassTimeSlotValidityPeriodId: null,
mondayClassTimeSlots: [],
isClassTimeSlotTypeModalVisible: false,
isClassTimeSlotValidityPeriodModalVisible: false,
};
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: "dummy",
ajaxRequestFunc: () =>
this.$api.call(ApiClassSchedule.getAllClassTimeValidityPeriods()),
ajaxResponse: (url, params, response) => response.data,
persistenceID: "core_class_schedule_validity_periods",
selectableRows: true,
columns: [
{
title: "gueltig von",
field: "gueltig_von",
width: 150,
},
{ title: "gueltig bis", field: "gueltig_bis", width: 150 },
{ title: "orgform kurzbz", field: "orgform_kurzbz", width: 150 },
{
title: "ausbildungssemester",
field: "ausbildungssemester",
width: 150,
},
{
title: "typ",
field: "unterrichtszeitentyp_kurzbz",
},
{
field: "unterrichtszeitengueltigkeit_id",
visible: false,
},
{
title: "Aktionen",
field: "actions",
minWidth: 150,
maxWidth: 150,
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement("div");
container.className = "d-flex gap-2";
let button = document.createElement("button");
button = document.createElement("button");
button.className = "btn btn-outline-secondary btn-action";
button.innerHTML = '<i class="fa fa-eye"></i>';
button.title = this.$p.t(
"classSchedule",
"btn_showClassTimeSlotValidityPeriod",
);
button.addEventListener("click", (event) =>
this.$router.push({
name: "validityPeriodOverview",
params: {
classTimeSlotValidityPeriodId:
cell.getData().unterrichtszeitengueltigkeit_id,
},
}),
);
container.append(button);
button = document.createElement("button");
button.className = "btn btn-outline-secondary btn-action";
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t(
"classSchedule",
"btn_editClassTimeSlotValidityPeriod",
);
button.addEventListener("click", (event) =>
this.editClassTimeSlotValidityPeriod(
cell.getData().unterrichtszeitengueltigkeit_id,
),
);
container.append(button);
button = document.createElement("button");
button.className =
"btn btn-outline-secondary btn-action bg-danger";
button.innerHTML = '<i class="fa fa-xmark text-white"></i>';
button.title = this.$p.t(
"classSchedule",
"btn_deleteClassTimeSlotValidityPeriod",
);
button.addEventListener("click", () => {
let isDeletionConfirmed = confirm(
this.$p.t(
"ui",
"deleteClassTimeSlotValidityPeriodConfirmation",
),
);
if (!isDeletionConfirmed) return;
this.deleteClassTimeSlotValidityPeriod(
cell.getData().unterrichtszeitengueltigkeit_id,
);
});
container.append(button);
return container;
},
frozen: true,
},
],
};
return options;
},
tabulatorEvents() {
const events = [
{
event: "tableBuilt",
handler: async () => {
const setHeader = (field, text) => {
const col =
this.$refs.classTimeSlotValidityPeriodsTable.tabulator.getColumn(
field,
);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector(".tabulator-col-title");
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader("nummer", this.$p.t("wawi", "nummer"));
setHeader("anmerkung", this.$p.t("global", "anmerkung"));
setHeader("retouram", this.$p.t("wawi", "retourdatum"));
setHeader("beschreibung", this.$p.t("global", "beschreibung"));
setHeader("kaution", this.$p.t("infocenter", "kaution"));
setHeader("ausgegebenam", this.$p.t("wawi", "ausgabedatum"));
setHeader("person_id", this.$p.t("person", "person_id"));
setHeader("uid", this.$p.t("person", "uid"));
},
},
];
return events;
},
},
methods: {
showClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = true;
},
editClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
this.editedClassTimeSlotValidityPeriodId = classTimeSlotValidityPeriodId;
},
deleteClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotValidityPeriod(
this.id,
classTimeSlotValidityPeriodId,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
window.scrollTo(0, 0);
this.$refs.classTimeSlotValidityPeriodsTable.reloadTable();
})
.catch((error) => {
console.error(
"Error deleting class time slot validity period:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
showClassTimeSlotTypeModal() {
this.isClassTimeSlotTypeModalVisible = true;
},
resetClassTimeSlotTypeModal() {
this.isClassTimeSlotTypeModalVisible = false;
},
resetClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = false;
},
},
async created() {
let getAllClassTimeValidityPeriodsResponse = await this.$api.call(
ApiClassSchedule.getAllClassTimeValidityPeriods(),
);
if (getAllClassTimeValidityPeriodsResponse.meta.status === "success") {
this.classTimeSlotValidityPeriods =
getAllClassTimeValidityPeriodsResponse.data;
} else {
console.error(
"Error fetching class time slot validity periods:",
getAllClassTimeValidityPeriodsResponse.meta.message,
);
}
},
mounted() {
this.$p
.loadCategory(["global", "lehre", "ui", "gruppenmanagement"])
.then(() => {
this.phrasesLoaded = true;
});
},
template: `
<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>
</div>
<class-schedule-type-modal
:isVisible="isClassTimeSlotTypeModalVisible"
@hideBsModal="resetClassTimeSlotTypeModal"
/>
<class-schedule-validity-period-modal
:isVisible="isClassTimeSlotValidityPeriodModalVisible"
:editedClassTimeSlotValidityPeriodId="editedClassTimeSlotValidityPeriodId"
@hideBsModal="() => { resetClassTimeSlotValidityPeriodModal(); editedClassTimeSlotValidityPeriodId = null; }"
@classTimeSlotValidityPeriodCreated="() => {
$refs.classTimeSlotValidityPeriodsTable.reloadTable();
resetClassTimeSlotValidityPeriodModal();
this.editedClassTimeSlotValidityPeriodId = null;
}"
@classTimeSlotValidityPeriodUpdated="() => {
$refs.classTimeSlotValidityPeriodsTable.reloadTable();
resetClassTimeSlotValidityPeriodModal();
this.editedClassTimeSlotValidityPeriodId = null;
}"
/>
<core-filter-cmpt
v-if="phrasesLoaded"
ref="classTimeSlotValidityPeriodsTable"
table-only
:side-menu="false"
:tabulator-options="tabulatorOptions"
:tabulator-events="[{ event: 'cellEdited'}]"
:new-btn-label="$p.t('ui', 'addClassTimeSlotValidityPeriodButton')"
new-btn-show
reload
@click:new="showClassTimeSlotValidityPeriodModal"
></core-filter-cmpt>
</div>
`,
};
@@ -0,0 +1,252 @@
import { CoreFilterCmpt } from "../filter/Filter.js";
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
export default {
name: "ClassScheduleValidityPeriodForm",
components: {
BsModal,
CoreForm,
FormInput,
},
inject: {
hasLehreUnterrichtszeitenTypWPermission:
"hasLehreUnterrichtszeitenTypWPermission",
},
props: {
isVisible: {
type: Boolean,
required: true,
},
},
watch: {
isVisible(newValue) {
if (newValue) {
this.$refs.classScheduleTypeModal.show();
} else {
this.$refs.classScheduleTypeModal.hide();
}
},
},
data: () => {
return {
isFormVisible: false,
isEditInProgress: false,
editedClassScheduleType: null,
classTimeSlotTypeFormData: {
isActive: true,
shortCode: "",
descriptions: [
{ lang: "de", value: "" },
{ lang: "en", value: "" },
],
},
classScheduleTypes: [],
};
},
methods: {
async getAllClassScheduleTypes() {
let getAllClassScheduleTypeResponse = await this.$api.call(
ApiClassSchedule.getAllClassScheduleTypes(),
);
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 });
}
return {
...type,
bezeichnung_mehrsprachig: descriptions,
};
},
);
} else {
console.error(
"Error fetching class schedule types:",
getAllClassScheduleTypeResponse.meta.message,
);
}
},
createClassTimeSlotType() {
return this.$refs.classTimeSlotTypeForm
.call(
ApiClassSchedule.createClassTimeSlotType({
isActive: this.classTimeSlotTypeFormData.isActive,
shortCode: this.classTimeSlotTypeFormData.shortCode,
descriptions: this.classTimeSlotTypeFormData.descriptions,
}),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$emit("classTimeSlotTypeCreated");
this.getAllClassScheduleTypes();
this.resetClassTimeSlotTypeForm();
});
},
editClassTimeSlotType(classScheduleType) {
this.isEditInProgress = true;
this.isFormVisible = true;
this.editedClassScheduleType = classScheduleType;
this.classTimeSlotTypeFormData = {
isActive: classScheduleType.aktiv,
shortCode: classScheduleType.unterrichtszeitentyp_kurzbz,
descriptions: classScheduleType.bezeichnung_mehrsprachig.map(
(desc) => ({
lang: desc.lang,
value: desc.value,
}),
),
};
},
updateClassTimeSlotType() {
return this.$refs.classTimeSlotTypeForm
.call(
ApiClassSchedule.updateClassTimeSlotType(
this.classTimeSlotTypeFormData.shortCode,
{
isActive: this.classTimeSlotTypeFormData.isActive,
descriptions: this.classTimeSlotTypeFormData.descriptions,
},
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$emit("classTimeSlotTypeUpdated");
this.getAllClassScheduleTypes();
this.resetClassTimeSlotTypeForm();
});
},
deleteClassTimeSlotType(classScheduleTypeShortCode) {
let isConfirmed = confirm(
this.$p.t("ui", "confirmDeleteClassTimeSlotType"),
);
if (!isConfirmed) {
return Promise.resolve();
}
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotType(
this.id,
classScheduleTypeShortCode,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
window.scrollTo(0, 0);
this.getAllClassScheduleTypes();
})
.catch((error) => {
console.error("Error deleting class time slot type:", error);
this.$fhcAlert.handleSystemError(error);
});
},
showClassTimeSlotTypeForm() {
this.isFormVisible = true;
},
hideClassTimeSlotTypeForm() {
this.resetClassTimeSlotTypeForm();
},
resetClassTimeSlotTypeForm() {
this.isEditInProgress = false;
this.isFormVisible = false;
this.classTimeSlotTypeFormData = {
isActive: true,
shortCode: "",
descriptions: [
{ lang: "de", value: "" },
{ lang: "en", value: "" },
],
};
},
},
async created() {
await this.getAllClassScheduleTypes();
},
template: `
<bs-modal ref="classScheduleTypeModal" size="md" @hideBsModal="() => { $emit('hideBsModal'); resetClassTimeSlotTypeForm(); }">
<template #title>
<p v-if="hasLehreUnterrichtszeitenTypWPermission" class="fw-bold mt-3">{{$p.t('ui', 'editClassTimeSlotTypeModalTitle')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('ui', 'existingClassScheduleTypesLabel')}}</p>
</template>
<div v-if="!isFormVisible && hasLehreUnterrichtszeitenTypWPermission" class="row mb-3">
<div class="col d-flex justify-content-end">
<button @click="showClassTimeSlotTypeForm" type="button" class="btn btn-primary">{{$p.t('global', 'create')}}</button>
</div>
</div>
<core-form v-else-if="isFormVisible && hasLehreUnterrichtszeitenTypWPermission" ref="classTimeSlotTypeForm" class="row g-3 pb-3">
<div v-if="!isEditInProgress" class="row mb-3">
<form-input
type="text"
name="shortCode"
:label="$p.t('ui/shortName')"
v-model="classTimeSlotTypeFormData.shortCode"
>
</form-input>
</div>
<div v-else class="row mb-3">
<p class="mb-0"><span class="fw-bold">{{$p.t('ui/shortName')}}:</span> {{editedClassScheduleType.unterrichtszeitentyp_kurzbz}}</p>
</div>
<div class="row mb-3">
<form-input
v-for="description in classTimeSlotTypeFormData.descriptions"
:key="description.lang"
type="textarea"
:name="description.lang"
:label="$p.t('ui/description') + ' (' + description.lang + ')'"
v-model="description.value"
>
</form-input>
</div>
<div class="row mb-3">
<div class="col d-flex align-items-center justify-content-end">
<form-input
type="checkbox"
name="isActive"
:label="$p.t('ui/isActive')"
v-model="classTimeSlotTypeFormData.isActive"
>
</form-input>
</div>
</div>
<div class="col d-flex justify-content-end gap-2">
<button type="button" class="btn btn-primary" @click="isEditInProgress ? updateClassTimeSlotType() : createClassTimeSlotType()">{{$p.t('ui', 'speichern')}}</button>
<button type="button" class="btn btn-secondary" @click="hideClassTimeSlotTypeForm">{{$p.t('ui', 'abbrechen')}}</button>
</div>
</core-form>
<div class="row mb-3">
<p v-if="hasLehreUnterrichtszeitenTypWPermission" class="fw-bold mb-2">{{$p.t('ui', 'existingClassScheduleTypesLabel')}}</p>
<div
>
<div
v-for="classScheduleType in classScheduleTypes"
:key="classScheduleType.unterrichtszeitentyp_kurzbz"
:value="classScheduleType.unterrichtszeitentyp_kurzbz"
:class='{"opacity-50": !classScheduleType.aktiv}'
class=" shadow-sm p-2 mb-2 bg-body rounded"
>
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="badge bg-secondary me-2">
{{classScheduleType.unterrichtszeitentyp_kurzbz}}
</span>
<div v-if="hasLehreUnterrichtszeitenTypWPermission" class="d-flex justify-content-between align-items-center gap-2">
<a href="#" @click.prevent="editClassTimeSlotType(classScheduleType)"><i class="fa fa-edit"></i></a>
<a href="#" @click.prevent="deleteClassTimeSlotType(classScheduleType.unterrichtszeitentyp_kurzbz)"><i class="fa fa-trash text-danger"></i></a>
</div>
</div>
<p>
{{classScheduleType.bezeichnung_mehrsprachig[0].value}} / {{classScheduleType.bezeichnung_mehrsprachig[1].value}}
</p>
</div>
</div>
</div>
</bs-modal>
`,
};
@@ -0,0 +1,241 @@
import { CoreFilterCmpt } from "../filter/Filter.js";
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
import ClassScheduleValidityPeriodFormTimeSlot from "./ClassScheduleValidityPeriodFormTimeSlot.js";
export default {
name: "ClassScheduleValidityPeriodForm",
components: {
BsModal,
CoreForm,
FormInput,
CoreFilterCmpt,
ClassScheduleValidityPeriodFormTimeSlot,
},
props: {
classTimeSlotValidityPeriod: {
type: Object,
required: true,
},
editedClassTimeSlots: {
type: Array,
required: false,
default: () => [],
},
},
emits: ["hideForm", "classTimeSlotsCreated", "classTimeSlotsEdited"],
watch: {
editedClassTimeSlots: {
handler(newVal) {
console.log("editedClassTimeSlots changed:", newVal);
this.classTimeSlots = JSON.parse(JSON.stringify(newVal));
console.log(
"classTimeSlots updated from editedClassTimeSlots:",
this.classTimeSlots,
);
},
immediate: true,
},
},
data: () => {
return {
classTimeSlots: [],
classTimeSlotTypes: [],
};
},
computed: {
areClassTimeSlotsEdited() {
return this.editedClassTimeSlots && this.editedClassTimeSlots.length > 0;
},
},
methods: {
hideForm() {
this.$emit("hideForm");
this.classTimeSlots = [];
},
addClassTimeSlotPerDay(day) {
this.classTimeSlots.push({
identifier: Math.random().toString(36).substr(2, 9),
wochentag: this.getWeekdayNumberFromDay(day),
startTime: "08:00",
endTime: "10:00",
classTimeSlotTypeShortcode: null,
});
},
removeClassTimeSlotPerDay(day, identifier) {
this.classTimeSlots = this.classTimeSlots.filter(
(slot) =>
!(
slot.wochentag === this.getWeekdayNumberFromDay(day) &&
slot.identifier === identifier
),
);
},
createClassTimeSlots() {
return this.$refs.classTimeSlotData
.call(
ApiClassSchedule.createClassTimeSlotsForValidityPeriod(
this.id,
this.$props.classTimeSlotValidityPeriod
.unterrichtszeitengueltigkeit_id,
{
classTimeSlots: this.classTimeSlots,
},
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.classTimeSlots = [];
this.$emit("classTimeSlotsCreated");
})
.catch((error) => {
console.error(
"Error creating class time slot validity period:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
editClassTimeSlots() {
return this.$refs.classTimeSlotData
.call(
ApiClassSchedule.editClassTimeSlotsForValidityPeriod(
this.id,
this.$props.classTimeSlotValidityPeriod
.unterrichtszeitengueltigkeit_id,
{
classTimeSlots: this.classTimeSlots,
},
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.classTimeSlots = [];
this.$emit("classTimeSlotsEdited");
})
.catch((error) => {
console.error(
"Error editing class time slot validity period:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
getWeekdayNumberFromDay(day) {
const days = {
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
saturday: 6,
sunday: 7,
};
return days[day.toLowerCase()] || null;
},
},
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 {
console.error(
"Error fetching class time slot types:",
getAllClassTimeSlotTypesResponse.meta.message,
);
}
},
template: `
<core-form ref="classTimeSlotData" class="shadow rounded p-2 row g-3">
<div class="d-flex gap-2">
<div class="col">
<div class="d-flex align-items-center gap-2">
<h3>Monday</h3>
<a class="ml-auto" @click="addClassTimeSlotPerDay('monday')"><i class="fa fa-plus-circle fs-4"></i></a>
</div>
<class-schedule-validity-period-form-time-slot
v-for="(timeSlot, index) in classTimeSlots.filter(slot => slot.wochentag === 1)"
:key="index"
:class-time-slot-types="classTimeSlotTypes"
@removeClassTimeSlot="removeClassTimeSlotPerDay('monday', timeSlot.identifier)"
v-model="timeSlot"
/>
</div>
<div class="col">
<div class="d-flex align-items-center gap-2">
<h3>Tuesday</h3>
<a class="ml-auto" @click="addClassTimeSlotPerDay('tuesday')"><i class="fa fa-plus-circle fs-4"></i></a>
</div>
<class-schedule-validity-period-form-time-slot
v-for="(timeSlot, index) in classTimeSlots.filter(slot => slot.wochentag === 2)"
:key="index"
:class-time-slot-types="classTimeSlotTypes"
@removeClassTimeSlot="removeClassTimeSlotPerDay('tuesday', timeSlot.identifier)"
v-model="timeSlot"
/>
</div>
<div class="col">
<div class="d-flex align-items-center gap-2">
<h3>Wednesday</h3>
<a class="ml-auto" @click="addClassTimeSlotPerDay('wednesday')"><i class="fa fa-plus-circle fs-4"></i></a>
</div>
<class-schedule-validity-period-form-time-slot
v-for="(timeSlot, index) in classTimeSlots.filter(slot => slot.wochentag === 3)"
:key="index"
:class-time-slot-types="classTimeSlotTypes"
@removeClassTimeSlot="removeClassTimeSlotPerDay('wednesday', timeSlot.identifier)"
v-model="timeSlot"
/>
</div>
<div class="col">
<div class="d-flex align-items-center gap-2">
<h3>Thursday</h3>
<a class="ml-auto" @click="addClassTimeSlotPerDay('thursday')"><i class="fa fa-plus-circle fs-4"></i></a>
</div>
<class-schedule-validity-period-form-time-slot
v-for="(timeSlot, index) in classTimeSlots.filter(slot => slot.wochentag === 4)"
:key="index"
:class-time-slot-types="classTimeSlotTypes"
@removeClassTimeSlot="removeClassTimeSlotPerDay('thursday', timeSlot.identifier)"
v-model="timeSlot"
/>
</div>
<div class="col">
<div class="d-flex align-items-center gap-2">
<h3>Friday</h3>
<a class="ml-auto" @click="addClassTimeSlotPerDay('friday')"><i class="fa fa-plus-circle fs-4"></i></a>
</div>
<class-schedule-validity-period-form-time-slot
v-for="(timeSlot, index) in classTimeSlots.filter(slot => slot.wochentag === 5)"
:key="index"
:class-time-slot-types="classTimeSlotTypes"
@removeClassTimeSlot="removeClassTimeSlotPerDay('friday', timeSlot.identifier)"
v-model="timeSlot"
/>
</div>
</div>
<div class="col-12 d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" @click="hideForm">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="!areClassTimeSlotsEdited" type="button" class="btn btn-primary" @click="createClassTimeSlots">{{$p.t('ui', 'speichern')}}</button>
<button v-else type="button" class="btn btn-primary" @click="editClassTimeSlots">{{$p.t('ui', 'btnAktualisieren')}}</button>
</div>
</core-form>
`,
};
@@ -0,0 +1,84 @@
import { CoreFilterCmpt } from "../filter/Filter.js";
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
export default {
name: "ClassScheduleValidityPeriodForm",
components: {
BsModal,
CoreForm,
FormInput,
CoreFilterCmpt,
},
props: {
modelValue: Object,
classTimeSlotTypes: Array,
},
emits: ["update:modelValue", "removeClassTimeSlot"],
data: () => {
return {
classTimeSlots: [],
};
},
computed: {
selectedClassTimeSlotType() {
return this.classTimeSlotTypes.find(
(type) =>
type.unterrichtszeitentyp_kurzbz ===
this.classTimeSlot.classTimeSlotTypeShortcode,
);
},
classTimeSlot: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
},
},
},
methods: {},
template: `
<div class="d-flex align-items-center mb-2">
<div
:style='[
{backgroundColor: selectedClassTimeSlotType ? selectedClassTimeSlotType.hintergrundfarbe : "#fff"}
]'
class="p-2 shadow d-flex flex-column gap-2 position-relative">
<a class="text-classTimeSlot position-absolute top-0 end-0 p-2 z-3" @click="$emit('removeClassTimeSlot', 'monday', classTimeSlot.identifier)"><i class="fa fa-trash text-danger"></i></a>
<form-input
type="select"
name="classTimeSlotType"
:label="$p.t('ui/classTimeSlotType')"
v-model="classTimeSlot.classTimeSlotTypeShortcode"
>
<option
v-for="classTimeSlotType in $props.classTimeSlotTypes"
:key="classTimeSlotType.unterrichtszeitentyp_kurzbz"
:value="classTimeSlotType.unterrichtszeitentyp_kurzbz"
>
{{classTimeSlotType.unterrichtszeitentyp_kurzbz}} - {{classTimeSlotType.bezeichnung_mehrsprachig[0].value}} / ({{classTimeSlotType.bezeichnung_mehrsprachig[1].value}})
</option>
</form-input>
<div class="d-flex rounded align-items-center justify-content-between gap-1">
<form-input
type="time"
name="classTimeSlotValidityPeriodFrom"
v-model="classTimeSlot.startTime"
/>
<div > - </div>
<form-input
type="time"
name="classTimeSlotValidityPeriodTo"
v-model="classTimeSlot.endTime"
/>
</div>
</div>
</div>
`,
};
@@ -0,0 +1,305 @@
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ApiStudiengang from "../../../js/api/factory/studiengang.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
import FormValidation from "../Form/Validation.js";
import ApiStudienPlan from "../../../js/api/factory/studienplan.js";
export default {
name: "ClassScheduleValidityPeriodModal",
components: {
BsModal,
CoreForm,
FormInput,
FormValidation,
},
props: {
isVisible: {
type: Boolean,
required: true,
},
editedClassTimeSlotValidityPeriodId: {
type: Number,
default: null,
},
},
emits: [
"hideBsModal",
"classTimeSlotValidityPeriodCreated",
"classTimeSlotValidityPeriodUpdated",
],
watch: {
isVisible(newValue) {
if (newValue) {
this.$refs.classTimeSlotValidityPeriodModal.show();
} else {
this.$refs.classTimeSlotValidityPeriodModal.hide();
}
},
editedClassTimeSlotValidityPeriodId(newValue) {
if (!newValue) return;
return this.$api
.call(ApiClassSchedule.getClassTimeValidityPeriod(newValue))
.then((response) => {
let validityPeriodData = response.data[0];
this.classTimeSlotValidityPeriodFormData = {
id: validityPeriodData.unterrichtszeitengueltigkeit_id,
degreeProgramShortcode: validityPeriodData.oe_kurzbz,
studyPlanId: validityPeriodData.studienplan_id,
classTimeSlotTypeShortcode:
validityPeriodData.unterrichtszeitentyp_kurzbz,
validityPeriodFrom: validityPeriodData.gueltig_von,
validityPeriodTo: validityPeriodData.gueltig_bis,
semester: validityPeriodData.ausbildungssemester,
description: validityPeriodData.anmerkung,
};
this.$refs.classTimeSlotValidityPeriodModal.show();
})
.catch((error) => {
console.error(
"Error fetching class time slot validity period details:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
},
data: () => {
return {
isFormVisible: false,
isEditInProgress: false,
degreePrograms: [],
studyPlans: [],
classTimeSlotTypes: [],
classTimeSlotValidityPeriodFormData: {
id: null,
degreeProgramShortcode: null,
studyPlanId: null,
classTimeSlotTypeShortcode: null,
validityPeriodFrom: null,
validityPeriodTo: null,
semester: null,
description: null,
},
};
},
methods: {
createClassTimeSlotValidityPeriod() {
return this.$refs.classTimeSlotValidityPeriodData
.call(
ApiClassSchedule.createClassTimeSlotValidityPeriod(
this.id,
this.classTimeSlotValidityPeriodFormData,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$refs.classTimeSlotValidityPeriodModal.hide();
this.resetClassTimeSlotValidityPeriodModal();
window.scrollTo(0, 0);
this.$emit("classTimeSlotValidityPeriodCreated");
})
.catch((error) => {
console.error(
"Error creating class time slot validity period:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
updateClassTimeSlotValidityPeriod() {
return this.$refs.classTimeSlotValidityPeriodData
.call(
ApiClassSchedule.updateClassTimeSlotValidityPeriod(
this.id,
this.classTimeSlotValidityPeriodFormData.id,
this.classTimeSlotValidityPeriodFormData,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$refs.classTimeSlotValidityPeriodModal.hide();
this.resetClassTimeSlotValidityPeriodModal();
window.scrollTo(0, 0);
this.$emit("classTimeSlotValidityPeriodUpdated");
})
.catch((error) => {
console.error(
"Error updating class time slot validity period:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
resetClassTimeSlotValidityPeriodModal() {
this.$refs.classTimeSlotValidityPeriodData?.clearValidation();
this.classTimeSlotValidityPeriodFormData = {
id: null,
degreeProgramShortcode: null,
studyPlanId: null,
classTimeSlotTypeShortcode: null,
validityPeriodFrom: null,
validityPeriodTo: null,
semester: null,
description: null,
};
},
},
async created() {
let getAllDegreeProgramsResponse = await this.$api.call(
ApiStudiengang.getAllDegreePrograms(),
);
if (getAllDegreeProgramsResponse.meta.status === "success") {
this.degreePrograms = getAllDegreeProgramsResponse.data;
} else {
console.error(
"Error fetching degree programs:",
getAllDegreeProgramsResponse.meta.message,
);
}
let getAllStudyPlansResponse = await this.$api.call(
ApiStudienPlan.getAllStudyPlans(),
);
if (getAllStudyPlansResponse.meta.status === "success") {
this.studyPlans = getAllStudyPlansResponse.data;
} else {
console.error(
"Error fetching study plans:",
getAllStudyPlansResponse.meta.message,
);
}
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 {
console.error(
"Error fetching class time slot types:",
getAllClassTimeSlotTypesResponse.meta.message,
);
}
},
template: `
<bs-modal ref="classTimeSlotValidityPeriodModal" @hideBsModal="() => { $emit('hideBsModal'); resetClassTimeSlotValidityPeriodModal(); }" size="md">
<template #title>
<p v-if="!classTimeSlotValidityPeriodFormData.id" class="fw-bold mt-3">{{$p.t('ui', 'addClassTimeSlotValidityPeriodModalTitle')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('ui', 'editClassTimeSlotValidityPeriodModalTitle')}}</p>
</template>
<core-form class="row g-3" ref="classTimeSlotValidityPeriodData">
<form-validation />
<div class="row mb-3">
<form-input
type="select"
name="degreeProgramShortcode"
:label="$p.t('lehre/studiengang') + ' *'"
v-model="classTimeSlotValidityPeriodFormData.degreeProgramShortcode"
>
<option
v-for="degreeProgram in degreePrograms"
:key="degreeProgram.studiengang_kz"
:value="degreeProgram.studiengang_kz"
>
{{degreeProgram.kurzbzlang}} - {{degreeProgram.bezeichnung}})
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
name="studyPlan"
:label="$p.t('lehre/studienplan')"
v-model="classTimeSlotValidityPeriodFormData.studyPlanId"
>
<option
v-for="studyPlan in studyPlans"
:key="studyPlan.studienplan_id"
:value="studyPlan.studienplan_id"
>
{{studyPlan.orgform_kurzbz}} - {{studyPlan.sprache}} - {{studyPlan.semesterwochen}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
name="classTimeSlotType"
:label="$p.t('ui/classTimeSlotType')"
v-model="classTimeSlotValidityPeriodFormData.classTimeSlotTypeShortcode"
>
<option
v-for="classTimeSlotType in classTimeSlotTypes"
:key="classTimeSlotType.unterrichtszeitentyp_kurzbz"
:value="classTimeSlotType.unterrichtszeitentyp_kurzbz"
>
{{classTimeSlotType.unterrichtszeitentyp_kurzbz}} - {{classTimeSlotType.bezeichnung_mehrsprachig[0].value}} / ({{classTimeSlotType.bezeichnung_mehrsprachig[1].value}})
</option>
</form-input>
</div>
<div class="row mb-3">
<div class="col-12 mb-3">
<label>{{$p.t('ui', 'validityPeriod')}}</label>
</div>
<div class="col">
<form-input
type="date"
name="validityPeriodFrom"
:label="$p.t('ui/von') + ' *'"
v-model="classTimeSlotValidityPeriodFormData.validityPeriodFrom"
/>
</div>
<div class="col">
<form-input
type="date"
name="validityPeriodTo"
:label="$p.t('global/bis') + ' *'"
v-model="classTimeSlotValidityPeriodFormData.validityPeriodTo"
/>
</div>
</div>
<div class="row mb-3">
<form-input
type="select"
id="ausbildungssemester"
name="semester"
:label="$p.t('lehre', 'ausbildungssemester')+ '*'"
v-model="classTimeSlotValidityPeriodFormData.semester"
>
<option v-for="sem in Array.from({length:8}).map((u,i) => i+1)" :key="sem" :value="sem">{{sem}}. Semester</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="textarea"
name="beschreibung"
:label="$p.t('global/beschreibung')"
v-model="classTimeSlotValidityPeriodFormData.description"
>
</form-input>
</div>
</core-form>
<template #footer>
<button v-if="!classTimeSlotValidityPeriodFormData.id" type="button" class="btn btn-primary" @click="createClassTimeSlotValidityPeriod">{{$p.t('ui', 'speichern')}}</button>
<button v-else type="button" class="btn btn-primary" @click="updateClassTimeSlotValidityPeriod">{{$p.t('ui', 'btnAktualisieren')}}</button>
</template>
</bs-modal>
`,
};
@@ -0,0 +1,386 @@
import { CoreFilterCmpt } from "../filter/Filter.js";
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
import ClassScheduleValidityPeriodForm from "./ClassScheduleValidityPeriodForm.js";
import ClassScheduleValidityPeriodModal from "./ClassScheduleValidityPeriodModal.js";
export default {
name: "ClassScheduleValidityPeriodOverview",
components: {
BsModal,
CoreForm,
FormInput,
CoreFilterCmpt,
ClassScheduleValidityPeriodForm,
ClassScheduleValidityPeriodModal,
},
data: () => {
return {
isClassTimeSlotFormVisible: false,
classTimeSlotValidityPeriodId: null,
classTimeSlotValidityPeriod: null,
classTimeSlots: [],
classTimeSlotTypes: [],
editedClassTimeSlots: [],
isClassTimeSlotValidityPeriodModalVisible: false,
editedClassTimeSlotValidityPeriodId: null,
};
},
computed: {
classScheduleValidityPeriodStartDate() {
if (!this.classTimeSlotValidityPeriod) return null;
let dateParts = this.classTimeSlotValidityPeriod.gueltig_von
.split("-")
.reverse();
return dateParts.join("/");
},
classScheduleValidityPeriodEndDate() {
if (!this.classTimeSlotValidityPeriod) return null;
let dateParts = this.classTimeSlotValidityPeriod.gueltig_bis
.split("-")
.reverse();
return dateParts.join("/");
},
},
methods: {
async fetchClassTimeValidityPeriod() {
let getClassTimeValidityPeriodResponse = await this.$api.call(
ApiClassSchedule.getClassTimeValidityPeriod(
this.classTimeSlotValidityPeriodId,
),
);
if (getClassTimeValidityPeriodResponse.meta.status === "success") {
this.classTimeSlotValidityPeriod =
getClassTimeValidityPeriodResponse.data[0];
if (!this.classTimeSlotValidityPeriod) {
this.$fhcAlert.alertError(
this.$p.t("ui", "classTimeSlotValidityPeriodNotFound"),
);
this.$router.push({ name: "overview" });
}
} else {
console.error(
"Error fetching class time slot validity period:",
getClassTimeValidityPeriodResponse.meta.message,
);
}
},
async fetchClassTimeSlots() {
let getClassTimeSlotsForValidityPeriodResponse = await this.$api.call(
ApiClassSchedule.getClassTimeSlotsForValidityPeriod(
this.classTimeSlotValidityPeriodId,
),
);
if (
getClassTimeSlotsForValidityPeriodResponse.meta.status === "success"
) {
let classTimeSlotsGroupedByWeek = [];
getClassTimeSlotsForValidityPeriodResponse.data.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],
});
}
});
this.classTimeSlots = classTimeSlotsGroupedByWeek;
} else {
console.error(
"Error fetching class time slots for validity period:",
this.classTimeSlots,
);
}
},
showClassTimeSlotForm() {
this.isClassTimeSlotFormVisible = true;
},
async editClassTimeSlotsForValidityPeriodPerGroup(groupIdentifikator) {
console.log("Editing class time slots for group:", groupIdentifikator);
await this.fetchClassTimeSlots();
console.log(
this.classTimeSlots.filter(
(group) => group.groupIdentifikator === groupIdentifikator,
),
);
this.editedClassTimeSlots =
this.classTimeSlots
.filter(
(group) => group.groupIdentifikator === groupIdentifikator,
)?.[0]
.slots.map((slot) => {
return {
...slot,
id: slot.unterrichtszeit_id,
startTime: slot.uhrzeit_von,
endTime: slot.uhrzeit_bis,
classTimeSlotTypeShortcode: slot.unterrichtszeitentyp_kurzbz,
};
}) || [];
console.log(this.editedClassTimeSlots);
this.showClassTimeSlotForm();
},
deleteClassTimeSlotsForValidityPeriodPerGroup(groupIdentifikator) {
let isDeletionConfirmed = confirm(
this.$p.t("ui", "confirmDeleteClassTimeSlotsForGroup"),
);
if (!isDeletionConfirmed) {
return;
}
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotsForValidityPeriodPerGroup(
this.id,
this.classTimeSlotValidityPeriodId,
groupIdentifikator,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
this.classTimeSlots = [];
this.fetchClassTimeSlots();
})
.catch((error) => {
console.error("Error deleting class time slots per group:", error);
this.$fhcAlert.handleSystemError(error);
});
},
getClassTimeSlotType(classTimeSlot) {
let classTimeSlotType = this.classTimeSlotTypes.find(
(type) =>
type.unterrichtszeitentyp_kurzbz ===
classTimeSlot.unterrichtszeitentyp_kurzbz,
);
return classTimeSlotType;
},
getClassTimeSlotBackgroundColor(classTimeSlot) {
let classTimeSlotType = this.classTimeSlotTypes.find(
(type) =>
type.unterrichtszeitentyp_kurzbz ===
classTimeSlot.unterrichtszeitentyp_kurzbz,
);
return classTimeSlotType ? classTimeSlotType.hintergrundfarbe : "#fff";
},
editClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
this.editedClassTimeSlotValidityPeriodId = classTimeSlotValidityPeriodId;
},
deleteClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
let isDeletionConfirmed = confirm(
this.$p.t("ui", "deleteClassTimeSlotValidityPeriodConfirmation"),
);
if (!isDeletionConfirmed) return;
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotValidityPeriod(
this.id,
classTimeSlotValidityPeriodId,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
this.$router.push({ name: "overview" });
})
.catch((error) => {
console.error(
"Error deleting class time slot validity period:",
error,
);
this.$fhcAlert.handleSystemError(error);
});
},
resetClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = false;
},
},
async created() {
this.classTimeSlotValidityPeriodId =
this.$route.params.classTimeSlotValidityPeriodId;
this.fetchClassTimeValidityPeriod();
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 {
console.error(
"Error fetching class time slot types:",
getAllClassTimeSlotTypesResponse.meta.message,
);
}
this.fetchClassTimeSlots();
},
template: `
<div class="container mt-4">
<div class='mb-5'>
<div class="d-flex align-items-center justify-content-between mb-2">
<div class="d-flex align-items-center gap-3">
<h1 class='m-0'>
{{ $p.t("ui", "classScheduleValidityPeriodOverviewHeading") }}
</h1>
<span class="m-0 badge bg-secondary">{{ classTimeSlotValidityPeriod?.orgform_kurzbz }}</span>
</div>
<div class="d-flex align-items-center gap-2">
<a
@click="editClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId)"
class="btn btn-link fs-3 p-0">
<i class="fa fa-edit"></i></a>
<a @click="deleteClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId)" class="btn btn-link text-danger fs-3 p-0"><i class="fa fa-trash"></i></a>
</div>
</div>
<h2>{{ classScheduleValidityPeriodStartDate }} - {{ classScheduleValidityPeriodEndDate }}</h2>
<p v-html="classTimeSlotValidityPeriod?.anmerkung"></p>
</div>
<div>
<div v-if='!isClassTimeSlotFormVisible' class="col-12 d-flex justify-content-end">
<button type="button" class="btn btn-primary" @click="showClassTimeSlotForm">{{$p.t('ui', 'addClassTimeSlotButton')}}</button>
</div>
<class-schedule-validity-period-form
v-else
:class-time-slot-validity-period="classTimeSlotValidityPeriod"
:edited-class-time-slots="editedClassTimeSlots"
@classTimeSlotsCreated="() => { isClassTimeSlotFormVisible = false; fetchClassTimeSlots(); this.editedClassTimeSlots = []; }"
@classTimeSlotsEdited="() => { isClassTimeSlotFormVisible = false; fetchClassTimeSlots(); this.editedClassTimeSlots = []; }"
@hideForm="() => { isClassTimeSlotFormVisible = false; this.editedClassTimeSlots = []; }"
class="mb-4"
/>
<div>
<h4>{{ $p.t("ui", "classScheduleValidityPeriodTimeSlots") }}</h4>
</div>
<div v-if="classTimeSlots && Object.keys(classTimeSlots).length > 0">
<div v-for="(classTimeSlotsPerWeek, index) in classTimeSlots" :key="index" class="row border-top rounded p-2 mt-4 mb-2">
<div class="col-12 d-flex align-items-center justify-content-end gap-2">
<a class="ml-auto" @click="editClassTimeSlotsForValidityPeriodPerGroup(classTimeSlotsPerWeek.groupIdentifikator)"><i class="fa fa-edit fs-5"></i></a>
<a class="ml-auto" @click="deleteClassTimeSlotsForValidityPeriodPerGroup(classTimeSlotsPerWeek.groupIdentifikator)"><i class="fa fa-trash text-danger fs-5"></i></a>
</div>
<div class="col">
<h3>Monday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 1)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
<div class="col">
<h3>Tuesday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 2)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
<div class="col">
<h3>Wednesday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 3)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
<div class="col">
<h3>Thursday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 4)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
<div class="col">
<h3>Friday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 5)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
<div class="col">
<h3>Saturday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 6)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
<div class="col">
<h3>Sunday</h3>
<div v-for="(classTimeSlot, innerIndex) in classTimeSlotsPerWeek.slots.filter(slot => slot.wochentag === 7)" :key="innerIndex" class="d-flex align-items-center mb-2">
<p
class='p-2 m-0 rounded'
:style="{backgroundColor: getClassTimeSlotBackgroundColor(classTimeSlot)}"
:title="getClassTimeSlotType(classTimeSlot) ? getClassTimeSlotType(classTimeSlot).bezeichnung_mehrsprachig.find(desc => desc.lang === 'de').value : ''"
>
{{ classTimeSlot.uhrzeit_von }} - {{ classTimeSlot.uhrzeit_bis }}
</p>
</div>
</div>
</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>
</div>
</div>
<class-schedule-validity-period-modal
:isVisible="isClassTimeSlotValidityPeriodModalVisible"
:editedClassTimeSlotValidityPeriodId="editedClassTimeSlotValidityPeriodId"
@hideBsModal="() => { resetClassTimeSlotValidityPeriodModal(); editedClassTimeSlotValidityPeriodId = null; }"
@classTimeSlotValidityPeriodUpdated="() => {
resetClassTimeSlotValidityPeriodModal();
this.editedClassTimeSlotValidityPeriodId = null;
fetchClassTimeValidityPeriod();
}"
/>
</div>
`,
};
+580
View File
@@ -57276,6 +57276,586 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'montag',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Montag',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Monday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'dienstag',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Dienstag',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Tuesday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'mittwoch',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Mittwoch',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Wednesday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'donnerstag',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Donnerstag',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Thursday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'freitag',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Freitag',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Friday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'samstag',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Samstag',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Saturday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'sonntag',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sonntag',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Sunday',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'deleteClassTimeSlotValidityPeriodConfirmation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Möchten Sie wirklich diese Unterrichtszeitgültigkeit löschen?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Do you really want to delete this class time slot validity period?',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'classScheduleValidityPeriodOverviewHeading',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Übersicht Unterrichtszeitgültigkeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Class Time Slot Validity Period Overview',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'addClassTimeSlotValidityPeriodModalTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeitgültigkeit hinzufügen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Add Class Time Slot Validity Period',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'addClassTimeSlotValidityPeriodButton',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeitgültigkeit hinzufügen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Add Class Time Slot Validity Period',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'editClassTimeSlotValidityPeriodModalTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeitgültigkeit bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Edit Class Time Slot Validity Period',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'confirmDeleteClassTimeSlotsForGroup',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Möchten Sie wirklich diese Unterrichtszeiten löschen?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Do you really want to delete these class time slots?',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'addClassTimeSlotTypeModalTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeittyp hinzufügen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Add Class Time Slot Type',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'editClassTimeSlotTypeModalTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeittyp bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Edit Class Time Slot Type',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'shortName',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'kurzbezeichnung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'short name',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'description',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'beschreibung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'description',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'existingClassScheduleTypesLabel',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Vorhandene Unterrichtszeittypen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Existing Class Schedule Types',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'confirmDeleteClassTimeSlotType',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Möchten Sie wirklich diesen Unterrichtszeittyp löschen?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Do you really want to delete this class time slot type?',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'classTimeSlotType',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeittyp',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Class Time Slot Type',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'isActive',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Aktiv',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Active',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'validityPeriod',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Gültigkeitszeitraum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Validity Period',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'addClassTimeSlotTypeButton',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeittyp hinzufügen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Add Class Time Slot Type',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'addClassTimeSlotTypeButton',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeittyp hinzufügen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Add Class Time Slot Type',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'classScheduleValidityPeriodTimeSlots',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeiten für diesen Gültigkeitszeitraum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Class time slots for this validity period',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'noClassScheduleValidityPeriodTimeSlotsFound',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Keine Unterrichtszeiten für diesen Gültigkeitszeitraum gefunden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No class time slots found for this validity period',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'classScheduleOverviewHeading',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Übersicht Unterrichtszeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Class Schedule Overview',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'addClassTimeSlotButton',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Unterrichtszeit hinzufügen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Add Class Time Slot',
'description' => '',
'insertvon' => 'system'
),
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'classTimeSlotValidityPeriodNotFound',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kein Gültigkeitszeitraum für diese Unterrichtszeit gefunden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No validity period found for this class time slot',
'description' => '',
'insertvon' => 'system'
),
)
),
// ### Phrases Dashboard Admin END
);