Compare commits

...

31 Commits

Author SHA1 Message Date
Harald Bamberger 0621564be7 Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-19 15:57:13 +01:00
Harald Bamberger 9e6c15a10d Merge branch 'bug-76010/StudVW_Archivieren_Ausbildungsvertrag_Aufgenommene' 2026-03-19 15:41:44 +01:00
ma0068 7603f8f12b Bugfix: use null instead of empty string, provide kuerzel 2026-03-18 14:32:09 +01:00
ma0068 17f94aabdf add loading skeletons for mitarbeiterHeader 2026-03-16 17:16:40 +01:00
Andreas Österreicher 96745525f1 Merge branch 'feature-71530/Error_beim_Archivieren_von_Diplomasupplement_STUDVW_Neu' 2026-03-16 11:28:56 +01:00
ma0068 c49e32c4ac use skeletons for all data in studentHeader 2026-03-16 11:26:23 +01:00
Andreas Österreicher 8d6e04ea77 Merge branch 'feature-61164/AbgabetoolQualityGates' 2026-03-16 11:09:52 +01:00
Andreas Österreicher d9e5acb52c Merge branch 'master' into feature-61164/AbgabetoolQualityGates 2026-03-16 11:08:44 +01:00
Andreas Österreicher e20ff52f5b Merge branch 'feature-75417/pep_finetuning' 2026-03-16 09:08:10 +01:00
Andreas Österreicher df05af98d2 FHB - Markierung von Outgoing ohne Endedatum 2026-03-16 09:01:33 +01:00
ma0068 df124db84a use primeVue Skeleton while loading Data for Semester, Verband and Gruppe 2026-03-13 12:40:29 +01:00
ma0068 29a4b4aadc use permission PERM_LOGGED for header, add condition one empty space for showing verband 2026-03-13 09:11:00 +01:00
ma0068 2682ea75ab use only one endpoint for detailheader 2026-03-12 16:58:48 +01:00
ma0068 b3a63a60e9 update phrase noTextInSem 2026-03-12 16:02:44 +01:00
Harald Bamberger a54dfaf0c7 Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:20:53 +01:00
Harald Bamberger 0764a597af do not allow foto editing in contract management 2026-03-12 15:18:26 +01:00
Harald Bamberger 595538d6bb Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:10:06 +01:00
Harald Bamberger 8c36fe585a filter component add missing this 2026-03-12 15:09:46 +01:00
Harald Bamberger a5329e5bba Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:03:56 +01:00
Harald Bamberger 37a79c4589 Merge branch 'master' into feature-71775/StudVW_ChangeSem_Behaviour 2026-03-12 15:03:28 +01:00
Harald Bamberger d4b81da437 fix filter component also consider tabulator 6 selectableRows option to determine if tabulator has selector 2026-03-12 14:34:42 +01:00
Harald Bamberger b91efb6189 stv notizperson fix permission check to be able to add notes to prestudents that do not have an uid 2026-03-12 13:56:03 +01:00
Harald Bamberger dd760f8210 finetune behavior 2026-03-12 11:10:13 +01:00
ma0068 dd87e893ba working version for relaod header and data without updateUrl 2026-03-11 17:13:59 +01:00
ma0068 127ce312ea reload Details and Detailheader without updateUrl 2026-03-10 08:28:11 +01:00
ma0068 2237e9f1b7 refactor detail Header, relaod array if semester changed, show no status if no status, phrase 2026-03-09 11:42:24 +01:00
ma0068 9b4fa132dc add status 2026-03-06 10:58:54 +01:00
Harald Bamberger daf332a102 add query_studiensemester_kurzbz to result of fetchStudents, fetchPrestudents and search 2026-03-06 10:39:37 +01:00
ma0048 f3986688f2 selfoverview:
nur aktive kategorien anzeigen
uebersetzungen hinzugefuegt
2026-02-26 12:50:37 +01:00
Alexei Karpenko f068b56083 PlausiIssueProducer: removed debugging output and die 2026-02-18 22:30:29 +01:00
Alexei Karpenko fa7a125727 Studierendenverwaltung archivieren: error message when no studentlehrverband found 2026-02-18 21:18:57 +01:00
14 changed files with 528 additions and 134 deletions
@@ -208,7 +208,6 @@ class Documents extends FHCAPI_Controller
$this->load->model('system/Vorlage_model', 'VorlageModel');
$result = $this->VorlageModel->load($xsl);
$this->addMeta("ress", $result);
$vorlage = current($this->getDataOrTerminateWithError($result));
if (!$vorlage)
show_404();
@@ -221,7 +220,7 @@ class Documents extends FHCAPI_Controller
'gedruckt' => true,
'insertamum' => date('c'),
'insertvon' => getAuthUID(),
'uid' => $this->input->post_get('uid') ?: '',
'uid' => $this->input->post_get('uid') ?: null,
'archiv' => true,
'signiert' => !!$sign_user,
'stud_selfservice' => $vorlage->stud_selfservice
@@ -251,6 +250,9 @@ class Documents extends FHCAPI_Controller
'studiensemester_kurzbz' => $ss,
'student_uid' => $akteData['uid']
]);
if (!hasData($result)) $this->terminateWithError($this->p->t("stv", "error_noLehrverbandAssigned"));
$res = current($this->getDataOrTerminateWithError($result));
$studiengang_kz = $res->studiengang_kz;
@@ -332,6 +334,7 @@ class Documents extends FHCAPI_Controller
if ($prestudent_id) {
$this->load->model('crm/prestudent_model', 'PrestudentModel');
$this->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addSelect('tbl_prestudent.*, UPPER(typ || kurzbz) AS kuerzel');
$result = $this->PrestudentModel->load($prestudent_id);
$prestudent = current($this->getDataOrTerminateWithError($result));
@@ -9,9 +9,10 @@ class Detailheader extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getHeader' => ['vertrag/mitarbeiter:r'],
'getPersonAbteilung' => ['vertrag/mitarbeiter:r'],
'getLeitungOrg' => ['vertrag/mitarbeiter:r'],
'getHeader' => self::PERM_LOGGED,
'getPersonAbteilung' => self::PERM_LOGGED,
'getLeitungOrg' => self::PERM_LOGGED,
'getSemesterStati' => self::PERM_LOGGED,
]);
}
@@ -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);
}
}
@@ -24,6 +24,7 @@ class NotizPerson extends Notiz_Controller
//Load Models
$this->load->model('person/Benutzer_model', 'BenutzerModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
//Permission checks for allowed Oes
if ($this->router->method == 'addNewNotiz')
@@ -38,7 +39,7 @@ class NotizPerson extends Notiz_Controller
{
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Person ID']), self::ERROR_TYPE_GENERAL);
}
$this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs);
$this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs);
}
if ( $this->router->method == 'updateNotiz')
@@ -59,7 +60,7 @@ class NotizPerson extends Notiz_Controller
$person_id = current($data)->person_id;
$allowedStgs = $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: [];
$this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs);
$this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs);
}
if ($this->router->method == 'deleteNotiz' )
@@ -78,7 +79,7 @@ class NotizPerson extends Notiz_Controller
}
$allowedStgs = $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: [];
$this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs);
$this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs);
}
}
@@ -99,44 +100,20 @@ class NotizPerson extends Notiz_Controller
}
//stv: if person has permission of one studiengang of person -> permission to add/update/delete Note
private function _checkIfBerechtigungForOneUidExists($person_id, $allowedStgs)
private function _checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs)
{
//get all studentUids of person_id
$result = $this->BenutzerModel->loadWhere(['person_id' => $person_id]);
$result = $this->PrestudentModel->loadWhere(['person_id' => $person_id]);
$data = $this->getDataOrTerminateWithError($result);
$checkarray = [];
foreach ($data as $item)
{
//check if isStudent
$result = $this->StudentModel->isStudent($item->uid);
$isStudent = $this->getDataOrTerminateWithError($result);
if($isStudent)
if(in_array($item->studiengang_kz, $allowedStgs))
{
$checkarray[] = $this->_checkAllowedStgsFromUid($item->uid, $allowedStgs);
return true;
}
}
if (!in_array(1, $checkarray))
return $this->terminateWithError($this->p->t('ui', 'error_keineBerechtigungStg'), self::ERROR_TYPE_GENERAL);
}
private function _checkAllowedStgsFromUid($student_uid, $allowedStgs)
{
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->loadWhere(['student_uid' => $student_uid]);
$data = $this->getDataOrTerminateWithError($result);
$studiengang_kz = current($data)->studiengang_kz;
if (!in_array($studiengang_kz, $allowedStgs))
{
return 0;
}
else
{
return 1;
}
$this->terminateWithError($this->p->t('ui', 'error_keineBerechtigungStg'), self::ERROR_TYPE_GENERAL);
}
}
@@ -144,6 +144,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');
@@ -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(
+2
View File
@@ -270,6 +270,8 @@ class LehreListHelper
} else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von > $stsemdatumvon || $row->von == '')) {
// if bis datum is not yet known but von is available already
$zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')';
} else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von <= $stsemdatumvon || $row->von == '') && ($row->bis == '' || $row->bis > date('Y-m-d'))){
$zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')';
}
+6
View File
@@ -34,4 +34,10 @@ export default {
url: 'api/frontend/v1/detailheader/detailheader/getLeitungOrg/' + oekurzbz,
};
},
getSemesterStati(prestudent_id){
return {
method: 'get',
url: 'api/frontend/v1/detailheader/detailheader/getSemesterStati/' + prestudent_id,
};
},
}
+216 -82
View File
@@ -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: {
@@ -38,6 +40,14 @@ export default {
'mitarbeiter',
].includes(value)
}
},
currentSemester: {
type: String,
default: ''
},
isLoading: { //if true, then parent isLoading
type: Boolean,
default: false
}
},
computed: {
@@ -60,8 +70,7 @@ export default {
},
hasTileUIDSlot() {
return !!this.$slots.uid
},
}
},
created(){
if (this.typeHeader === 'student') {
@@ -71,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);
@@ -86,13 +95,26 @@ 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: {},
isFetchingIssues: false
isFetchingIssues: false,
noCurrentStatus: false,
semesterStatiLoading: false,
leitungOrgLoading: false,
departmentDataLoading: false,
headerDataMaLoading: false
};
},
methods: {
@@ -108,29 +130,40 @@ export default {
});
},
getHeader(person_id) {
this.headerDataMaLoading = 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.headerDataMaLoading = 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);
@@ -179,6 +212,33 @@ export default {
} else {
return 'data:image/jpeg;base64,' + foto;
}
},
getSemesterStati(prestudent_id){
this.semesterStatiLoading = true;
this.$api
.call(ApiDetailHeader.getSemesterStati(prestudent_id))
.then(result => {
this.semesterStati = result.data;
this.setNoCurrentStatus();
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.semesterStatiLoading = false;
});
},
setNoCurrentStatus() {
if(!Array.isArray(this.semesterStati))
{
this.noCurrentStatus = false;
}
if(!this.semesterStati.some(item => item.studiensemester_kurzbz === this.currentSemester)) {
this.noCurrentStatus = true;
}
else
{
this.noCurrentStatus = false;
}
}
},
template: `
@@ -200,7 +260,6 @@ export default {
</modal-upload-foto>
<template v-if="typeHeader==='student'">
<div
v-for="person in headerData"
:key="person.person_id"
@@ -236,62 +295,100 @@ export default {
<small class="text-muted">{{person.uid}}</small>
</div>
<div v-if="headerData.length == 1">
<div class="d-flex align-items-center gap-3">
<h2 class="h4">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}
<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<div v-if="headerData.length == 1">
<div v-if="!isLoading" class="d-flex align-items-center gap-3">
<h2 class="h4">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}
<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<div v-else class="d-flex align-items-center gap-3">
<pv-skeleton width="15rem" height="2rem" borderRadius="16px"></pv-skeleton>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<h5 class="h6">
<strong class="text-muted">{{$p.t('lehre', 'studiengang')}} </strong>
{{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}})
<strong v-if="headerData[0].semester" class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
{{headerData[0].semester}}
<strong v-if="headerData[0].verband" class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
</h5>
<h5 class="h6 d-flex align-items-center flex-wrap gap-1">
<strong class="text-muted">{{$p.t('lehre', 'studiengang')}} </strong>
<span v-if="!isLoading">
{{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}})
</span>
<span v-else>
<pv-skeleton width="10rem"></pv-skeleton>
</span>
<template v-if="!semesterStatiLoading">
<strong v-if="headerData[0].semester != null" class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
{{headerData[0].semester}}
<strong v-if="headerData[0].gruppe !== null && headerData[0].verband != ' '" class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe !== null && headerData[0].gruppe != ' '" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
</template>
<template v-else>
<strong class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
<pv-skeleton size="1rem" class="mr-2"></pv-skeleton>
<strong class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
<pv-skeleton size="1rem" class="mr-2"></pv-skeleton>
<strong class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
<pv-skeleton size="1rem" class="mr-2"></pv-skeleton>
</template>
</h5>
<h5 class="h6">
<strong class="text-muted">Email </strong>
<span>
<a :href="'mailto:'+headerData[0]?.mail_intern">{{headerData[0].mail_intern}}</a>
</span>
<strong v-if="headerData[0].statusofsemester" class="text-muted"> | Status </strong>
{{headerData[0].statusofsemester}}
</h5>
</div>
<div v-if="headerData.length == 1" class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
<div class="d-flex py-1">
<div class="px-2" style="min-width: 100px;">
<slot name="issues"></slot>
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueGammaTile"></slot></h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueBetaTile"></slot></h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueAlphaTile"></slot></h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted text-center"><slot name="uid"></slot></h6>
</div>
<h5 class="h6 d-flex align-items-center flex-wrap gap-1">
<strong class="text-muted">Email </strong>
<span v-if="!isLoading">
<a :href="'mailto:'+headerData[0]?.mail_intern">{{headerData[0].mail_intern}}</a>
</span>
<span v-else>
<pv-skeleton width="10rem"></pv-skeleton>
</span>
<strong class="text-muted"> | Status </strong>
<span v-if="noCurrentStatus">
<strong class="text-danger">{{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }}</strong>
</span>
<span v-else>
{{headerData[0].statusofsemester}}
</span>
</h5>
</div>
<div v-if="headerData.length == 1" class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
<div class="d-flex py-1">
<div class="px-2" style="min-width: 100px;">
<slot name="issues"></slot>
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueGammaTile"></slot>
</h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueBetaTile"></slot>
</h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueAlphaTile"></slot>
</h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="uid"></slot>
</h6>
</div>
</div>
</div>
</template>
@@ -330,27 +427,51 @@ export default {
<!--show Ma-Details-->
<div class="col-md-9 text-nowrap mt-2">
<h4>{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}<span v-if="headerDataMa?.titelpost">, </span> {{headerDataMa.titelpost}}</h4>
<strong class="text-muted">{{departmentData.organisationseinheittyp_kurzbz}}</strong>
{{departmentData.bezeichnung}}
<span v-if="leitungData.uid"> | </span>
<strong v-if="leitungData.uid" class="text-muted">Vorgesetzte*r </strong>
<a href="#" @click.prevent="goToLeitung">
{{leitungData.titelpre}} {{leitungData.vorname}} {{leitungData.nachname}}
</a>
<p>
<strong class="text-muted">Email </strong>
<span v-if="headerDataMa && (headerDataMa.alias === undefined || headerDataMa.alias === null || headerDataMa.alias === '')">
<a :href="'mailto:' + mitarbeiter_uid + '@' + domain">
{{ mitarbeiter_uid }}@{{ domain }}
<h4 v-if="!headerDataMaLoading">{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}<span v-if="headerDataMa?.titelpost">, </span> {{headerDataMa.titelpost}}</h4>
<h4 v-else><pv-skeleton width="15rem" height="2rem" borderRadius="16px"></pv-skeleton></h4>
<div class="d-flex align-items-center flex-wrap gap-1">
<strong class="text-muted">{{departmentData.organisationseinheittyp_kurzbz}}</strong>
<span v-if="!departmentDataLoading">
{{departmentData.bezeichnung}}
</span>
<span v-else>
<pv-skeleton width="12rem"></pv-skeleton>
</span>
<span v-if="leitungData.uid"> | </span>
<strong v-if="leitungData.uid" class="text-muted">Vorgesetzte*r </strong>
<span v-if="!leitungOrgLoading">
<a href="#" @click.prevent="goToLeitung">
{{leitungData.titelpre}} {{leitungData.vorname}} {{leitungData.nachname}}
</a>
</span>
<span v-else>
<a :href="'mailto:'+headerDataMa?.alias+'@'+domain">{{headerDataMa.alias}}@{{domain}}</a>
<pv-skeleton width="5rem"></pv-skeleton>
</span>
<span v-if="headerDataMa?.telefonklappe" class="mb-2"> | <strong class="text-muted">DW </strong>{{headerDataMa?.telefonklappe}}</span>
</p>
</div>
<div class="d-flex align-items-center gap-2 flex-nowrap">
<div class="d-flex align-items-center gap-1">
<strong class="text-muted">Email</strong>
<template v-if="!headerDataMaLoading">
<a :href="'mailto:' + (headerDataMa?.alias || mitarbeiter_uid) + '@' + domain">
{{ (headerDataMa?.alias || mitarbeiter_uid) + '@' + domain }}
</a>
</template>
<pv-skeleton v-else width="10rem"></pv-skeleton>
</div>
<div v-if="headerDataMa?.telefonklappe" class="d-flex align-items-center gap-1">
<span>|</span>
<strong class="text-muted">DW</strong>
<template v-if="!headerDataMaLoading">
{{ headerDataMa.telefonklappe }}
</template>
<pv-skeleton v-else width="4rem"></pv-skeleton>
</div>
</div>
<slot name="tag"></slot>
</div>
<div class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
@@ -360,20 +481,33 @@ export default {
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueGammaTile"></slot></h6>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueGammaTile"></slot>
</h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueBetaTile" :valueBetaTile="valueBetaTile"></slot></h6>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueBetaTile"></slot>
</h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueAlphaTile"></slot></h6>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueAlphaTile"></slot>
</h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted text-center"><slot name="uid"></slot></h6>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="uid"></slot>
</h6>
</div>
</div>
</div>
@@ -212,6 +212,7 @@ export default {
'url_studiengang': function (newVal, oldVal) {
if (newVal !== oldVal) {
this.checkUrlStudiengang();
this.$refs.stvList.clearSelection();
}
},
'url_mode': function () {
@@ -2,12 +2,18 @@ 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
export default {
name: "DetailsPrestudent",
inject: {
currentSemester: {
from: 'currentSemester',
},
},
components: {
FhcTabs,
FhcHeader
@@ -15,7 +21,8 @@ export default {
data() {
return {
configStudent: {},
configStudents: {}
configStudents: {},
localStudent: null
};
},
props: {
@@ -40,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 child
},
tile_PersId(){
let tile = this.students[0].person_id != null ? this.students[0].person_id : '-';
return tile;
@@ -57,6 +67,21 @@ export default {
'$p.user_language.value'(n, o) {
if (n !== o && o !== undefined)
this.loadConfig();
},
currentSemester(newVal) {
if (
Array.isArray(this.students) &&
this.students.length === 1 &&
newVal !== this.students[0].query_studiensemester_kurzbz
) {
this.reloadDataStudent();
}
else {
this.localStudent = null;
}
},
students() {
this.localStudent = null;
}
},
methods: {
@@ -78,6 +103,20 @@ export default {
if (this.$refs.tabs?.$refs?.current?.reload)
this.$refs.tabs.$refs.current.reload();
},
reloadDataStudent(){
this.localStudent = null;
const studentArr = this.students;
if (!studentArr || !studentArr.length) {
return;
}
this.$api
.call(ApiStudent.uid(studentArr[0].uid, this.currentSemester))
.then(result => {
this.localStudent = result.data;
});
},
reloadList() {
this.$emit('reload');
},
@@ -92,10 +131,12 @@ export default {
</div>
<div v-else-if="configStudent && configStudents" class="d-flex flex-column h-100">
<fhc-header
:headerData="students"
:headerData="localStudent || students"
:currentSemester="currentSemester"
typeHeader="student"
@reload="reloadList"
fotoEditable
:isLoading="isLoading"
>
<template #uid>{{students[0].uid}}</template>
<template #titleAlphaTile>PersID</template>
@@ -107,9 +148,9 @@ export default {
</fhc-header>
<fhc-tabs
v-if="students.length == 1"
ref="tabs"
ref="tabs"
:useprimevue="true"
:modelValue="students[0]"
:modelValue="(Array.isArray(localStudent) && localStudent[0]) || students[0]"
:config="config"
:default="$route.params.tab"
style="flex: 1 1 0%; height: 0%"
@@ -391,15 +391,17 @@ export default {
actionNewPrestudent() {
this.$refs.new.open();
},
rowSelectionChanged(data, rows) {
rowSelectionChanged(data, rows, selected, deselected) {
this.selectedcount = data.length;
this.lastSelected = this.selected;
if(selected.length > 0 || deselected.length > 0){
this.lastSelected = this.selected;
this.$emit('update:selected', data);
}
//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);
},
autoSelectRows(data) {
if (Array.isArray(this.lastSelected) && this.lastSelected.length){
@@ -546,6 +548,10 @@ export default {
this.changeFocus(this.focusObj, el);
}
},
clearSelection(){
this.lastSelected = [];
this.$emit('update:selected',[]);
},
//methods tags
addedTag(addedTag)
{
@@ -71,7 +71,6 @@ export default {
:mitarbeiter_uid="this.mitarbeiter_uid"
typeHeader="mitarbeiter"
:domain="config.domain"
fotoEditable
@redirectToLeitung="handleSelection"
>
<template #uid>{{tile_MaUid}}</template>
+2 -2
View File
@@ -280,7 +280,7 @@ export const CoreFilterCmpt = {
});
}
if (tabulatorOptions.selectable || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length))
if (tabulatorOptions.selectable || tabulatorOptions.selectableRows || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length))
this.tabulatorHasSelector = true;
if (this.idField) {
@@ -358,7 +358,7 @@ export const CoreFilterCmpt = {
}
},
_updateTabulator() {
this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length;
this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.tabulatorOptions.selectableRows || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length;
this.tabulator.setColumns(this.filteredColumns);
this.tabulator.setData(this.filteredData);
this._setHeaderFilter()
+212 -4
View File
@@ -3903,6 +3903,172 @@ $phrases = array(
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'geplZeitraum',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'geplanter Zeitraum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'planned Period',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'bitteAuswaehlen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte auswählen...',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please select...',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'hinweisLehrende',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Hinweis für Lehrende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Note for Lecturers',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'lehreinheiten',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lehreinheiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Teaching Units',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'lead',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Leitung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Lead',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'teamlead',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Team / Leitung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Team / Lead',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'ausblick_lvplanung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ausblick auf Ihre mögliche LV-Planung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Preview of Your Potential Course Planning',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'detailselfoverview',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '<b>Achtung:</b> die vorliegenden Informationen stellen eine Vorabplanung dar und sind als Anfrage an Sie gedacht. <br /><br />
Die Beauftragung der tatsächlichen Lehrveranstaltungen erfolgt durch Ihre Kompetenzfeldleitung. <br /><br />
Ihre aktuell gültigen Lehraufträge und den LV Plan des aktuellen Semesters (Termine) finden Sie wie gewohnt unter „mein CIS“ -> „LV-Plan Hauptmenü“ bzw. „Lehrauftragsverwaltung“.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '<b>Please note:</b> The information provided represents a preliminary planning and is intended as an inquiry to you.<br /><br />
The official assignment of the actual courses will be carried out by your Competence Field Manager.<br /><br />
Your currently valid teaching assignments and the course schedule for the current semester (dates) can be found as usual under “My CIS” “Schedule Main Menu” or “Teaching Assignment Administration”',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
@@ -44726,9 +44892,9 @@ array(
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Für den gesamten Studiengang verbindlicher Termin.
Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich,
'text' => "Für den gesamten Studiengang verbindlicher Termin.
Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich,
haben Studierende bei der Studiengangsassistenz um eine Korrektur dieses Termins anzusuchen.",
'description' => '',
'insertvon' => 'system'
@@ -44919,7 +45085,7 @@ array(
array(
'sprache' => 'German',
'text' => "Verspätete Projektabgabe ist bei Terminen, welche von der Studiengangsassistenz für den gesamten Studiengang fixiert wurden nicht erlaubt!
Um einen Endupload durchführen zu können, müssen Sie ein positiv benotetes Quality Gate 1 & Quality Gate 2 in der relevanten Projektarbeit absolviert haben.",
'description' => '',
'insertvon' => 'system'
@@ -57008,6 +57174,48 @@ I have been informed that I am under no obligation to consent to the transmissio
)
),
// ### Refactor Messages END
//
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'error_noLehrverbandAssigned',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'StudentIn ist in diesem Semester keinem Lehrverband zugeteilt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Student has no assignment to any teaching association',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'textNoStatusInSem',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kein Status im {sem}',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No status in {sem}',
'description' => '',
'insertvon' => 'system'
)
)
),
);