From cf59bcff12b4e4ad1a14ccbcd34ffbef7478dcf7 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Tue, 19 Aug 2025 09:16:26 +0200 Subject: [PATCH 001/275] add Functionality sendInfomail - expand InputComponent for dynamic adding of actionButton to Form Upload Image - add Phrases for Infomail --- public/js/components/Form/Input.js | 4 +- public/js/components/Form/Upload/Image.js | 13 +++++- .../Studentenverwaltung/Details/Details.js | 12 ++++++ system/phrasesupdate.php | 42 ++++++++++++++++++- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/public/js/components/Form/Input.js b/public/js/components/Form/Input.js index c85272aab..4044b32fb 100644 --- a/public/js/components/Form/Input.js +++ b/public/js/components/Form/Input.js @@ -28,7 +28,8 @@ export default { label: String, // NOTE(chris): remove these from $attrs array to prevent doubled event listeners onInput: [Array, Function], - 'onUpdate:modelValue': [Array, Function] + 'onUpdate:modelValue': [Array, Function], + titleActionButton: String }, data() { return { @@ -314,6 +315,7 @@ export default { :id="idCmp" :name="name" :class="validationClass" + :titleActionButton="titleActionButton" @update:model-value="clearValidationForThisName" > diff --git a/public/js/components/Form/Upload/Image.js b/public/js/components/Form/Upload/Image.js index b0b8d5b78..4b96d4c85 100644 --- a/public/js/components/Form/Upload/Image.js +++ b/public/js/components/Form/Upload/Image.js @@ -1,9 +1,14 @@ export default { emits: [ - 'update:modelValue' + 'update:modelValue', + 'actionbutton-clicked' ], props: { - modelValue: String + modelValue: String, + titleActionButton: { + type: String, + default: "" + } }, computed: { valueAsBase64DataString() { @@ -28,6 +33,9 @@ export default { }, deleteImage() { this.$emit('update:modelValue', ''); + }, + emitAction(){ + this.$emit('actionbutton-clicked', this.modelValue); } }, template: ` @@ -42,6 +50,7 @@ export default { + diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Details.js index 45ee3bdd5..61eea7605 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Details.js @@ -158,6 +158,16 @@ export default { }, reload(){ this.updateStudent(this.modelValue); + }, + sendInfomail(){ + const subject = this.$p.t('person', 'betreffProfilfoto'); + const subjectEncoded = encodeURIComponent(subject); + + const body = this.$p.t('person', 'mailText_profilfoto'); + const bodyWithNewLines = body.replace(/\\n/g, '\n'); + const bodyEncoded = encodeURIComponent(bodyWithNewLines); + + window.location.href = "mailto:" + this.modelValue.mail_intern + "?subject=" + subjectEncoded + "&body=" + bodyEncoded; } }, created() { @@ -400,8 +410,10 @@ export default { container-class="col-4 stv-details-details-foto" :label="$p.t('person', 'foto')" type="UploadImage" + titleActionButton="Infomail" v-model="data.foto" name="foto" + @actionbutton-clicked="sendInfomail" > No Image diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 4a3cfd7ec..3b0e607c9 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -30281,8 +30281,6 @@ array( ) ) ), - - //ProfilUpdate Phrasen ende array( 'app' => 'core', @@ -46677,6 +46675,46 @@ and represent the current state of research on the topic. The prescribed citatio ) ) ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'mailText_profilfoto', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Hallo,\n\nIhr Profilbild wurde entfernt, da es nicht den aktuellen Bildrichtlinen entspricht. Bitte laden Sie unter CIS->Profil ein neues Profilbild hoch!\n\nDanke!", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Hello,\n\nYour profile picture has been removed because it does not comply with the current image guidelines. Please upload a new profile picture under CIS->Profile!\n\nThank you!", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'betreffProfilfoto', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Profilbild', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Profile picture', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // FHC-4 Finetuning END //**************************** CORE/search From 4c6d645cc62bea6ae5cdbb8a2451940994a019e0 Mon Sep 17 00:00:00 2001 From: chfhtw Date: Tue, 18 Nov 2025 15:02:55 +0100 Subject: [PATCH 002/275] Studienverlauf verlinken im neuen FAS --- .../Stv/Studentenverwaltung/Details/Noten.js | 13 +++++++++++- system/phrasesupdate.php | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js index 71b8096ec..817465879 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js @@ -25,6 +25,11 @@ export default { stdsem: '' }; }, + computed: { + gradeListLink() { + return this.$api.getUri('/person/gradelist/index/' + this.modelValue.uid); + } + }, methods: { reload() { this.$refs.zeugnis.$refs.table.reloadTable(); @@ -39,8 +44,14 @@ export default { const savedPath = window.localStorage.getItem(LOCAL_STORAGE_ID); this.stdsem = savedPath || ''; }, - template: ` + template: /* html */`
+
+ + + +
+
+
+ +
+
+ +
+
+
` +}; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js index 71b8096ec..3217f192c 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js @@ -2,6 +2,9 @@ import NotenZeugnis from './Noten/Zeugnis.js'; import NotenTeacher from './Noten/Teacher.js'; import NotenRepeater from './Noten/Repeater.js'; +import ApiStvGrades from '../../../../api/factory/stv/grades.js'; +import { highlightGesamtnote } from "../../../../helpers/DocumentHelper"; + const LOCAL_STORAGE_ID = 'stv_details_noten_2024-11-25_stdsem_all'; export default { @@ -22,7 +25,10 @@ export default { }, data() { return { - stdsem: '' + stdsem: '', + endpoint: ApiStvGrades, + zeugnisLoaded: false, + teacherLoaded: false, }; }, methods: { @@ -33,6 +39,30 @@ export default { }, saveStdsem(event) { window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value); + }, + onZeugnisLoaded() { + this.zeugnisLoaded = true; + this.checkHighlight(); + }, + onTeacherLoaded() { + this.teacherLoaded = true; + this.checkHighlight(); + }, + checkHighlight() + { + if (!this.zeugnisLoaded || !this.teacherLoaded) + return; + + if (!this.$refs.zeugnis || !this.$refs.teacher) + return; + + let zeugnisTable = this.$refs.zeugnis.$refs.table.tabulator; + let teacherTable = this.$refs.teacher.$refs.table.tabulator; + + if (!zeugnisTable || !teacherTable) + return; + + highlightGesamtnote(zeugnisTable, teacherTable); } }, created() { @@ -49,10 +79,10 @@ export default {
- +
- +
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js index e566d3a09..f25d291cf 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js @@ -1,13 +1,12 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; -import ApiStvGrades from '../../../../../api/factory/stv/grades.js'; - export default { components: { CoreFilterCmpt }, emits: [ - "copied" + "copied", + "loaded" ], inject: { currentSemester: { @@ -16,47 +15,127 @@ export default { } }, props: { - student: Object, - allSemester: Boolean + endpoint: { + type: Object, + required: true + }, + id: { + type: [Number, String], + required: true + }, + allSemester: Boolean, + optionalTabulatorOptions: Object, }, data() { return { - tabulatorEvents: [] + tabulatorEvents: [ + { + event: "dataProcessed", + handler: () => this.$emit("loaded"), + }, + ] }; }, computed: { tabulatorOptions() { return { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvGrades.getTeacherProposal( - this.student.prestudent_id, + ajaxRequestFunc: () => this.$api.call(this.endpoint.getTeacherProposal( + this.id, (!this.allSemester ? this.currentSemester : null) )), ajaxResponse: (url, params, response) => { return response.data || []; }, columns: [ - { field: 'lehrveranstaltung_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') }, - { field: 'note_bezeichnung', title: this.$p.t('lehre/note') }, - { field: 'mitarbeiter_uid', title: this.$p.t('profil/mitarbeiterIn'), visible: false }, - { field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false }, - { field: 'freigabedatum', title: this.$p.t('stv/grades_approvaldate'), visible: false }, - { field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false }, - { field: 'note', title: this.$p.t('stv/grades_numericgrade'), visible: false }, - { field: 'student_uid', title: this.$p.t('profil/studentIn'), visible: false }, - { field: 'lehrveranstaltung_id', title: this.$p.t('lehre/lehrveranstaltung_id'), visible: false }, - { field: 'punkte', title: this.$p.t('stv/grades_points'), visible: false } + { + field: 'lehrveranstaltung_bezeichnung', + title: this.$p.t('lehre/lehrveranstaltung'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_bezeichnung ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_bezeichnung || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'note_bezeichnung', + title: this.$p.t('lehre/note'), + visible: this.optionalTabulatorOptions?.visibleColumns?.note_bezeichnung ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.note_bezeichnung || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'mitarbeiter_uid', + title: this.$p.t('profil/mitarbeiterIn'), + visible: this.optionalTabulatorOptions?.visibleColumns?.mitarbeiter_uid ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.mitarbeiter_uid || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'benotungsdatum', + title: this.$p.t('stv/grades_gradingdate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.benotungsdatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.benotungsdatum || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'freigabedatum', + title: this.$p.t('stv/grades_approvaldate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.freigabedatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.freigabedatum || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'studiensemester_kurzbz', + title: this.$p.t('lehre/studiensemester'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiensemester_kurzbz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiensemester_kurzbz || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'note', + title: this.$p.t('stv/grades_numericgrade'), + visible: this.optionalTabulatorOptions?.visibleColumns?.note ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.note || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'student_uid', + title: this.$p.t('profil/studentIn'), + visible: this.optionalTabulatorOptions?.visibleColumns?.student_uid ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.student_uid || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'lehrveranstaltung_id', + title: this.$p.t('lehre/lehrveranstaltung_id'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_id ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_id || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'punkte', + title: this.$p.t('stv/grades_points'), + visible: this.optionalTabulatorOptions?.visibleColumns?.punkte ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.punkte || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'vorname', + title: this.$p.t('person/vorname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.vorname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.vorname || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'nachname', + title: this.$p.t('person/nachname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.nachname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.nachname || this.optionalTabulatorOptions?.headerFilter || false + } ], + columnDefaults: { + headerFilter: this.optionalTabulatorOptions?.headerFilter ?? false, + }, layout: 'fitDataStretch', height: '100%', selectable: true, + selectableRows: true, // needed for Tabulator v6 + selectableRowsRangeMode: 'click', // needed for Tabulator v6 selectableRangeMode: 'click', - persistenceID: 'stv-details-noten-teacher' + persistenceID: this.optionalTabulatorOptions?.persistenceTeacherID ?? 'stv-details-noten-teacher-2025120401', }; } }, watch: { - student(n) { + id() { this.$refs.table.reloadTable(); }, allSemester(n) { @@ -67,7 +146,7 @@ export default { copyGrades(selected) { const promises = selected.map( grade => this.$api - .call(ApiStvGrades.copyTeacherProposalToCertificate(grade), { + .call(this.endpoint.copyTeacherProposalToCertificate(grade), { errorHeader: grade.lehrveranstaltung_bezeichnung }) .then(() => { diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js index 4d0996b6b..8e921c668 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js @@ -2,8 +2,6 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; import ZeugnisActions from './Zeugnis/Actions.js'; import ZeugnisDocuments from './Zeugnis/Documents.js'; -import ApiStvGrades from '../../../../../api/factory/stv/grades.js'; - export default { name: 'Zeugnis', components: { @@ -11,6 +9,7 @@ export default { ZeugnisActions, ZeugnisDocuments }, + emits: ['loaded'], inject: { config: { from: 'config', @@ -22,7 +21,15 @@ export default { } }, props: { - student: Object, + endpoint: { + type: Object, + required: true + }, + id: { + type: [Number, String], + required: true + }, + optionalTabulatorOptions: Object, allSemester: Boolean }, data() { @@ -35,6 +42,10 @@ export default { return item; }) }, + { + event: "dataProcessed", + handler: () => this.$emit("loaded") + }, { event: "rowSelected", handler: row => row.getElement().style.zIndex = 12 @@ -53,7 +64,7 @@ export default { computed: { tabulatorOptions() { const listPromise = this.$api - .call(ApiStvGrades.list()) + .call(this.endpoint.list()) .then(res => res.data.map(({bezeichnung: label, note: value}) => ({label, value}))); let gradeField = { @@ -86,7 +97,7 @@ export default { note_bezeichnung })) // send to backend - .then(data => this.$api.call(ApiStvGrades.updateCertificate(data))) + .then(data => this.$api.call(this.endpoint.updateCertificate(data))) // get bezeichnung again .then(() => listPromise) .then(list => list.find(el => el.value == note)) @@ -109,7 +120,7 @@ export default { if (filterTerm) { return this.$api .call( - ApiStvGrades.getGradeFromPoints( + this.endpoint.getGradeFromPoints( filterTerm, cell.getData().lehrveranstaltung_id, this.currentSemester @@ -142,27 +153,146 @@ export default { .then(() => node.innerText = this.$p.t('ui/loading')) .catch(this.$fhcAlert.handleSystemError); gradeField.editorParams.placeholderLoading = node; + gradeField.visible = this.optionalTabulatorOptions?.visibleColumns?.note ?? true + gradeField.headerFiler = this.optionalTabulatorOptions?.headerFilter?.note || this.optionalTabulatorOptions?.note || false } const columns = [ - { field: 'zeugnis', title: this.$p.t('stv/grades_zeugnis'), formatter: 'tickCross' }, - { field: 'lehrveranstaltung_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') }, - gradeField, - { field: 'uebernahmedatum', title: this.$p.t('stv/grades_takeoverdate'), visible: false }, - { field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false }, - { field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false }, - { field: 'note_number', title: this.$p.t('stv/grades_numericgrade'), visible: false, formatter: cell => cell.getData().note, tooltip: (evt, cell) => cell.getData().note }, - { field: 'lehrveranstaltung_id', title: this.$p.t('lehre/lehrveranstaltung_id'), visible: false }, - { field: 'studiengang', title: this.$p.t('lehre/studiengang'), visible: false }, - { field: 'studiengang_kz', title: this.$p.t('lehre/studiengangskennzahlLehre'), visible: false }, - { field: 'studiengang_lv', title: this.$p.t('stv/grades_studiengang_lv'), visible: false }, - { field: 'studiengang_kz_lv', title: this.$p.t('stv/grades_studiengang_kz_lv'), visible: false }, - { field: 'semester_lv', title: this.$p.t('stv/grades_semester_lv'), visible: false }, - { field: 'ects_lv', title: this.$p.t('lehre/ects'), visible: false }, - { field: 'lehrform', title: this.$p.t('lehre/lehrform'), visible: false }, - { field: 'kurzbz', title: this.$p.t('lehre/kurzbz'), visible: false }, - { field: 'punkte', title: this.$p.t('stv/grades_points'), visible: false }, - { field: 'lehrveranstaltung_bezeichnung_english', title: this.$p.t('stv/grades_lehrveranstaltung_bezeichnung_english'), visible: false } + { + field: 'zeugnis', + title: this.$p.t('stv/grades_zeugnis'), + formatter: 'tickCross', + visible: this.optionalTabulatorOptions?.visibleColumns?.zeugnis ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.zeugnis || this.optionalTabulatorOptions?.zeugnis || false + }, + { + field: 'lehrveranstaltung_bezeichnung', + title: this.$p.t('lehre/lehrveranstaltung'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_bezeichnung ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_bezeichnung || this.optionalTabulatorOptions?.lehrveranstaltung_bezeichnung || false + + }, + gradeField, + { + field: 'uebernahmedatum', + title: this.$p.t('stv/grades_takeoverdate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.uebernahmedatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.uebernahmedatum || this.optionalTabulatorOptions?.uebernahmedatum || false + + }, + { + field: 'benotungsdatum', + title: this.$p.t('stv/grades_gradingdate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.benotungsdatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.benotungsdatum || this.optionalTabulatorOptions?.benotungsdatum || false + + }, + { + field: 'studiensemester_kurzbz', + title: this.$p.t('lehre/studiensemester'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiensemester_kurzbz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiensemester_kurzbz || this.optionalTabulatorOptions?.studiensemester_kurzbz || false + + }, + { + field: 'note_number', + title: this.$p.t('stv/grades_numericgrade'), + visible: this.optionalTabulatorOptions?.visibleColumns?.note_number ?? false, + formatter: cell => cell.getData().note, tooltip: (evt, cell) => cell.getData().note, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.note_number || this.optionalTabulatorOptions?.note_number || false + + }, + { + field: 'lehrveranstaltung_id', + title: this.$p.t('lehre/lehrveranstaltung_id'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_id ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_id || this.optionalTabulatorOptions?.lehrveranstaltung_id || false + + }, + { + field: 'studiengang', + title: this.$p.t('lehre/studiengang'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang || this.optionalTabulatorOptions?.studiengang || false + + }, + { + field: 'studiengang_kz', + title: this.$p.t('lehre/studiengangskennzahlLehre'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang_kz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang_kz || this.optionalTabulatorOptions?.studiengang_kz || false + + }, + { + field: 'studiengang_lv', + title: this.$p.t('stv/grades_studiengang_lv'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang_lv || this.optionalTabulatorOptions?.studiengang_lv || false + + }, + { + field: 'studiengang_kz_lv', + title: this.$p.t('stv/grades_studiengang_kz_lv'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang_kz_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang_kz_lv || this.optionalTabulatorOptions?.studiengang_kz_lv || false + + }, + { + field: 'semester_lv', + title: this.$p.t('stv/grades_semester_lv'), + visible: this.optionalTabulatorOptions?.visibleColumns?.semester_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.semester_lv || this.optionalTabulatorOptions?.semester_lv || false + + }, + { + field: 'ects_lv', + title: this.$p.t('lehre/ects'), + visible: this.optionalTabulatorOptions?.visibleColumns?.ects_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.ects_lv || this.optionalTabulatorOptions?.ects_lv || false + + }, + { + field: 'lehrform', + title: this.$p.t('lehre/lehrform'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrform ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrform || this.optionalTabulatorOptions?.lehrform || false + + }, + { + field: 'kurzbz', + title: this.$p.t('lehre/kurzbz'), + visible: this.optionalTabulatorOptions?.visibleColumns?.kurzbz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.kurzbz || this.optionalTabulatorOptions?.kurzbz || false + + }, + { + field: 'punkte', + title: this.$p.t('stv/grades_points'), + visible: this.optionalTabulatorOptions?.visibleColumns?.punkte ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.punkte || this.optionalTabulatorOptions?.punkte || false + + }, + { + field: 'vorname', + title: this.$p.t('person/vorname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.vorname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.vorname || this.optionalTabulatorOptions?.vorname || false + + }, + { + field: 'nachname', + title: this.$p.t('person/nachname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.nachname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.nachname || this.optionalTabulatorOptions?.nachname || false + + }, + { + field: 'lehrveranstaltung_bezeichnung_english', + title: this.$p.t('stv/grades_lehrveranstaltung_bezeichnung_english'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_bezeichnung_english ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_bezeichnung_english || this.optionalTabulatorOptions?.lehrveranstaltung_bezeichnung_english || false + + } ]; const hasDocuments = ['both', 'inline'].includes(this.config.documents); @@ -208,8 +338,8 @@ export default { return { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvGrades.getCertificate( - this.student.prestudent_id, + ajaxRequestFunc: () => this.$api.call(this.endpoint.getCertificate( + this.id, (!this.allSemester ? this.currentSemester : null) )), ajaxResponse: (url, params, response) => { @@ -218,9 +348,11 @@ export default { columns, height: '100%', layout: 'fitDataStretchFrozen', - selectable: 1, + selectable: true, selectableRangeMode: 'click', - persistenceID: 'stv-details-noten-zeugnis-2025112401', + selectableRowsRangeMode: 'click', //needed for Tabulator v6 + selectableRows: true, //needed for Tabulator v6 + persistenceID: this.optionalTabulatorOptions?.persistenceZeugnisID ?? 'stv-details-noten-zeugnis-2025120302', persistence:{ columns: ["width", "visible", "frozen"] } @@ -228,7 +360,8 @@ export default { } }, watch: { - student(n) { + id() + { this.$refs.table.reloadTable(); }, allSemester(n) { @@ -239,7 +372,7 @@ export default { setGrade(data) { this.$api .call( - ApiStvGrades.updateCertificate(data), + this.endpoint.updateCertificate(data), { errorHeader: data.lehrveranstaltung_bezeichnung } ) .then(this.$refs.table.reloadTable) @@ -252,7 +385,7 @@ export default { .confirmDelete() .then(result => result ? data : Promise.reject({handled:true})) .then(data => this.$api.call( - ApiStvGrades.deleteCertificate(data), + this.endpoint.deleteCertificate(data), { errorHeader: data.lehrveranstaltung_bezeichnung } )) .then(this.$refs.table.reloadTable) @@ -261,7 +394,7 @@ export default { } }, created() { - this.$p.loadCategory(['global', 'stv', 'lehre']) + this.$p.loadCategory(['global', 'stv', 'lehre', 'person']) .then(() => { if (this.$refs.table.tableBuilt) this.$refs.table.tabulator.columnManager.setColumns(this.tabulatorOptions.columns); diff --git a/public/js/helpers/DocumentHelper.js b/public/js/helpers/DocumentHelper.js new file mode 100644 index 000000000..c37c632d5 --- /dev/null +++ b/public/js/helpers/DocumentHelper.js @@ -0,0 +1,44 @@ +export function highlightGesamtnote(zeugnisTable, teacherTable) +{ + let zeugnisData = zeugnisTable.getData(); + + let studentsWithNote = new Set(); + + zeugnisData.forEach(row => { + let student = row.uid; + let lv = row.lehrveranstaltung_id; + let note = row.note; + + if (!student || !lv) + return; + + if (note == null || note === "") + return; + + let key = `${student}_${lv}`; + studentsWithNote.add(key); + }); + + teacherTable.deselectRow(); + + teacherTable.getRows().forEach(row => { + let data = row.getData(); + + let student = data.student_uid; + let lv = data.lehrveranstaltung_id; + let note = data.note; + + if (!student || !lv) + return; + + if (note == null || note === "") + return; + + let key = `${student}_${lv}`; + + if (!studentsWithNote.has(key)) + { + row.select(); + } + }); +} \ No newline at end of file From 60f128b314a73bb2edd6046620ccd48e4d8a8c4d Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 9 Dec 2025 13:00:40 +0100 Subject: [PATCH 011/275] tabs gleichgezogen --- public/js/components/LVVerwaltung/Setup/Tabs.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/js/components/LVVerwaltung/Setup/Tabs.js b/public/js/components/LVVerwaltung/Setup/Tabs.js index fbb18bedf..5425b5169 100644 --- a/public/js/components/LVVerwaltung/Setup/Tabs.js +++ b/public/js/components/LVVerwaltung/Setup/Tabs.js @@ -53,17 +53,21 @@ export default { From ab63bf9d74def7764a66c7942e9410a241892692 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 12 Jan 2026 09:27:27 +0100 Subject: [PATCH 012/275] gruppen in baum anzeigen und drag and drop von gruppen --- .../controllers/api/frontend/v1/lv/Gruppe.php | 68 +++++++- .../api/frontend/v1/lv/StgTree.php | 146 +++++++++++++++++- .../education/Lehreinheitgruppe_model.php | 47 +++--- public/css/Lvverwaltung.css | 6 + public/js/api/lehrveranstaltung/gruppe.js | 8 + .../LVVerwaltung/Details/Gruppen.js | 66 +++++--- .../components/LVVerwaltung/LVVerwaltung.js | 2 +- .../Stv/Studentenverwaltung/Verband.js | 26 +++- public/js/helpers/DragAndDrop.js | 4 + 9 files changed, 325 insertions(+), 48 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/Gruppe.php b/application/controllers/api/frontend/v1/lv/Gruppe.php index daebe8a61..f60d3a99a 100644 --- a/application/controllers/api/frontend/v1/lv/Gruppe.php +++ b/application/controllers/api/frontend/v1/lv/Gruppe.php @@ -15,6 +15,7 @@ class Gruppe extends FHCAPI_Controller 'getBenutzerSearch' => ['admin:r', 'assistenz:r'], 'getAllSearch' => ['admin:r', 'assistenz:r'], 'getByLehreinheit' => ['admin:r', 'assistenz:r'], + 'getGruppe' => ['admin:r', 'assistenz:r'], ]); $this->_ci = &get_instance(); @@ -67,7 +68,7 @@ class Gruppe extends FHCAPI_Controller $this->checkPermission($lehreinheit_id); - $result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, !($lehrverband === 'false')); + $result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, $lehrverband === true || $lehrverband === 'true'); if (isError($result)) $this->terminateWithError(getError($result)); @@ -75,6 +76,71 @@ class Gruppe extends FHCAPI_Controller $this->terminateWithSuccess($result); } + public function getGruppe() + { + $lehrverband = $this->input->post('lehrverband'); + + $gruppen_result = array(); + + if ($lehrverband === false) + { + $gruppen_result = $this->_ci->GruppeModel->loadWhere(array( + 'studiengang_kz' => $this->input->post('stg_kz'), + 'gruppe_kurzbz' => $this->input->post('gruppe_kurzbz'), + 'aktiv' => true + )); + } + else if ($lehrverband === true) + { + + if (!isEmptyString($this->input->post('verband'))) + { + $this->LehrverbandModel->db->where('verband', $this->input->post('verband')); + } + else + { + $this->LehrverbandModel->db->group_start(); + $this->LehrverbandModel->db->where("trim(verband) = ''"); + $this->LehrverbandModel->db->or_where("verband IS NULL"); + $this->LehrverbandModel->db->group_end(); + } + + if (!isEmptyString($this->input->post('gruppe'))) + { + $this->LehrverbandModel->db->where('gruppe', $this->input->post('gruppe')); + } + else + { + $this->LehrverbandModel->db->group_start(); + $this->LehrverbandModel->db->where("trim(gruppe) = ''"); + $this->LehrverbandModel->db->or_where("gruppe IS NULL"); + $this->LehrverbandModel->db->group_end(); + } + + if (!isEmptyString((string)$this->input->post('semester'))) + { + $this->LehrverbandModel->db->where('semester', $this->input->post('semester')); + } + else + { + + $this->LehrverbandModel->db->group_start(); + $this->LehrverbandModel->db->where("semester = ''"); + $this->LehrverbandModel->db->or_where("semester IS NULL"); + $this->LehrverbandModel->db->group_end(); + } + + $gruppen_result = $this->LehrverbandModel->loadWhere(array('studiengang_kz' => $this->input->post('stg_kz'), 'aktiv' => true)); + } + + if (!hasData($gruppen_result)) + return $this->terminateWithError('No group found'); + + $gruppen_array = getData($gruppen_result)[0]; + + $this->terminateWithSuccess($gruppen_array->gid); + } + public function getByLehreinheit($lehreinheit_id = null) { if (is_null($lehreinheit_id) || !ctype_digit((string)$lehreinheit_id)) diff --git a/application/controllers/api/frontend/v1/lv/StgTree.php b/application/controllers/api/frontend/v1/lv/StgTree.php index 8272da978..f36d4030f 100644 --- a/application/controllers/api/frontend/v1/lv/StgTree.php +++ b/application/controllers/api/frontend/v1/lv/StgTree.php @@ -25,7 +25,25 @@ class StgTree extends FHCAPI_Controller return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]); } - return $this->getStudiengang($method); + $count = count($params); + if (!$count) + return $this->getStudiengang($method); + + if ($count == 1) { + if (is_numeric($params[0])) + return $this->getSemester($method, $params[0]); + else + return $this->getStudiengang($method, $params[0]); + } + if ($count == 2) { + if (is_numeric($params[0])) + return $this->getVerband($method, $params[0], $params[1]); + else + return $this->getSemester($method, $params[1], $params[0]); + } + + + show_404(); } @@ -64,16 +82,27 @@ class StgTree extends FHCAPI_Controller $this->terminateWithSuccess($list); } - protected function getStudiengang($studiengang_kz) + protected function getStudiengang($studiengang_kz, $org_form = null) { $link = $studiengang_kz . '/'; + if ($org_form !== null) + $link .= $org_form . '/'; $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); $this->StudiengangModel->addDistinct(); $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false); - $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester ORDER BY verband, gruppe LIMIT 1)) AS name", false); - $this->StudiengangModel->addSelect("TRUE AS leaf", false); + $this->StudiengangModel->addSelect("CONCAT( + UPPER(CONCAT(typ, kurzbz)), + '-', + semester, + ( + SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END + FROM public.tbl_lehrverband + WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester + ORDER BY verband, gruppe LIMIT 1 + ) + ) AS name", false); $this->StudiengangModel->addSelect('semester'); $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); @@ -111,6 +140,115 @@ class StgTree extends FHCAPI_Controller $list = array_merge($list, $result); } } + $this->terminateWithSuccess($list); + } + + protected function getSemester($studiengang_kz, $semester, $org_form = null) + { + $link = $studiengang_kz . '/'; + if ($org_form !== null) + $link .= $org_form . '/'; + $link .= $semester . '/'; + + + $this->load->model('organisation/Gruppe_model', 'GruppeModel'); + + $this->GruppeModel->addDistinct(); + $this->GruppeModel->addSelect("CONCAT(" . $this->GruppeModel->escape($link . 'grp/') . ", gruppe_kurzbz) AS link", false); + $this->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false); + $this->GruppeModel->addSelect("TRUE AS leaf", false); + + $this->GruppeModel->addSelect('sort'); + $this->GruppeModel->addSelect('gruppe_kurzbz'); + $this->GruppeModel->addSelect($this->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + + $this->GruppeModel->addOrder('sort'); + $this->GruppeModel->addOrder('gruppe_kurzbz'); + + $where = [ + 'studiengang_kz' => $studiengang_kz, + 'semester' => $semester, + 'lehre' => true, + 'sichtbar' => true, + 'aktiv' => true, + 'direktinskription' => false + ]; + + if ($org_form !== null) + $where['orgform_kurzbz'] = $org_form; + + $result = $this->GruppeModel->loadWhere($where); + + $list = $this->getDataOrTerminateWithError($result); + + $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", verband) AS link", false); + $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false); + $this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf"); + + $this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester'); + $this->StudiengangModel->addSelect('verband'); + $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + + $this->StudiengangModel->addOrder('verband'); + + $this->StudiengangModel->addGroupBy('link, name, verband'); + + $where = [ + 'v.studiengang_kz' => $studiengang_kz, + 'v.semester' => $semester, + 'v.verband !=' => '', + 'v.aktiv' => true + ]; + + if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all? + $where['v.orgform_kurzbz'] = $org_form; + + $result = $this->StudiengangModel->loadWhere($where); + $result = $this->getDataOrTerminateWithError($result); + + $list = array_merge($list, $result); + + $this->terminateWithSuccess($list); + } + + protected function getVerband($studiengang_kz, $semester, $verband, $org_form = null) + { + $link = $studiengang_kz . '/'; + if ($org_form !== null) + $link .= $org_form . '/'; + $link .= $semester . '/'. $verband . '/'; + + + $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->StudiengangModel->addDistinct(); + $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", gruppe) AS link", false); + $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false); + $this->StudiengangModel->addSelect("TRUE AS leaf", false); + + $this->StudiengangModel->addSelect('v.semester'); + $this->StudiengangModel->addSelect('v.verband'); + $this->StudiengangModel->addSelect('gruppe'); + $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + + $this->StudiengangModel->addOrder('gruppe'); + + $where = [ + 'v.studiengang_kz' => $studiengang_kz, + 'v.semester' => $semester, + 'v.verband' => $verband, + 'v.gruppe !=' => '', + 'v.aktiv' => true + ]; + + if ($org_form !== null && $semester) + $where['v.orgform_kurzbz'] = $org_form; + + $result = $this->StudiengangModel->loadWhere($where); + + $list = $this->getDataOrTerminateWithError($result); $this->terminateWithSuccess($list); } diff --git a/application/models/education/Lehreinheitgruppe_model.php b/application/models/education/Lehreinheitgruppe_model.php index dee8bbfe1..139d639cb 100644 --- a/application/models/education/Lehreinheitgruppe_model.php +++ b/application/models/education/Lehreinheitgruppe_model.php @@ -341,7 +341,7 @@ class Lehreinheitgruppe_model extends DB_Model $this->db->where('lehreinheit_id', $lehreinheit_id); $this->db->where('studiengang_kz', $gruppen_array->studiengang_kz); - if (!isEmptyString($gruppen_array->semester)) + if (!isEmptyString((string)$gruppen_array->semester)) { $this->db->where('semester', $gruppen_array->semester); } @@ -444,30 +444,37 @@ class Lehreinheitgruppe_model extends DB_Model ) ELSE tbl_gruppe.beschreibung END AS beschreibung"); - $this->addSelect("CASE - WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN - ( - SELECT EXISTS ( - SELECT 1 - FROM lehre.tbl_stundenplandev - WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id - AND studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz - AND semester = tbl_lehreinheitgruppe.semester - AND TRIM(COALESCE(verband, '')) = TRIM(tbl_lehreinheitgruppe.verband) - AND TRIM(COALESCE(gruppe, '')) = TRIM(tbl_lehreinheitgruppe.gruppe) - AND (gruppe_kurzbz IS NULL OR gruppe_kurzbz = '') - ) - ) - ELSE + $this->addSelect(" + CASE + WHEN trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, '')) = '' THEN ( SELECT EXISTS ( SELECT 1 - FROM lehre.tbl_stundenplandev - WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id - AND gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz + FROM lehre.tbl_stundenplandev sp + WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id + AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz + AND sp.semester = tbl_lehreinheitgruppe.semester + AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, '')) + AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, '')) + AND trim(COALESCE(sp.gruppe_kurzbz, '')) = '' ) ) - END AS verplant"); + ELSE + ( + SELECT EXISTS ( + SELECT 1 + FROM lehre.tbl_stundenplandev sp + WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id + AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz + AND sp.semester = tbl_lehreinheitgruppe.semester + AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, '')) + AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, '')) + AND trim(COALESCE(sp.gruppe_kurzbz, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, '')) + ) + ) + END AS verplant + "); + $this->addJoin('tbl_studiengang', 'studiengang_kz', 'LEFT'); $this->addJoin('public.tbl_gruppe', 'gruppe_kurzbz', 'LEFT'); diff --git a/public/css/Lvverwaltung.css b/public/css/Lvverwaltung.css index 6e0f4c760..b8152d39a 100644 --- a/public/css/Lvverwaltung.css +++ b/public/css/Lvverwaltung.css @@ -38,3 +38,9 @@ textarea[name="anmerkung"] { { border-color: black; } + +.node-verband { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; +} diff --git a/public/js/api/lehrveranstaltung/gruppe.js b/public/js/api/lehrveranstaltung/gruppe.js index 44340bec7..23db4c069 100644 --- a/public/js/api/lehrveranstaltung/gruppe.js +++ b/public/js/api/lehrveranstaltung/gruppe.js @@ -18,6 +18,14 @@ export default { params: newData }; }, + getGruppe(gruppe) + { + return { + method: 'post', + url: '/api/frontend/v1/lv/gruppe/getGruppe/', + params: gruppe + }; + }, getByLehreinheit(lehreinheit_id) { return { diff --git a/public/js/components/LVVerwaltung/Details/Gruppen.js b/public/js/components/LVVerwaltung/Details/Gruppen.js index 024bf6468..5244136cc 100644 --- a/public/js/components/LVVerwaltung/Details/Gruppen.js +++ b/public/js/components/LVVerwaltung/Details/Gruppen.js @@ -2,6 +2,7 @@ import {CoreFilterCmpt} from "../../filter/Filter.js"; import FormForm from '../../Form/Form.js'; import FormInput from '../../Form/Input.js'; import ApiGruppe from "../../../api/lehrveranstaltung/gruppe.js"; +import drop from '../../../directives/drop.js'; export default{ name: "LVGruppen", components: { @@ -9,6 +10,9 @@ export default{ FormForm, FormInput }, + directives: { + drop + }, props: { lehreinheit_id: Number }, @@ -187,28 +191,48 @@ export default{ this.reload(); }) }, + onDropVerband(node, gruppe) { + this.$api.call(ApiGruppe.getGruppe(gruppe)) + .then(result => result.data) + .then(result => { + + let newData = { + 'gid': result, + 'lehreinheit_id': this.lehreinheit_id, + 'lehrverband': gruppe.lehrverband, + } + this.$api.call(ApiGruppe.add(newData)) + .then(result => { + this.reload() + }) + }); + + }, + }, template: ` - - - - +
+ + + + +
` }; \ No newline at end of file diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 42c50bdfa..9a68c3781 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -285,7 +285,7 @@ export default {
- + diff --git a/public/js/components/Stv/Studentenverwaltung/Verband.js b/public/js/components/Stv/Studentenverwaltung/Verband.js index 1ff9d2ed9..4490901e9 100644 --- a/public/js/components/Stv/Studentenverwaltung/Verband.js +++ b/public/js/components/Stv/Studentenverwaltung/Verband.js @@ -1,5 +1,6 @@ import drop from '../../../directives/drop.js'; import dragClick from '../../../directives/dragClick.js'; +import draggable from '../../../directives/draggable.js'; import ApiStvGroups from '../../../api/factory/stv/group.js'; import ApiStvDetails from '../../../api/factory/stv/details.js'; @@ -11,7 +12,8 @@ export default { }, directives: { drop, - dragClick + dragClick, + draggable }, inject: { $reloadList: { @@ -40,6 +42,10 @@ export default { preselectedKey: { type: String, default: null + }, + dragEnabled: { + type: Boolean, + default: false } }, data() { @@ -79,6 +85,22 @@ export default { } }, methods: { + dragVerband(node) { + const gruppe = node?.data || {}; + + const id = gruppe.gruppe_kurzbz ?? gruppe.link ?? node?.key ?? null; + + return [{ + type: 'verband', + id: id, + gruppe_kurzbz: gruppe.gruppe_kurzbz ?? null, + semester: gruppe.semester ?? null, + verband: gruppe.verband ?? null, + gruppe: gruppe.gruppe ?? null, + lehrverband: gruppe.gruppe_kurzbz == null, + stg_kz: gruppe.stg_kz ?? null, + }]; + }, findNodeByKey(key, arr) { if (!arr) arr = this.nodes; @@ -359,7 +381,9 @@ export default { :data-tree-item-key="node.key" :title="node.data.studiengang_kz" v-drag-click="() => toggleTreeNode(node)" + class="node-verband" v-drop:link-strict.student-collection="(evt, students) => dropStudents(node, students)" + v-draggable:link-strict="dragEnabled && dragVerband(node)" > {{ node.data.name }} diff --git a/public/js/helpers/DragAndDrop.js b/public/js/helpers/DragAndDrop.js index f98697eb2..3eeff0ee2 100644 --- a/public/js/helpers/DragAndDrop.js +++ b/public/js/helpers/DragAndDrop.js @@ -30,6 +30,10 @@ const TYPE_DEFINITION = { prestudent: { id: "prestudent_id", dragIcon: "fa-solid fa-user-graduate text-muted" + }, + verband: { + id: "gruppe_id", + dragIcon: "fa-solid fa-user-group text-muted" } // TODO: IMPLEMENT OTHER TYPES }; From e26387a75a6075ee1c9ab2bc87bb96af52338907 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 19 Jan 2026 15:08:59 +0100 Subject: [PATCH 013/275] lektoren tausch auch bei verplanten lektoren hinzugefuegt --- .../controllers/api/frontend/v1/lv/Config.php | 86 +++++++ .../controllers/api/frontend/v1/lv/Lektor.php | 3 + application/libraries/LektorLib.php | 240 ++++++++++++------ .../models/ressource/Reservierung_model.php | 11 + .../models/ressource/Stundenplandev_model.php | 11 + .../models/ressource/Zeitsperre_model.php | 36 +++ application/views/LVVerwaltung.php | 2 + public/js/api/lehrveranstaltung/config.js | 15 ++ .../components/LVVerwaltung/LVVerwaltung.js | 57 ++++- .../components/LVVerwaltung/Lektor/Daten.js | 5 +- 10 files changed, 382 insertions(+), 84 deletions(-) create mode 100644 application/controllers/api/frontend/v1/lv/Config.php create mode 100644 public/js/api/lehrveranstaltung/config.js diff --git a/application/controllers/api/frontend/v1/lv/Config.php b/application/controllers/api/frontend/v1/lv/Config.php new file mode 100644 index 000000000..4f39c6b8b --- /dev/null +++ b/application/controllers/api/frontend/v1/lv/Config.php @@ -0,0 +1,86 @@ + ['admin:r', 'assistenz:r'], + 'set' => ['admin:r', 'assistenz:r'], + ]); + + $this->_ci = &get_instance(); + $this->_setAuthUID(); + + $this->loadPhrases([ + 'lehre' + ]); + + $this->_ci->load->library('VariableLib', ['uid' => $this->_uid]); + $this->_ci->load->library('PermissionLib'); + + } + + + public function get() + { + if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan'))) + $this->terminateWithSuccess([]); + + $ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision'); + $ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre'); + $ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung'); + + $config['ignore_kollision'] = [ + "type" => "checkbox", + "label" => 'ignore_kollision', + "value" => $ignore_kollision, + ]; + + $config['ignore_zeitsperre'] = [ + "type" => "checkbox", + "label" => 'ignore_zeitsperre', + "value" => $ignore_zeitsperre, + ]; + + $config['ignore_reservierung'] = [ + "type" => "checkbox", + "label" => 'ignore_reservierung', + "value" => $ignore_reservierung, + ]; + + $this->terminateWithSuccess($config); + } + public function set() + { + if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan'))) + $this->terminateWithSuccess([]); + + $this->load->model('system/Variable_model', 'VariableModel'); + + foreach (['ignore_kollision','ignore_zeitsperre','ignore_reservierung'] as $variable) + { + if ($this->_ci->input->post($variable) !== null) + { + $this->VariableModel->update(array('uid' => $this->_uid, 'name' => $variable), array('wert' => $this->input->post($variable))); + + } + } + + $this->terminateWithSuccess(); + } + + private function _setAuthUID() + { + $this->_uid = getAuthUID(); + + if (!$this->_uid) + show_error('User authentification failed'); + } +} diff --git a/application/controllers/api/frontend/v1/lv/Lektor.php b/application/controllers/api/frontend/v1/lv/Lektor.php index cce7f6e8b..7db23e75f 100644 --- a/application/controllers/api/frontend/v1/lv/Lektor.php +++ b/application/controllers/api/frontend/v1/lv/Lektor.php @@ -144,6 +144,9 @@ class Lektor extends FHCAPI_Controller if (!$lehreinheit_permission) $this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess')); + if (!is_null($this->getLektorVertrag($lehreinheit_id, $mitarbeiter_uid)) && (array_key_exists('mitarbeiter_uid', $formData) && $mitarbeiter_uid !== $formData['mitarbeiter_uid'])) + $this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess')); + $result = $this->_ci->lektorlib->updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $formData); if (isError($result)) $this->terminateWithError(getError($result)); diff --git a/application/libraries/LektorLib.php b/application/libraries/LektorLib.php index bbe630eaf..0e96a1070 100644 --- a/application/libraries/LektorLib.php +++ b/application/libraries/LektorLib.php @@ -17,7 +17,12 @@ class LektorLib $this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); $this->_ci->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel'); $this->_ci->load->model('person/Benutzer_model', 'BenutzerModel'); + $this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); + $this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel'); + $this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel'); + $this->_ci->load->library('PhrasesLib', array('lehre')); + $this->_ci->load->library('VariableLib', ['uid' => getAuthUID()]); } public function addLektorToLehreinheit($lehreinheit_id, $mitarbeiter_uid) @@ -78,10 +83,13 @@ class LektorLib public function updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $new_data) { + $old_uid = $mitarbeiter_uid; + $new_uid = isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid; + $this->_ci->LehreinheitmitarbeiterModel->addSelect('lehre.tbl_lehreinheitmitarbeiter.*, lehre.tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.studiengang_kz'); $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id'); $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); - $lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); + $lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid)); if (isError($lehreinheit_result)) return $lehreinheit_result; @@ -89,27 +97,47 @@ class LektorLib $lehreinheit = getData($lehreinheit_result)[0]; + $semesterstunden_alt = $lehreinheit->semesterstunden; + $semesterstunden_neu = isset($new_data['semesterstunden']) ? $new_data['semesterstunden'] : $semesterstunden_alt; + $bismelden_neu = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden; + $neue_stunden_eingerechnet = (bool)$bismelden_neu; + $alte_stunden_eingerechnet = (bool)$lehreinheit->bismelden; + $stundenplan_update = false; - //TODO kollision check, wird vorerst nicht implementiert -> nur über das FAS möglich - if (isset($new_data['mitarbeiter_uid']) && $new_data['mitarbeiter_uid'] !== $mitarbeiter_uid) + if ($old_uid !== $new_uid) { - $this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel'); - $this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id'); - $this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid'); - $this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid'); - $verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); - - if (hasData($verplant)) - return error($this->_ci->phraseslib->t("lehre", "lektorbereitsverplant")); - - $lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_data['mitarbeiter_uid'], 'lehreinheit_id' => $lehreinheit_id)); + $lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_uid, 'lehreinheit_id' => $lehreinheit_id)); if (hasData($lehreinheit_data)) return error($this->_ci->phraseslib->t("lehre", "bereitzugeteilt")); + $this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id'); + $this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid'); + $verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid)); + + if (hasData($verplant)) + { + $kollision = $this->hasKollision(getData($verplant), $new_uid); + + $ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision'); + + if ($kollision === false || $ignore_kollision == 'true') + { + $stundenplan_update = true; + } + else if (is_array($kollision)) + { + return error( "Änderung fehlgeschlagen! Die Änderung des Lektors führt zu ".count($kollision)." Kollision(en) im LV-Plan. Deaktivieren Sie die Kollisionspruefung oder wenden Sie sich an die LV-Planung!\n zB. $kollision[0]"); + } + else + { + return error($kollision); + } + } } + $warning = ''; - if (isset($new_data['semesterstunden'])) + if (($semesterstunden_neu !== '' && $semesterstunden_alt !== '') && (($semesterstunden_neu > $semesterstunden_alt) || $neue_stunden_eingerechnet)) { $studiengang_result = $this->_ci->StudiengangModel->loadWhere(array('studiengang_kz' => $lehreinheit->studiengang_kz)); if (isError($studiengang_result)) return $studiengang_result; @@ -120,7 +148,7 @@ class LektorLib if (isError($studiensemester_result)) return $studiensemester_result; $studiensemester = getData($studiensemester_result)[0]; - $echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'echterdv'); + $echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($new_uid, $studiensemester->start, $studiensemester->ende, 'echterdv'); $echter_dv = false; @@ -129,83 +157,78 @@ class LektorLib $echter_dv = true; } - $neue_stunden_eingerechnet = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden; - $alte_stunden_eingerechnet = $lehreinheit->bismelden; + $stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv); + if (isError($stundengrenze_result)) return $stundengrenze_result; - if (($new_data['semesterstunden'] > $lehreinheit->semesterstunden) || $neue_stunden_eingerechnet) + $stundengrenze = getData($stundengrenze_result)[0]; + + $oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz); + $oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array(); + + if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu)}) as summe"); + else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt)) as summe"); + else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu)})) as summe"); + else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe"); + + $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id'); + $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); + $this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); + + $this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', $new_uid); + $this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz); + $this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true); + $this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%'); + + if (count($oe_array) > 0) { - $stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv); - if (isError($stundengrenze_result)) return $stundengrenze_result; - - $stundengrenze = getData($stundengrenze_result)[0]; - - $oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz); - $oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array(''); - - if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])}) as summe"); - else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden)) as summe"); - else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])})) as summe"); - else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe"); - - $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id'); - $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); - $this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); - - $this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', (isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid)); - $this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz); - $this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true); - $this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%'); - $this->_ci->LehreinheitmitarbeiterModel->db->where_in('tbl_studiengang.oe_kurzbz', $oe_array); + } + if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE') + && is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) + && count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0) + { + $this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE); + } - if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE') - && is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) - && count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0) + $summe_result = $this->_ci->LehreinheitmitarbeiterModel->load(); + + if (isError($summe_result)) return $summe_result; + + if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden'); + + $summe = getData($summe_result)[0]->summe; + + if ($summe > $stundengrenze->stunden) + { + if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin'))) { - $this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE); + if (!$this->LehrauftragAufFirma($new_uid)) + return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n"); + } + else + { + $warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n"; } - $summe_result = $this->_ci->LehreinheitmitarbeiterModel->load(); + $stunden_limit_result = $this->getStundenInstitut($new_uid, $lehreinheit->studiensemester_kurzbz, $oe_array); - if (isError($summe_result)) return $summe_result; - - if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden'); - - $summe = getData($summe_result)[0]->summe; - - if ($summe > $stundengrenze->stunden) + if (hasData($stunden_limit_result)) { - - if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin'))) + $stunden_limit_array = getData($stunden_limit_result); + foreach ($stunden_limit_array as $stunden_limit) { - if (!$this->LehrauftragAufFirma(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid)) - return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n"); - } - else - { - $warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n"; - } - - $stunden_limit_result = $this->getStundenInstitut($mitarbeiter_uid, $lehreinheit->studiensemester_kurzbz, $oe_array); - - if (hasData($stunden_limit_result)) - { - $stunden_limit_array = getData($stunden_limit_result); - foreach ($stunden_limit_array as $stunden_limit) - { - $warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n"; - } + $warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n"; } } } } - $benutzer_result = $this->_ci->BenutzerModel->load(array(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid)); + $benutzer_result = $this->_ci->BenutzerModel->load(array($new_uid)); if (isError($benutzer_result)) return $benutzer_result; @@ -240,10 +263,24 @@ class LektorLib $updateData['updatevon'] = getAuthUID(); $updateData['updateamum'] = date('Y-m-d H:i:s'); - $result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid), $updateData); + $result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid), $updateData); if (isError($result)) return $result; + if ($stundenplan_update) + { + $update_result = $this->_ci->StundenplandevModel->update([ + 'lehreinheit_id' => $lehreinheit_id, + 'mitarbeiter_uid' => $old_uid, + ], [ + 'mitarbeiter_uid' => $new_uid, + 'updateamum' => date('Y-m-d H:i:s'), + 'updatevon' => getAuthUID() + ]); + + if (isError($update_result)) return $update_result; + } + if ($warning !== '') return success(['warning' => $warning]); return success('Erfolgreich geupdated'); @@ -347,4 +384,53 @@ class LektorLib $this->_ci->LehreinheitmitarbeiterModel->addGroupBy('tbl_studiengang.bezeichnung'); return $this->_ci->LehreinheitmitarbeiterModel->load(); } +//if($rechte->isBerechtigt('lv-plan')) + + private function hasKollision($stunden, $mitarbeiter) + { + $kollision = array(); + + $ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre'); + $ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung'); + + foreach ($stunden as $stunde) + { + $stundenplan_result = $this->_ci->StundenplandevModel->lektorHasStundenplandevEintrag($mitarbeiter, $stunde->datum, $stunde->stunde); + + if (isError($stundenplan_result)) + return $stundenplan_result; + + if (hasData($stundenplan_result)) + { + $stundenplan_result = getData($stundenplan_result)[0]; + $kollision[] = "Kollision stundenplandev: $stundenplan_result->stundenplandev_id|$stundenplan_result->lektor|$stundenplan_result->ort_kurzbz|$stundenplan_result->stg_kurzbz-$stundenplan_result->semester$stundenplan_result->verband$stundenplan_result->gruppe$stundenplan_result->gruppe_kurzbz - $stundenplan_result->datum/$stundenplan_result->stunde"; + } + else + { + if ($ignore_zeitsperre == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER)))) + { + $zeitsperre_result = $this->_ci->ZeitsperreModel->checkIfZeitsperreExists($mitarbeiter, $stunde->datum, $stunde->stunde); + + if (hasData($zeitsperre_result)) + { + $zeitsperre_result = getData($zeitsperre_result)[0]; + $kollision[] = "Kollision (Zeitsperre): $zeitsperre_result->zeitsperre_id|$zeitsperre_result->mitarbeiter_uid|$zeitsperre_result->zeitsperretyp_kurzbz - $zeitsperre_result->vondatum/$zeitsperre_result->vonstunde|$zeitsperre_result->bisdatum/$zeitsperre_result->bisstunde"; + } + } + + if ($ignore_reservierung == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER)))) + { + $reservierung_result = $this->_ci->ReservierungModel->lektorHasReservierung($mitarbeiter, $stunde->datum, $stunde->stunde); + + if (hasData($reservierung_result)) + { + $reservierung_result = getData($reservierung_result)[0]; + $kollision[] = "Kollision (Reservierung): $reservierung_result->reservierung_id|$reservierung_result->uid|$reservierung_result->ort_kurzbz|$reservierung_result->stg_kurzbz-$reservierung_result->semester$reservierung_result->verband$reservierung_result->gruppe$reservierung_result->gruppe_kurzbz - $reservierung_result->datum/$reservierung_result->stunde"; + } + } + } + } + + return isEmptyArray($kollision) ? false : $kollision; + } } diff --git a/application/models/ressource/Reservierung_model.php b/application/models/ressource/Reservierung_model.php index 0c391ea20..8049838eb 100644 --- a/application/models/ressource/Reservierung_model.php +++ b/application/models/ressource/Reservierung_model.php @@ -135,4 +135,15 @@ class Reservierung_model extends DB_Model return $this->execQuery($query, [$uid, $uid]); } + public function lektorHasReservierung($uid, $datum, $stunde) + { + $qry = "SELECT reservierung_id, uid, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde + FROM lehre.vw_reservierung + WHERE uid = ? + AND datum = ? + AND stunde = ?"; + + return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]); + } + } diff --git a/application/models/ressource/Stundenplandev_model.php b/application/models/ressource/Stundenplandev_model.php index e718ba073..355283b90 100644 --- a/application/models/ressource/Stundenplandev_model.php +++ b/application/models/ressource/Stundenplandev_model.php @@ -241,4 +241,15 @@ class Stundenplandev_model extends DB_Model return $this->delete(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); } + + public function lektorHasStundenplandevEintrag($uid, $datum, $stunde) + { + $qry = "SELECT stundenplandev_id, lektor, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde + FROM lehre.vw_stundenplandev + WHERE uid = ? + AND datum = ? + AND stunde = ?"; + + return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]); + } } diff --git a/application/models/ressource/Zeitsperre_model.php b/application/models/ressource/Zeitsperre_model.php index 078d29d8b..50ba4cb1d 100644 --- a/application/models/ressource/Zeitsperre_model.php +++ b/application/models/ressource/Zeitsperre_model.php @@ -61,4 +61,40 @@ class Zeitsperre_model extends DB_Model return $this->execQuery($qry); } + + public function checkIfZeitsperreExists($uid, $datum, $stunde) + { + $this->db->select("*"); + + $this->db->where('mitarbeiter_uid', $uid); + $this->db->where('zeitsperretyp_kurzbz !=', 'ZVerfueg'); + + $this->db->group_start(); + + $this->db->where('vondatum <', $datum); + $this->db->or_group_start(); + $this->db->where('vondatum', $datum); + $this->db->group_start(); + $this->db->where('vonstunde <=', $stunde); + $this->db->or_where('vonstunde IS NULL', null, false); + $this->db->group_end(); + $this->db->group_end(); + $this->db->group_end(); + + + $this->db->group_start(); + $this->db->where('bisdatum >', $datum); + $this->db->or_group_start(); + $this->db->where('bisdatum', $datum); + $this->db->group_start(); + $this->db->where('bisstunde >=', $stunde); + $this->db->or_where('bisstunde IS NULL', null, false); + $this->db->group_end(); + + $this->db->group_end(); + $this->db->group_end(); + + return $this->load(); + + } } diff --git a/application/views/LVVerwaltung.php b/application/views/LVVerwaltung.php index 4cebae839..42d4631f1 100644 --- a/application/views/LVVerwaltung.php +++ b/application/views/LVVerwaltung.php @@ -31,6 +31,8 @@ lv-root="" :permissions="" :config="" + avatar-url="" + logout-url="" > diff --git a/public/js/api/lehrveranstaltung/config.js b/public/js/api/lehrveranstaltung/config.js new file mode 100644 index 000000000..40d275145 --- /dev/null +++ b/public/js/api/lehrveranstaltung/config.js @@ -0,0 +1,15 @@ +export default { + get() { + return { + method: 'get', + url: '/api/frontend/v1/lv/config/get' + }; + }, + set(params) { + return { + method: 'post', + url: 'api/frontend/v1/lv/config/set', + params + }; + } +}; diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 42c50bdfa..4854854b9 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -8,9 +8,10 @@ import LvTabs from "./Setup/Tabs.js"; import ApiDetails from "../../api/lehrveranstaltung/details.js"; import ApiLektor from "../../api/lehrveranstaltung/lektor.js"; -import ApiGruppe from "../../api/lehrveranstaltung/gruppe.js"; import ApiStudiengangTree from "../../api/lehrveranstaltung/studiengangtree.js"; import ApiSearchbar from "../../api/factory/searchbar.js"; +import AppConfig from "../AppConfig.js"; +import ApiLvConfig from "../../api/lehrveranstaltung/config.js"; export default { @@ -23,6 +24,7 @@ export default { StvStudiensemester, LvTable, LvTabs, + AppConfig }, props: { defaultSemester: String, @@ -32,7 +34,9 @@ export default { stg: { type: String, required: false }, semester: { type: [Number, String], required: false, default: null }, studiensemester_kurzbz: { type: String, required: false, default: null }, - emp: { type: String, required: false, default: null } + emp: { type: String, required: false, default: null }, + avatarUrl: String, + logoutUrl: String, }, provide() { @@ -44,7 +48,6 @@ export default { lehreinheitAnmerkungDefault: (this.config.lehreinheitAnmerkungDefault || '').replace(/\\n/g, '\n'), lehreinheitRaumtypDefault: this.config.lehreinheitRaumtypDefault, lehreinheitRaumtypAlternativeDefault: this.config.lehreinheitRaumtypAlternativeDefault, - permissionLehrveranstaltung: this.permissions['lehre/lehrveranstaltung'], permissionGruppenEntfernen: this.permissions['lv-plan/gruppenentfernen'], permissionLektorEntfernen: this.permissions['lv-plan/lektorentfernen'], @@ -72,6 +75,8 @@ export default { }, data() { return { + appconfig:{}, + configEndpoints: ApiLvConfig, selected: [], studiengang: "", filter: {}, @@ -269,6 +274,51 @@ export default { +
@@ -327,5 +377,6 @@ export default {
+ ` }; diff --git a/public/js/components/LVVerwaltung/Lektor/Daten.js b/public/js/components/LVVerwaltung/Lektor/Daten.js index 21e5dc196..0263b236f 100644 --- a/public/js/components/LVVerwaltung/Lektor/Daten.js +++ b/public/js/components/LVVerwaltung/Lektor/Daten.js @@ -140,7 +140,7 @@ export default{ { this.$emit('changedLektor', this.changed.mitarbeiter_uid); } - if (this.changed.semesterstunden || this.changed.stundensatz) + else if (this.changed.semesterstunden || this.changed.stundensatz) { this.$emit('changedCosts'); } @@ -183,9 +183,6 @@ export default{ }, }, - created() { - this.getLektorData() - }, template: `
From bb236796835f4a325b54056621f31990d7b81f3a Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 12 Feb 2026 10:45:32 +0100 Subject: [PATCH 014/275] auskommentierten code entfernt --- application/libraries/LektorLib.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/libraries/LektorLib.php b/application/libraries/LektorLib.php index 0e96a1070..a89ccdf6b 100644 --- a/application/libraries/LektorLib.php +++ b/application/libraries/LektorLib.php @@ -384,7 +384,6 @@ class LektorLib $this->_ci->LehreinheitmitarbeiterModel->addGroupBy('tbl_studiengang.bezeichnung'); return $this->_ci->LehreinheitmitarbeiterModel->load(); } -//if($rechte->isBerechtigt('lv-plan')) private function hasKollision($stunden, $mitarbeiter) { From fc01fa045e75d3438d6a7ee68df1e7303ac40517 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 23 Feb 2026 13:44:33 +0100 Subject: [PATCH 015/275] add and use variable semester_berechnet for using ausbildungssemester in status interessent --- application/controllers/api/frontend/v1/stv/Students.php | 7 +++++++ public/js/components/Stv/Studentenverwaltung/List.js | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index acacca052..816ed5b87 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -905,6 +905,13 @@ class Students extends FHCAPI_Controller $this->PrestudentModel->addSelect('pls.status_kurzbz AS status'); $this->PrestudentModel->addSelect('pls.datum AS status_datum'); $this->PrestudentModel->addSelect('pls.bestaetigtam AS status_bestaetigung'); + $this->PrestudentModel->addSelect(" + CASE + WHEN pls.status_kurzbz = 'Interessent' + THEN pls.ausbildungssemester + ELSE s.semester + END AS semester_berechnet + "); $this->PrestudentModel->addSelect( "(SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp='email' AND person_id=p.person_id AND zustellung LIMIT 1) AS mail_privat", false diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index 045326ae6..3a6de21c7 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -81,7 +81,7 @@ export default { } }, {title:"Geschlecht", field:"geschlecht", headerFilter: "list", headerFilterParams: {values:{'m':'männlich','w':'weiblich','x':'divers','u':'unbekannt'}, listOnEmpty:true, autocomplete:true}}, - {title:"Sem.", field:"semester", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}}, + {title:"Sem.", field:"semester_berechnet", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}}, {title:"Verb.", field:"verband", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}}, {title:"Grp.", field:"gruppe", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}}, {title:"Studiengang", field:"studiengang", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}}, @@ -157,7 +157,7 @@ export default { selectable: true, selectableRangeMode: 'click', index: 'prestudent_id', - persistenceID: 'stv-list' + persistenceID: 'stv-list-20260223_01' }, tabulatorEvents: [ { @@ -263,7 +263,7 @@ export default { ersatzkennzeichen: capitalize(this.$p.t('person/ersatzkennzeichen')), gebdatum: capitalize(this.$p.t('person/geburtsdatum')), geschlecht: capitalize(this.$p.t('person/geschlecht')), - semester: capitalize(this.$p.t('lehre/sem')), + semester_berechnet: capitalize(this.$p.t('lehre/sem')), verband: capitalize(this.$p.t('lehre/verb')), gruppe: capitalize(this.$p.t('lehre/grp')), studiengang: capitalize(this.$p.t('lehre/studiengang')), From c127c0900ec62842c095b046582c83dfcba461d7 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Wed, 25 Feb 2026 14:28:41 +0100 Subject: [PATCH 016/275] refactor dropdown for pruefer, add localStorage for formVariables, add multiactions for adding new finalexam --- .../api/frontend/v1/stv/Abschlusspruefung.php | 16 -- .../js/api/factory/stv/abschlusspruefung.js | 7 - public/js/api/stv/abschlusspruefung.js | 3 - .../Abschlusspruefung/Abschlusspruefung.js | 185 +++++++++++++----- 4 files changed, 139 insertions(+), 72 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php index def4f6502..f77de7021 100644 --- a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php +++ b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php @@ -20,7 +20,6 @@ class Abschlusspruefung extends FHCAPI_Controller 'getBeurteilungen' => ['admin:rw', 'assistenz:rw'], 'getAkadGrade' => ['admin:rw', 'assistenz:rw'], 'getMitarbeiter' => ['admin:rw', 'assistenz:rw'], - 'getPruefer' => ['admin:rw', 'assistenz:rw'], 'getTypStudiengang' => ['admin:rw', 'assistenz:rw'], 'checkForExistingExams' => ['admin:rw', 'assistenz:rw'], ]); @@ -242,21 +241,6 @@ class Abschlusspruefung extends FHCAPI_Controller $this->terminateWithSuccess($result ?: []); } - public function getPruefer() - { - $searchString = $this->input->get('searchString') ?? ''; - - $this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel'); - - $result = $this->MitarbeiterModel->searchMitarbeiter($searchString, 'ohneMaUid'); - - if (isError($result)) { - $this->terminateWithError($result, self::ERROR_TYPE_GENERAL); - } - - $this->terminateWithSuccess($result ?: []); - } - public function getNoten() { $this->load->model('education/Note_model', 'NoteModel'); diff --git a/public/js/api/factory/stv/abschlusspruefung.js b/public/js/api/factory/stv/abschlusspruefung.js index dcba61887..7a1a088ea 100644 --- a/public/js/api/factory/stv/abschlusspruefung.js +++ b/public/js/api/factory/stv/abschlusspruefung.js @@ -90,13 +90,6 @@ export default { params: { searchString } }; }, - getPruefer(searchString) { - return { - method: 'get', - url: 'api/frontend/v1/stv/abschlusspruefung/getPruefer', - params: { searchString } - }; - }, getNoten() { return { method: 'get', diff --git a/public/js/api/stv/abschlusspruefung.js b/public/js/api/stv/abschlusspruefung.js index dccc10085..26d9c17ab 100644 --- a/public/js/api/stv/abschlusspruefung.js +++ b/public/js/api/stv/abschlusspruefung.js @@ -35,9 +35,6 @@ export default { getMitarbeiter(searchString){ return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getMitarbeiter/' + searchString); }, - getPruefer(searchString){ - return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getPruefer/' + searchString); - }, getNoten(){ return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getNoten/'); }, diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js index d46e1f299..444bf1097 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js @@ -42,6 +42,16 @@ export default { } return this.student.map(e => e.uid); }, + studentNames() { + if (this.student.uid) + { + return [this.student.vorname + ' ' + this.student.nachname]; + } + + const array = this.student.map(e => ' ' + e.vorname + ' ' + e.nachname + '(' + e.uid +')'); + + return array.toString(); + }, studentKzs(){ if (this.student.uid) { @@ -60,7 +70,7 @@ export default { } let retval = this.isBerechtigtDocAndOdt.includes(this.stgInfo.oe_kurzbz); return retval; - } + }, }, props: { student: Object @@ -260,7 +270,6 @@ export default { arrNoten: [], selectedVorsitz: null, filteredMitarbeiter: [], - filteredPersons: [], selectedPruefer1: null, selectedPruefer2: null, selectedPruefer3: null, @@ -300,10 +309,50 @@ export default { .catch(this.$fhcAlert.handleSystemError); }, actionNewAbschlusspruefung() { - this.resetForm(); - this.statusNew = true; - this.$refs.finalexamModal.show(); this.setDefaultFormData(); + this.statusNew = true; + + //prepare local Storage + let STORAGE_KEY = 'finalExamDefaultData'; + const id = '20260224_01'; + const stored = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {}; + + if (stored[id]) { + const data = stored[id]; + + this.formData.pruefungstyp_kurzbz = data.pruefungstyp_kurzbz; + this.formData.datum = data.datum; + this.formData.sponsion = data.sponsion; + this.formData.akadgrad_id = data.akadgrad_id; + + if (data.vorsitz_uid) { + this.selectedVorsitz = { + mitarbeiter_uid: data.vorsitz_uid, + person_id: data.vorsitz_person_id, + label: data.vorsitz_label + }; + } + + if (data.pruefer1_person_id) { + this.selectedPruefer1 = { + person_id: data.pruefer1_person_id, + label: data.pruefer1_label + }; + } + if (data.pruefer2_person_id) { + this.selectedPruefer2 = { + person_id: data.pruefer2_person_id, + label: data.pruefer2_label + }; + } + if (data.pruefer3_person_id) { + this.selectedPruefer3 = { + person_id: data.pruefer3_person_id, + label: data.pruefer3_label + }; + } + } + this.$refs.finalexamModal.show(); }, actionEditAbschlusspruefung(abschlusspruefung_id) { this.resetForm(); @@ -350,6 +399,29 @@ export default { .then(this.deleteAbschlusspruefung) .catch(this.$fhcAlert.handleSystemError); }, + saveOrUpdateLocalStorage(){ + let STORAGE_KEY = 'finalExamDefaultData'; + + const id = '20260224_01'; + const stored = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {}; + + stored[id] = { + pruefungstyp_kurzbz: this.formData.pruefungstyp_kurzbz, + vorsitz_uid: this.selectedVorsitz?.mitarbeiter_uid || null, + vorsitz_person_id: this.selectedVorsitz?.person_id || null, + vorsitz_label: this.selectedVorsitz?.label || null, + pruefer1_person_id: this.selectedPruefer1?.person_id || null, + pruefer1_label: this.selectedPruefer1?.label || null, + pruefer2_person_id: this.selectedPruefer2?.person_id || null, + pruefer2_label: this.selectedPruefer2?.label || null, + pruefer3_person_id: this.selectedPruefer3?.person_id || null, + pruefer3_label: this.selectedPruefer3?.label || null, + akadgrad_id: this.formData.akadgrad_id, + datum: this.formData.datum, + sponsion: this.formData.sponsion + }; + localStorage.setItem(STORAGE_KEY, JSON.stringify(stored)); + }, addNewAbschlusspruefung() { const dataToSend = { uid: this.student.uid, @@ -360,6 +432,8 @@ export default { .call(ApiStvAbschlusspruefung.addNewAbschlusspruefung(dataToSend)) .then(response => { this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + //save formData fields in LocalStorage + this.saveOrUpdateLocalStorage(); this.hideModal('finalexamModal'); this.resetForm(); }) @@ -368,6 +442,26 @@ export default { this.reload(); }); }, + async addNewAbschlusspruefungMulti(){ + try { + for (const student of this.studentUids) { + + await this.$refs.formFinalExam.call( + ApiStvAbschlusspruefung.addNewAbschlusspruefung({ + uid: student, + formData: this.formData + }) + ); + } + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + //save formData fields in LocalStorage + this.saveOrUpdateLocalStorage(); + this.hideModal('finalexamModal'); + this.resetForm(); + } catch (error) { + this.$fhcAlert.handleSystemError(error); + } + }, hideModal(modalRef){ this.$refs[modalRef].hide(); }, @@ -389,6 +483,9 @@ export default { id: abschlusspruefung_id, formData: this.formData }; + //uncomment if also save data in local storage for update + //this.saveOrUpdateLocalStorage(); + return this.$refs.formFinalExam .call(ApiStvAbschlusspruefung.updateAbschlusspruefung(dataToSend)) .then(response => { @@ -430,7 +527,6 @@ export default { this.selectedPruefer1 = null; this.selectedPruefer2 = null; this.selectedPruefer3 = null; - }, setDefaultFormData() { @@ -481,33 +577,6 @@ export default { } }); }, - searchPerson(event) { - if (this.abortController.persons) { - this.abortController.persons.abort(); - } - - this.abortController.persons = new AbortController(); - - return this.$api - .call(ApiStvAbschlusspruefung.getPruefer(event.query)) - .then(result => { - this.filteredPersons = []; - for (let person of result.data.retval) { - this.filteredPersons.push( - { - label: this.getPersonLabel( - person.titelpre, - person.nachname, - person.vorname, - person.titelpost, - person.person_uid - ), - person_id: person.person_id - } - ); - } - }); - }, }, created() { this.$api @@ -539,7 +608,7 @@ export default { .catch(this.$fhcAlert.handleSystemError); this.$api - .call(ApiStvAbschlusspruefung.getAkadGrade(this.student.studiengang_kz)) + .call(ApiStvAbschlusspruefung.getAkadGrade(this.stg_kz)) .then(result => { this.arrAkadGrad = result.data; }) @@ -560,7 +629,8 @@ export default {

{{this.$p.t('stv','tab_finalexam')}}

-
+
+

{{$p.t('abschlusspruefung', 'abschluessPruefungAnlegen')}}

{{$p.t('abschlusspruefung', 'abschluessPruefungBearbeiten')}}

+ {{studentNames}} - + {{this.$p.t('global','details')}}

[{{$p.t('ui', 'neu')}}]

+
- +
+ + @@ -735,6 +825,7 @@ export default { @@ -792,6 +883,7 @@ export default { >
-
+

{{$p.t('abschlusspruefung', 'zurBeurteilung')}}

@@ -820,8 +912,9 @@ export default { From 233a6e8b8915173eb59add4a3d2a3741161c1305 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 25 Feb 2026 15:25:36 +0100 Subject: [PATCH 017/275] =?UTF-8?q?lvverwaltung:=20-=20event=20hinzugefueg?= =?UTF-8?q?t=20damit=20tabs=20erweitert=20werden=20k=C3=B6nnen=20-=20beric?= =?UTF-8?q?hte=20hinzugefuegt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/api/frontend/v1/lv/Setup.php | 7 ++ .../statistik/lehrauftragsliste_gst.xls.php | 6 ++ content/statistik/lvplanung.php | 6 ++ .../components/LVVerwaltung/LVVerwaltung.js | 98 ++++++++++++++++- system/phrasesupdate.php | 100 ++++++++++++++++++ 5 files changed, 215 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/Setup.php b/application/controllers/api/frontend/v1/lv/Setup.php index eea4befa5..2c1c1d842 100644 --- a/application/controllers/api/frontend/v1/lv/Setup.php +++ b/application/controllers/api/frontend/v1/lv/Setup.php @@ -19,6 +19,8 @@ if (!defined('BASEPATH')) exit('No direct script access allowed'); +use CI3_Events as Events; + class Setup extends FHCAPI_Controller { private $_ci; @@ -65,6 +67,11 @@ class Setup extends FHCAPI_Controller 'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Notiz.js'), 'config' => [] ); + + Events::trigger('lvv_conf_tabs', function & () use (&$tabs) { + return $tabs; + }); + $this->terminateWithSuccess($tabs); } diff --git a/content/statistik/lehrauftragsliste_gst.xls.php b/content/statistik/lehrauftragsliste_gst.xls.php index 990216c09..d44c91e63 100644 --- a/content/statistik/lehrauftragsliste_gst.xls.php +++ b/content/statistik/lehrauftragsliste_gst.xls.php @@ -41,6 +41,12 @@ else $user = get_uid(); loadVariables($user); +if (isset($_GET['studiensemester_kurzbz'])) +{ + if (check_stsem($_GET['studiensemester_kurzbz'])) + $semester_aktuell = $_GET['studiensemester_kurzbz']; +} + //Studiengang laden $studiengang = new studiengang($studiengang_kz); diff --git a/content/statistik/lvplanung.php b/content/statistik/lvplanung.php index b0169dade..fe69e4160 100644 --- a/content/statistik/lvplanung.php +++ b/content/statistik/lvplanung.php @@ -50,6 +50,12 @@ else $user = get_uid(); loadVariables($user); +if (isset($_GET['studiensemester_kurzbz'])) +{ + if (check_stsem($_GET['studiensemester_kurzbz'])) + $semester_aktuell = $_GET['studiensemester_kurzbz']; +} + $rechte = new benutzerberechtigung(); $rechte->getBerechtigungen($user); diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 42c50bdfa..8b688cdb3 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -8,7 +8,6 @@ import LvTabs from "./Setup/Tabs.js"; import ApiDetails from "../../api/lehrveranstaltung/details.js"; import ApiLektor from "../../api/lehrveranstaltung/lektor.js"; -import ApiGruppe from "../../api/lehrveranstaltung/gruppe.js"; import ApiStudiengangTree from "../../api/lehrveranstaltung/studiengangtree.js"; import ApiSearchbar from "../../api/factory/searchbar.js"; @@ -205,6 +204,21 @@ export default { this.$router.push({ name: routeName, params: newParams }); this.selected = []; }, + isDisabled(item) + { + if (!item?.requires?.length) + return false; + + const values = { + stg: this.stg, + emp: this.emp, + studiensemester: this.selectedStudiensemester, + semester: this.semester + }; + + return item.requires.some(req => !values[req]); + + } }, created() { this.$p.loadCategory(['lehre', 'person', 'global']) @@ -239,6 +253,62 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); }, + computed: + { + appMenuExtraItems() + { + let extraItems = []; + + const studiengang_kz = this.stg || ''; + const studiensemester = this.selectedStudiensemester; + const semester = this.semester || ''; + const uid = this.emp || ''; + + extraItems.push({ + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/statistik/lvplanung.xls.php' + + '?studiengang_kz=' + studiengang_kz + + '&studiensemester_kurzbz=' + studiensemester + + '&semester=' + semester, + description: 'lehre/lvplanung', + requires: ['stg'] + }); + + extraItems.push({ + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/statistik/lehrauftragsliste_gst.xls.php' + + '?studiengang_kz=' + studiengang_kz + + '&studiensemester_kurzbz=' + studiensemester + + '&semester=' + semester, + description: 'lehre/lehrauftragsliste', + requires: ['stg'] + }); + + extraItems.push({ + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/pdfExport.php?xml=lehrauftrag.xml.php' + + '&xsl=Lehrauftrag' + + '&stg_kz=' + studiengang_kz + + '&ss=' + studiensemester, + description: 'lehre/lehrauftraege', + requires: ['stg'] + }); + + extraItems.push({ + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/pdfExport.php?xml=lehrauftrag.xml.php' + + '&xsl=Lehrauftrag' + + '&stg_kz=' + studiengang_kz + + '&ss=' + studiensemester + + '&uid=' + uid, + description: 'lehre/lehrauftragslisteemp', + requires: ['emp'] + }); + + return extraItems; + + } + }, template: /* html */`
@@ -278,7 +348,31 @@ export default {
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index b09a24a6f..f24efcebc 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -48901,7 +48901,7 @@ array( 'description' => '', 'insertvon' => 'system' ), - array(w + array( 'sprache' => 'English', 'text' => 'Teaching Assignments', 'description' => '', @@ -48949,6 +48949,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'extrakvverwaltung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrveranstaltung Verwaltung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course Management', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // feature-55614 end array( 'app' => 'softwarebereitstellung', From daf332a10283e00282a41f8fb3c29cf72af4626f Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Fri, 6 Mar 2026 10:39:37 +0100 Subject: [PATCH 019/275] add query_studiensemester_kurzbz to result of fetchStudents, fetchPrestudents and search --- application/controllers/api/frontend/v1/stv/Students.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index acacca052..53137a472 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -468,6 +468,8 @@ class Students extends FHCAPI_Controller $this->PrestudentModel->addSelect("'' AS verband"); $this->PrestudentModel->addSelect("'' AS gruppe"); $this->addSelectPrioRel(); + $query_studiensemester_kurzbz = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : '\'NULL\''; + $this->PrestudentModel->addSelect($query_studiensemester_kurzbz . ' as query_studiensemester_kurzbz'); $this->addFilter($studiensemester_kurzbz); @@ -588,6 +590,7 @@ class Students extends FHCAPI_Controller $this->PrestudentModel->addSelect('v.verband'); $this->PrestudentModel->addSelect('v.gruppe'); $this->PrestudentModel->addSelect("'' AS priorisierung_relativ"); + $this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz'); $where = []; @@ -798,6 +801,7 @@ class Students extends FHCAPI_Controller $this->PrestudentModel->addSelect("COALESCE(v.semester::text, CASE WHEN public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent') THEN public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)::text ELSE ''::text END) AS semester", false); $this->PrestudentModel->addSelect('v.verband'); $this->PrestudentModel->addSelect('v.gruppe'); + $this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz'); //add status per semester $this->PrestudentModel->addSelect( From 9b4fa132dc1fde8fe57fc7b9971f5a4e0f1abb53 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Fri, 6 Mar 2026 10:58:54 +0100 Subject: [PATCH 020/275] add status --- public/js/components/Stv/Studentenverwaltung.js | 3 ++- .../js/components/Stv/Studentenverwaltung/List.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index cfd229fb0..a7d97d4fc 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -208,6 +208,7 @@ export default { 'url_studiengang': function (newVal, oldVal) { if (newVal !== oldVal) { this.checkUrlStudiengang(); + this.$refs.stvList.clearSelection(); } }, 'url_mode': function () { @@ -420,7 +421,7 @@ export default { this.$refs.searchbar.$refs.input.blur(); this.$refs.searchbar.abort(); this.$refs.searchbar.hideresult(); - } + }, }, created() { if (!this.url_studiensemester_kurzbz) { diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index 045326ae6..af893f7de 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -324,10 +324,13 @@ export default { actionNewPrestudent() { this.$refs.new.open(); }, - rowSelectionChanged(data, rows) { + rowSelectionChanged(data, rows, selected, deselected) { this.selectedcount = data.length; - this.lastSelected = this.selected; - this.$emit('update:selected', data); + + if(selected.length > 0 || deselected.length > 0){ + this.lastSelected = this.selected; + this.$emit('update:selected', data); + } // set selected elements draggable const tableEl = this.$refs.table?.$refs?.table; @@ -471,6 +474,10 @@ export default { if (el != this.focusObj) this.changeFocus(this.focusObj, el); } + }, + clearSelection(){ + this.lastSelected = []; + this.$emit('update:selected',[]); } }, // TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component From 2237e9f1b72f16e63f0d65da13e910272eb280be Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 9 Mar 2026 11:42:24 +0100 Subject: [PATCH 021/275] refactor detail Header, relaod array if semester changed, show no status if no status, phrase --- .../frontend/v1/detailheader/DetailHeader.php | 30 +++++++++++++ public/js/api/factory/detailHeader.js | 6 +++ .../components/DetailHeader/DetailHeader.js | 44 ++++++++++++++++--- .../js/components/Stv/Studentenverwaltung.js | 8 +++- .../Stv/Studentenverwaltung/Details.js | 29 +++++++++++- system/phrasesupdate.php | 20 +++++++++ 6 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 application/controllers/api/frontend/v1/detailheader/DetailHeader.php diff --git a/application/controllers/api/frontend/v1/detailheader/DetailHeader.php b/application/controllers/api/frontend/v1/detailheader/DetailHeader.php new file mode 100644 index 000000000..be9fad27a --- /dev/null +++ b/application/controllers/api/frontend/v1/detailheader/DetailHeader.php @@ -0,0 +1,30 @@ + ['admin:r', 'assistenz:r'], + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getSemesterStati($prestudent_id) + { + $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + + $result = $this->PrestudentstatusModel->getAllPrestudentstatiWithStudiensemester($prestudent_id); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + +} diff --git a/public/js/api/factory/detailHeader.js b/public/js/api/factory/detailHeader.js index 74563bd4b..9e50e8ebb 100644 --- a/public/js/api/factory/detailHeader.js +++ b/public/js/api/factory/detailHeader.js @@ -34,4 +34,10 @@ export default { url: 'api/frontend/v1/vertraege/vertraege/getLeitungOrg/' + oekurzbz, }; }, + getSemesterStati(prestudent_id){ + return { + method: 'get', + url: 'api/frontend/v1/detailheader/DetailHeader/getSemesterStati/' + prestudent_id, + }; + }, } \ No newline at end of file diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index 04c6d1835..f0db9fe5d 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -7,6 +7,9 @@ export default { from: 'configDomain', default: 'technikum-wien.at' }, + currentSemester: { + from: 'currentSemester', + }, }, props: { headerData: { @@ -51,6 +54,10 @@ export default { console.error("Error loading department data:", error); }); } + + if(this.headerData){ + this.getSemesterStati(this.headerData[0].prestudent_id); + } }, watch: { person_id: { @@ -65,12 +72,27 @@ export default { }, deep: true, }, + currentSemester: { + handler(newVal) { + if (newVal) { + if(!this.semesterStati.some(item => item.studiensemester_kurzbz === this.currentSemester)) { + this.noCurrentStatus = true; + } + else + { + this.noCurrentStatus = false; + } + } + }, + deep: true, + }, }, data(){ return{ headerDataMa: {}, departmentData: {}, leitungData: {}, + noCurrentStatus: false }; }, methods: { @@ -109,6 +131,14 @@ export default { } else { return 'data:image/jpeg;base64,' + foto; } + }, + getSemesterStati(prestudent_id){ + return this.$api + .call(ApiDetailHeader.getSemesterStati(prestudent_id)) + .then(result => { + this.semesterStati = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); } }, template: ` @@ -149,11 +179,11 @@ export default { {{headerData[0].person_id}} | {{$p.t('lehre', 'studiengang')}} {{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}}) - | {{$p.t('lehre', 'semester')}} + | {{$p.t('lehre', 'semester')}} {{headerData[0].semester}} | {{$p.t('lehre', 'verband')}} {{headerData[0].verband}} - | {{$p.t('lehre', 'gruppe')}} + | {{$p.t('lehre', 'gruppe')}} {{headerData[0].gruppe}} @@ -162,12 +192,16 @@ export default { {{headerData[0].mail_intern}} - | Status - {{headerData[0].statusofsemester}} + | Status + + {{$p.t('lehre', 'textNoStatus')}} + + + {{headerData[0].statusofsemester}} + | {{$p.t('person', 'matrikelnummer')}} {{headerData[0].matr_nr}} -
diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index a7d97d4fc..fae85b653 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -422,6 +422,12 @@ export default { this.$refs.searchbar.abort(); this.$refs.searchbar.hideresult(); }, + handleReloadStudent(students){ + this.$refs.stvList.updateUrl( + ApiStv.students.uid(students[0].uid,'CURRENT_SEMESTER'), + true + ); + } }, created() { if (!this.url_studiensemester_kurzbz) { @@ -629,7 +635,7 @@ export default { diff --git a/public/js/components/Stv/Studentenverwaltung/Details.js b/public/js/components/Stv/Studentenverwaltung/Details.js index dca08c07f..4d49aba0d 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details.js @@ -8,6 +8,11 @@ import ApiStvApp from '../../../api/factory/stv/app.js'; export default { name: "DetailsPrestudent", + inject: { + currentSemester: { + from: 'currentSemester', + }, + }, components: { FhcTabs, FhcHeader @@ -21,6 +26,9 @@ export default { props: { students: Array }, + emits: [ + 'reloadStudent:students' + ], computed: { appRoot() { return FHC_JS_DATA_STORAGE_OBJECT.app_root; @@ -39,13 +47,30 @@ export default { return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid)); } return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid)); - } + }, +/* studiensemester(){ + if(this.students) + return this.students.query_studiensemester_kurzbz; + else + return null; + //return this.students?[0].query_studiensemester_kurzbz || null; + },*/ }, watch: { '$p.user_language.value'(n, o) { if (n !== o && o !== undefined) this.loadConfig(); + }, + currentSemester(newVal) { + if ( + this.students && + this.students.length > 0 && + newVal !== this.students[0].query_studiensemester_kurzbz + ) { + this.$emit('reloadStudent:students', this.students); + } } + }, methods: { loadConfig() { @@ -65,7 +90,7 @@ export default { reload() { if (this.$refs.tabs?.$refs?.current?.reload) this.$refs.tabs.$refs.current.reload(); - } + }, }, created() { this.loadConfig(); diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 580f15bc2..b1f4b004a 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -56144,6 +56144,26 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### Refactor Messages END + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'textNoStatus', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kein Status', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No status', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), ); From 127ce312ea478027b827754c1ca2f889667d0638 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Tue, 10 Mar 2026 08:28:11 +0100 Subject: [PATCH 022/275] reload Details and Detailheader without updateUrl --- public/js/components/Stv/Studentenverwaltung.js | 15 ++++++++++++--- .../components/Stv/Studentenverwaltung/Details.js | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index fae85b653..52b4b9d07 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -423,10 +423,19 @@ export default { this.$refs.searchbar.hideresult(); }, handleReloadStudent(students){ - this.$refs.stvList.updateUrl( + //TODO(Manu) check with Harald + + this.$refs.details.reload(); //reload simply without updateUrl + //this.selected = students; + +/* this.$refs.stvList.updateUrl( ApiStv.students.uid(students[0].uid,'CURRENT_SEMESTER'), true - ); + );*/ +/* this.$refs.stvList.updateUrl( + ApiStv.students.uid(students[0].uid,'CURRENT_SEMESTER'), + true + );*/ } }, created() { @@ -635,7 +644,7 @@ export default { diff --git a/public/js/components/Stv/Studentenverwaltung/Details.js b/public/js/components/Stv/Studentenverwaltung/Details.js index 4d49aba0d..519b0b1b5 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details.js @@ -27,7 +27,7 @@ export default { students: Array }, emits: [ - 'reloadStudent:students' + 'reload-students:students' ], computed: { appRoot() { @@ -67,7 +67,7 @@ export default { this.students.length > 0 && newVal !== this.students[0].query_studiensemester_kurzbz ) { - this.$emit('reloadStudent:students', this.students); + this.$emit('reload-students', this.students); } } From dd87e893baa4ba0878310f97de1549ef5df2910b Mon Sep 17 00:00:00 2001 From: ma0068 Date: Wed, 11 Mar 2026 17:13:59 +0100 Subject: [PATCH 023/275] working version for relaod header and data without updateUrl --- .../components/DetailHeader/DetailHeader.js | 319 +++++++++--------- .../js/components/Stv/Studentenverwaltung.js | 9 +- .../Stv/Studentenverwaltung/Details.js | 51 ++- 3 files changed, 206 insertions(+), 173 deletions(-) diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index f0db9fe5d..3e7187d27 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -55,9 +55,9 @@ export default { }); } - if(this.headerData){ +/* if (this.headerData && this.headerData.length) { //bereits im watcher this.getSemesterStati(this.headerData[0].prestudent_id); - } + }*/ }, watch: { person_id: { @@ -86,167 +86,176 @@ export default { }, deep: true, }, + headerData: { + handler(newVal) { + if (this.typeHeader === 'student' && newVal?.length) { + this.getSemesterStati(newVal[0].prestudent_id); + } + }, + immediate: true + }, }, - data(){ - return{ - headerDataMa: {}, - departmentData: {}, - leitungData: {}, - noCurrentStatus: false - }; - }, - methods: { - getHeader(person_id) { - return this.$api - .call(ApiDetailHeader.getHeader(person_id)) - .then(result => { - this.headerDataMa = result.data; +data(){ +return{ + headerDataMa: {}, + departmentData: {}, + leitungData: {}, + noCurrentStatus: false +}; +}, +methods: { +getHeader(person_id) { + return this.$api + .call(ApiDetailHeader.getHeader(person_id)) + .then(result => { + this.headerDataMa = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - }, - loadDepartmentData(mitarbeiter_uid) { - return this.$api - .call(ApiDetailHeader.getPersonAbteilung(mitarbeiter_uid)) - .then(result => { - this.departmentData = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - }, - getLeitungOrg(oekurzbz){ - return this.$api - .call(ApiDetailHeader.getLeitungOrg(oekurzbz)) - .then(result => { - this.leitungData = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - }, - redirectToLeitung(){ - this.$emit('redirectToLeitung', { - person_id: this.leitungData.person_id}); - }, - getFotoSrc(foto) { - if(foto === null) { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'skin/images/profilbild_dummy.jpg'; - } else { - return 'data:image/jpeg;base64,' + foto; - } - }, - getSemesterStati(prestudent_id){ - return this.$api - .call(ApiDetailHeader.getSemesterStati(prestudent_id)) - .then(result => { - this.semesterStati = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - } - }, - template: ` -
+ }) + .catch(this.$fhcAlert.handleSystemError); +}, +loadDepartmentData(mitarbeiter_uid) { + return this.$api + .call(ApiDetailHeader.getPersonAbteilung(mitarbeiter_uid)) + .then(result => { + this.departmentData = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); +}, +getLeitungOrg(oekurzbz){ + return this.$api + .call(ApiDetailHeader.getLeitungOrg(oekurzbz)) + .then(result => { + this.leitungData = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); +}, +redirectToLeitung(){ + this.$emit('redirectToLeitung', { + person_id: this.leitungData.person_id}); +}, +getFotoSrc(foto) { + if(foto === null) { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'skin/images/profilbild_dummy.jpg'; + } else { + return 'data:image/jpeg;base64,' + foto; + } +}, +getSemesterStati(prestudent_id){ + return this.$api + .call(ApiDetailHeader.getSemesterStati(prestudent_id)) + .then(result => { + this.semesterStati = result.data; + //console.log("getSemesterStati loaded for prestudent_id " + prestudent_id); + }) + .catch(this.$fhcAlert.handleSystemError); +} +}, +template: ` +
- - +
+` } diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index 52b4b9d07..e62ccfe7d 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -422,12 +422,13 @@ export default { this.$refs.searchbar.abort(); this.$refs.searchbar.hideresult(); }, - handleReloadStudent(students){ - //TODO(Manu) check with Harald - + handleReloadStudent_DEPR(students){ + // @reload-students="handleReloadStudent" + //TODO(Manu) delete this.$refs.details.reload(); //reload simply without updateUrl //this.selected = students; + //analoge to url_studiensemester_kurzbz /* this.$refs.stvList.updateUrl( ApiStv.students.uid(students[0].uid,'CURRENT_SEMESTER'), true @@ -644,7 +645,7 @@ export default { diff --git a/public/js/components/Stv/Studentenverwaltung/Details.js b/public/js/components/Stv/Studentenverwaltung/Details.js index 519b0b1b5..95d791ef9 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details.js @@ -2,6 +2,7 @@ import FhcTabs from "../../Tabs.js"; import FhcHeader from "../../DetailHeader/DetailHeader.js"; import ApiStvApp from '../../../api/factory/stv/app.js'; +import ApiStudent from '../../../api/factory/stv/students.js'; // TODO(chris): alt & title // TODO(chris): phrasen @@ -20,15 +21,13 @@ export default { data() { return { configStudent: {}, - configStudents: {} + configStudents: {}, + localStudent: null }; }, props: { students: Array }, - emits: [ - 'reload-students:students' - ], computed: { appRoot() { return FHC_JS_DATA_STORAGE_OBJECT.app_root; @@ -48,27 +47,29 @@ export default { } return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid)); }, -/* studiensemester(){ - if(this.students) - return this.students.query_studiensemester_kurzbz; - else - return null; - //return this.students?[0].query_studiensemester_kurzbz || null; - },*/ + //for reloading component if data changes + headerKey() { + return this.students?.[0]?.uid || this.localStudent?.[0]?.uid || "empty"; + } }, watch: { '$p.user_language.value'(n, o) { if (n !== o && o !== undefined) this.loadConfig(); }, + //ohne zusätzlichen Watcher reload header und details currentSemester(newVal) { if ( this.students && this.students.length > 0 && newVal !== this.students[0].query_studiensemester_kurzbz ) { - this.$emit('reload-students', this.students); + console.log("studiensemester_kurzbz " + this.students[0].query_studiensemester_kurzbz + " vs " + newVal); + this.reloadDataStudent(); } + }, + headerKey(newVal){ + this.reloadDataStudent(); } }, @@ -91,10 +92,31 @@ export default { if (this.$refs.tabs?.$refs?.current?.reload) this.$refs.tabs.$refs.current.reload(); }, + reloadDataStudent(){ + //TODO(check) + this.localStudent = null; + const studentArr = this.students; + + if (!studentArr || !studentArr.length) { + console.log("no data"); + return; + } + + console.log("uid " + studentArr[0].uid); + + this.$api + .call(ApiStudent.uid(studentArr[0].uid, this.currentSemester)) + .then(result => { + this.localStudent = result.data; + }); + } }, created() { this.loadConfig(); }, + /* //TODO(remove) + * {{headerKey}} {{localStudent?.[0]?.uid}}
{{ students?.[0]?.uid }} + * */ template: `
@@ -102,7 +124,8 @@ export default {
@@ -110,7 +133,7 @@ export default { v-if="students.length == 1" ref="tabs" :useprimevue="true" - :modelValue="students[0]" + :modelValue="localStudent[0] || students[0]" :config="config" :default="$route.params.tab" style="flex: 1 1 0%; height: 0%" From dd760f821089c5c2962951731e9f6af47a8b9ce8 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Thu, 12 Mar 2026 11:10:13 +0100 Subject: [PATCH 024/275] finetune behavior --- .../api/frontend/v1/stv/Student.php | 1 + .../components/DetailHeader/DetailHeader.js | 1 - .../js/components/Stv/Studentenverwaltung.js | 16 ------------ .../Stv/Studentenverwaltung/Details.js | 26 +++++++------------ 4 files changed, 10 insertions(+), 34 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Student.php b/application/controllers/api/frontend/v1/stv/Student.php index 2fb354f0c..4711836db 100644 --- a/application/controllers/api/frontend/v1/stv/Student.php +++ b/application/controllers/api/frontend/v1/stv/Student.php @@ -140,6 +140,7 @@ class Student extends FHCAPI_Controller . $this->PrestudentModel->escape($studiensemester_kurzbz) . ") AS statusofsemester" ); + $this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz'); $this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT'); $this->PrestudentModel->addJoin('public.tbl_benutzer b', 'student_uid = uid', 'LEFT'); diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index 3e7187d27..b414f07ea 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -145,7 +145,6 @@ getSemesterStati(prestudent_id){ .call(ApiDetailHeader.getSemesterStati(prestudent_id)) .then(result => { this.semesterStati = result.data; - //console.log("getSemesterStati loaded for prestudent_id " + prestudent_id); }) .catch(this.$fhcAlert.handleSystemError); } diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index e62ccfe7d..a7d97d4fc 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -422,22 +422,6 @@ export default { this.$refs.searchbar.abort(); this.$refs.searchbar.hideresult(); }, - handleReloadStudent_DEPR(students){ - // @reload-students="handleReloadStudent" - //TODO(Manu) delete - this.$refs.details.reload(); //reload simply without updateUrl - //this.selected = students; - - //analoge to url_studiensemester_kurzbz -/* this.$refs.stvList.updateUrl( - ApiStv.students.uid(students[0].uid,'CURRENT_SEMESTER'), - true - );*/ -/* this.$refs.stvList.updateUrl( - ApiStv.students.uid(students[0].uid,'CURRENT_SEMESTER'), - true - );*/ - } }, created() { if (!this.url_studiensemester_kurzbz) { diff --git a/public/js/components/Stv/Studentenverwaltung/Details.js b/public/js/components/Stv/Studentenverwaltung/Details.js index 95d791ef9..8312097af 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details.js @@ -46,10 +46,6 @@ export default { return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid)); } return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid)); - }, - //for reloading component if data changes - headerKey() { - return this.students?.[0]?.uid || this.localStudent?.[0]?.uid || "empty"; } }, watch: { @@ -57,21 +53,21 @@ export default { if (n !== o && o !== undefined) this.loadConfig(); }, - //ohne zusätzlichen Watcher reload header und details currentSemester(newVal) { if ( - this.students && - this.students.length > 0 && + Array.isArray(this.students) && + this.students.length === 1 && newVal !== this.students[0].query_studiensemester_kurzbz ) { - console.log("studiensemester_kurzbz " + this.students[0].query_studiensemester_kurzbz + " vs " + newVal); this.reloadDataStudent(); } + else { + this.localStudent = null; + } }, - headerKey(newVal){ - this.reloadDataStudent(); + students() { + this.localStudent = null; } - }, methods: { loadConfig() { @@ -114,9 +110,6 @@ export default { created() { this.loadConfig(); }, - /* //TODO(remove) - * {{headerKey}} {{localStudent?.[0]?.uid}}
{{ students?.[0]?.uid }} - * */ template: `
@@ -124,16 +117,15 @@ export default {
Date: Thu, 12 Mar 2026 16:02:44 +0100 Subject: [PATCH 025/275] update phrase noTextInSem --- public/js/components/DetailHeader/DetailHeader.js | 2 +- system/phrasesupdate.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index 53ed9eee4..fe51f4500 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -306,7 +306,7 @@ export default { | Status - {{$p.t('lehre', 'textNoStatus')}} + {{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }} {{headerData[0].statusofsemester}} diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index e3e15482f..22cf066fd 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -56951,18 +56951,18 @@ I have been informed that I am under no obligation to consent to the transmissio array( 'app' => 'core', 'category' => 'lehre', - 'phrase' => 'textNoStatus', + 'phrase' => 'textNoStatusInSem', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Kein Status', + 'text' => 'Kein Status im {sem}', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'No status', + 'text' => 'No status in {sem}', 'description' => '', 'insertvon' => 'system' ) From 2682ea75ab386c2956445f97a6ac153b05fc117c Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 12 Mar 2026 16:58:48 +0100 Subject: [PATCH 026/275] use only one endpoint for detailheader --- .../frontend/v1/detailheader/DetailHeader.php | 30 ------------------- .../frontend/v1/detailheader/Detailheader.php | 12 ++++++++ public/js/api/factory/detailHeader.js | 2 +- 3 files changed, 13 insertions(+), 31 deletions(-) delete mode 100644 application/controllers/api/frontend/v1/detailheader/DetailHeader.php diff --git a/application/controllers/api/frontend/v1/detailheader/DetailHeader.php b/application/controllers/api/frontend/v1/detailheader/DetailHeader.php deleted file mode 100644 index be9fad27a..000000000 --- a/application/controllers/api/frontend/v1/detailheader/DetailHeader.php +++ /dev/null @@ -1,30 +0,0 @@ - ['admin:r', 'assistenz:r'], - ]); - } - - //------------------------------------------------------------------------------------------------------------------ - // Public methods - - public function getSemesterStati($prestudent_id) - { - $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); - - $result = $this->PrestudentstatusModel->getAllPrestudentstatiWithStudiensemester($prestudent_id); - - $data = $this->getDataOrTerminateWithError($result); - - $this->terminateWithSuccess($data); - } - -} diff --git a/application/controllers/api/frontend/v1/detailheader/Detailheader.php b/application/controllers/api/frontend/v1/detailheader/Detailheader.php index ada10c5b6..e35885efa 100644 --- a/application/controllers/api/frontend/v1/detailheader/Detailheader.php +++ b/application/controllers/api/frontend/v1/detailheader/Detailheader.php @@ -12,6 +12,7 @@ class Detailheader extends FHCAPI_Controller 'getHeader' => ['vertrag/mitarbeiter:r'], 'getPersonAbteilung' => ['vertrag/mitarbeiter:r'], 'getLeitungOrg' => ['vertrag/mitarbeiter:r'], + 'getSemesterStati' => ['admin:r', 'assistenz:r'] ]); } @@ -48,6 +49,17 @@ class Detailheader extends FHCAPI_Controller $this->terminateWithSuccess(current($data)); } + public function getSemesterStati($prestudent_id) + { + $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + + $result = $this->PrestudentstatusModel->getAllPrestudentstatiWithStudiensemester($prestudent_id); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + } diff --git a/public/js/api/factory/detailHeader.js b/public/js/api/factory/detailHeader.js index 4b40d0c7f..02a371f15 100644 --- a/public/js/api/factory/detailHeader.js +++ b/public/js/api/factory/detailHeader.js @@ -37,7 +37,7 @@ export default { getSemesterStati(prestudent_id){ return { method: 'get', - url: 'api/frontend/v1/detailheader/DetailHeader/getSemesterStati/' + prestudent_id, + url: 'api/frontend/v1/detailheader/detailheader/getSemesterStati/' + prestudent_id, }; }, } \ No newline at end of file From 29a4b4aadc744cce19061767e77513907afca3b4 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Fri, 13 Mar 2026 09:11:00 +0100 Subject: [PATCH 027/275] use permission PERM_LOGGED for header, add condition one empty space for showing verband --- .../api/frontend/v1/detailheader/Detailheader.php | 8 ++++---- public/js/components/DetailHeader/DetailHeader.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/controllers/api/frontend/v1/detailheader/Detailheader.php b/application/controllers/api/frontend/v1/detailheader/Detailheader.php index e35885efa..719afb2c6 100644 --- a/application/controllers/api/frontend/v1/detailheader/Detailheader.php +++ b/application/controllers/api/frontend/v1/detailheader/Detailheader.php @@ -9,10 +9,10 @@ class Detailheader extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getHeader' => ['vertrag/mitarbeiter:r'], - 'getPersonAbteilung' => ['vertrag/mitarbeiter:r'], - 'getLeitungOrg' => ['vertrag/mitarbeiter:r'], - 'getSemesterStati' => ['admin:r', 'assistenz:r'] + 'getHeader' => self::PERM_LOGGED, + 'getPersonAbteilung' => self::PERM_LOGGED, + 'getLeitungOrg' => self::PERM_LOGGED, + 'getSemesterStati' => self::PERM_LOGGED, ]); } diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index fe51f4500..d3c23185e 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -293,9 +293,9 @@ export default { {{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}}) | {{$p.t('lehre', 'semester')}} {{headerData[0].semester}} - | {{$p.t('lehre', 'verband')}} + | {{$p.t('lehre', 'verband')}} {{headerData[0].verband}} - | {{$p.t('lehre', 'gruppe')}} + | {{$p.t('lehre', 'gruppe')}} {{headerData[0].gruppe}} From df124db84a8c4f4bff02c5d5f983fd99bd4484b6 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Fri, 13 Mar 2026 12:40:29 +0100 Subject: [PATCH 028/275] use primeVue Skeleton while loading Data for Semester, Verband and Gruppe --- .../components/DetailHeader/DetailHeader.js | 121 ++++++++++-------- 1 file changed, 66 insertions(+), 55 deletions(-) diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index d3c23185e..dfd15e0be 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -1,11 +1,13 @@ import ApiDetailHeader from "../../api/factory/detailHeader.js"; import ApiHandleFoto from "../../api/factory/fotoHandling.js"; import ModalUploadFoto from "./Modal/UploadFoto.js"; +import PvSkeleton from "../../../../index.ci.php/public/js/components/primevue/skeleton/skeleton.esm.min.js"; export default { name: 'DetailHeader', components: { - ModalUploadFoto + ModalUploadFoto, + PvSkeleton }, props: { headerData: { @@ -276,67 +278,76 @@ export default { {{person.uid}}
-
-
-

- {{headerData[0].titelpre}} - {{headerData[0].vorname}} - {{headerData[0].nachname}} - , - {{headerData[0].titelpost}} -

-
unruly
-
+
+
+

+ {{headerData[0].titelpre}} + {{headerData[0].vorname}} + {{headerData[0].nachname}} + , + {{headerData[0].titelpost}} +

+
unruly
+
-
- {{$p.t('lehre', 'studiengang')}} - {{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}}) +
+ {{$p.t('lehre', 'studiengang')}} + {{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}}) + + +
-
- Email - - {{headerData[0].mail_intern}} - - | Status - - {{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }} - - - {{headerData[0].statusofsemester}} - -
- -
-
-
-
- -
-
-

-
-
-
-

-
-
-
-

-
-
-
-

UID

-
-
+
+ Email + + {{headerData[0].mail_intern}} + + | Status + + {{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }} + + + {{headerData[0].statusofsemester}} + +
+
+
+
+
+ +
+
+

+
+
+
+

+
+
+
+

+
+
+
+

UID

+
+
From c49e32c4ac1db69046d41cfb49ca7747e8f86fe7 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 16 Mar 2026 11:26:23 +0100 Subject: [PATCH 029/275] use skeletons for all data in studentHeader --- .../components/DetailHeader/DetailHeader.js | 68 +++++++++++++++---- .../Stv/Studentenverwaltung/Details.js | 4 ++ 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index dfd15e0be..be335114b 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -44,6 +44,10 @@ export default { currentSemester: { type: String, default: '' + }, + isLoading: { + type: Boolean, + default: false } }, computed: { @@ -107,7 +111,10 @@ export default { leitungData: {}, isFetchingIssues: false, noCurrentStatus: false, - semesterStatiLoading: false + semesterStatiLoading: false, + leitungOrgLoading: false, + departmentDataLoading: false, + headerDataLoading: false }; }, methods: { @@ -123,29 +130,40 @@ export default { }); }, getHeader(person_id) { + this.headerDataLoading = true; return this.$api .call(ApiDetailHeader.getHeader(person_id)) .then(result => { this.headerDataMa = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.headerDataLoading = false; + }); }, loadDepartmentData(mitarbeiter_uid) { + this.departmentDataLoading = true; return this.$api .call(ApiDetailHeader.getPersonAbteilung(mitarbeiter_uid)) .then(result => { this.departmentData = result.data; }) - .catch(this.$fhcAlert.handleSystemError); + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.departmentDataLoading = false; + }); }, getLeitungOrg(oekurzbz){ + this.leitungOrgLoading = true; return this.$api .call(ApiDetailHeader.getLeitungOrg(oekurzbz)) .then(result => { this.leitungData = result.data; }) - .catch(this.$fhcAlert.handleSystemError); + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.leitungOrgLoading = false; + }); }, async goToLeitung() { this.loadHeaderData(this.leitungData.person_id, this.leitungData.uid); @@ -205,7 +223,7 @@ export default { }) .catch(this.$fhcAlert.handleSystemError) .finally(() => { - this.semesterStatiLoading = false + this.semesterStatiLoading = false; }); }, setNoCurrentStatus() { @@ -279,7 +297,7 @@ export default {
-
+

{{headerData[0].titelpre}} {{headerData[0].vorname}} @@ -289,10 +307,19 @@ export default {

unruly
+
+ +
unruly
+
{{$p.t('lehre', 'studiengang')}} + {{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}}) + + + +
-
+
Email - + {{headerData[0].mail_intern}} + + + | Status {{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }} @@ -332,19 +362,31 @@ export default {

-
+
+ + +

-
+
+ + +

-
+
+ + +

UID

-
+
+ + +
diff --git a/public/js/components/Stv/Studentenverwaltung/Details.js b/public/js/components/Stv/Studentenverwaltung/Details.js index eb996eda0..bd5d42f9f 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details.js @@ -47,6 +47,9 @@ export default { } return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid)); }, + isLoading() { + return this.students === null; //null-> loading, [] -> empty, [...] -> data, necessary for skeleton in tiles + }, tile_PersId(){ let tile = this.students[0].person_id != null ? this.students[0].person_id : '-'; return tile; @@ -133,6 +136,7 @@ export default { typeHeader="student" @reload="reloadList" fotoEditable + :isLoading="isLoading" > From 17f94aabdf73eabf3ebafa1027b2c36ae0ccc4a1 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 16 Mar 2026 17:16:40 +0100 Subject: [PATCH 030/275] add loading skeletons for mitarbeiterHeader --- .../components/DetailHeader/DetailHeader.js | 94 +++++++++++++------ .../Stv/Studentenverwaltung/Details.js | 2 +- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index be335114b..ada6034fc 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -45,7 +45,7 @@ export default { type: String, default: '' }, - isLoading: { + isLoading: { //if true, then parent isLoading type: Boolean, default: false } @@ -80,7 +80,7 @@ export default { } else if (this.typeHeader === 'mitarbeiter') { if (!this.person_id || !this.mitarbeiter_uid || !this.domain) { throw new Error( - '[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are requried.' + '[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are required.' ) } this.loadHeaderData(this.person_id, this.mitarbeiter_uid); @@ -114,7 +114,7 @@ export default { semesterStatiLoading: false, leitungOrgLoading: false, departmentDataLoading: false, - headerDataLoading: false + headerDataMaLoading: false }; }, methods: { @@ -130,7 +130,7 @@ export default { }); }, getHeader(person_id) { - this.headerDataLoading = true; + this.headerDataMaLoading = true; return this.$api .call(ApiDetailHeader.getHeader(person_id)) .then(result => { @@ -138,7 +138,7 @@ export default { }) .catch(this.$fhcAlert.handleSystemError) .finally(() => { - this.headerDataLoading = false; + this.headerDataMaLoading = false; }); }, loadDepartmentData(mitarbeiter_uid) { @@ -260,7 +260,6 @@ export default { diff --git a/public/js/components/Messages/Details/NewMessage/NewDiv.js b/public/js/components/Messages/Details/NewMessage/NewDiv.js index 00b4e92a8..6a0dfa222 100644 --- a/public/js/components/Messages/Details/NewMessage/NewDiv.js +++ b/public/js/components/Messages/Details/NewMessage/NewDiv.js @@ -55,6 +55,7 @@ export default { previewBody: "", replyData: null, messageSent: false, + isSending: false } }, methods: { @@ -104,16 +105,21 @@ export default { data.append('data', JSON.stringify(this.formData)); data.append('ids', JSON.stringify(this.id)); + //for disabling sendButton and show Spinner + this.isSending = true; + return this.$api .call(ApiMessages.sendMessage(this.typeId, data)) .then(response => { - if(this.openMode == "inSamePage") + if(this.openMode == "inSamePage") { this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent')); + } this.hideTemplate(); this.resetForm(); this.messageSent = true; }).catch(this.$fhcAlert.handleSystemError) .finally(() => { + this.isSending = false; if(this.openMode == "inSamePage" && this.id.length == 1 ){ this.$emit('reloadTable'); } @@ -413,7 +419,12 @@ export default { - +
From d4e170037ba21641da0d65d15586bc8e08e5b15d Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 18 May 2026 10:11:17 +0200 Subject: [PATCH 079/275] add order Nachname, Vorname --- application/models/person/Person_model.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/models/person/Person_model.php b/application/models/person/Person_model.php index 636e26a4c..e02229624 100644 --- a/application/models/person/Person_model.php +++ b/application/models/person/Person_model.php @@ -173,8 +173,13 @@ class Person_model extends DB_Model LIMIT 1 ) AS uid"); - $this->addOrder('uid'); - }; + $this->addOrder('uid, lower(nachname), lower(vorname)'); + } + else + { + $this->addOrder('lower(nachname), lower(vorname)'); + } + $result = $this->loadWhere( 'lower(nachname) like '.$this->db->escape('%'.mb_strtolower($filter).'%')." From e2497e19585fed507d5123f455ec9bd544d196c2 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 18 May 2026 17:25:48 +0200 Subject: [PATCH 080/275] add Infomodal Beratungsgespraech in case of Abmeldung and configEntry --- application/config/studierendenantrag.php | 10 ++ .../controllers/lehre/Studierendenantrag.php | 3 +- .../views/lehre/Antrag/Student/List.php | 93 +++++++++++++++++-- system/phrasesupdate.php | 20 ++++ 4 files changed, 116 insertions(+), 10 deletions(-) diff --git a/application/config/studierendenantrag.php b/application/config/studierendenantrag.php index 4e25aef28..33d8b88fc 100644 --- a/application/config/studierendenantrag.php +++ b/application/config/studierendenantrag.php @@ -168,3 +168,13 @@ $config['stgkz_blacklist_wiederholung'] = []; * @var array An array of noten ids */ $config['note_blacklist_wiederholung'] = []; + + +/** + * Modal with Infotext of TLC + * if true, there will be shown an info modal in the studentsView after the first button ABMELDUNG + * + * @var boolean + */ +$config['showInfoModalAbmeldung'] = true; + diff --git a/application/controllers/lehre/Studierendenantrag.php b/application/controllers/lehre/Studierendenantrag.php index 107c9af96..12f12efe8 100644 --- a/application/controllers/lehre/Studierendenantrag.php +++ b/application/controllers/lehre/Studierendenantrag.php @@ -25,7 +25,8 @@ class Studierendenantrag extends FHC_Controller // Load language phrases $this->loadPhrases([ - 'studierendenantrag' + 'studierendenantrag', + 'ui' ]); if (strtolower($this->router->method) === 'leitung') diff --git a/application/views/lehre/Antrag/Student/List.php b/application/views/lehre/Antrag/Student/List.php index 6d769dafe..3c360cd0c 100644 --- a/application/views/lehre/Antrag/Student/List.php +++ b/application/views/lehre/Antrag/Student/List.php @@ -30,6 +30,12 @@ if(defined('CIS4')){ } ?> +load->config('studierendenantrag'); + $showInfoModalAbmeldung = $this->config->item('showInfoModalAbmeldung'); +?> +
@@ -43,17 +49,86 @@ if(defined('CIS4')){

()

+ + + +
-

p->t('studierendenantrag', 'calltoaction_' . $type); ?>

-
- - p->t('studierendenantrag', 'antrag_typ_' . $type); ?> - +

p->t('studierendenantrag', 'calltoaction_' . $type); ?>

+
+ + + + data-bs-toggle="modal" + + > + p->t('studierendenantrag', 'antrag_typ_' . $type); ?> + + + +
- + + + + diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 5f6269365..d32821125 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -58177,6 +58177,26 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### Phrases Dashboard Admin END + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'link_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Weiter zur Abmeldung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Continue to De-registration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), ); From 4d3c73c78f11e15b60557f4f07d2a15edd1520d1 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 19 May 2026 10:21:56 +0200 Subject: [PATCH 081/275] add join condition for notizzuordung via prestudent_id only --- application/controllers/api/frontend/v1/stv/Students.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index b5ad79cf0..db546288c 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -894,7 +894,7 @@ class Students extends FHCAPI_Controller n.start, n.ende FROM public.tbl_notizzuordnung AS nz - JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id + JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id AND nz.prestudent_id IS NOT NULL JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz " . $whereTags . " From 7d3096c2e8ce53d1a61838e778ea1cd306ad5bbf Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 19 May 2026 10:28:08 +0200 Subject: [PATCH 082/275] readd required assignments for tags removed during merge --- public/js/components/Stv/Studentenverwaltung/List.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index 51c6faf5b..7e97e26c1 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -422,6 +422,16 @@ export default { if(selected.length > 0 || deselected.length > 0){ this.lastSelected = this.selected; + + //for tags + this.selectedRows = this.$refs.table.tabulator.getSelectedRows(); + this.selectedColumnValues = this.selectedRows.filter( + row => row.getData().prestudent_id !== undefined + && row.getData().prestudent_id + ).map( + row => row.getData().prestudent_id + ); + this.$emit('update:selected', data); } }, From 9fb00f6cbdd2db2eef5e7fd8cec719a6feccf0ff Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 19 May 2026 16:40:18 +0200 Subject: [PATCH 083/275] remove orphaned end tag, add right padding --- public/js/components/DetailHeader/DetailHeader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index 3f81c3134..b7f280068 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -355,7 +355,7 @@ export default { class="btn btn-outline btn-light mb-1" :title="'Automatische Tags fuer ' + currentSemester + ' neu laden'" > - + {{currentSemester}}
unruly
From 981c97173edd8841b669671cce878f9c495529d9 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 19 May 2026 16:51:24 +0200 Subject: [PATCH 084/275] add padding to export icon link --- public/js/components/Stv/Studentenverwaltung/List.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index db72bbbcf..aa7155350 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -666,7 +666,7 @@ export default { -->