diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php
index 455b1e8fc..564247688 100644
--- a/application/controllers/api/frontend/v1/stv/Config.php
+++ b/application/controllers/api/frontend/v1/stv/Config.php
@@ -99,6 +99,11 @@ class Config extends FHCAPI_Controller
'title' => $this->p->t('stv', 'tab_resources'),
'component' => './Stv/Studentenverwaltung/Details/Betriebsmittel.js'
];
+ $result['groups'] = [
+ 'title' => $this->p->t('stv', 'tab_groups'),
+ 'component' => './Stv/Studentenverwaltung/Details/Gruppen.js'
+ ];
+
$result['grades'] = [
'title' => $this->p->t('stv', 'tab_grades'),
'component' => './Stv/Studentenverwaltung/Details/Noten.js',
@@ -318,7 +323,7 @@ class Config extends FHCAPI_Controller
$title_eng = $this->p->t("global", "englisch");
$title_ff = $this->p->t("stv", "document_certificate");
$title_lv = $this->p->t("stv", "document_coursecertificate");
-
+
$link_ff = "documents/export/" .
"zertifikat.rdf.php/" .
"Zertifikat" .
diff --git a/application/controllers/api/frontend/v1/stv/Gruppen.php b/application/controllers/api/frontend/v1/stv/Gruppen.php
new file mode 100644
index 000000000..c30816f2a
--- /dev/null
+++ b/application/controllers/api/frontend/v1/stv/Gruppen.php
@@ -0,0 +1,80 @@
+ ['admin:r', 'assistenz:r'],
+ 'deleteGruppe' => ['admin:rw', 'assistenz:rw'],
+ ]);
+
+ // Load Libraries
+ $this->load->library('VariableLib', ['uid' => getAuthUID()]);
+
+ // Load language phrases
+ $this->loadPhrases([
+ 'ui', 'gruppenmanagement'
+ ]);
+
+ // Load models
+ $this->load->model('person/Benutzergruppe_model', 'BenutzergruppeModel');
+ $this->load->model('organisation/Gruppe_model', 'GruppeModel');
+ }
+
+ public function getGruppen($student_uid)
+ {
+ $this->BenutzergruppeModel ->addSelect('gruppe_kurzbz');
+ $this->BenutzergruppeModel ->addSelect('bezeichnung');
+ $this->BenutzergruppeModel ->addSelect('generiert');
+ $this->BenutzergruppeModel ->addSelect('uid');
+ $this->BenutzergruppeModel ->addSelect('studiensemester_kurzbz');
+ $this->BenutzergruppeModel ->addJoin('public.tbl_gruppe', 'gruppe_kurzbz');
+ $this->BenutzergruppeModel-> addOrder('bezeichnung', 'ASC');
+
+ $result = $this->BenutzergruppeModel->loadWhere(
+ array(
+ 'uid' => $student_uid
+ )
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function deleteGruppe()
+ {
+ $student_uid = $this->input->post('id');
+ $gruppe_kurzbz = $this->input->post('gruppe_kurzbz');
+
+ //Validate if automatic group generation
+ $result = $this->GruppeModel-> loadWhere(
+ array(
+ 'gruppe_kurzbz' => $gruppe_kurzbz
+ )
+ );
+ $data = $this->getDataOrTerminateWithError($result);
+ $generation = current($data);
+
+ if($generation->generiert)
+ {
+ $this->terminateWithError($this->p->t('gruppenmanagement', 'error_deleteGeneratedGroups'), self::ERROR_TYPE_GENERAL);
+ }
+
+ $result = $this->BenutzergruppeModel->delete(
+ array(
+ 'gruppe_kurzbz' => $gruppe_kurzbz,
+ 'uid' => $student_uid
+ )
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ return $this->terminateWithSuccess($data);
+ }
+}
diff --git a/public/js/api/factory/stv/group.js b/public/js/api/factory/stv/group.js
new file mode 100644
index 000000000..b395fbf43
--- /dev/null
+++ b/public/js/api/factory/stv/group.js
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2025 fhcomplete.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export default {
+ getGruppen(id) {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/stv/Gruppen/getGruppen/' + id
+ };
+ },
+ deleteGroup(params) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/stv/Gruppen/deleteGruppe/',
+ params
+ };
+ }
+}
+
diff --git a/public/js/api/stv.js b/public/js/api/stv.js
index f93c46bcf..63e53f4fb 100644
--- a/public/js/api/stv.js
+++ b/public/js/api/stv.js
@@ -2,6 +2,7 @@ import verband from './stv/verband.js';
import students from './stv/students.js';
import filter from './stv/filter.js';
import konto from './stv/konto.js';
+import group from './stv/group.js';
import kontakt from './stv/kontakt.js';
import prestudent from './stv/prestudent.js';
import status from './stv/status.js';
@@ -17,6 +18,7 @@ export default {
students,
filter,
konto,
+ group,
kontakt,
prestudent,
status,
diff --git a/public/js/api/stv/group.js b/public/js/api/stv/group.js
new file mode 100644
index 000000000..af6e6e122
--- /dev/null
+++ b/public/js/api/stv/group.js
@@ -0,0 +1,8 @@
+export default {
+ getGruppen(url, config, params) {
+ return this.$fhcApi.get('api/frontend/v1/stv/Gruppen/getGruppen/' + params.id);
+ },
+ deleteGroup(params) {
+ return this.$fhcApi.post('api/frontend/v1/stv/Gruppen/deleteGruppe/', params);
+ }
+}
\ No newline at end of file
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Gruppen.js b/public/js/components/Stv/Studentenverwaltung/Details/Gruppen.js
new file mode 100644
index 000000000..96fc6c772
--- /dev/null
+++ b/public/js/components/Stv/Studentenverwaltung/Details/Gruppen.js
@@ -0,0 +1,20 @@
+import GruppenList from './Gruppen/Gruppen.js';
+
+export default {
+ name: 'TabGroups',
+ components: {
+ GruppenList
+ },
+ props: {
+ modelValue: Object
+ },
+ methods: {
+ reload() {
+ this.$refs.gruppen.$refs.table.reloadTable();
+ }
+ },
+ template: `
+
+
+
`
+};
\ No newline at end of file
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Gruppen/Gruppen.js b/public/js/components/Stv/Studentenverwaltung/Details/Gruppen/Gruppen.js
new file mode 100644
index 000000000..67f422efb
--- /dev/null
+++ b/public/js/components/Stv/Studentenverwaltung/Details/Gruppen/Gruppen.js
@@ -0,0 +1,182 @@
+import {CoreFilterCmpt} from "../../../../filter/Filter.js";
+
+import ApiStvGroups from '../../../../../api/factory/stv/group.js';
+
+export default {
+ name: 'TblGroups',
+ components: {
+ CoreFilterCmpt,
+ },
+ inject: {
+ currentSemester: {
+ from: 'currentSemester',
+ },
+ },
+ props: {
+ student: Object
+ },
+ data() {
+ return {
+ tabulatorOptions: {
+ ajaxURL: 'dummy',
+ ajaxRequestFunc: () => this.$api.call(
+ ApiStvGroups.getGruppen(this.student.uid)
+ ),
+ ajaxResponse: (url, params, response) => response.data,
+ initialFilter: [
+ {field: "uid", type: "=", value: this.student.uid},
+ [
+ {field: "studiensemester_kurzbz", type: "=", value: this.currentSemester},
+ {field: "studiensemester_kurzbz", type: "=", value: null}
+ ]
+ ],
+ columns: [
+ {title: "Gruppe", field: "gruppe_kurzbz"},
+ {title: "Bezeichnung", field: "bezeichnung"},
+ {title: "Semester", field: "studiensemester_kurzbz"},
+ {
+ title: "automatisch generiert",
+ field: "generiert",
+ formatter: "tickCross",
+ hozAlign: "center",
+ formatterParams: {
+ tickElement: '',
+ crossElement: ''
+ }
+ },
+ {title: "UID", field: "uid"},
+ {
+ title: 'Aktionen', field: 'actions',
+ minWidth: 150, // Ensures Action-buttons will be always fully displayed
+ formatter: (cell, formatterParams, onRendered) => {
+ const container = document.createElement('div');
+ container.className = "d-flex gap-2";
+
+ const data = cell.getData();
+
+ const button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary btn-action';
+ button.innerHTML = '';
+ button.title = this.$p.t('ui', 'loeschen');
+ button.addEventListener('click', () =>
+ this.actionDeleteGroup(data.gruppe_kurzbz)
+ );
+ if (data.generiert)
+ button.disabled = true;
+ container.append(button);
+
+ return container;
+ },
+ frozen: true
+ },
+ ],
+ layout: 'fitDataFill',
+ height: 'auto',
+ selectable: true,
+ index: 'group_id',
+ persistenceID: 'stv-details-gruppe'
+ },
+ tabulatorEvents: [
+ {
+ event: 'tableBuilt',
+ handler: async () => {
+
+ await this.$p.loadCategory(['global', 'person', 'stv', 'ui', 'gruppenmanagement']);
+
+ let cm = this.$refs.table.tabulator.columnManager;
+
+ cm.getColumnByField('gruppe_kurzbz').component.updateDefinition({
+ title: this.$p.t('gruppenmanagement', 'gruppe')
+ });
+
+ cm.getColumnByField('bezeichnung').component.updateDefinition({
+ title: this.$p.t('ui', 'bezeichnung')
+ });
+
+ cm.getColumnByField('generiert').component.updateDefinition({
+ title: this.$p.t('gruppenmanagement', 'automatisch_generiert')
+ });
+
+ cm.getColumnByField('uid').component.updateDefinition({
+ title: this.$p.t('ui', 'student_uid')
+ });
+
+ //Interference with Filter if not commented out
+ /*
+ cm.getColumnByField('studiensemester_kurzbz').component.updateDefinition({
+ title: this.$p.t('lehre', 'studiensemester')
+ });*/
+
+ }
+ }
+ ],
+ }
+ },
+ methods: {
+ actionDeleteGroup(gruppe_kurzbz) {
+ this.$fhcAlert
+ .confirmDelete()
+ .then(result => result
+ ? gruppe_kurzbz
+ : Promise.reject({handled: true}))
+ .then(this.deleteGroup)
+ .catch(this.$fhcAlert.handleSystemError);
+
+ },
+ deleteGroup(gruppe_kurzbz) {
+ const group_id = {
+ id: this.student.uid,
+ gruppe_kurzbz: gruppe_kurzbz
+ };
+
+ return this.$api
+ .call(ApiStvGroups.deleteGroup(group_id))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ window.scrollTo(0, 0);
+ this.reload();
+ });
+ },
+ reload() {
+ this.$refs.table.reloadTable();
+ },
+ },
+ watch: {
+ currentSemester(newVal) {
+ if (newVal) {
+
+ this.$refs.table.tabulator.clearFilter(); // Clear old filters
+
+ this.$refs.table.tabulator.setFilter([
+ {field: "uid", type: "=", value: this.student.uid},
+ [
+ {field: "studiensemester_kurzbz", type: "=", value: newVal},
+ {field: "studiensemester_kurzbz", type: "=", value: null}
+ ]
+ ]);
+
+
+ }
+ },
+ student() {
+ this.$refs.table.reloadTable();
+ }
+ },
+ template: `
+
+
{{$p.t('stv', 'tab_groups')}}
+
+
+
+
+ `
+}
\ No newline at end of file
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php
index cb13d0c58..1c8c07f23 100644
--- a/system/phrasesupdate.php
+++ b/system/phrasesupdate.php
@@ -39869,6 +39869,90 @@ array(
)
)
),
+ //////////// FHC4 Phrases Gruppen Start ////////////
+ array(
+ 'app' => 'core',
+ 'category' => 'stv',
+ 'phrase' => 'tab_groups',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Gruppen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Groups',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'gruppenmanagement',
+ 'phrase' => 'gruppe',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Gruppe',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Group',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'gruppenmanagement',
+ 'phrase' => 'automatisch_generiert',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'automatisch generiert',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'automatically generated',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'gruppenmanagement',
+ 'phrase' => 'error_deleteGeneratedGroups',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Automatisch generierte Gruppenzuordnungen können nicht gelöscht werden.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Automatically generated group assignments cannot be deleted.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ )
+ //////////// FHC4 Phrases Gruppen End ////////////
+
+ ),
// FHC4 Phrases CleanUpTasks End
array(
'app' => 'core',