mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 20:29:29 +00:00
518 lines
17 KiB
JavaScript
518 lines
17 KiB
JavaScript
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,
|
|
PvSkeleton
|
|
},
|
|
props: {
|
|
headerData: {
|
|
type: Object,
|
|
required: false
|
|
},
|
|
person_id: {
|
|
type: Number,
|
|
required: false
|
|
},
|
|
mitarbeiter_uid: {
|
|
type: String,
|
|
required: false
|
|
},
|
|
fotoEditable: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: false
|
|
},
|
|
domain: {
|
|
type: String,
|
|
required: false
|
|
},
|
|
typeHeader: {
|
|
type: String,
|
|
default: 'student',
|
|
validator(value) {
|
|
return [
|
|
'student',
|
|
'mitarbeiter',
|
|
].includes(value)
|
|
}
|
|
},
|
|
currentSemester: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
isLoading: { //if true, then parent isLoading
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
computed: {
|
|
appRoot() {
|
|
return FHC_JS_DATA_STORAGE_OBJECT.app_root;
|
|
},
|
|
validatedHeaderData() {
|
|
if (this.typeHeader === 'student') return this.headerData;
|
|
if (this.typeHeader === 'mitarbeiter') return this.person_id;
|
|
return null;
|
|
},
|
|
hasTileAlphaSlot() {
|
|
return !!this.$slots.titleAlphaTile
|
|
},
|
|
hasTileBetaSlot() {
|
|
return !!this.$slots.titleBetaTile
|
|
},
|
|
hasTileGammaSlot() {
|
|
return !!this.$slots.titleGammaTile
|
|
},
|
|
hasTileUIDSlot() {
|
|
return !!this.$slots.uid
|
|
}
|
|
},
|
|
created(){
|
|
if (this.typeHeader === 'student') {
|
|
if (!this.headerData) {
|
|
throw new Error('[DetailHeader] "headerData" is required.')
|
|
}
|
|
} 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 required.'
|
|
)
|
|
}
|
|
this.loadHeaderData(this.person_id, this.mitarbeiter_uid);
|
|
}
|
|
},
|
|
watch: {
|
|
person_id: {
|
|
handler(newVal) {
|
|
if (newVal) {
|
|
this.loadHeaderData(newVal, this.mitarbeiter_uid);
|
|
}
|
|
},
|
|
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,
|
|
noCurrentStatus: false,
|
|
semesterStatiLoading: false,
|
|
leitungOrgLoading: false,
|
|
departmentDataLoading: false,
|
|
headerDataMaLoading: false
|
|
};
|
|
},
|
|
methods: {
|
|
loadHeaderData(person_id, mitarbeiter_uid){
|
|
this.getHeader(person_id);
|
|
this.loadDepartmentData(mitarbeiter_uid)
|
|
.then(() => {
|
|
// Call getLeitungOrg only after departmentData is loaded
|
|
this.getLeitungOrg(this.departmentData.oe_kurzbz);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error loading header data: ", error);
|
|
});
|
|
},
|
|
getHeader(person_id) {
|
|
this.headerDataMaLoading = true;
|
|
return this.$api
|
|
.call(ApiDetailHeader.getHeader(person_id))
|
|
.then(result => {
|
|
this.headerDataMa = result.data;
|
|
})
|
|
.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)
|
|
.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)
|
|
.finally(() => {
|
|
this.leitungOrgLoading = false;
|
|
});
|
|
},
|
|
async goToLeitung() {
|
|
this.loadHeaderData(this.leitungData.person_id, this.leitungData.uid);
|
|
this.redirectToLeitung();
|
|
},
|
|
redirectToLeitung() {
|
|
this.$emit('redirectToLeitung', {
|
|
person_id: this.leitungData.person_id,
|
|
uid: this.leitungData.uid
|
|
});
|
|
},
|
|
showModal(person_id){
|
|
this.$refs.modalFoto.open(person_id);
|
|
},
|
|
showDeleteModal(person_id){
|
|
this.$fhcAlert
|
|
.confirmDelete()
|
|
.then(result => result
|
|
? person_id
|
|
: Promise.reject({handled: true}))
|
|
.then(this.deleteFoto)
|
|
.catch(this.$fhcAlert.handleSystemError);
|
|
},
|
|
deleteFoto(person_id){
|
|
return this.$api
|
|
.call(ApiHandleFoto.deleteFoto(person_id))
|
|
.then(result => {
|
|
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
|
|
})
|
|
.catch(this.$fhcAlert.handleSystemError)
|
|
.finally(()=> {
|
|
this.reload();
|
|
});
|
|
},
|
|
reload() {
|
|
if(this.person_id) {
|
|
this.loadHeaderData(this.person_id, this.mitarbeiter_uid);
|
|
}
|
|
else {
|
|
this.$emit('reload');
|
|
}
|
|
},
|
|
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){
|
|
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: `
|
|
<div class="core-header d-flex justify-content-start align-items-center w-100 overflow-auto pb-2 gap-3" style="max-height:9rem; min-width: 37.5rem;">
|
|
|
|
<modal-upload-foto
|
|
v-if="person_id"
|
|
ref="modalFoto"
|
|
:person_id="person_id"
|
|
@reload="reload"
|
|
>
|
|
</modal-upload-foto>
|
|
<modal-upload-foto
|
|
v-else
|
|
ref="modalFoto"
|
|
:person_id="headerData[0].person_id"
|
|
@reload="reload"
|
|
>
|
|
</modal-upload-foto>
|
|
|
|
<template v-if="typeHeader==='student'">
|
|
<div
|
|
v-for="person in headerData"
|
|
:key="person.person_id"
|
|
class="foto-container d-flex flex-column align-items-center h-100 position-relative d-inline-block"
|
|
>
|
|
<img
|
|
class="d-block rounded"
|
|
style="height: 75px;"
|
|
alt="Profilbild"
|
|
:src="getFotoSrc(person.foto)"
|
|
/>
|
|
|
|
<template v-if="person.foto_sperre">
|
|
<i class="fa fa-lock text-secondary bg-light rounded d-flex justify-content-center align-items-center position-absolute top-0 end-0"
|
|
style="z-index: 1; font-size: 1rem; width: 1.25rem; height: 1.25rem;"
|
|
>
|
|
</i>
|
|
</template>
|
|
<template v-if="fotoEditable">
|
|
<button
|
|
type="button"
|
|
class="fotoedit buttonleft btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
|
|
@click="showDeleteModal(headerData[0].person_id)">
|
|
<i class="fa fa-xmark"></i>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="fotoedit buttonright btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
|
|
@click="showModal(headerData[0].person_id)">
|
|
<i class="fa fa-pen"></i>
|
|
</button>
|
|
</template>
|
|
<small class="text-muted">{{person.uid}}</small>
|
|
</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 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 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>
|
|
|
|
<template v-if="typeHeader==='mitarbeiter'">
|
|
|
|
<div class="foto-container col-md-2 d-flex justify-content-start align-items-center w-30 pb-3 gap-3 mt-3 position-relative" style="max-height: 8rem; max-width: 6rem; overflow: hidden;">
|
|
<img
|
|
class="d-block w-100 h-100 rounded"
|
|
style="height: 84px; object-fit: contain;"
|
|
alt="Profilbild"
|
|
:src="getFotoSrc(headerDataMa.foto)"
|
|
/>
|
|
<template v-if="headerDataMa.foto_sperre">
|
|
<i
|
|
class=" fa fa-lock text-secondary bg-light rounded d-flex justify-content-center align-items-center position-absolute top-0 end-0"
|
|
style="z-index: 1; font-size: 1rem; width: 1.25rem; height: 1.25rem;"
|
|
></i>
|
|
</template>
|
|
<template v-if="fotoEditable">
|
|
<button
|
|
type="button"
|
|
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
|
|
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
|
|
@click="showDeleteModal(person_id)">
|
|
<i class="fa fa-xmark"></i>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
|
|
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
|
|
@click="showModal(person_id)">
|
|
<i class="fa fa-pen"></i>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
|
|
<!--show Ma-Details-->
|
|
<div class="col-md-9 text-nowrap mt-2">
|
|
<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>
|
|
<pv-skeleton width="5rem"></pv-skeleton>
|
|
</span>
|
|
</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">
|
|
<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>
|
|
</div>
|
|
`
|
|
}
|