From a8c4fc760742b58d58df0239324b376c61125a92 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 20 Nov 2025 14:06:12 +0100 Subject: [PATCH 01/14] CIS4 #69146 Lvplan for View StG/Semester/Verband/Gruppe - new API Endpoint lvPlan/eventsStgOrg - new view Cis/LvPlan/StgOrg - new component LvPlan/StgOrg.js - new overview Cis/OverviewLvPlan with Dropdown - phrases --- application/config/routes.php | 3 + .../controllers/Cis/OverviewLvPlan.php | 39 ++++ application/controllers/Cis/StgOrgLvPlan.php | 39 ++++ .../controllers/api/frontend/v1/LvPlan.php | 103 +++++++++- application/libraries/StundenplanLib.php | 18 ++ .../models/ressource/Stundenplan_model.php | 78 ++++++++ public/js/api/factory/lvPlan.js | 29 ++- public/js/apps/Dashboard/Fhc.js | 22 +++ .../components/Cis/LvPlan/OverviewLvPlan.js | 170 ++++++++++++++++ public/js/components/Cis/LvPlan/StgOrg.js | 187 ++++++++++++++++++ system/phrasesupdate.php | 103 +++++++++- 11 files changed, 782 insertions(+), 9 deletions(-) create mode 100644 application/controllers/Cis/OverviewLvPlan.php create mode 100644 application/controllers/Cis/StgOrgLvPlan.php create mode 100644 public/js/components/Cis/LvPlan/OverviewLvPlan.js create mode 100644 public/js/components/Cis/LvPlan/StgOrg.js diff --git a/application/config/routes.php b/application/config/routes.php index eba6688ff..3ed3c8323 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -64,6 +64,9 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1'; $route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1'; $route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1'; $route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1'; +//Routes for Lvplan Verband and page for dropdown Stg/Semester/Verband/Gruppe +$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1'; +$route['Cis/OverviewLvPlan/.*'] = 'Cis/OverviewLvPlan/index/$1'; // Studierendenverwaltung List Routes $route['api/frontend/v1/stv/[sS]tudents/inout'] = 'api/frontend/v1/stv/Students/index'; diff --git a/application/controllers/Cis/OverviewLvPlan.php b/application/controllers/Cis/OverviewLvPlan.php new file mode 100644 index 000000000..5fe9d9779 --- /dev/null +++ b/application/controllers/Cis/OverviewLvPlan.php @@ -0,0 +1,39 @@ + ['basis/cis:r'] + ]); + + // Load Config + $this->load->config('calendar'); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + + $viewData = array( + 'uid'=>getAuthUID(), + 'timezone' => $this->config->item('timezone') + ); + + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'OverviewLvPlan']); + } +} \ No newline at end of file diff --git a/application/controllers/Cis/StgOrgLvPlan.php b/application/controllers/Cis/StgOrgLvPlan.php new file mode 100644 index 000000000..834d49d55 --- /dev/null +++ b/application/controllers/Cis/StgOrgLvPlan.php @@ -0,0 +1,39 @@ + ['basis/cis:r'] + ]); + + // Load Config + $this->load->config('calendar'); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + + $viewData = array( + 'uid'=>getAuthUID(), + 'timezone' => $this->config->item('timezone') + ); + + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'StgOrgLvPlan']); + } +} diff --git a/application/controllers/api/frontend/v1/LvPlan.php b/application/controllers/api/frontend/v1/LvPlan.php index 28b48e3f1..8a73dcaa4 100644 --- a/application/controllers/api/frontend/v1/LvPlan.php +++ b/application/controllers/api/frontend/v1/LvPlan.php @@ -41,7 +41,12 @@ class LvPlan extends FHCAPI_Controller 'getLehreinheitStudiensemester' => self::PERM_LOGGED, 'studiensemesterDateInterval' => self::PERM_LOGGED, 'getLvPlanForStudiensemester' => self::PERM_LOGGED, - 'getLv' => self::PERM_LOGGED + 'getLv' => self::PERM_LOGGED, + 'eventsStgOrg' => self::PERM_LOGGED, + 'fetchFerienEvents' => self::PERM_LOGGED, + 'getStudiengaenge' => self::PERM_LOGGED, + 'getLehrverband' => self::PERM_LOGGED, + ]); $this->load->library('LogLib'); @@ -83,7 +88,7 @@ class LvPlan extends FHCAPI_Controller // form validation $this->form_validation->set_rules('start_date', "start_date", "required"); $this->form_validation->set_rules('end_date', "end_date", "required"); - + if (!$this->form_validation->run()) $this->terminateWithValidationErrors($this->form_validation->error_array()); @@ -100,7 +105,7 @@ class LvPlan extends FHCAPI_Controller // fetching ferien events $ferienEvents = $this->fetchFerienEvents($start_date, $end_date); - + $this->terminateWithSuccess(array_merge( $lvplanEvents, @@ -109,6 +114,49 @@ class LvPlan extends FHCAPI_Controller )); } + /** + * fetches LvPlan for studiengang / semester / verband / gruppe + * + * @access public + */ + public function eventsStgOrg() + { + $this->load->library('StundenplanLib'); + + // form validation + $this->form_validation->set_rules('start_date', "start_date", "required"); + $this->form_validation->set_rules('end_date', "end_date", "required"); + //$this->form_validation->set_rules('stg_kz', "stg_kz", "required"); //no validation show empty calendar + + if (!$this->form_validation->run()) + { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + $stgOrgEvents = []; + $ferienEvents = []; + } + + else + { + $start_date = $this->input->post('start_date', true); + $end_date = $this->input->post('end_date', true); + $stg_kz = $this->input->post('stg_kz', true); + $sem = $this->input->post('sem', true); + $verband = $this->input->post('verband', true); + $gruppe = $this->input->post('gruppe', true); + + $result = $this->stundenplanlib->getEventsStgOrg($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe); + $stgOrgEvents = $this->getDataOrTerminateWithError($result); + + $result = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $stg_kz); + $ferienEvents = $this->getDataOrTerminateWithError($result); + } + + $this->terminateWithSuccess(array_merge( + $stgOrgEvents, + $ferienEvents + )); + } + /** * fetches LvPlan and Ferien events together for the lv * @@ -137,7 +185,6 @@ class LvPlan extends FHCAPI_Controller // fetching ferien events $ferienEvents = $this->fetchFerienEvents($start_date, $end_date); - $this->terminateWithSuccess(array_merge( $lvplanEvents, @@ -287,6 +334,48 @@ class LvPlan extends FHCAPI_Controller return $this->terminateWithSuccess(current($result)); } + public function getStudiengaenge() + { + $this->load->model('organisation/Studiengang_model','StudiengangModel'); + + $this->StudiengangModel->addOrder('typ'); + $this->StudiengangModel->addOrder('kurzbz'); + $result = $this->StudiengangModel->loadWhere([ + 'aktiv' => true + ]); + + $data = $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($data); + } + + public function getLehrverband($studiengang_kz, $semester=null, $verband=null) + { + $this->load->model('organisation/Lehrverband_model','LehrverbandModel'); + + $where = [ + 'aktiv' => true, + 'studiengang_kz' => $studiengang_kz, + ]; + + if ($semester !== null && $semester !== 'null') { + $where['semester'] = $semester; + } + if ($verband !== null && $verband !== 'null') { + $where['verband'] = $verband; + } + + $this->LehrverbandModel->addOrder('studiengang_kz'); + $this->LehrverbandModel->addOrder('semester'); + $this->LehrverbandModel->addOrder('verband'); + $this->LehrverbandModel->addOrder('gruppe'); + $result = $this->LehrverbandModel->loadWhere($where); + + $data = $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($data); + } + /** * fetch moodle events * @@ -338,7 +427,7 @@ class LvPlan extends FHCAPI_Controller $currentStudiensemester = $this->StudiensemesterModel->getByDate($start_date); $currentStudiensemester = $this->getDataOrTerminateWithError($currentStudiensemester); - + if ($currentStudiensemester) { $studentsemester_kurzbz = current($currentStudiensemester)->studiensemester_kurzbz; @@ -347,7 +436,7 @@ class LvPlan extends FHCAPI_Controller "studiensemester_kurzbz" => $studentsemester_kurzbz ]); $studiengang = $this->getDataOrTerminateWithError($studiengang); - + if ($studiengang) $studiengang_kz = current($studiengang)->studiengang_kz; else @@ -357,7 +446,7 @@ class LvPlan extends FHCAPI_Controller } $ferienEvents = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $studiengang_kz); - + return $this->getDataOrTerminateWithError($ferienEvents); } } diff --git a/application/libraries/StundenplanLib.php b/application/libraries/StundenplanLib.php index 7ed64da2c..5e4b4e453 100644 --- a/application/libraries/StundenplanLib.php +++ b/application/libraries/StundenplanLib.php @@ -445,6 +445,24 @@ class StundenplanLib return success($ferienEventsFlattened); } + public function getEventsStgOrg( $start, $end, $stg_kz, $sem, $verband, $gruppe) + { + $this->_ci =& get_instance(); + + $this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel'); + + $stundenplan_data = $this->_ci->StundenplanModel->getStundenplanStudiengang($start, $end, $stg_kz, $sem, $verband, $gruppe); + if (isError($stundenplan_data)) + return $stundenplan_data; + $stundenplan_data = getData($stundenplan_data) ?? []; + + $function_error = $this->expandObjectInformation($stundenplan_data); + if ($function_error) + return $function_error; + + return success($stundenplan_data); + } + // start of the private functions ######################################################################################################## // function used to sort an array of studiensemester strings diff --git a/application/models/ressource/Stundenplan_model.php b/application/models/ressource/Stundenplan_model.php index 389be582d..168ba1bda 100644 --- a/application/models/ressource/Stundenplan_model.php +++ b/application/models/ressource/Stundenplan_model.php @@ -388,6 +388,84 @@ class Stundenplan_model extends DB_Model ORDER BY datum, beginn", [$start_date, $end_date, $ma_uid]); } + + /** + * queries Stundenplan and filters by studiengang, semester, verband gruppe + * + * @return void + */ + public function getStundenplanStudiengang($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe) { + + $qry_params = [$start_date, $end_date, $stg_kz]; + + $qry = " + SELECT + 'lehreinheit' as type, beginn, ende, datum, + CONCAT(lehrfach,'-',lehrform) as topic, + array_agg(DISTINCT lektor) as lektor, + array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe, + string_agg(DISTINCT ort_kurzbz, '/') as ort_kurzbz, + array_agg(DISTINCT lehreinheit_id) as lehreinheit_id, + titel, lehrfach, lehrform, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id + FROM + ( + SELECT unr,datum,beginn, ende, + CASE + WHEN sp.mitarbeiter_kurzbz IS NOT NULL THEN sp.mitarbeiter_kurzbz + ELSE sp.lektor + END as lektor, + CASE + WHEN gruppe_kurzbz IS NOT NULL THEN gruppe_kurzbz + ELSE CONCAT(UPPER(sp.stg_typ),UPPER(sp.stg_kurzbz),'-',COALESCE(CAST(sp.semester AS varchar),'/'),COALESCE(CAST(sp.verband AS varchar),'/')) + END as gruppen_kuerzel, + (SELECT bezeichnung + FROM public.tbl_organisationseinheit + WHERE oe_kurzbz IN( + SELECT oe_kurzbz + FROM lehre.tbl_lehrveranstaltung + WHERE lehrveranstaltung_id = sp.lehrveranstaltung_id + )) as organisationseinheit, + sp.ort_kurzbz, sp.studiengang_kz, sp.titel,sp.lehreinheit_id,sp.lehrfach_id,sp.anmerkung,fix,lehrveranstaltung_id,stg_kurzbzlang,stg_bezeichnung,stg_typ,fachbereich_kurzbz,lehrfach,lehrfach_bez,farbe,lehrform,anmerkung_lehreinheit,gruppe, verband, semester,stg_kurzbz + FROM ( + SELECT sp.* + FROM lehre.vw_stundenplan sp + WHERE + sp.datum >= ? + AND sp.datum <= ? + ) sp + JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = sp.stunde + WHERE studiengang_kz = ? "; + + if($sem != NULL) + { + $qry_params[] = $sem; + $qry .= " AND (semester = ? OR semester IS NULL)"; + } + if($verband != NULL) + { + $qry_params[] = $verband; + $qry .= " AND (verband = ? OR verband IS NULL OR verband = '0' OR verband = '')"; + } + if($gruppe != NULL) + { + $qry_params[] = $gruppe; + $qry .= " AND (gruppe = ? OR gruppe IS NULL OR gruppe = '0' OR gruppe = '') "; + } + $qry.= " AND ( + gruppe_kurzbz is null OR EXISTS( + SELECT 1 + FROM + public.tbl_gruppe WHERE gruppe_kurzbz = sp.gruppe_kurzbz AND direktinskription = false + ) + )"; + + $qry.= " ) as subquery + + GROUP BY unr, datum, beginn, ende, titel, lehrform, lehrfach, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id + ORDER BY datum, beginn; "; + + return $this->execReadOnlyQuery($qry, $qry_params); + } /** * NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester. diff --git a/public/js/api/factory/lvPlan.js b/public/js/api/factory/lvPlan.js index 0b179c9ba..01c7253d6 100644 --- a/public/js/api/factory/lvPlan.js +++ b/public/js/api/factory/lvPlan.js @@ -92,5 +92,32 @@ export default { method: 'get', url: '/api/frontend/v1/LvPlan/getLv/' + lehrveranstaltung_id }; - } + }, + eventsStgOrg(start_date, end_date, stg_kz, sem, verband, gruppe) { + console.log("stg_Kz" + stg_kz + " sem " + sem + " vb " + verband + " gr " + gruppe); + return { + method: 'post', + url: '/api/frontend/v1/lvPlan/eventsStgOrg', + params: { start_date, end_date, stg_kz, sem, verband, gruppe } + }; + }, + getStudiengaenge(){ + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/getStudiengaenge' + } + }, + getLehrverband(stg_kz, sem){ + return { + method: 'get', + url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}` + } + }, + getGruppe(stg_kz, sem, verband){ + return { + method: 'get', + url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}/${verband}` + } + }, + }; \ No newline at end of file diff --git a/public/js/apps/Dashboard/Fhc.js b/public/js/apps/Dashboard/Fhc.js index 190ed1a93..4a19695ae 100644 --- a/public/js/apps/Dashboard/Fhc.js +++ b/public/js/apps/Dashboard/Fhc.js @@ -16,6 +16,8 @@ import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js"; import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js"; import Studium from "../../components/Cis/Studium/Studium.js"; +import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js"; +import OverviewLvPlan from "../../components/Cis/LvPlan/OverviewLvPlan.js"; import ApiRenderers from '../../api/factory/renderers.js'; import ApiRouteInfo from '../../api/factory/routeinfo.js'; @@ -190,6 +192,26 @@ const router = VueRouter.createRouter({ }; } }, + { + path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?`, + name: 'StgOrgLvPlan', + component: StgOrgLvPlan, + props(route) { + return { + propsViewData: route.params + }; + } + }, + { + path: `/Cis/OverviewLvPlan`, + name: 'OverviewLvPlan', + component: OverviewLvPlan, + props(route) { + return { + propsViewData: route.params + }; + } + }, { path: `/Cis4`, name: 'Cis4', diff --git a/public/js/components/Cis/LvPlan/OverviewLvPlan.js b/public/js/components/Cis/LvPlan/OverviewLvPlan.js new file mode 100644 index 000000000..4a7cdec60 --- /dev/null +++ b/public/js/components/Cis/LvPlan/OverviewLvPlan.js @@ -0,0 +1,170 @@ +import FormForm from '../../Form/Form.js'; +import FormInput from '../../Form/Input.js'; + +import ApiLvPlan from '../../../api/factory/lvPlan.js'; + +export default { + name: "OverviewLvPlan", + components: { + FormForm, + FormInput, + }, + props: { + viewData: Object, + propsViewData: Object + }, + data() { + return { + formData: { + stgkz: null, + semester: null, + verband: null, + gruppe: null, + }, + listStg: [], + listSem: [], + listVerband: [], + listGroup: [], + dataLvStudiengang: {} + }; + }, + methods: { + loadLvPlan(){ + if(!this.formData.stgkz){ + this.$fhcAlert.alertError(this.$p.t('LvPlan', 'chooseStg')); + return; + } + this.$router.push({ + name: "StgOrgLvPlan", + params: { + mode: "Week", + focus_date: this.currentDay, + stgkz: this.formData.stgkz, + sem: this.formData.semester, + verband: this.formData.verband, + gruppe: this.formData.gruppe, + } + }); + }, + loadListSem(){ + this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1); + this.loadListVerband(); + }, + loadListVerband(){ + this.$api + .call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.semester, this.formData.verband)) + .then(result => { + const data = result.data; + const mappedData = data.map(item => item.verband); + this.listVerband = [...new Set(mappedData.filter(v => + v !== null && + v !== undefined && + String(v).trim() !== "" + ))] + .sort(); + }) + .catch(this.$fhcAlert.handleSystemError); + this.loadListGroup(); + }, + loadListGroup(){ + this.$api + .call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.semester, this.formData.verband)) + .then(result => { + const data = result.data; + const mappedData = data.map(item => item.gruppe); + this.listGroup = [...new Set(mappedData.filter(v => + v !== null && + v !== undefined && + String(v).trim() !== ""))] + .sort(); + }) + .catch(this.$fhcAlert.handleSystemError); + } + }, + computed: { + maxSemester(){ + const currentStg = this.listStg.find( + item => item.studiengang_kz === this.formData.stgkz + ); + return currentStg.max_semester; + }, + currentDay() { + if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) + return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); + return this.propsViewData?.focus_date; + }, + }, + created(){ + this.$api + .call(ApiLvPlan.getStudiengaenge()) + .then(result => { + this. listStg = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + }, + template: ` +
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ `, +}; diff --git a/public/js/components/Cis/LvPlan/StgOrg.js b/public/js/components/Cis/LvPlan/StgOrg.js new file mode 100644 index 000000000..b6e4a37b4 --- /dev/null +++ b/public/js/components/Cis/LvPlan/StgOrg.js @@ -0,0 +1,187 @@ +import FhcCalendar from "../../Calendar/LvPlan.js"; + +import ApiLvPlan from '../.././../api/factory/lvPlan.js'; +import ApiAuthinfo from '../../../api/factory/authinfo.js'; + +export const DEFAULT_MODE_LVPLAN = 'Week'; + +export default { + name: 'LvPlanStgOrg', + inject: { + cisRoot: { + from: 'cisRoot' + }, + }, + components: { + FhcCalendar, + }, + props: { + viewData: Object, + propsViewData: Object + }, + data() { + return { + studiensemester_kurzbz: null, + studiensemester_start: null, + studiensemester_ende: null, + uid: null, + isMitarbeiter: false, + isStudent: false, + listStg: [], + currentStgBezeichnung: null + }; + }, + computed:{ + currentDay() { + if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) + return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); + return this.propsViewData?.focus_date; + }, + currentMode() { + if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase())) + return DEFAULT_MODE_LVPLAN; + return this.propsViewData?.mode; + }, + downloadLinks() { + if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid) + return false; + + let type = false; + type = this.isStudent ? 'student' : type; + type = this.isMitarbeiter ? 'lektor' : type; + if (false === type) + { + return; + } + + const opts = { zone: this.viewData.timezone }; + const start = luxon.DateTime + .fromISO(this.studiensemester_start, opts) + .toUnixInteger(); + const ende = luxon.DateTime + .fromISO(this.studiensemester_ende, opts) + .toUnixInteger(); + + const download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'cis/private/lvplan/stpl_kalender.php' + + '?type=' + type + + '&pers_uid=' + this.uid + + '&begin=' + start + + '&ende=' + ende; + + return [ + { title: "excel", icon: 'fa-solid fa-file-excel', link: download_link + '&format=excel' }, + { title: "csv", icon: 'fa-solid fa-file-csv', link: download_link + '&format=csv' }, + { title: "ical1", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=1&target=ical' }, + { title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' } + ]; + } + }, + methods: { + handleChangeDate(day, newMode) { + return this.handleChangeMode(newMode, day); + }, + handleChangeMode(newMode, day) { + const mode = newMode[0].toUpperCase() + newMode.slice(1) + const focus_date = day.toISODate(); + + this.$router.push({ + name: "StgOrgLvPlan", + params: { + mode, + focus_date, + stgkz: this.propsViewData.stgkz, + sem: this.propsViewData.sem, + verband: this.propsViewData.verband, + gruppe: this.propsViewData.gruppe, + } + }); + }, + updateRange(rangeInterval) { + this.$api + .call(ApiLvPlan.studiensemesterDateInterval( + rangeInterval.end.startOf('week').toISODate() + )) + .then(res => { + this.studiensemester_kurzbz = res.data.studiensemester_kurzbz; + this.studiensemester_start = res.data.start; + this.studiensemester_ende = res.data.ende; + }); + }, + getPromiseFunc(start, end) { + return [ + this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)), + //local for test +/* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.stgkz, this.sem, this.verband, this.gruppe))*/ + ]; + }, + backToDropdown(){ + this.$router.push({ + name: "OverviewLvPlan", + }); + } + }, + created(){ + this.$api + .call(ApiAuthinfo.getAuthInfo()) + .then(res => { + this.uid = res.data.uid; + this.isMitarbeiter = res.data.isMitarbeiter; + this.isStudent = res.data.isStudent; + }); + + if(this.propsViewData.stgkz) { + this.$api + .call(ApiLvPlan.getStudiengaenge()) + .then(result => { + const currentStg = result.data.find( + item => item.studiengang_kz == this.propsViewData.stgkz + ); + this.currentStgBezeichnung = currentStg.kurzbzlang + " - " + currentStg.bezeichnung; + }) + .catch(this.$fhcAlert.handleSystemError); + } + }, + template: ` +
+

{{ $p.t('LvPlan/headerLvPlanLvVerband') }}

+

{{currentStgBezeichnung}} + Semester: {{propsViewData.sem}} + Verband: {{propsViewData.verband}} + Gruppe: {{propsViewData.gruppe}} + +

+

{{ $p.t('LvPlan/noStgProvided') }}

+ + + + +
+ `, +}; \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 096d5086c..8a4b34dc9 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -51467,7 +51467,108 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), - // ### DOKUMENTE ERSTELLEN PHRASEN END ### + // ### LvPlanStgOrg START ### + array( + 'app' => 'core', + 'category' => 'LvPlan', + 'phrase' => 'noStgProvided', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'kein Studiengang hinterlegt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'no degree-program provided', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'LvPlan', + 'phrase' => 'headerLvPlanLvVerband', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lv-Plan Lehrverband', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course-Plan Teaching Association', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'LvPlan', + 'phrase' => 'chooseStg', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studiengang auswählen...', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Select a degree-program...', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'LvPlan', + 'phrase' => 'loadLvPlan', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrverband laden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Load Course-Plan', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'LvPlan', + 'phrase' => 'backToDropdown', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zurück zum Auswahl-Dropdown', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Back to Dropdown', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // ### LvPlanStgOrg END ### ); From 5e929df966afad8ad404896e314f79c7d1704548 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Tue, 25 Nov 2025 12:01:49 +0100 Subject: [PATCH 02/14] stgOrg: calendar and dropdown selection on one page --- .../controllers/api/frontend/v1/LvPlan.php | 4 +- .../components/Cis/LvPlan/OverviewLvPlan.js | 13 +- public/js/components/Cis/LvPlan/StgOrg.js | 287 +++++++++++++++--- .../js/components/Cis/LvPlan/StgOrg_DEPR.js | 187 ++++++++++++ .../Cis/Renderer/Lehreinheit/calendarEvent.js | 2 +- 5 files changed, 450 insertions(+), 43 deletions(-) create mode 100644 public/js/components/Cis/LvPlan/StgOrg_DEPR.js diff --git a/application/controllers/api/frontend/v1/LvPlan.php b/application/controllers/api/frontend/v1/LvPlan.php index 8a73dcaa4..d0f4fa7ba 100644 --- a/application/controllers/api/frontend/v1/LvPlan.php +++ b/application/controllers/api/frontend/v1/LvPlan.php @@ -358,10 +358,10 @@ class LvPlan extends FHCAPI_Controller 'studiengang_kz' => $studiengang_kz, ]; - if ($semester !== null && $semester !== 'null') { + if ($semester !== null && $semester !== 'null' && $semester !== 'undefined') { $where['semester'] = $semester; } - if ($verband !== null && $verband !== 'null') { + if ($verband !== null && $verband !== 'null' && $verband !== 'undefined') { $where['verband'] = $verband; } diff --git a/public/js/components/Cis/LvPlan/OverviewLvPlan.js b/public/js/components/Cis/LvPlan/OverviewLvPlan.js index 4a7cdec60..dafdc13f5 100644 --- a/public/js/components/Cis/LvPlan/OverviewLvPlan.js +++ b/public/js/components/Cis/LvPlan/OverviewLvPlan.js @@ -17,7 +17,7 @@ export default { return { formData: { stgkz: null, - semester: null, + sem: null, verband: null, gruppe: null, }, @@ -25,7 +25,7 @@ export default { listSem: [], listVerband: [], listGroup: [], - dataLvStudiengang: {} + // dataLvStudiengang: {} }; }, methods: { @@ -34,13 +34,14 @@ export default { this.$fhcAlert.alertError(this.$p.t('LvPlan', 'chooseStg')); return; } + console.log(this.$route.name, this.$route.params); this.$router.push({ name: "StgOrgLvPlan", params: { mode: "Week", focus_date: this.currentDay, stgkz: this.formData.stgkz, - sem: this.formData.semester, + sem: this.formData.sem, verband: this.formData.verband, gruppe: this.formData.gruppe, } @@ -52,7 +53,7 @@ export default { }, loadListVerband(){ this.$api - .call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.semester, this.formData.verband)) + .call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.sem, this.formData.verband)) .then(result => { const data = result.data; const mappedData = data.map(item => item.verband); @@ -68,7 +69,7 @@ export default { }, loadListGroup(){ this.$api - .call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.semester, this.formData.verband)) + .call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.sem, this.formData.verband)) .then(result => { const data = result.data; const mappedData = data.map(item => item.gruppe); @@ -122,7 +123,7 @@ export default { diff --git a/public/js/components/Cis/LvPlan/StgOrg.js b/public/js/components/Cis/LvPlan/StgOrg.js index b6e4a37b4..f7deff692 100644 --- a/public/js/components/Cis/LvPlan/StgOrg.js +++ b/public/js/components/Cis/LvPlan/StgOrg.js @@ -1,3 +1,5 @@ +import FormForm from '../../Form/Form.js'; +import FormInput from '../../Form/Input.js'; import FhcCalendar from "../../Calendar/LvPlan.js"; import ApiLvPlan from '../.././../api/factory/lvPlan.js'; @@ -7,12 +9,9 @@ export const DEFAULT_MODE_LVPLAN = 'Week'; export default { name: 'LvPlanStgOrg', - inject: { - cisRoot: { - from: 'cisRoot' - }, - }, components: { + FormForm, + FormInput, FhcCalendar, }, props: { @@ -21,17 +20,46 @@ export default { }, data() { return { + localProps: {}, studiensemester_kurzbz: null, studiensemester_start: null, studiensemester_ende: null, uid: null, isMitarbeiter: false, isStudent: false, + currentStgBezeichnung: null, + formData: { + stgkz: null, + sem: null, + verband: null, + gruppe: null, + }, listStg: [], - currentStgBezeichnung: null + listSem: [1,2,3,4,5,6,7,8,9,10], + listVerband: [], + listGroup: [], + rangeIntervalFirst: null }; }, - computed:{ + computed: { + maxSemester(){ + const currentStg = this.listStg.find( + item => item.studiengang_kz === this.formData.stgkz + ); + return currentStg.max_semester; + }, + //use local props, for viewDataProps cannot be changed +/* currentDay() { + if (!this.localProps?.focus_date || isNaN(new Date(this.localProps.focus_date))) + return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); + return this.localProps.focus_date; + }, + + currentMode() { + if (!this.localProps?.mode || !['day', 'week', 'month'].includes(this.localProps.mode.toLowerCase())) + return DEFAULT_MODE_LVPLAN; + return this.localProps.mode; + },*/ currentDay() { if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); @@ -78,14 +106,94 @@ export default { } }, methods: { + loadLvPlan(){ + if(!this.formData.stgkz){ + this.$fhcAlert.alertError(this.$p.t('LvPlan', 'chooseStg')); + return; + } + +/* if(this.rangeIntervalFirst) { + console.log("in range IntervalFirst "); + this.updateRange(this.rangeIntervalFirst); + } + + console.log("formParameters: " + this.formData.stgkz + " focusdate" + this.currentDay);*/ + +/* const newFocus = luxon.DateTime.fromISO(this.propsViewData.focus_date) + .setZone(this.viewData.timezone) + .toISODate() + "_";*/ // minimaler Unterschied + + //also use router push here + this.$router.push({ + name: "StgOrgLvPlan", + params: { + mode: this.currentMode, + focus_date: this.currentDay, + stgkz: this.formData.stgkz, + sem: this.formData.sem, + verband: this.formData.verband, + gruppe: this.formData.gruppe, + }, + }); + + //wenn sich formvariablen ändern? + if(this.rangeIntervalFirst) { + console.log("in range IntervalFirst second try "); + const start = luxon.DateTime.fromISO(this.rangeIntervalFirst.start).toISODate(); + const end = luxon.DateTime.fromISO(this.rangeIntervalFirst.end).toISODate(); + console.log("start und end" + start + " - " + end); + this.getPromiseFunc(start, end); + } + + }, + loadListSem(){ + this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1); + this.loadListVerband(); + }, + loadListVerband(){ + this.$api + .call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.semester, this.formData.verband)) + .then(result => { + const data = result.data; + const mappedData = data.map(item => item.verband); + this.listVerband = [...new Set(mappedData.filter(v => + v !== null && + v !== undefined && + String(v).trim() !== "" + ))] + .sort(); + }) + .catch(this.$fhcAlert.handleSystemError); + this.loadListGroup(); + }, + loadListGroup(){ + this.$api + .call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.semester, this.formData.verband)) + .then(result => { + const data = result.data; + const mappedData = data.map(item => item.gruppe); + this.listGroup = [...new Set(mappedData.filter(v => + v !== null && + v !== undefined && + String(v).trim() !== ""))] + .sort(); + }) + .catch(this.$fhcAlert.handleSystemError); + }, handleChangeDate(day, newMode) { + console.log("in handleChangeDate: day: " + day + " newMode " + newMode); return this.handleChangeMode(newMode, day); }, handleChangeMode(newMode, day) { - const mode = newMode[0].toUpperCase() + newMode.slice(1) + console.log("in handleChangeMode: day: " + day + " newMode " + newMode); + console.log(this.$route.name, this.$route.params); + const mode = newMode[0].toUpperCase() + newMode.slice(1); const focus_date = day.toISODate(); - this.$router.push({ + console.log(this.formData.stgkz, this.formData.sem, this.formData.verband, this.formData.gruppe); + + +/* this.$router.push({ name: "StgOrgLvPlan", params: { mode, @@ -95,9 +203,52 @@ export default { verband: this.propsViewData.verband, gruppe: this.propsViewData.gruppe, } + });*/ + + this.$router.push({ + name: "StgOrgLvPlan", + params: { + mode, + focus_date, + stgkz: this.formData.stgkz, + sem: this.formData.sem, + verband: this.formData.verband, + gruppe: this.formData.gruppe, + }, }); + + //try for reload + if(this.rangeIntervalFirst) { + console.log("in range IntervalFirst second try "); + const start = luxon.DateTime.fromISO(this.rangeIntervalFirst.start).toISODate(); + const end = luxon.DateTime.fromISO(this.rangeIntervalFirst.end).toISODate(); + console.log("start und end" + start + " - " + end); + this.getPromiseFunc(start, end); + } + }, updateRange(rangeInterval) { + console.log("in updateRange " + rangeInterval); + + //initialise at first run + if(!this.rangeIntervalFirst) { + this.rangeIntervalFirst = rangeInterval; + console.log("new local RI " + this.rangeIntervalFirst); + } + + +/* const startOfWeek = rangeInterval.end.startOf('week').toISODate(); + + console.log("in updateRange", startOfWeek);*/ +/* + if (!rangeInterval || !rangeInterval.end || !rangeInterval.end.startOf) { + console.warn("updateRange: invalid rangeInterval", rangeInterval); + return; + } + + const startOfWeek = rangeInterval.end.startOf('week').toISODate(); + + console.log("in updateRange", startOfWeek);*/ this.$api .call(ApiLvPlan.studiensemesterDateInterval( rangeInterval.end.startOf('week').toISODate() @@ -109,17 +260,18 @@ export default { }); }, getPromiseFunc(start, end) { + console.log("start " + start + " end " + end); + console.log(this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe); + console.log(this.formData.stgkz, this.formData.sem, this.formData.verband, this.formData.gruppe); + return [ - this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)), + //this.$api.call(ApiLvPlan.eventsStgOrg(start, end, this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)), + //variante die von außen funktioniert +/* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)),*/ //local for test -/* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.stgkz, this.sem, this.verband, this.gruppe))*/ + this.$api.call(ApiLvPlan.eventsStgOrg(start, end, this.formData.stgkz, this.formData.sem, this.formData.verband, this.formData.gruppe)) ]; }, - backToDropdown(){ - this.$router.push({ - name: "OverviewLvPlan", - }); - } }, created(){ this.$api @@ -130,30 +282,95 @@ export default { this.isStudent = res.data.isStudent; }); - if(this.propsViewData.stgkz) { - this.$api - .call(ApiLvPlan.getStudiengaenge()) - .then(result => { - const currentStg = result.data.find( - item => item.studiengang_kz == this.propsViewData.stgkz - ); - this.currentStgBezeichnung = currentStg.kurzbzlang + " - " + currentStg.bezeichnung; - }) - .catch(this.$fhcAlert.handleSystemError); + this.$api + .call(ApiLvPlan.getStudiengaenge()) + .then(result => { + this. listStg = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + //this.localProps = { ...this.propsViewData }; + + if(this.propsViewData) { + this.formData.stgkz = this.propsViewData.stgkz ? this.propsViewData.stgkz: null; + this.formData.sem = this.propsViewData.sem ? this.propsViewData.sem: null; + this.formData.verband = this.propsViewData.verband ? this.propsViewData.verband: null; + this.formData.gruppe = this.propsViewData.gruppe ? this.propsViewData.gruppe: null; } }, + /* + + v-if="propsViewData && propsViewData.stgkz" + {{propsViewData}} || {{formData}} || {{viewData}} + */ + template: `
-

{{ $p.t('LvPlan/headerLvPlanLvVerband') }}

-

{{currentStgBezeichnung}} - Semester: {{propsViewData.sem}} - Verband: {{propsViewData.verband}} - Gruppe: {{propsViewData.gruppe}} - -

-

{{ $p.t('LvPlan/noStgProvided') }}

+ +
+ + + + + + + + + + + + + + + + + + + + +
`, + + }; \ No newline at end of file diff --git a/public/js/components/Cis/LvPlan/StgOrg_DEPR.js b/public/js/components/Cis/LvPlan/StgOrg_DEPR.js new file mode 100644 index 000000000..02606a1db --- /dev/null +++ b/public/js/components/Cis/LvPlan/StgOrg_DEPR.js @@ -0,0 +1,187 @@ +import FhcCalendar from "../../Calendar/LvPlan.js"; + +import ApiLvPlan from '../.././../api/factory/lvPlan.js'; +import ApiAuthinfo from '../../../api/factory/authinfo.js'; + +export const DEFAULT_MODE_LVPLAN = 'Week'; + +export default { + name: 'LvPlanStgOrg', + inject: { + cisRoot: { + from: 'cisRoot' + }, + }, + components: { + FhcCalendar, + }, + props: { + viewData: Object, + propsViewData: Object + }, + data() { + return { + studiensemester_kurzbz: null, + studiensemester_start: null, + studiensemester_ende: null, + uid: null, + isMitarbeiter: false, + isStudent: false, + listStg: [], + currentStgBezeichnung: null + }; + }, + computed:{ + currentDay() { + if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) + return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); + return this.propsViewData?.focus_date; + }, + currentMode() { + if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase())) + return DEFAULT_MODE_LVPLAN; + return this.propsViewData?.mode; + }, + downloadLinks() { + if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid) + return false; + + let type = false; + type = this.isStudent ? 'student' : type; + type = this.isMitarbeiter ? 'lektor' : type; + if (false === type) + { + return; + } + + const opts = { zone: this.viewData.timezone }; + const start = luxon.DateTime + .fromISO(this.studiensemester_start, opts) + .toUnixInteger(); + const ende = luxon.DateTime + .fromISO(this.studiensemester_ende, opts) + .toUnixInteger(); + + const download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'cis/private/lvplan/stpl_kalender.php' + + '?type=' + type + + '&pers_uid=' + this.uid + + '&begin=' + start + + '&ende=' + ende; + + return [ + { title: "excel", icon: 'fa-solid fa-file-excel', link: download_link + '&format=excel' }, + { title: "csv", icon: 'fa-solid fa-file-csv', link: download_link + '&format=csv' }, + { title: "ical1", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=1&target=ical' }, + { title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' } + ]; + } + }, + methods: { + handleChangeDate(day, newMode) { + return this.handleChangeMode(newMode, day); + }, + handleChangeMode(newMode, day) { + const mode = newMode[0].toUpperCase() + newMode.slice(1) + const focus_date = day.toISODate(); + + this.$router.push({ + name: "StgOrgLvPlan", + params: { + mode, + focus_date, + stgkz: this.propsViewData.stgkz, + sem: this.propsViewData.sem, + verband: this.propsViewData.verband, + gruppe: this.propsViewData.gruppe, + } + }); + }, + updateRange(rangeInterval) { + this.$api + .call(ApiLvPlan.studiensemesterDateInterval( + rangeInterval.end.startOf('week').toISODate() + )) + .then(res => { + this.studiensemester_kurzbz = res.data.studiensemester_kurzbz; + this.studiensemester_start = res.data.start; + this.studiensemester_ende = res.data.ende; + }); + }, + getPromiseFunc(start, end) { + return [ + this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)), + //local for test + /* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.stgkz, this.sem, this.verband, this.gruppe))*/ + ]; + }, + backToDropdown(){ + this.$router.push({ + name: "OverviewLvPlan", + }); + } + }, + created(){ + this.$api + .call(ApiAuthinfo.getAuthInfo()) + .then(res => { + this.uid = res.data.uid; + this.isMitarbeiter = res.data.isMitarbeiter; + this.isStudent = res.data.isStudent; + }); + + if(this.propsViewData.stgkz) { + this.$api + .call(ApiLvPlan.getStudiengaenge()) + .then(result => { + const currentStg = result.data.find( + item => item.studiengang_kz == this.propsViewData.stgkz + ); + this.currentStgBezeichnung = currentStg.kurzbzlang + " - " + currentStg.bezeichnung; + }) + .catch(this.$fhcAlert.handleSystemError); + } + }, + template: ` +
+

{{ $p.t('LvPlan/headerLvPlanLvVerband') }}

+

{{currentStgBezeichnung}} + Semester: {{propsViewData.sem}} + Verband: {{propsViewData.verband}} + Gruppe: {{propsViewData.gruppe}} + +

+

{{ $p.t('LvPlan/noStgProvided') }}

+ + + + +
+ `, +}; \ No newline at end of file diff --git a/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js b/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js index badbf537f..d4ddeaf60 100644 --- a/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js +++ b/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js @@ -31,7 +31,7 @@ export default { this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n") + "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3]) ].join(": ")); - } else {console.log(this.event.lektor); + } else {//console.log(this.event.lektor); tooltipArray.push([ this.$p.t('lehre/lektor'), this.event.lektor.map(lektor => lektor.kurzbz).join("\n") From 6c2a2e4665f57b5da31edf39535c43afe32888ca Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Thu, 27 Nov 2025 18:15:38 +0100 Subject: [PATCH 03/14] work in progress, add reset function to EventLoader composable, to be able to reload when external parameters are changed --- public/js/apps/Dashboard/Fhc.js | 2 +- public/js/components/Calendar/LvPlan.js | 8 ++++++-- public/js/components/Cis/LvPlan/StgOrg.js | 8 ++++++++ public/js/composables/EventLoader.js | 17 ++++++++++++++--- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/public/js/apps/Dashboard/Fhc.js b/public/js/apps/Dashboard/Fhc.js index 4a19695ae..e14396cc3 100644 --- a/public/js/apps/Dashboard/Fhc.js +++ b/public/js/apps/Dashboard/Fhc.js @@ -193,7 +193,7 @@ const router = VueRouter.createRouter({ } }, { - path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?`, + path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?/:stgkz?/:sem?/:verband?/:gruppe?`, name: 'StgOrgLvPlan', component: StgOrgLvPlan, props(route) { diff --git a/public/js/components/Calendar/LvPlan.js b/public/js/components/Calendar/LvPlan.js index e0e918f01..6b4257853 100644 --- a/public/js/components/Calendar/LvPlan.js +++ b/public/js/components/Calendar/LvPlan.js @@ -88,12 +88,15 @@ export default { updateRange(rangeInterval) { this.rangeInterval = rangeInterval; this.$emit('update:range', rangeInterval); + }, + resetEventLoader() { + this.reset(); } }, setup(props, context) { const rangeInterval = Vue.ref(null); - const { events, lv } = useEventLoader(rangeInterval, props.getPromiseFunc); + const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc); Vue.watch(lv, newValue => { context.emit('update:lv', newValue); @@ -102,7 +105,8 @@ export default { return { rangeInterval, events, - lv + lv, + reset }; }, created() { diff --git a/public/js/components/Cis/LvPlan/StgOrg.js b/public/js/components/Cis/LvPlan/StgOrg.js index f7deff692..807d47e26 100644 --- a/public/js/components/Cis/LvPlan/StgOrg.js +++ b/public/js/components/Cis/LvPlan/StgOrg.js @@ -136,7 +136,10 @@ export default { }, }); + this.$refs['calendar'].resetEventLoader(); + //wenn sich formvariablen ändern? +/* if(this.rangeIntervalFirst) { console.log("in range IntervalFirst second try "); const start = luxon.DateTime.fromISO(this.rangeIntervalFirst.start).toISODate(); @@ -145,6 +148,8 @@ export default { this.getPromiseFunc(start, end); } + */ + }, loadListSem(){ this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1); @@ -218,6 +223,7 @@ export default { }); //try for reload + /* if(this.rangeIntervalFirst) { console.log("in range IntervalFirst second try "); const start = luxon.DateTime.fromISO(this.rangeIntervalFirst.start).toISODate(); @@ -225,6 +231,7 @@ export default { console.log("start und end" + start + " - " + end); this.getPromiseFunc(start, end); } + */ }, updateRange(rangeInterval) { @@ -372,6 +379,7 @@ export default { { + const reload = () => { const range = Vue.toValue(rangeInterval); if (!(range instanceof luxon.Interval)) return; @@ -132,7 +132,18 @@ export function useEventLoader(rangeInterval, getPromiseFunc) { } }) }); - }) + }; - return { events: allEvents, lv } + Vue.watchEffect(reload); + + const reset = () => { + loading_id = 0; + events.value = []; + loadingEvents.value = []; + eventsLoaded.splice(0, eventsLoaded.length); + + reload(); + } + + return { events: allEvents, lv, reset } } \ No newline at end of file From f0641ddd6de591adc06bbd3980144538e8540b7f Mon Sep 17 00:00:00 2001 From: ma0068 Date: Tue, 2 Dec 2025 17:30:11 +0100 Subject: [PATCH 04/14] refactor Dropdown - load dropdown only if dropdown before was chosen - validation if field in between is empty - ensure no null value gets into route --- public/js/api/factory/lvPlan.js | 1 - public/js/components/Cis/LvPlan/StgOrg.js | 153 ++++++---------------- system/phrasesupdate.php | 40 ++++++ 3 files changed, 78 insertions(+), 116 deletions(-) diff --git a/public/js/api/factory/lvPlan.js b/public/js/api/factory/lvPlan.js index 01c7253d6..33e82cff9 100644 --- a/public/js/api/factory/lvPlan.js +++ b/public/js/api/factory/lvPlan.js @@ -94,7 +94,6 @@ export default { }; }, eventsStgOrg(start_date, end_date, stg_kz, sem, verband, gruppe) { - console.log("stg_Kz" + stg_kz + " sem " + sem + " vb " + verband + " gr " + gruppe); return { method: 'post', url: '/api/frontend/v1/lvPlan/eventsStgOrg', diff --git a/public/js/components/Cis/LvPlan/StgOrg.js b/public/js/components/Cis/LvPlan/StgOrg.js index 807d47e26..3a1d58fb7 100644 --- a/public/js/components/Cis/LvPlan/StgOrg.js +++ b/public/js/components/Cis/LvPlan/StgOrg.js @@ -48,18 +48,6 @@ export default { ); return currentStg.max_semester; }, - //use local props, for viewDataProps cannot be changed -/* currentDay() { - if (!this.localProps?.focus_date || isNaN(new Date(this.localProps.focus_date))) - return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); - return this.localProps.focus_date; - }, - - currentMode() { - if (!this.localProps?.mode || !['day', 'week', 'month'].includes(this.localProps.mode.toLowerCase())) - return DEFAULT_MODE_LVPLAN; - return this.localProps.mode; - },*/ currentDay() { if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); @@ -112,48 +100,49 @@ export default { return; } -/* if(this.rangeIntervalFirst) { - console.log("in range IntervalFirst "); - this.updateRange(this.rangeIntervalFirst); + if(!this.formData.sem && (this.formData.verband || this.formData.gruppe)){ + this.$fhcAlert.alertError(this.$p.t('LvPlan', 'error_SemMissing')); + return; } - console.log("formParameters: " + this.formData.stgkz + " focusdate" + this.currentDay);*/ + if(!this.formData.verband && this.formData.gruppe){ + this.$fhcAlert.alertError(this.$p.t('LvPlan', 'error_VerbandMissing')); + return; + } -/* const newFocus = luxon.DateTime.fromISO(this.propsViewData.focus_date) - .setZone(this.viewData.timezone) - .toISODate() + "_";*/ // minimaler Unterschied + const params = { + mode: this.currentMode, + focus_date: this.currentDay, + stgkz: this.formData.stgkz, + sem: this.formData.sem, + verband: this.formData.verband, + gruppe: this.formData.gruppe, + }; + + //ensure logic: no value after a null value in route + if(params.sem == null) + { + params.verband = null; + params.gruppe = null; + } + if(params.verband == null) { + params.gruppe = null; + } + + //delete all null values to avoid null in router + Object.keys(params).forEach( + key => params[key] == null && delete params[key] + ); - //also use router push here this.$router.push({ name: "StgOrgLvPlan", - params: { - mode: this.currentMode, - focus_date: this.currentDay, - stgkz: this.formData.stgkz, - sem: this.formData.sem, - verband: this.formData.verband, - gruppe: this.formData.gruppe, - }, + params, }); this.$refs['calendar'].resetEventLoader(); - - //wenn sich formvariablen ändern? -/* - if(this.rangeIntervalFirst) { - console.log("in range IntervalFirst second try "); - const start = luxon.DateTime.fromISO(this.rangeIntervalFirst.start).toISODate(); - const end = luxon.DateTime.fromISO(this.rangeIntervalFirst.end).toISODate(); - console.log("start und end" + start + " - " + end); - this.getPromiseFunc(start, end); - } - - */ - }, loadListSem(){ this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1); - this.loadListVerband(); }, loadListVerband(){ this.$api @@ -169,7 +158,6 @@ export default { .sort(); }) .catch(this.$fhcAlert.handleSystemError); - this.loadListGroup(); }, loadListGroup(){ this.$api @@ -186,30 +174,12 @@ export default { .catch(this.$fhcAlert.handleSystemError); }, handleChangeDate(day, newMode) { - console.log("in handleChangeDate: day: " + day + " newMode " + newMode); return this.handleChangeMode(newMode, day); }, handleChangeMode(newMode, day) { - console.log("in handleChangeMode: day: " + day + " newMode " + newMode); - console.log(this.$route.name, this.$route.params); const mode = newMode[0].toUpperCase() + newMode.slice(1); const focus_date = day.toISODate(); - console.log(this.formData.stgkz, this.formData.sem, this.formData.verband, this.formData.gruppe); - - -/* this.$router.push({ - name: "StgOrgLvPlan", - params: { - mode, - focus_date, - stgkz: this.propsViewData.stgkz, - sem: this.propsViewData.sem, - verband: this.propsViewData.verband, - gruppe: this.propsViewData.gruppe, - } - });*/ - this.$router.push({ name: "StgOrgLvPlan", params: { @@ -221,41 +191,8 @@ export default { gruppe: this.formData.gruppe, }, }); - - //try for reload - /* - if(this.rangeIntervalFirst) { - console.log("in range IntervalFirst second try "); - const start = luxon.DateTime.fromISO(this.rangeIntervalFirst.start).toISODate(); - const end = luxon.DateTime.fromISO(this.rangeIntervalFirst.end).toISODate(); - console.log("start und end" + start + " - " + end); - this.getPromiseFunc(start, end); - } - */ - }, updateRange(rangeInterval) { - console.log("in updateRange " + rangeInterval); - - //initialise at first run - if(!this.rangeIntervalFirst) { - this.rangeIntervalFirst = rangeInterval; - console.log("new local RI " + this.rangeIntervalFirst); - } - - -/* const startOfWeek = rangeInterval.end.startOf('week').toISODate(); - - console.log("in updateRange", startOfWeek);*/ -/* - if (!rangeInterval || !rangeInterval.end || !rangeInterval.end.startOf) { - console.warn("updateRange: invalid rangeInterval", rangeInterval); - return; - } - - const startOfWeek = rangeInterval.end.startOf('week').toISODate(); - - console.log("in updateRange", startOfWeek);*/ this.$api .call(ApiLvPlan.studiensemesterDateInterval( rangeInterval.end.startOf('week').toISODate() @@ -267,15 +204,7 @@ export default { }); }, getPromiseFunc(start, end) { - console.log("start " + start + " end " + end); - console.log(this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe); - console.log(this.formData.stgkz, this.formData.sem, this.formData.verband, this.formData.gruppe); - return [ - //this.$api.call(ApiLvPlan.eventsStgOrg(start, end, this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)), - //variante die von außen funktioniert -/* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)),*/ - //local for test this.$api.call(ApiLvPlan.eventsStgOrg(start, end, this.formData.stgkz, this.formData.sem, this.formData.verband, this.formData.gruppe)) ]; }, @@ -296,8 +225,6 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); - //this.localProps = { ...this.propsViewData }; - if(this.propsViewData) { this.formData.stgkz = this.propsViewData.stgkz ? this.propsViewData.stgkz: null; this.formData.sem = this.propsViewData.sem ? this.propsViewData.sem: null; @@ -305,12 +232,6 @@ export default { this.formData.gruppe = this.propsViewData.gruppe ? this.propsViewData.gruppe: null; } }, - /* - - v-if="propsViewData && propsViewData.stgkz" - {{propsViewData}} || {{formData}} || {{viewData}} - */ - template: `
@@ -321,7 +242,7 @@ export default { v-model="formData.stgkz" @change="loadListSem(formData.stgkz)" > - + + + +