mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-14 02:29:27 +00:00
add prefiltering on validity period form component
This commit is contained in:
@@ -120,7 +120,7 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/prestudent/(:num)'] = 'a
|
||||
$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';
|
||||
$route['lehre/ClassSchedule/.*'] = 'lehre/ClassSchedule/index';
|
||||
|
||||
// load routes from extensions, also look for environment-specific configs
|
||||
$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions'];
|
||||
|
||||
@@ -27,6 +27,7 @@ class ClassScheduleApi extends FHCAPI_Controller
|
||||
{
|
||||
parent::__construct([
|
||||
'getAllClassTimeValidityPeriods'=> array('lehre/unterrichtszeiten_gk:r'),
|
||||
'getAllClassTimeValidityPeriodsPerOrganizationalUnit' => array('lehre/unterrichtszeiten_gk:r'),
|
||||
'getClassTimeValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
|
||||
'createClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
|
||||
'updateClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
|
||||
@@ -60,14 +61,40 @@ class ClassScheduleApi extends FHCAPI_Controller
|
||||
|
||||
public function getAllClassTimeValidityPeriods()
|
||||
{
|
||||
$this->ClassTimeSlotValidityPeriodModel->addSelect(
|
||||
'lehre.tbl_unterrichtszeiten_gueltigkeit.*,' .
|
||||
'public.tbl_organisationseinheit.bezeichnung as organisationseinheit_bezeichnung,' .
|
||||
'public.tbl_organisationseinheit.organisationseinheittyp_kurzbz as organisationseinheit_organisationseinheittyp_kurzbz,' .
|
||||
'lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung,' .
|
||||
'lehre.tbl_unterrichtszeiten_typ.bezeichnung_mehrsprachig as unterrichtszeiten_typ_bezeichnung_mehrsprachig, '
|
||||
);
|
||||
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
|
||||
$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');
|
||||
$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 getAllClassTimeValidityPeriodsPerOrganizationalUnit($organizationUnitId)
|
||||
{
|
||||
$this->ClassTimeSlotValidityPeriodModel->addSelect(
|
||||
'lehre.tbl_unterrichtszeiten_gueltigkeit.*,' .
|
||||
'public.tbl_organisationseinheit.bezeichnung as organisationseinheit_bezeichnung,' .
|
||||
'public.tbl_organisationseinheit.organisationseinheittyp_kurzbz as organisationseinheit_organisationseinheittyp_kurzbz,' .
|
||||
'lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung,' .
|
||||
'lehre.tbl_unterrichtszeiten_typ.bezeichnung_mehrsprachig as unterrichtszeiten_typ_bezeichnung_mehrsprachig, '
|
||||
);
|
||||
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
|
||||
$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');
|
||||
$class_time_slot_validity_period_res = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz' => $organizationUnitId]);
|
||||
$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->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');
|
||||
|
||||
@@ -21,6 +21,7 @@ class Studienplan extends FHCAPI_Controller
|
||||
// TODO(chris): access!
|
||||
parent::__construct([
|
||||
'getAllStudyPlans' => self::PERM_LOGGED,
|
||||
'getStudyPlansByOrganizationalUnitAndSemesterDates' => self::PERM_LOGGED,
|
||||
'getBySemester' => self::PERM_LOGGED
|
||||
]);
|
||||
}
|
||||
@@ -33,6 +34,26 @@ class Studienplan extends FHCAPI_Controller
|
||||
$this->terminateWithSuccess($studien_plan_result);
|
||||
}
|
||||
|
||||
public function getStudyPlansByOrganizationalUnitAndSemesterDates($organizationalUnitShortCode)
|
||||
{
|
||||
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('organisation/Studienordnung_model', 'StudienordnungModel');
|
||||
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
|
||||
|
||||
$startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]')));
|
||||
$endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]')));
|
||||
if (!$startDate || !$endDate) {
|
||||
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Start- oder Enddatum']), self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
$studyPlansResponse = $this->StudienplanModel->getStudyPlansForOrganizationalUnitAndDatesQueryResponse($organizationalUnitShortCode, $startDate, $endDate);
|
||||
if (isError($studyPlansResponse)) $this->terminateWithError(getError($studyPlansResponse), self::ERROR_TYPE_DB);
|
||||
if (!hasData($studyPlansResponse)) return $this->terminateWithSuccess(null);
|
||||
|
||||
return $this->terminateWithSuccess($this->getDataOrTerminateWithError($studyPlansResponse));
|
||||
}
|
||||
|
||||
public function getBySemester()
|
||||
{
|
||||
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
|
||||
|
||||
@@ -26,7 +26,9 @@ class Studiensemester extends FHCAPI_Controller
|
||||
'getAll' => self::PERM_LOGGED,
|
||||
'getAktNext' => self::PERM_LOGGED,
|
||||
'getStudienjahrByStudiensemester' => self::PERM_LOGGED,
|
||||
'getAllStudiensemesterAndAktOrNext' => self::PERM_LOGGED
|
||||
'getAllStudiensemesterAndAktOrNext' => self::PERM_LOGGED,
|
||||
'getStudySemestersByOrganizationalUnitAndDates' => self::PERM_LOGGED,
|
||||
'getStudySemestersByStudyPlanAndDates' => self::PERM_LOGGED,
|
||||
)
|
||||
);
|
||||
// Load model StudiensemesterModel
|
||||
@@ -166,4 +168,51 @@ class Studiensemester extends FHCAPI_Controller
|
||||
|
||||
$this->terminateWithSuccess(array($studiensemester, $aktuell));
|
||||
}
|
||||
|
||||
public function getStudySemestersByOrganizationalUnitAndDates($organizationalUnitShortCode)
|
||||
{
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('organisation/Studienordnung_model', 'StudienordnungModel');
|
||||
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
|
||||
|
||||
$startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]')));
|
||||
$endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]')));
|
||||
if (!$startDate || !$endDate) {
|
||||
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Start- oder Enddatum']), self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
$studyPlansResponse = $this->StudienplanModel->getStudyPlansForOrganizationalUnitQueryResponse($organizationalUnitShortCode);
|
||||
if (isError($studyPlansResponse)) $this->terminateWithError(getError($studyPlansResponse), self::ERROR_TYPE_DB);
|
||||
if (!hasData($studyPlansResponse)) return $this->terminateWithSuccess(null);
|
||||
|
||||
$studyPlans = $this->getDataOrTerminateWithError($studyPlansResponse);
|
||||
$studyPlansIds = array_map(function ($studyPlan) { return $studyPlan->studienplan_id; }, $studyPlans);
|
||||
|
||||
$studySemestersResponse = $this->StudiensemesterModel->getStudySemestersByStudyPlansAndDatesQueryResponse($studyPlansIds, $startDate, $endDate);
|
||||
if (isError($studySemestersResponse)) $this->terminateWithError(getError($studySemestersResponse), self::ERROR_TYPE_DB);
|
||||
if (!hasData($studySemestersResponse)) return $this->terminateWithSuccess([]);
|
||||
|
||||
$studySemesters = $this->getDataOrTerminateWithError($studySemestersResponse);
|
||||
|
||||
return $this->terminateWithSuccess($studySemesters);
|
||||
}
|
||||
|
||||
public function getStudySemestersByStudyPlanAndDates()
|
||||
{
|
||||
$studyPlansId = $this->input->get('filter[studyPlanId]');
|
||||
|
||||
$startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]')));
|
||||
$endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]')));
|
||||
if (!$startDate || !$endDate) {
|
||||
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Start- oder Enddatum']), self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
$studySemestersResponse = $this->StudiensemesterModel->getStudySemestersByStudyPlansAndDatesQueryResponse([$studyPlansId], $startDate, $endDate);
|
||||
if (isError($studySemestersResponse)) $this->terminateWithError(getError($studySemestersResponse), self::ERROR_TYPE_DB);
|
||||
if (!hasData($studySemestersResponse)) return $this->terminateWithSuccess([]);
|
||||
|
||||
$studySemesters = $this->getDataOrTerminateWithError($studySemestersResponse);
|
||||
|
||||
return $this->terminateWithSuccess($studySemesters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,5 +168,30 @@ class Studienplan_model extends DB_Model
|
||||
return $this->loadWhere([
|
||||
'person_id' => $person_id
|
||||
]);
|
||||
}
|
||||
|
||||
public function getStudyPlansForOrganizationalUnitQueryResponse($organizationalUnitShortCode)
|
||||
{
|
||||
$query = "SELECT sp.* FROM lehre.tbl_studienplan sp
|
||||
JOIN lehre.tbl_studienordnung so ON sp.studienordnung_id = so.studienordnung_id
|
||||
JOIN public.tbl_studiengang sg ON so.studiengang_kz = sg.studiengang_kz
|
||||
WHERE sg.oe_kurzbz = ?";
|
||||
|
||||
return $this->execReadOnlyQuery($query, array($organizationalUnitShortCode));
|
||||
}
|
||||
|
||||
public function getStudyPlansForOrganizationalUnitAndDatesQueryResponse($organizationalUnitShortCode, $startDate, $endDate)
|
||||
{
|
||||
$query = "SELECT DISTINCT sp.* FROM lehre.tbl_studienplan sp
|
||||
JOIN lehre.tbl_studienordnung so ON sp.studienordnung_id = so.studienordnung_id
|
||||
JOIN public.tbl_studiengang sg ON so.studiengang_kz = sg.studiengang_kz
|
||||
AND sg.oe_kurzbz = ? AND sp.studienplan_id IN (
|
||||
SELECT sps.studienplan_id
|
||||
FROM lehre.tbl_studienplan_semester sps
|
||||
JOIN public.tbl_studiensemester ss ON ss.studiensemester_kurzbz = sps.studiensemester_kurzbz
|
||||
WHERE (ss.start >= ? AND ss.ende <= ?) OR (ss.start <= ? AND ss.ende >= ?)
|
||||
)";
|
||||
|
||||
return $this->execReadOnlyQuery($query, array($organizationalUnitShortCode, $startDate, $endDate, $startDate, $endDate));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,4 +347,24 @@ class Studiensemester_model extends DB_Model
|
||||
$result = $this->load($studiensemester_kurzbz);
|
||||
return hasData($result);
|
||||
}
|
||||
|
||||
public function getStudySemestersByStudyPlansAndDatesQueryResponse($studyPlanIds, $fromDate, $toDate)
|
||||
{
|
||||
$subQuery = "(
|
||||
SELECT ARRAY_AGG(DISTINCT sp.semester)
|
||||
FROM lehre.tbl_studienplan_semester sp
|
||||
WHERE sp.studienplan_id IN ?
|
||||
AND sp.studiensemester_kurzbz = s.studiensemester_kurzbz
|
||||
) AS semester_numbers";
|
||||
|
||||
$query = "SELECT DISTINCT s.*," . $subQuery . "
|
||||
FROM public.tbl_studiensemester s
|
||||
JOIN lehre.tbl_studienplan_semester sp ON sp.studiensemester_kurzbz = s.studiensemester_kurzbz
|
||||
WHERE sp.studienplan_id IN ? AND (
|
||||
(s.start >= ? AND s.ende <= ?) OR
|
||||
(s.start <= ? AND s.ende >= ?)
|
||||
)";
|
||||
|
||||
return $this->execQuery($query, array($studyPlanIds, $studyPlanIds, $fromDate, $toDate, $fromDate, $toDate));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.fhc-resize-vertical {
|
||||
cursor: ns-resize;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.fhc-drag-handle:hover {
|
||||
@@ -21,4 +21,24 @@
|
||||
|
||||
.fhc-w-fit {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.tabulator-group-level-0 {
|
||||
background-color: #c6c6c6 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tabulator-group-level-1 {
|
||||
background-color: #f5f5f5 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tabulator-cell {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
div[role="row"] {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ export default {
|
||||
url: "/api/frontend/v1/ClassScheduleApi/getAllClassTimeValidityPeriods",
|
||||
};
|
||||
},
|
||||
getAllClassTimeValidityPeriodsPerOrganizationalUnit(organizationUnitId) {
|
||||
return {
|
||||
method: "get",
|
||||
url: `/api/frontend/v1/ClassScheduleApi/getAllClassTimeValidityPeriodsPerOrganizationalUnit/${organizationUnitId}`,
|
||||
};
|
||||
},
|
||||
getClassTimeValidityPeriod(classTimeSlotValidityPeriodId) {
|
||||
return {
|
||||
method: "get",
|
||||
|
||||
@@ -5,12 +5,27 @@ export default {
|
||||
url: "api/frontend/v1/organisation/studienplan/getAllStudyPlans",
|
||||
};
|
||||
},
|
||||
getStudienplaeneBySemester(studiengang_kz, studiensemester_kurzbz, ausbildungssemester, orgform_kurzbz)
|
||||
{
|
||||
return {
|
||||
method: 'get',
|
||||
url: 'api/frontend/v1/organisation/studienplan/getBySemester',
|
||||
params: { studiengang_kz, studiensemester_kurzbz, ausbildungssemester, orgform_kurzbz },
|
||||
};
|
||||
}
|
||||
}
|
||||
getStudyPlansByOrganizationalUnitAndSemesterDates(organizationalUnitShortCode, startDate, endDate) {
|
||||
return {
|
||||
method: "get",
|
||||
url: `api/frontend/v1/organisation/studienplan/getStudyPlansByOrganizationalUnitAndSemesterDates/${organizationalUnitShortCode}?filter[startDate]=${startDate}&filter[endDate]=${endDate}`,
|
||||
};
|
||||
},
|
||||
getStudienplaeneBySemester(
|
||||
studiengang_kz,
|
||||
studiensemester_kurzbz,
|
||||
ausbildungssemester,
|
||||
orgform_kurzbz,
|
||||
) {
|
||||
return {
|
||||
method: "get",
|
||||
url: "api/frontend/v1/organisation/studienplan/getBySemester",
|
||||
params: {
|
||||
studiengang_kz,
|
||||
studiensemester_kurzbz,
|
||||
ausbildungssemester,
|
||||
orgform_kurzbz,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,5 +29,17 @@ export default {
|
||||
url: 'api/frontend/v1/organisation/studiensemester/getAll',
|
||||
params: { order, start }
|
||||
};
|
||||
}
|
||||
},
|
||||
getStudySemestersByOrganizationalUnitAndDates(organizationalUnitShortCode, startDate, endDate) {
|
||||
return {
|
||||
method: "get",
|
||||
url: `api/frontend/v1/organisation/studiensemester/getStudySemestersByOrganizationalUnitAndDates/${organizationalUnitShortCode}?filter[startDate]=${startDate}&filter[endDate]=${endDate}`,
|
||||
};
|
||||
},
|
||||
getStudySemestersByStudyPlanAndDates(studyPlanId, startDate, endDate) {
|
||||
return {
|
||||
method: "get",
|
||||
url: `api/frontend/v1/organisation/studiensemester/getStudySemestersByStudyPlanAndDates?filter[studyPlanId]=${studyPlanId}&filter[startDate]=${startDate}&filter[endDate]=${endDate}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
import ClassScheduleOverview from "../../components/ClassSchedule/ClassScheduleOverview.js";
|
||||
import ClassScheduleValidityPeriodOverview from "../../components/ClassSchedule/ClassScheduleValidityPeriodOverview.js";
|
||||
import ClassScheduleOrgUnitGroupedValidityPeriodsOverview from "../../components/ClassSchedule/ClassScheduleOrgUnitGroupedValidityPeriodsOverview.js";
|
||||
|
||||
import FhcAlert from "../../plugins/FhcAlert.js";
|
||||
import Phrasen from "../../plugins/Phrasen.js";
|
||||
@@ -34,6 +35,18 @@ const router = VueRouter.createRouter({
|
||||
path: `/${ciPath}/lehre/ClassSchedule`,
|
||||
component: ClassScheduleOverview,
|
||||
},
|
||||
{
|
||||
name: "validityPeriodsOverviewByOrganizationUnit",
|
||||
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/groupedOverviewByOrganizationUnit/:organizationalUnitShortCode`,
|
||||
component: ClassScheduleOrgUnitGroupedValidityPeriodsOverview,
|
||||
params: true,
|
||||
},
|
||||
{
|
||||
name: "validityPeriodsOverviewByOrganizationUnitAndStudyPlan",
|
||||
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/groupedOverviewByOrganizationUnit/:organizationalUnitShortCode/studyPlan/:studyPlanId`,
|
||||
component: ClassScheduleOrgUnitGroupedValidityPeriodsOverview,
|
||||
params: true,
|
||||
},
|
||||
{
|
||||
name: "validityPeriodOverview",
|
||||
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/:classTimeSlotValidityPeriodId`,
|
||||
@@ -47,6 +60,7 @@ const app = Vue.createApp({
|
||||
components: {
|
||||
ClassScheduleOverview,
|
||||
ClassScheduleValidityPeriodOverview,
|
||||
ClassScheduleOrgUnitGroupedValidityPeriodsOverview,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import draggable from "../../directives/draggable.js";
|
||||
import drop from "../../directives/drop.js";
|
||||
import FormInput from "../Form/Input.js";
|
||||
|
||||
export default {
|
||||
name: "ClassScheduleCalendarSelector",
|
||||
components: {
|
||||
FormInput,
|
||||
},
|
||||
directives: {
|
||||
draggable,
|
||||
drop,
|
||||
@@ -30,91 +26,91 @@ export default {
|
||||
emits: ["overlaysChanged"],
|
||||
watch: {
|
||||
editedOverlays: {
|
||||
handler(newVal) {
|
||||
setTimeout(() => {
|
||||
this.overlays = [];
|
||||
async handler(newVal) {
|
||||
await this.$nextTick();
|
||||
|
||||
this.$refs.calendarContainer
|
||||
.querySelectorAll("div[id^='overlay-']")
|
||||
.forEach((element) => {
|
||||
element.remove();
|
||||
});
|
||||
newVal
|
||||
.map((slot) => {
|
||||
return {
|
||||
...slot,
|
||||
startingTimeSlot: this.timeSlotsInDay.find((timeSlot) =>
|
||||
timeSlot.startsWith(slot.startTime.substr(0, 5)),
|
||||
),
|
||||
endingTimeSlot: this.timeSlotsInDay.find((timeSlot) =>
|
||||
timeSlot.endsWith(slot.endTime.substr(0, 5)),
|
||||
),
|
||||
};
|
||||
})
|
||||
.forEach((overlay) => {
|
||||
let firstElementDataNumber =
|
||||
(overlay.weekday - 1) * this.timeSlotsInDay.length +
|
||||
this.timeSlotsInDay.indexOf(overlay.startingTimeSlot);
|
||||
let lastElementDataNumber =
|
||||
(overlay.weekday - 1) * this.timeSlotsInDay.length +
|
||||
this.timeSlotsInDay.indexOf(overlay.endingTimeSlot);
|
||||
|
||||
let firstSelectedElement =
|
||||
this.$refs.calendarSelectorContainer.querySelector(
|
||||
"div[data-number='" + firstElementDataNumber + "']",
|
||||
);
|
||||
let lastSelectedElement =
|
||||
this.$refs.calendarSelectorContainer.querySelector(
|
||||
"div[data-number='" + lastElementDataNumber + "']",
|
||||
);
|
||||
if (!firstSelectedElement || !lastSelectedElement) {
|
||||
this.$fhcAlert.alertError(
|
||||
this.$p.t("ui", "classTimeSlotLoadingErrorMessage"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
firstSelectedElement.style.backgroundColor =
|
||||
this.selectedTimeSlotLabelColor;
|
||||
lastSelectedElement.style.backgroundColor =
|
||||
this.selectedTimeSlotLabelColor;
|
||||
|
||||
this.currentFirstSelectedElementNumber = firstSelectedElement
|
||||
? parseInt(firstSelectedElement.getAttribute("data-number"))
|
||||
: null;
|
||||
|
||||
this.currentLastSelectedElementNumber = lastSelectedElement
|
||||
? parseInt(lastSelectedElement.getAttribute("data-number"))
|
||||
: null;
|
||||
|
||||
this.createOverlay();
|
||||
|
||||
this.$refs.calendarSelectorContainer
|
||||
.querySelectorAll("div[class*='part-body']")
|
||||
.forEach((child) => {
|
||||
child.style.backgroundColor = this.defaultTimeSlotLabelColor;
|
||||
});
|
||||
|
||||
this.currentFirstSelectedElementNumber = null;
|
||||
this.currentLastSelectedElementNumber = null;
|
||||
});
|
||||
|
||||
this.overlays = this.overlays.map((existingOverlay, index) => {
|
||||
let existingOverlayInNewVal = newVal[index];
|
||||
|
||||
existingOverlay.databaseId = existingOverlayInNewVal.databaseId;
|
||||
existingOverlay.type = existingOverlayInNewVal.type;
|
||||
existingOverlay.hexColor =
|
||||
this.classTimeSlotTypes.find(
|
||||
(type) =>
|
||||
type.unterrichtszeitentyp_kurzbz ===
|
||||
existingOverlayInNewVal.type,
|
||||
)?.hintergrundfarbe || null;
|
||||
return {
|
||||
...existingOverlay,
|
||||
};
|
||||
this.overlays = [];
|
||||
await this.$nextTick();
|
||||
this.$refs.calendarContainer
|
||||
.querySelectorAll("div[id^='overlay-']")
|
||||
.forEach((element) => {
|
||||
element.remove();
|
||||
});
|
||||
}, 500);
|
||||
newVal
|
||||
.map((slot) => {
|
||||
return {
|
||||
...slot,
|
||||
startingTimeSlot: this.timeSlotsInDay.find((timeSlot) =>
|
||||
timeSlot.startsWith(slot.startTime.substr(0, 5)),
|
||||
),
|
||||
endingTimeSlot: this.timeSlotsInDay.find((timeSlot) =>
|
||||
timeSlot.endsWith(slot.endTime.substr(0, 5)),
|
||||
),
|
||||
};
|
||||
})
|
||||
.forEach((overlay) => {
|
||||
let firstElementDataNumber =
|
||||
(overlay.weekday - 1) * this.timeSlotsInDay.length +
|
||||
this.timeSlotsInDay.indexOf(overlay.startingTimeSlot);
|
||||
let lastElementDataNumber =
|
||||
(overlay.weekday - 1) * this.timeSlotsInDay.length +
|
||||
this.timeSlotsInDay.indexOf(overlay.endingTimeSlot);
|
||||
|
||||
let firstSelectedElement =
|
||||
this.$refs.calendarSelectorContainer.querySelector(
|
||||
"div[data-number='" + firstElementDataNumber + "']",
|
||||
);
|
||||
let lastSelectedElement =
|
||||
this.$refs.calendarSelectorContainer.querySelector(
|
||||
"div[data-number='" + lastElementDataNumber + "']",
|
||||
);
|
||||
if (!firstSelectedElement || !lastSelectedElement) {
|
||||
this.$fhcAlert.alertError(
|
||||
this.$p.t("ui", "classTimeSlotLoadingErrorMessage"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
firstSelectedElement.style.backgroundColor =
|
||||
this.selectedTimeSlotLabelColor;
|
||||
lastSelectedElement.style.backgroundColor =
|
||||
this.selectedTimeSlotLabelColor;
|
||||
|
||||
this.currentFirstSelectedElementNumber = firstSelectedElement
|
||||
? parseInt(firstSelectedElement.getAttribute("data-number"))
|
||||
: null;
|
||||
|
||||
this.currentLastSelectedElementNumber = lastSelectedElement
|
||||
? parseInt(lastSelectedElement.getAttribute("data-number"))
|
||||
: null;
|
||||
|
||||
this.createOverlay();
|
||||
|
||||
this.$refs.calendarSelectorContainer
|
||||
.querySelectorAll("div[class*='part-body']")
|
||||
.forEach((child) => {
|
||||
child.style.backgroundColor = this.defaultTimeSlotLabelColor;
|
||||
});
|
||||
|
||||
this.currentFirstSelectedElementNumber = null;
|
||||
this.currentLastSelectedElementNumber = null;
|
||||
});
|
||||
|
||||
this.overlays = this.overlays.map((existingOverlay, index) => {
|
||||
let existingOverlayInNewVal = newVal[index];
|
||||
|
||||
existingOverlay.databaseId = existingOverlayInNewVal.databaseId;
|
||||
existingOverlay.type = existingOverlayInNewVal.type;
|
||||
existingOverlay.hexColor =
|
||||
this.classTimeSlotTypes.find(
|
||||
(type) =>
|
||||
type.unterrichtszeitentyp_kurzbz ===
|
||||
existingOverlayInNewVal.type,
|
||||
)?.hintergrundfarbe || null;
|
||||
return {
|
||||
...existingOverlay,
|
||||
};
|
||||
});
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
@@ -129,13 +125,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
daysInWeek: [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
this.$p.t("ui", "monday"),
|
||||
this.$p.t("ui", "tuesday"),
|
||||
this.$p.t("ui", "wednesday"),
|
||||
this.$p.t("ui", "thursday"),
|
||||
this.$p.t("ui", "friday"),
|
||||
this.$p.t("ui", "saturday"),
|
||||
this.$p.t("ui", "sunday"),
|
||||
],
|
||||
timeSlotsInDay: [
|
||||
"08:00-08:45",
|
||||
@@ -173,6 +169,8 @@ export default {
|
||||
x: null,
|
||||
y: null,
|
||||
},
|
||||
currentlyEditedOverlayId: null,
|
||||
visiblePopover: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -209,15 +207,9 @@ export default {
|
||||
createOverlay() {
|
||||
let overlayElement;
|
||||
|
||||
if (!this.$props.isPreviewMode) {
|
||||
overlayElement = this.$refs.calendarSelectorContainer.querySelector(
|
||||
"#overlays-container",
|
||||
).children[0];
|
||||
} else {
|
||||
overlayElement = this.$refs.calendarSelectorContainer.querySelector(
|
||||
"#overlays-container-preview",
|
||||
).children[0];
|
||||
}
|
||||
overlayElement = this.$refs.calendarSelectorContainer.querySelector(
|
||||
"#overlays-container",
|
||||
).children[0];
|
||||
|
||||
let firstSelectedChild =
|
||||
this.$refs.calendarSelectorContainer.querySelector(
|
||||
@@ -709,11 +701,15 @@ export default {
|
||||
let dropzoneItem = event.target;
|
||||
if (!dropzoneItem) return;
|
||||
|
||||
|
||||
if (!dropzoneItem.getAttribute("data-time")) {
|
||||
let dropzoneOverlay = this.overlays.find((overlay) => overlay.id === dropzoneItem.id);
|
||||
let dropzoneOverlay = this.overlays.find(
|
||||
(overlay) => overlay.id === dropzoneItem.id,
|
||||
);
|
||||
if (!dropzoneOverlay) {
|
||||
console.error("Could not find overlay for dropzone item with id " + dropzoneItem.id);
|
||||
console.error(
|
||||
"Could not find overlay for dropzone item with id " +
|
||||
dropzoneItem.id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -726,7 +722,7 @@ export default {
|
||||
|
||||
let startElementNumber = dropzoneOverlay.startingTimeSlotElementNumber;
|
||||
let startElement = this.$refs.calendarSelectorContainer.querySelector(
|
||||
`[data-number='${startElementNumber}']`
|
||||
`[data-number='${startElementNumber}']`,
|
||||
);
|
||||
|
||||
// get mouse position
|
||||
@@ -742,9 +738,10 @@ export default {
|
||||
// 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 + "']",
|
||||
);
|
||||
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;
|
||||
@@ -756,7 +753,7 @@ export default {
|
||||
});
|
||||
if (!closestPartBody) return;
|
||||
|
||||
dropzoneItem = closestPartBody;
|
||||
dropzoneItem = closestPartBody;
|
||||
}
|
||||
|
||||
let newStartTimeSlot =
|
||||
@@ -1351,6 +1348,101 @@ export default {
|
||||
overlay.endingTimeSlotElementNumber
|
||||
);
|
||||
},
|
||||
getOverlayClassScheduleTypeTitle(overlayId) {
|
||||
let overlay = this.overlays.find((overlay) => overlay.id === overlayId);
|
||||
if (!overlay) return "";
|
||||
|
||||
let typeDescriptions =
|
||||
this.classTimeSlotTypes.find(
|
||||
(type) => type.unterrichtszeitentyp_kurzbz === overlay.type,
|
||||
)?.bezeichnung_mehrsprachig || "";
|
||||
if (!typeDescriptions) return "";
|
||||
|
||||
return typeDescriptions[0].value || "";
|
||||
},
|
||||
handleChangeClassTimeSlotTypeForOverlay(newType) {
|
||||
let classTimeSlotType = this.classTimeSlotTypes.find(
|
||||
(type) => type.unterrichtszeitentyp_kurzbz === newType,
|
||||
);
|
||||
if (!classTimeSlotType) {
|
||||
console.error(
|
||||
"Could not find class time slot type for newType: " + newType,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.overlays = this.overlays.map((overlay) => {
|
||||
if (overlay.id === this.currentlyEditedOverlayId) {
|
||||
return {
|
||||
...overlay,
|
||||
type: classTimeSlotType.unterrichtszeitentyp_kurzbz,
|
||||
hexColor: classTimeSlotType.hintergrundfarbe,
|
||||
};
|
||||
}
|
||||
return overlay;
|
||||
});
|
||||
|
||||
this.currentlyEditedOverlayId = null;
|
||||
},
|
||||
showOverlayClassTimeTypePopover(overlayId) {
|
||||
let overlayElement = this.$refs.calendarSelectorContainer.querySelector(
|
||||
`#${overlayId}`,
|
||||
);
|
||||
if (!overlayElement) return;
|
||||
|
||||
if (this.visiblePopover) {
|
||||
this.visiblePopover.dispose();
|
||||
this.visiblePopover = null;
|
||||
return;
|
||||
}
|
||||
this.visiblePopover = new bootstrap.Popover(overlayElement, {
|
||||
title: this.$p.t("ui", "classTimeSlotType"),
|
||||
html: true,
|
||||
content: this.$refs.classScheduleTypeSelectorContainer.innerHTML,
|
||||
placement: "left",
|
||||
});
|
||||
this.visiblePopover.show();
|
||||
setTimeout(() => {
|
||||
document
|
||||
.querySelectorAll(".class-schedule-type-selector-option")
|
||||
.forEach((option) => {
|
||||
option.addEventListener("click", (event) => {
|
||||
let selectedTypeDescription = event.currentTarget.innerText;
|
||||
|
||||
let selectedType = this.classTimeSlotTypes.find((type) =>
|
||||
type.bezeichnung_mehrsprachig.some(
|
||||
(desc) => desc.value === selectedTypeDescription,
|
||||
),
|
||||
);
|
||||
if (!selectedType) {
|
||||
console.error(
|
||||
"Could not find class time slot type for selected description: " +
|
||||
selectedTypeDescription,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.overlays = this.overlays.map((overlay) => {
|
||||
if (overlay.id === overlayId) {
|
||||
return {
|
||||
...overlay,
|
||||
type: selectedType.unterrichtszeitentyp_kurzbz,
|
||||
hexColor: selectedType.hintergrundfarbe,
|
||||
};
|
||||
}
|
||||
return overlay;
|
||||
});
|
||||
|
||||
this.visiblePopover.dispose();
|
||||
this.visiblePopover = null;
|
||||
});
|
||||
});
|
||||
}, 10);
|
||||
},
|
||||
},
|
||||
unmounted() {
|
||||
this.visiblePopover?.dispose();
|
||||
this.visiblePopover = null;
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div ref="calendarSelectorContainer" >
|
||||
@@ -1601,21 +1693,34 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!isPreviewMode" id="overlays-container" class='d-none'>
|
||||
<div id="overlays-container" class='d-none'>
|
||||
<div
|
||||
v-for="(index) in 50"
|
||||
v-draggable:copyLink.noimage="selectedDragObject"
|
||||
@mousedown='overlaySelectionChanged($event, "overlay-item-" + index)'
|
||||
@mouseover="handleMouseOverOnOverlay('overlay-item-' + index)"
|
||||
:id="'overlay-item-' + index"
|
||||
:class="{
|
||||
'fhc-drag-handle': !$props.isPreviewMode,
|
||||
}"
|
||||
:style="{ backgroundColor: this.overlays.find(overlay => overlay.id === 'overlay-item-' + index)?.hexColor || this.defaultOverlayColor }"
|
||||
class="d-none fhc-pointer-events-all flex-column justify-content-between align-items-center shadow rounded-1 fhc-drag-handle"
|
||||
draggable='true'
|
||||
:title="getOverlayClassScheduleTypeTitle('overlay-item-' + index)"
|
||||
class="d-none fhc-pointer-events-all flex-column justify-content-between align-items-center shadow rounded-1"
|
||||
:draggable='!$props.isPreviewMode ? "true" : "false"'
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center gap-1 p-2 overflow-scroll"
|
||||
>
|
||||
<a
|
||||
v-if="!$props.isPreviewMode"
|
||||
@mousedown.stop="showOverlayClassTimeTypePopover('overlay-item-' + index)"
|
||||
:title="$p.t('ui', 'bearbeiten')"
|
||||
class="position-absolute top-0 start-0 p-1 d-flex gap-1 fhc-cursor-pointer"
|
||||
>
|
||||
<i class="fa fa-edit text-primary fs-6"></i>
|
||||
</a>
|
||||
<a
|
||||
v-if="!$props.isPreviewMode"
|
||||
@mousedown.stop="deleteOverlay('overlay-item-' + index)"
|
||||
:title="$p.t('global', 'loeschen')"
|
||||
class="position-absolute top-0 end-0 p-1 d-flex gap-1 fhc-cursor-pointer"
|
||||
@@ -1623,43 +1728,20 @@ export default {
|
||||
<i class="fa fa-trash text-danger fs-6"></i>
|
||||
</a>
|
||||
<p
|
||||
v-if="!isOverlayMinimallySized(this.overlays.find(overlay => overlay.id === 'overlay-item-' + index))"
|
||||
:style="'color: ' + this.overlayTextColor"
|
||||
class="bg-transparent rounded-1 py-0 m-0 "
|
||||
>
|
||||
{{ getOverlayTimeSlotSpan("overlay-item-" + index) }}
|
||||
</p>
|
||||
<div v-if="this.overlays.length">
|
||||
<form-input
|
||||
:modelValue="this.overlays.find(overlay => overlay.id === 'overlay-item-' + index)?.type"
|
||||
@mousedown.stop
|
||||
|
||||
@change="(event) => {
|
||||
let overlay = this.overlays.find(overlay => overlay.id === 'overlay-item-' + index);
|
||||
if (!overlay) return;
|
||||
|
||||
overlay.type = event.target.value;
|
||||
overlay.hexColor = this.classTimeSlotTypes.find(type => type.unterrichtszeitentyp_kurzbz === event.target.value)?.hintergrundfarbe;
|
||||
}"
|
||||
:class="{
|
||||
'w-75': isOverlayMinimallySized(this.overlays.find(overlay => overlay.id === 'overlay-item-' + index)),
|
||||
}"
|
||||
type="select"
|
||||
name="classTimeSlotType"
|
||||
class="p-0 m-0 fs-6"
|
||||
>
|
||||
<option
|
||||
v-for="type in $props.classTimeSlotTypes"
|
||||
:key="type.unterrichtszeitentyp_kurzbz"
|
||||
:value="type.unterrichtszeitentyp_kurzbz"
|
||||
selected
|
||||
>
|
||||
{{type.unterrichtszeitentyp_kurzbz}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<span
|
||||
v-if="!isOverlayMinimallySized(this.overlays.find(overlay => overlay.id === 'overlay-item-' + index))"
|
||||
class="badge badge-pill bg-light text-dark"
|
||||
>
|
||||
{{ this.getOverlayClassScheduleTypeTitle('overlay-item-' + index) }}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
<span
|
||||
v-if="!$props.isPreviewMode"
|
||||
@mousedown.stop="handleMouseDownOnResizer($event, 'overlay-item-resizer-' + index, 'overlay-item-' + index)"
|
||||
:id="'overlay-item-resizer-' + index"
|
||||
:class="{
|
||||
@@ -1671,34 +1753,17 @@ export default {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isPreviewMode" id="overlays-container-preview" class='d-none'>
|
||||
<div
|
||||
v-for="(index) in 50"
|
||||
:id="'overlay-item-preview-' + index"
|
||||
:style="{ backgroundColor: this.overlays.find(overlay => overlay.id === 'overlay-item-preview-' + index)?.hexColor || this.defaultOverlayColor }"
|
||||
:class="isOverlayMinimallySized(this.overlays.find(overlay => overlay.id === 'overlay-item-preview-' + index)) ? 'justify-content-center' : 'justify-content-between'"
|
||||
class="d-flex flex-column align-items-center shadow rounded-1 "
|
||||
<div ref="classScheduleTypeSelectorContainer" class="d-none">
|
||||
<div class='d-flex flex-column gap-2'>
|
||||
<span v-for="(type, index) in classTimeSlotTypes"
|
||||
:key="index"
|
||||
:data-type="type.unterrichtszeitentyp_kurzbz"
|
||||
class="btn btn-sm btn-outline-dark class-schedule-type-selector-option"
|
||||
>
|
||||
<div
|
||||
:class="{
|
||||
'p-2': !isOverlayMinimallySized(this.overlays.find(overlay => overlay.id === 'overlay-item-preview-' + index)),
|
||||
}"
|
||||
class="d-flex flex-column justify-content-center align-items-center gap-0"
|
||||
>
|
||||
<span
|
||||
v-if="this.$props.isPreviewMode"
|
||||
class="badge badge-pill bg-light text-dark"
|
||||
>
|
||||
{{ this.overlays.find(overlay => overlay.id === 'overlay-item-preview-' + index)?.type }}
|
||||
</span>
|
||||
<p
|
||||
v-if="!isOverlayMinimallySized(this.overlays.find(overlay => overlay.id === 'overlay-item-preview-' + index))"
|
||||
:style="'color: ' + this.overlayTextColor"
|
||||
class="bg-transparent rounded-1 py-0 m-0 "
|
||||
>{{ getOverlayTimeSlotSpan("overlay-item-preview-" + index) }}</p>
|
||||
</div>
|
||||
{{ type.bezeichnung_mehrsprachig[0].value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
|
||||
import ClassScheduleValidityPeriodPreview from "./ClassScheduleValidityPeriodPreview.js";
|
||||
|
||||
export default {
|
||||
name: "ClassScheduleOrgUnitGroupedValidityPeriodsOverview",
|
||||
components: {
|
||||
ClassScheduleValidityPeriodPreview,
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
organizationalUnitShortCode: null,
|
||||
studyPlanId: null,
|
||||
classScheduleValidityPeriods: [],
|
||||
classScheduleTimeSlots: [],
|
||||
classTimeSlotTypes: [],
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
async created() {
|
||||
this.organizationalUnitShortCode = this.$route.params.organizationalUnitShortCode;
|
||||
this.studyPlanId = parseInt(this.$route.params.studyPlanId);
|
||||
|
||||
let getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse =
|
||||
await this.$api.call(
|
||||
ApiClassSchedule.getAllClassTimeValidityPeriodsPerOrganizationalUnit(
|
||||
this.organizationalUnitShortCode,
|
||||
),
|
||||
);
|
||||
if (
|
||||
getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse.meta
|
||||
.status === "success"
|
||||
) {
|
||||
this.classScheduleValidityPeriods =
|
||||
getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse.data.filter(
|
||||
(period) => {
|
||||
if (!this.studyPlanId) return true;
|
||||
|
||||
return period.studienplan_id === this.studyPlanId;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
"Error fetching class time slot validity periods:",
|
||||
getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse.meta
|
||||
.message,
|
||||
);
|
||||
}
|
||||
|
||||
for (let validityPeriod of this.classScheduleValidityPeriods) {
|
||||
if (this.studyPlanId && validityPeriod.studienplan_id !== this.studyPlanId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let getClassTimeSlotsForValidityPeriodResponse = await this.$api.call(
|
||||
ApiClassSchedule.getClassTimeSlotsForValidityPeriod(
|
||||
validityPeriod.unterrichtszeitengueltigkeit_id,
|
||||
),
|
||||
);
|
||||
if (
|
||||
getClassTimeSlotsForValidityPeriodResponse.meta.status === "success"
|
||||
) {
|
||||
this.classScheduleTimeSlots = [
|
||||
...this.classScheduleTimeSlots,
|
||||
...getClassTimeSlotsForValidityPeriodResponse.data,
|
||||
];
|
||||
} else {
|
||||
console.error(
|
||||
`Error fetching class time slots for validity period ${validityPeriod.unterrichtszeitengueltigkeit_id}:`,
|
||||
getClassTimeSlotsForValidityPeriodResponse.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: /* html */ `
|
||||
<div class="container mt-4">
|
||||
<div class="my-2">
|
||||
<h2 v-if="!this.studyPlanId">{{ $p.t("ui", "classScheduleOrgUnitGroupedValidityPeriodsOverviewTitle") }}</h2>
|
||||
<h2 v-else>{{ $p.t("ui", "classScheduleStudyPlanGroupedValidityPeriodsOverviewTitle") }}</h2>
|
||||
</div>
|
||||
<class-schedule-validity-period-preview
|
||||
v-for="validityPeriod in classScheduleValidityPeriods"
|
||||
:key="validityPeriod.unterrichtszeitengueltigkeit"
|
||||
:class-schedule-validity-period="validityPeriod"
|
||||
:class-time-slots="classScheduleTimeSlots.filter(slot => slot.unterrichtszeitengueltigkeit_id === validityPeriod.unterrichtszeitengueltigkeit_id)"
|
||||
:class-time-slot-types="classTimeSlotTypes"
|
||||
class="shadow"
|
||||
/>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -38,34 +38,48 @@ export default {
|
||||
tabulatorOptions() {
|
||||
const options = {
|
||||
ajaxURL: "dummy",
|
||||
ajaxRequestFunc: () =>
|
||||
this.$api.call(ApiClassSchedule.getAllClassTimeValidityPeriods()),
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
ajaxRequestFunc: async () =>
|
||||
await this.getParsedClassTimeSlotValidityPeriodData(),
|
||||
ajaxResponse: (url, params, response) => response,
|
||||
persistenceID: "core_class_schedule_validity_periods",
|
||||
selectableRows: true,
|
||||
columns: [
|
||||
{
|
||||
title: "gueltig von",
|
||||
field: "gueltig_von",
|
||||
title: this.$p.t("ui", "zeitraum"),
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
const data = cell.getData();
|
||||
const validFrom = new Date(data.gueltig_von).toLocaleDateString(
|
||||
"de-AT",
|
||||
{
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
},
|
||||
);
|
||||
data.gueltig_von;
|
||||
const validTo = new Date(data.gueltig_bis).toLocaleDateString(
|
||||
"de-AT",
|
||||
{
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
},
|
||||
);
|
||||
return `${validFrom ? validFrom : "?"} - ${validTo ? validTo : "?"}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: this.$p.t("global", "typ"),
|
||||
field: "unterrichtszeiten_typ_bezeichnung_mehrsprachig",
|
||||
width: 150,
|
||||
},
|
||||
{ title: "gueltig bis", field: "gueltig_bis", width: 150 },
|
||||
{ title: "orgform kurzbz", field: "orgform_kurzbz", width: 150 },
|
||||
{
|
||||
title: "ausbildungssemester",
|
||||
title: this.$p.t("lehre", "sem"),
|
||||
field: "ausbildungssemester",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: "typ",
|
||||
field: "unterrichtszeitentyp_kurzbz",
|
||||
},
|
||||
{
|
||||
field: "unterrichtszeitengueltigkeit_id",
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
title: "Aktionen",
|
||||
title: this.$p.t("global", "actions"),
|
||||
field: "actions",
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
@@ -135,38 +149,102 @@ export default {
|
||||
frozen: true,
|
||||
},
|
||||
],
|
||||
groupBy: [
|
||||
"organisationseinheit_bezeichnung_extended",
|
||||
"studienplan_bezeichnung",
|
||||
],
|
||||
groupHeader: [
|
||||
function (value, count, data) {
|
||||
let container = document.createElement("span");
|
||||
|
||||
container.className =
|
||||
"d-flex align-items-center justify-content-between";
|
||||
container.style.display = "inline-block";
|
||||
container.style.width = "100%";
|
||||
|
||||
let label = document.createElement("span");
|
||||
label.textContent =
|
||||
value + " (" + count + " item" + (count > 1 ? "s" : "") + ")";
|
||||
container.append(label);
|
||||
|
||||
let button = document.createElement("button");
|
||||
button.className =
|
||||
"btn btn-sm btn-outline-secondary fhc-btn-for-org-unit-grouping";
|
||||
button.style.marginLeft = "10px";
|
||||
button.innerHTML = '<i class="fa fa-eye"></i>';
|
||||
|
||||
button.dataset.organizationalUnitShortCode = data[0].oe_kurzbz;
|
||||
|
||||
container.append(button);
|
||||
return container;
|
||||
},
|
||||
function (value, count, data) {
|
||||
let container = document.createElement("span");
|
||||
|
||||
container.className =
|
||||
"d-flex align-items-center justify-content-between";
|
||||
container.style.display = "inline-block";
|
||||
container.style.width = "100%";
|
||||
|
||||
let label = document.createElement("span");
|
||||
label.textContent =
|
||||
value + " (" + count + " item" + (count > 1 ? "s" : "") + ")";
|
||||
container.append(label);
|
||||
|
||||
let button = document.createElement("button");
|
||||
button.className =
|
||||
"btn btn-sm btn-outline-secondary fhc-btn-for-org-unit-and-study-plan-grouping";
|
||||
button.style.marginLeft = "10px";
|
||||
button.innerHTML = '<i class="fa fa-eye"></i>';
|
||||
button.title = 22;
|
||||
|
||||
button.dataset.organizationalUnitShortCode = data[0].oe_kurzbz;
|
||||
button.dataset.studyPlanId = data[0].studienplan_id;
|
||||
|
||||
container.append(button);
|
||||
return container;
|
||||
},
|
||||
],
|
||||
};
|
||||
return options;
|
||||
},
|
||||
tabulatorEvents() {
|
||||
const events = [
|
||||
{
|
||||
event: "tableBuilt",
|
||||
event: "renderComplete",
|
||||
handler: async () => {
|
||||
const setHeader = (field, text) => {
|
||||
const col =
|
||||
this.$refs.classTimeSlotValidityPeriodsTable.tabulator.getColumn(
|
||||
field,
|
||||
);
|
||||
if (!col) return;
|
||||
document
|
||||
.querySelectorAll(".fhc-btn-for-org-unit-grouping")
|
||||
.forEach((button) => {
|
||||
button.addEventListener("click", (e) => {
|
||||
let organizationalUnitShortCode =
|
||||
button.dataset.organizationalUnitShortCode;
|
||||
|
||||
const el = col.getElement();
|
||||
if (!el || !el.querySelector) return;
|
||||
this.$router.push({
|
||||
name: "validityPeriodsOverviewByOrganizationUnit",
|
||||
params: {
|
||||
organizationalUnitShortCode,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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"));
|
||||
document
|
||||
.querySelectorAll(".fhc-btn-for-org-unit-and-study-plan-grouping")
|
||||
.forEach((button) => {
|
||||
button.addEventListener("click", (e) => {
|
||||
let organizationalUnitShortCode =
|
||||
button.dataset.organizationalUnitShortCode;
|
||||
let studyPlanId = button.dataset.studyPlanId;
|
||||
this.$router.push({
|
||||
name: "validityPeriodsOverviewByOrganizationUnitAndStudyPlan",
|
||||
params: {
|
||||
organizationalUnitShortCode,
|
||||
studyPlanId,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -174,6 +252,41 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
test() {
|
||||
alert("test");
|
||||
},
|
||||
async getParsedClassTimeSlotValidityPeriodData() {
|
||||
let getAllClassTimeValidityPeriodsResponse = await this.$api.call(
|
||||
ApiClassSchedule.getAllClassTimeValidityPeriods(),
|
||||
);
|
||||
|
||||
if (getAllClassTimeValidityPeriodsResponse.meta.status === "success") {
|
||||
let generalWord = this.$p.t("ui", "general");
|
||||
return getAllClassTimeValidityPeriodsResponse.data.map(
|
||||
function (period) {
|
||||
period.organisationseinheit_bezeichnung_extended =
|
||||
period.organisationseinheit_bezeichnung +
|
||||
" - " +
|
||||
period.organisationseinheit_organisationseinheittyp_kurzbz;
|
||||
if (!period.studienplan_bezeichnung) {
|
||||
period.studienplan_bezeichnung = generalWord;
|
||||
}
|
||||
period.unterrichtszeiten_typ_bezeichnung_mehrsprachig =
|
||||
period.unterrichtszeiten_typ_bezeichnung_mehrsprachig[0]?.split(
|
||||
":",
|
||||
)[1];
|
||||
return {
|
||||
...period,
|
||||
};
|
||||
},
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
"Error fetching class time slot validity periods:",
|
||||
getAllClassTimeValidityPeriodsResponse.meta.message,
|
||||
);
|
||||
}
|
||||
},
|
||||
showClassTimeSlotValidityPeriodModal() {
|
||||
this.isClassTimeSlotValidityPeriodModalVisible = true;
|
||||
},
|
||||
@@ -227,12 +340,12 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.$p
|
||||
.loadCategory(["global", "lehre", "ui", "gruppenmanagement"])
|
||||
.loadCategory(["global", "lehre", "ui", "gruppenmanagement", "core"])
|
||||
.then(() => {
|
||||
this.phrasesLoaded = true;
|
||||
});
|
||||
},
|
||||
template: /* html */`
|
||||
template: /* html */ `
|
||||
<div class="container mt-4">
|
||||
<h1 class='mb-5'>{{ $p.t("ui", "classScheduleOverviewHeading") }}</h1>
|
||||
<div class="row mb-3">
|
||||
@@ -265,7 +378,7 @@ export default {
|
||||
table-only
|
||||
:side-menu="false"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="[{ event: 'cellEdited'}]"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
:new-btn-label="$p.t('ui', 'addClassTimeSlotValidityPeriodButton')"
|
||||
new-btn-show
|
||||
reload
|
||||
|
||||
@@ -21,6 +21,7 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["hideBsModal", "classTimeSlotTypeCreated", "classTimeSlotTypeUpdated"],
|
||||
watch: {
|
||||
isVisible(newValue) {
|
||||
if (newValue) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
|
||||
import ApiStudienPlan from "../../../js/api/factory/studienplan.js";
|
||||
import ApiStudienSemester from "../../../js/api/factory/studiensemester.js";
|
||||
import ApiOrganizationalUnit from "../../../js/api/factory/organizationalUnit.js";
|
||||
import { formatDate } from "../../helpers/DateHelpers.js";
|
||||
|
||||
import BsModal from "../Bootstrap/Modal.js";
|
||||
import CoreForm from "../Form/Form.js";
|
||||
@@ -66,6 +68,35 @@ export default {
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
},
|
||||
async "classTimeSlotValidityPeriodFormData.organizationalUnitShortCode"(
|
||||
newValue,
|
||||
) {
|
||||
if (!newValue) {
|
||||
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.refetchFilterableOptions();
|
||||
},
|
||||
async "classTimeSlotValidityPeriodFormData.validityPeriodFrom"(newValue) {
|
||||
if (!newValue) {
|
||||
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.refetchFilterableOptions();
|
||||
},
|
||||
async "classTimeSlotValidityPeriodFormData.validityPeriodTo"(newValue) {
|
||||
if (!newValue) {
|
||||
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.refetchFilterableOptions();
|
||||
},
|
||||
async "classTimeSlotValidityPeriodFormData.studyPlanId"(newValue) {
|
||||
this.refetchFilterableOptions();
|
||||
},
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
@@ -73,6 +104,8 @@ export default {
|
||||
isEditInProgress: false,
|
||||
organizationalUnits: [],
|
||||
studyPlans: [],
|
||||
studySemesters: [],
|
||||
studySemestersByNumber: [],
|
||||
classTimeSlotTypes: [],
|
||||
classTimeSlotValidityPeriodFormData: {
|
||||
id: null,
|
||||
@@ -86,7 +119,157 @@ export default {
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isStudyPlanSelectDisabled() {
|
||||
return (
|
||||
!this.classTimeSlotValidityPeriodFormData.organizationalUnitShortCode ||
|
||||
!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom ||
|
||||
!this.classTimeSlotValidityPeriodFormData.validityPeriodTo
|
||||
);
|
||||
},
|
||||
formattedValidityPeriodFrom() {
|
||||
if (!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom)
|
||||
return null;
|
||||
|
||||
return formatDate(
|
||||
this.classTimeSlotValidityPeriodFormData.validityPeriodFrom,
|
||||
);
|
||||
},
|
||||
formattedValidityPeriodTo() {
|
||||
if (!this.classTimeSlotValidityPeriodFormData.validityPeriodTo)
|
||||
return null;
|
||||
return formatDate(
|
||||
this.classTimeSlotValidityPeriodFormData.validityPeriodTo,
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async refetchFilterableOptions() {
|
||||
this.studyPlans = await this.getTargetedStudyPlans(
|
||||
this.classTimeSlotValidityPeriodFormData.organizationalUnitShortCode,
|
||||
this.formattedValidityPeriodFrom,
|
||||
this.formattedValidityPeriodTo,
|
||||
);
|
||||
|
||||
let studySemestersByDates =
|
||||
await this.getStudySemestersByOrganizationalUnitAndDates(
|
||||
this.classTimeSlotValidityPeriodFormData.organizationalUnitShortCode,
|
||||
this.formattedValidityPeriodFrom,
|
||||
this.formattedValidityPeriodTo,
|
||||
);
|
||||
|
||||
let studySemesters = new Array(
|
||||
...new Set(
|
||||
studySemestersByDates
|
||||
.map((s) => s.semester_numbers)
|
||||
.flat()
|
||||
.sort((a, b) => a - b),
|
||||
),
|
||||
);
|
||||
|
||||
this.studySemestersByNumber = studySemesters;
|
||||
if (this.classTimeSlotValidityPeriodFormData.studyPlanId) {
|
||||
let studySemestersByStudyProgramId =
|
||||
await this.getStudySemestersByStudyPlanAndDates(
|
||||
this.classTimeSlotValidityPeriodFormData.studyPlanId,
|
||||
this.formattedValidityPeriodFrom,
|
||||
this.formattedValidityPeriodTo,
|
||||
);
|
||||
studySemestersByStudyProgramId = new Array(
|
||||
...new Set(
|
||||
studySemestersByStudyProgramId
|
||||
.map((s) => s.semester_numbers)
|
||||
.flat()
|
||||
.sort((a, b) => a - b),
|
||||
),
|
||||
);
|
||||
|
||||
studySemesters = studySemestersByStudyProgramId;
|
||||
}
|
||||
|
||||
this.studySemestersByNumber = studySemesters;
|
||||
},
|
||||
async getTargetedStudyPlans(
|
||||
organizationalUnitShortCode,
|
||||
validityPeriodFrom,
|
||||
validityPeriodTo,
|
||||
) {
|
||||
if (
|
||||
!organizationalUnitShortCode ||
|
||||
!validityPeriodFrom ||
|
||||
!validityPeriodTo
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let getAllStudyPlansResponse = await this.$api.call(
|
||||
ApiStudienPlan.getStudyPlansByOrganizationalUnitAndSemesterDates(
|
||||
organizationalUnitShortCode,
|
||||
validityPeriodFrom,
|
||||
validityPeriodTo,
|
||||
),
|
||||
);
|
||||
if (getAllStudyPlansResponse.meta.status !== "success") {
|
||||
console.error(
|
||||
"Error fetching study plans:",
|
||||
getAllStudyPlansResponse.meta.message,
|
||||
);
|
||||
}
|
||||
|
||||
return getAllStudyPlansResponse.data?.length
|
||||
? getAllStudyPlansResponse.data
|
||||
: [];
|
||||
},
|
||||
async getStudySemestersByOrganizationalUnitAndDates(
|
||||
organizationalUnitShortCode,
|
||||
validityPeriodFrom,
|
||||
validityPeriodTo,
|
||||
) {
|
||||
if (
|
||||
!organizationalUnitShortCode ||
|
||||
!validityPeriodFrom ||
|
||||
!validityPeriodTo
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let getStudySemestersResponse = await this.$api.call(
|
||||
ApiStudienSemester.getStudySemestersByOrganizationalUnitAndDates(
|
||||
organizationalUnitShortCode,
|
||||
validityPeriodFrom,
|
||||
validityPeriodTo,
|
||||
),
|
||||
);
|
||||
if (getStudySemestersResponse.meta.status !== "success") {
|
||||
console.error(
|
||||
"Error fetching study semesters by organizational unit and dates:",
|
||||
getStudySemestersResponse.meta.message,
|
||||
);
|
||||
}
|
||||
|
||||
return getStudySemestersResponse.data?.length
|
||||
? getStudySemestersResponse.data
|
||||
: [];
|
||||
},
|
||||
async getStudySemestersByStudyPlanAndDates(studyProgramId, validityPeriodFrom, validityPeriodTo) {
|
||||
if (!studyProgramId || !validityPeriodFrom || !validityPeriodTo) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let getStudySemestersResponse = await this.$api.call(
|
||||
ApiStudienSemester.getStudySemestersByStudyPlanAndDates(studyProgramId, validityPeriodFrom, validityPeriodTo),
|
||||
);
|
||||
if (getStudySemestersResponse.meta.status !== "success") {
|
||||
console.error(
|
||||
"Error fetching study semesters:",
|
||||
getStudySemestersResponse.meta.message,
|
||||
);
|
||||
}
|
||||
|
||||
return getStudySemestersResponse.data?.length
|
||||
? getStudySemestersResponse.data
|
||||
: [];
|
||||
},
|
||||
createClassTimeSlotValidityPeriod() {
|
||||
return this.$refs.classTimeSlotValidityPeriodData
|
||||
.call(
|
||||
@@ -153,7 +336,9 @@ export default {
|
||||
ApiOrganizationalUnit.getAllOrganizationalUnits(),
|
||||
);
|
||||
if (getAllOrganizationalUnitsResponse.meta.status === "success") {
|
||||
this.organizationalUnits = getAllOrganizationalUnitsResponse.data;
|
||||
this.organizationalUnits = getAllOrganizationalUnitsResponse.data.sort(
|
||||
(a, b) => a.bezeichnung.localeCompare(b.bezeichnung),
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
"Error fetching organizational units:",
|
||||
@@ -197,7 +382,7 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
template: /*html*/ `
|
||||
<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>
|
||||
@@ -207,33 +392,82 @@ export default {
|
||||
<form-validation />
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
v-model="classTimeSlotValidityPeriodFormData.organizationalUnitShortCode"
|
||||
:label="$p.t('lehre/organisationseinheit') + ' *'"
|
||||
type="select"
|
||||
name="organizationalUnitShortCode"
|
||||
:label="$p.t('lehre/organisationseinheit') + ' *'"
|
||||
v-model="classTimeSlotValidityPeriodFormData.organizationalUnitShortCode"
|
||||
>
|
||||
<option
|
||||
v-for="organizationalUnit in organizationalUnits"
|
||||
:key="organizationalUnit.oe_kurzbz"
|
||||
:value="organizationalUnit.oe_kurzbz"
|
||||
>
|
||||
{{organizationalUnit.bezeichnung}}
|
||||
{{organizationalUnit.bezeichnung}} - {{ organizationalUnit.organisationseinheittyp_kurzbz }}
|
||||
</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
|
||||
v-model="classTimeSlotValidityPeriodFormData.validityPeriodFrom"
|
||||
:label="$p.t('ui/von') + ' *'"
|
||||
:teleport="true"
|
||||
:enable-time-picker="false"
|
||||
type="datePicker"
|
||||
name="validityPeriodFrom"
|
||||
format="dd.MM.yyyy"
|
||||
auto-apply
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<form-input
|
||||
v-model="classTimeSlotValidityPeriodFormData.validityPeriodTo"
|
||||
:label="$p.t('global/bis') + ' *'"
|
||||
:teleport="true"
|
||||
:enable-time-picker="false"
|
||||
type="datePicker"
|
||||
name="validityPeriodTo"
|
||||
format="dd.MM.yyyy"
|
||||
auto-apply
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
v-model="classTimeSlotValidityPeriodFormData.studyPlanId"
|
||||
:label="$p.t('lehre/studienplan')"
|
||||
:disabled="isStudyPlanSelectDisabled"
|
||||
type="select"
|
||||
name="studyPlan"
|
||||
>
|
||||
<option :value="null"> - </option>
|
||||
<option
|
||||
v-for="studyPlan in studyPlans"
|
||||
:key="studyPlan.studienplan_id"
|
||||
:value="studyPlan.studienplan_id"
|
||||
>
|
||||
{{studyPlan.bezeichnung}} - {{studyPlan.studienplan_id}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
name="studyPlan"
|
||||
:label="$p.t('lehre/studienplan')"
|
||||
v-model="classTimeSlotValidityPeriodFormData.studyPlanId"
|
||||
id="ausbildungssemester"
|
||||
name="semester"
|
||||
:disabled="isStudyPlanSelectDisabled"
|
||||
:label="$p.t('lehre', 'ausbildungssemester')+ '*'"
|
||||
v-model="classTimeSlotValidityPeriodFormData.semester"
|
||||
>
|
||||
<option
|
||||
v-for="studyPlan in studyPlans"
|
||||
:key="studyPlan.studienplan_id"
|
||||
:value="studyPlan.studienplan_id"
|
||||
v-for="studySemester in studySemestersByNumber"
|
||||
:key="studySemester"
|
||||
:value="studySemester"
|
||||
>
|
||||
{{studyPlan.orgform_kurzbz}} - {{studyPlan.sprache}} - {{studyPlan.semesterwochen}}
|
||||
{{studySemester}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
@@ -249,42 +483,10 @@ export default {
|
||||
:key="classTimeSlotType.unterrichtszeitentyp_kurzbz"
|
||||
:value="classTimeSlotType.unterrichtszeitentyp_kurzbz"
|
||||
>
|
||||
{{classTimeSlotType.unterrichtszeitentyp_kurzbz}} - {{classTimeSlotType.bezeichnung_mehrsprachig[0].value}} / ({{classTimeSlotType.bezeichnung_mehrsprachig[1].value}})
|
||||
{{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"
|
||||
|
||||
@@ -248,6 +248,7 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
<h2>{{ classScheduleValidityPeriodStartDate }} - {{ classScheduleValidityPeriodEndDate }}</h2>
|
||||
<p>{{ $p.t("lehre", "ausbildungssemester") }}: {{ classTimeSlotValidityPeriod?.ausbildungssemester }}</p>
|
||||
<p v-html="classTimeSlotValidityPeriod?.anmerkung"></p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import ClassScheduleCalendarSelector from "./ClassScheduleCalendarSelector.js";
|
||||
|
||||
export default {
|
||||
name: "ClassScheduleValidityPeriodPreview",
|
||||
components: {
|
||||
ClassScheduleCalendarSelector,
|
||||
},
|
||||
props: {
|
||||
classScheduleValidityPeriod: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
classTimeSlots: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
classTimeSlotTypes: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
classScheduleValidityPeriodStartDate() {
|
||||
if (!this.$props.classScheduleValidityPeriod) return null;
|
||||
|
||||
let dateParts = this.$props.classScheduleValidityPeriod.gueltig_von
|
||||
.split("-")
|
||||
.reverse();
|
||||
return dateParts.join("/");
|
||||
},
|
||||
classScheduleValidityPeriodEndDate() {
|
||||
if (!this.$props.classScheduleValidityPeriod) return null;
|
||||
let dateParts = this.$props.classScheduleValidityPeriod.gueltig_bis
|
||||
.split("-")
|
||||
.reverse();
|
||||
return dateParts.join("/");
|
||||
},
|
||||
classScheduleValidityPeriodStudyPlan() {
|
||||
return this.$props.classScheduleValidityPeriod.studienplan_bezeichnung;
|
||||
},
|
||||
parsedClassTimeSlots() {
|
||||
let classTimeSlotsGroupedByWeek = [];
|
||||
|
||||
this.$props.classTimeSlots.forEach((slot) => {
|
||||
let groupIdentifikator = slot["unterrichtszeit_gruppe_identifikator"];
|
||||
let existingGroup = classTimeSlotsGroupedByWeek.find(
|
||||
(group) => group.groupIdentifikator === groupIdentifikator,
|
||||
);
|
||||
if (existingGroup) {
|
||||
existingGroup.slots.push(slot);
|
||||
} else {
|
||||
classTimeSlotsGroupedByWeek.push({
|
||||
groupIdentifikator,
|
||||
slots: [slot],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return classTimeSlotsGroupedByWeek;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showClassScheduleValidityPeriod(classScheduleValidityPeriodId) {
|
||||
|
||||
this.$router.push({
|
||||
name: "validityPeriodOverview",
|
||||
params: {
|
||||
classTimeSlotValidityPeriodId: classScheduleValidityPeriodId,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
async created() {},
|
||||
template: /* html */ `
|
||||
<div class="container mt-4">
|
||||
<div class='py-3 d-flex align-items-center justify-content-between'>
|
||||
<div>
|
||||
<h2>{{ classScheduleValidityPeriodStartDate }} - {{ classScheduleValidityPeriodEndDate }}</h2>
|
||||
<h4>{{ classScheduleValidityPeriodStudyPlan }}</h4>
|
||||
</div>
|
||||
<a
|
||||
@click="showClassScheduleValidityPeriod($props.classScheduleValidityPeriod.unterrichtszeitengueltigkeit_id)"
|
||||
class="ml-auto btn btn-link"
|
||||
><i class="fa fa-eye fs-5"></i></a>
|
||||
</div>
|
||||
<div class='py-3 mb-4'>
|
||||
<div v-if="parsedClassTimeSlots && parsedClassTimeSlots.length > 0">
|
||||
<div v-for="(classTimeSlotsPerWeek, index) in parsedClassTimeSlots" :key="index" class="row border-top rounded pt-1 pb-5">
|
||||
<class-schedule-calendar-selector
|
||||
:class-time-slot-types="this.classTimeSlotTypes"
|
||||
:edited-overlays="classTimeSlotsPerWeek.slots.map((slot) => {
|
||||
return {
|
||||
databaseId: slot.id,
|
||||
id: slot.identifier,
|
||||
weekday: slot.wochentag,
|
||||
type: slot.unterrichtszeitentyp_kurzbz,
|
||||
startTime: slot.uhrzeit_von,
|
||||
endTime: slot.uhrzeit_bis,
|
||||
};
|
||||
})"
|
||||
:isPreviewMode="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="d-flex align-items-center justify-content-center border rounded p-2">
|
||||
<p>{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export function formatDate(d) {
|
||||
// parameter is of type Date
|
||||
if(d instanceof Date)
|
||||
{
|
||||
if (isNaN(date.valueOf())) {
|
||||
if (isNaN(d.valueOf())) {
|
||||
return 'N/A';
|
||||
}
|
||||
// if the date is an invalid string then creating a date from the string will fail and N/A is returned
|
||||
|
||||
@@ -57279,7 +57279,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'montag',
|
||||
'phrase' => 'monday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -57299,7 +57299,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'dienstag',
|
||||
'phrase' => 'tuesday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -57319,7 +57319,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'mittwoch',
|
||||
'phrase' => 'wednesday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -57339,7 +57339,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'donnerstag',
|
||||
'phrase' => 'thursday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -57359,7 +57359,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'freitag',
|
||||
'phrase' => 'friday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -57379,7 +57379,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'samstag',
|
||||
'phrase' => 'saturday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -57399,7 +57399,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'sonntag',
|
||||
'phrase' => 'sunday',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
@@ -58336,7 +58336,86 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
)
|
||||
)
|
||||
),
|
||||
// ### Phrases Dashboard Admin END
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'general',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'allgemein',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'general',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'classScheduleOrgUnitGroupedValidityPeriodsOverviewTitle',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Übersicht Unterrichtszeit Gültigkeitszeiträume nach Organisationseinheit gruppiert',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Overview of class schedule validity periods grouped by organizational unit',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'classScheduleStudyPlanGroupedValidityPeriodsOverviewTitle',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Übersicht Unterrichtszeit Gültigkeitszeiträume nach Studienplan gruppiert',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Overview of class schedule validity periods grouped by study plan',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'selectClassTimeSlotTypeTitle',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Übersicht Unterrichtszeit Gültigkeitszeiträume nach Studienplan gruppiert',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Select class time slot type',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user