template cleanup profil

This commit is contained in:
Johann Hoffmann
2024-11-21 17:32:34 +01:00
parent 1a6b30588d
commit 16cfb0a3eb
12 changed files with 1122 additions and 1345 deletions
@@ -17,61 +17,50 @@ export default {
<div class="gy-2 row justify-content-center align-items-center">
<!-- column 1 in the address row -->
<!-- column 1 in the address row -->
<div class="col-1 text-center">
<i class="fa fa-location-dot fa-lg" style="color:#00649C "></i>
<i class="fa fa-location-dot fa-lg" style="color:#00649C "></i>
</div>
<div class="col-11 col-sm-8 col-xl-11 col-xxl-8 order-1">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('person','strasse')}}</div>
<span class="form-underline-content">{{data.strasse}} </span>
</div>
</div>
<!-- column 2 in the address row -->
<div class="offset-1 offset-sm-0 offset-xl-1 offset-xxl-0 order-2 order-sm-4 order-xl-2 order-xxl-4 col-11 col-sm-5 col-xl-11 col-xxl-5 ">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('global','typ')}}</div>
<span class="form-underline-content">{{data.typ}} </span>
</div>
</div>
<div class="offset-1 order-3 order-sm-3 col-11 col-sm-6 col-xl-7 col-xxl-6 ">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('person','ort')}}</div>
<span class="form-underline-content">{{data.ort}} </span>
<div class="form-underline-titel">{{$p.t('person','strasse')}}</div>
<span class="form-underline-content">{{data.strasse}} </span>
</div>
</div>
<div class=" offset-1 offset-sm-0 order-4 order-sm-2 order-xl-4 order-xxl-2 col-11 col-sm-3 col-xl-4 col-xxl-3 ">
<!-- column 2 in the address row -->
<div class="offset-1 offset-sm-0 offset-xl-1 offset-xxl-0 order-2 order-sm-4 order-xl-2 order-xxl-4 col-11 col-sm-5 col-xl-11 col-xxl-5">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('person','plz')}}</div>
<span class="form-underline-content">{{data.plz}} </span>
<div class="form-underline-titel">{{$p.t('global','typ')}}</div>
<span class="form-underline-content">{{data.typ}} </span>
</div>
</div>
<div class="offset-1 order-3 order-sm-3 col-11 col-sm-6 col-xl-7 col-xxl-6">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('person','ort')}}</div>
<span class="form-underline-content">{{data.ort}} </span>
</div>
</div>
<div class=" offset-1 offset-sm-0 order-4 order-sm-2 order-xl-4 order-xxl-2 col-11 col-sm-3 col-xl-4 col-xxl-3">
<div class="form-underline">
<div class="form-underline-titel">{{$p.t('person','plz')}}</div>
<span class="form-underline-content">{{data.plz}} </span>
</div>
</div>
<div v-if="withZustelladresse" class="order-5 offset-1 col-11">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('person','zustelladresse')}}</div>
<div class="ms-2 form-check ">
<input class="form-check-input" type="checkbox" @click.prevent :checked="data.zustelladresse" >
<div class="form-underline">
<div class="form-underline-titel">{{$p.t('person','zustelladresse')}}</div>
<div class="ms-2 form-check ">
<input class="form-check-input" type="checkbox" @click.prevent :checked="data.zustelladresse" >
</div>
</div>
</div>
</div>
</div>
</div>
`
`
};
@@ -1,212 +1,197 @@
import EditProfil from "../ProfilModal/EditProfil.js";
//? EditProfil is the modal used to edit the profil updates
export default {
components: { EditProfil },
props: {
data: {
type: Object,
},
},
components: {EditProfil},
props: {
data: {
type: Object,
},
},
inject: [
"getZustellkontakteCount",
"getZustelladressenCount",
"profilUpdateStates",
"profilUpdateTopic",
],
inject: [
"getZustellkontakteCount",
"getZustelladressenCount",
"profilUpdateStates",
"profilUpdateTopic",
],
emits: ["fetchUpdates"],
emits: ["fetchUpdates"],
data() {
return {
showUpdateModal: false,
content: null,
editProfilTitle: this.$p.t("profil", "profilBearbeiten"),
};
},
data() {
return {
showUpdateModal: false,
content: null,
editProfilTitle: this.$p.t("profil", "profilBearbeiten"),
};
},
methods: {
hideEditProfilModal: function () {
//? checks the editModal component property result, if the user made a successful request or not
if (this.$refs.updateEditModal.result) {
this.$emit("fetchUpdates");
} else {
// when modal was closed without submitting request
}
this.showUpdateModal = false;
},
methods: {
hideEditProfilModal: function () {
//? checks the editModal component property result, if the user made a successful request or not
if (this.$refs.updateEditModal.result) {
this.$emit("fetchUpdates");
} else {
// when modal was closed without submitting request
}
this.showUpdateModal = false;
},
async showEditProfilModal(updateRequest) {
async showEditProfilModal(updateRequest) {
let view = this.getView(updateRequest.topic, updateRequest.status);
let data = null;
let content = null;
let files = null;
let withFiles = false;
let view = this.getView(updateRequest.topic, updateRequest.status);
if (view === "TextInputDokument") {
data = {
titel: updateRequest.topic,
value: updateRequest.requested_change.value,
};
const filesFromDatabase =
await this.$fhcApi.factory.profilUpdate.getProfilRequestFiles(
updateRequest.profil_update_id
).then((res) => {
return res.data;
});
let data = null;
let content = null;
let files = null;
let withFiles = false;
files = filesFromDatabase;
if (files) {
withFiles = true;
}
} else {
data = updateRequest.requested_change;
}
if (view === "TextInputDokument") {
data = {
titel: updateRequest.topic,
value: updateRequest.requested_change.value,
};
content = {
updateID: updateRequest.profil_update_id,
view: view,
data: data,
withFiles: withFiles,
topic: updateRequest.topic,
files: files,
};
const filesFromDatabase =
await this.$fhcApi.factory.profilUpdate.getProfilRequestFiles(
updateRequest.profil_update_id
).then((res) => {
return res.data;
});
if (view === "EditAdresse") {
const isMitarbeiter = await this.$fhcApi.factory.profil.isMitarbeiter(updateRequest.uid).then((res)=>res.data);
files = filesFromDatabase;
if (files) {
withFiles = true;
}
} else {
data = updateRequest.requested_change;
}
if (isMitarbeiter) {
content["isMitarbeiter"] = isMitarbeiter;
}
}
content = {
updateID: updateRequest.profil_update_id,
view: view,
data: data,
withFiles: withFiles,
topic: updateRequest.topic,
files: files,
};
//? adds the status information if the profil update request was rejected or accepted
if (updateRequest.status !== this.profilUpdateStates["Pending"]) {
content["status"] = updateRequest.status;
content["status_message"] = updateRequest.status_message;
content["status_timestamp"] = updateRequest.status_timestamp;
}
if (view === "EditAdresse") {
//? update data of the reactive content
this.content = content;
this.editProfilTitle = updateRequest.topic;
const isMitarbeiter = await this.$fhcApi.factory.profil.isMitarbeiter(updateRequest.uid).then((res) => res.data);
//? only show the popup if also the right content is available
if (content) {
this.showUpdateModal = true;
// after a state change, wait for the DOM updates to complete
Vue.nextTick(() => {
this.$refs.updateEditModal.show();
});
}
},
if (isMitarbeiter) {
content["isMitarbeiter"] = isMitarbeiter;
}
}
deleteRequest: function (item) {
this.$fhcApi.factory.profilUpdate.deleteProfilRequest(item.profil_update_id).then(
(res) => {
if (res.data.error) {
//? open alert
console.error("error happened",res.data);
} else {
this.$emit("fetchUpdates");
}
}
);
},
getView: function (topic, status) {
if (!(status === this.profilUpdateStates["Pending"])) {
return "Status";
}
//? adds the status information if the profil update request was rejected or accepted
if (updateRequest.status !== this.profilUpdateStates["Pending"]) {
content["status"] = updateRequest.status;
content["status_message"] = updateRequest.status_message;
content["status_timestamp"] = updateRequest.status_timestamp;
}
switch (topic) {
case this.profilUpdateTopic["Private Kontakte"]:
return "EditKontakt";
case this.profilUpdateTopic["Add Kontakt"]:
return "EditKontakt";
case this.profilUpdateTopic["Delete Kontakt"]:
return "Kontakt";
case this.profilUpdateTopic["Private Adressen"]:
return "EditAdresse";
case this.profilUpdateTopic["Add Adresse"]:
return "EditAdresse";
case this.profilUpdateTopic["Delete Adresse"]:
return "Adresse";
default:
return "TextInputDokument";
}
},
//? update data of the reactive content
this.content = content;
this.editProfilTitle = updateRequest.topic;
},
created() {
},
//? only show the popup if also the right content is available
if (content) {
this.showUpdateModal = true;
// after a state change, wait for the DOM updates to complete
Vue.nextTick(() => {
this.$refs.updateEditModal.show();
});
}
},
computed: {},
deleteRequest: function (item) {
this.$fhcApi.factory.profilUpdate.deleteProfilRequest(item.profil_update_id).then(
(res) => {
if (res.data.error) {
//? open alert
console.error("error happened", res.data);
} else {
this.$emit("fetchUpdates");
}
}
);
},
template: /*html*/ `
<div class="card " >
getView: function (topic, status) {
if (!(status === this.profilUpdateStates["Pending"])) {
return "Status";
}
switch (topic) {
case this.profilUpdateTopic["Private Kontakte"]:
return "EditKontakt";
case this.profilUpdateTopic["Add Kontakt"]:
return "EditKontakt";
case this.profilUpdateTopic["Delete Kontakt"]:
return "Kontakt";
case this.profilUpdateTopic["Private Adressen"]:
return "EditAdresse";
case this.profilUpdateTopic["Add Adresse"]:
return "EditAdresse";
case this.profilUpdateTopic["Delete Adresse"]:
return "Adresse";
default:
return "TextInputDokument";
}
},
},
created() {
},
computed: {},
template: /*html*/ `
<div class="card">
<edit-profil v-if="showUpdateModal" ref="updateEditModal" @hideBsModal="hideEditProfilModal" :value="content" :title="editProfilTitle"></edit-profil>
<div class="card-header">
{{$p.t('profilUpdate','profilUpdates')}}
</div>
<div class="card-body" >
<div class="table-responsive text-nowrap">
<table class="m-0 table table-hover align-middle">
<thead >
<tr >
<th scope="col">{{$p.t('profilUpdate','topic')}}</th>
<th scope="col">{{$p.t('global','status')}}</th>
<th scope="col">{{$p.t('global','datum')}}</th>
<th class="text-center" scope="col">{{$p.t('ui','aktion')}}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data" :style="item.status==profilUpdateStates['Accepted']?'background-color:lightgreen':item.status===profilUpdateStates['Rejected']?'background-color:lightcoral':''">
<td class="align-middle text-wrap ">{{item.topic}}</td>
<td class="align-middle " >{{item.status}}</td>
<td class="align-middle">{{item.status_timestamp?item.status_timestamp:item.insertamum}}</td>
<template v-if="item.status === profilUpdateStates['Pending']">
<td>
<div class="d-flex flex-row justify-content-evenly">
<template v-if="item.topic.toLowerCase().includes('delete')">
<div class="align-middle text-center"><i role="button" @click="showEditProfilModal(item)" class="fa fa-eye"></i></div>
</template>
<template v-else >
<div class="align-middle text-center" ><i style="color:#00639c" @click="showEditProfilModal(item)" role="button" class="fa fa-edit"></i></div>
</template>
<div class="align-middle text-center"><i style="color:red" role="button" @click="deleteRequest(item)" class="fa fa-trash"></i></div>
</div>
</td>
</template>
<template v-else>
<td class="align-middle text-center">
<div class="d-flex flex-row justify-content-evenly">
<i role="button" @click="showEditProfilModal(item)" class="fa fa-eye"></i>
</div>
</td>
</template>
</tr>
</tbody>
</table>
<div class="card-header">{{$p.t('profilUpdate','profilUpdates')}}</div>
<div class="card-body">
<div class="table-responsive text-nowrap">
<table class="m-0 table table-hover align-middle">
<thead>
<tr>
<th scope="col">{{$p.t('profilUpdate','topic')}}</th>
<th scope="col">{{$p.t('global','status')}}</th>
<th scope="col">{{$p.t('global','datum')}}</th>
<th class="text-center" scope="col">{{$p.t('ui','aktion')}}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data" :style="item.status==profilUpdateStates['Accepted']?'background-color:lightgreen':item.status===profilUpdateStates['Rejected']?'background-color:lightcoral':''">
<td class="align-middle text-wrap ">{{item.topic}}</td>
<td class="align-middle " >{{item.status}}</td>
<td class="align-middle">{{item.status_timestamp?item.status_timestamp:item.insertamum}}</td>
<template v-if="item.status === profilUpdateStates['Pending']">
<td>
<div class="d-flex flex-row justify-content-evenly">
<template v-if="item.topic.toLowerCase().includes('delete')">
<div class="align-middle text-center"><i role="button" @click="showEditProfilModal(item)" class="fa fa-eye"></i></div>
</template>
<template v-else >
<div class="align-middle text-center" ><i style="color:#00639c" @click="showEditProfilModal(item)" role="button" class="fa fa-edit"></i></div>
</template>
<div class="align-middle text-center"><i style="color:red" role="button" @click="deleteRequest(item)" class="fa fa-trash"></i></div>
</div>
</td>
</template>
<template v-else>
<td class="align-middle text-center">
<div class="d-flex flex-row justify-content-evenly">
<i role="button" @click="showEditProfilModal(item)" class="fa fa-eye"></i>
</div>
</td>
</template>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
`,
</div>
`,
};
@@ -1,20 +1,16 @@
export default {
props:{
data:{
type:String,
}
},
data(){
return {
}
},mounted(){
},
template: /*html*/`
props: {
data: {
type: String,
}
},
data() {
return {}
},
template: /*html*/`
<div class="card">
<div class="card-body">
<span >{{$p.t('profil','fhAusweisStatus',[data])}}</span>
<span >{{$p.t('profil','fhAusweisStatus',[data])}}</span>
</div>
</div>`,
}
@@ -13,38 +13,26 @@ export default{
},
template:/*html*/`
<div class="gy-2 row align-items-center justify-content-center">
<div class="col-1 text-center" >
<i class="fa-solid " :class="{...(data.kontakt.includes('@')?{'fa-envelope':true}:{'fa-phone':true})}" style="color:rgb(0, 100, 156)"></i>
</div>
<div :class="{...(data.anmerkung? {'col-11':true, 'col-md-6':true, 'col-xl-11':true, 'col-xxl-6':true} : {'col-10':true, 'col-xl-9':true, 'col-xxl-10':true})}">
<!-- rendering KONTAKT emails -->
<div class="form-underline ">
<div class="form-underline-titel">{{data.kontakttyp}}</div>
<a v-if="data.kontakt.includes('@')" role="link" :aria-disabled="view?true:false" :href="!view?('mailto:'+data.kontakt):null" class="form-underline-content">{{data.kontakt}} </a>
<a v-else role="link" :aria-disabled="view?true:false" :href="!view?('tel:'+data.kontakt):null" class="form-underline-content">{{data.kontakt}} </a>
<div class="col-1 text-center" >
<i class="fa-solid " :class="{...(data.kontakt.includes('@')?{'fa-envelope':true}:{'fa-phone':true})}" style="color:rgb(0, 100, 156)"></i>
</div>
<div :class="{...(data.anmerkung? {'col-11':true, 'col-md-6':true, 'col-xl-11':true, 'col-xxl-6':true} : {'col-10':true, 'col-xl-9':true, 'col-xxl-10':true})}">
<!-- rendering KONTAKT emails -->
<div class="form-underline ">
<div class="form-underline-titel">{{data.kontakttyp}}</div>
<a v-if="data.kontakt.includes('@')" role="link" :aria-disabled="view?true:false" :href="!view?('mailto:'+data.kontakt):null" class="form-underline-content">{{data.kontakt}} </a>
<a v-else role="link" :aria-disabled="view?true:false" :href="!view?('tel:'+data.kontakt):null" class="form-underline-content">{{data.kontakt}} </a>
</div>
</div>
<div v-if="data?.anmerkung" class="offset-1 offset-md-0 offset-xl-1 offset-xxl-0 order-2 order-sm-1 col-10 col-md-4 col-xl-9 col-xxl-4 ">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('global','anmerkung')}}</div>
<span class="form-underline-content">{{data.anmerkung}} </span>
</div>
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('global','anmerkung')}}</div>
<span class="form-underline-content">{{data.anmerkung}} </span>
</div>
</div>
<div class="text-center col-1 col-sm-1 order-2 order-lg-1 col-xl-2 col-xxl-1 allign-middle">
<i v-if="data.zustellung" class="fa-solid fa-check"></i>
<i v-else="data.zustellung" class="fa-solid fa-xmark"></i>
</div>
</div>
</div>
`,
`,
};
@@ -11,30 +11,23 @@ export default {
},
template: /*html*/`
<div class="card">
<div class="card-header">
{{title}}
{{title}}
</div>
<div class="card-body">
<h6 class="card-title">{{$p.t('profil','mailverteilerMitglied')}}</h6>
<h6 class="card-title">{{$p.t('profil','mailverteilerMitglied')}}</h6>
<div class="card-text row text-break mb-2" v-for="verteiler in data">
<div class="col-12 ">
<div class="row">
<div class="col-1 ">
<i class="fa-solid fa-envelope" style="color: #00649C;"></i>
<div class="row">
<div class="col-1 ">
<i class="fa-solid fa-envelope" style="color: #00649C;"></i>
</div>
<div class="col">
<a :href="verteiler.mailto"><b>{{verteiler.gruppe_kurzbz}}</b></a>
</div>
</div>
<div class="col">
<a :href="verteiler.mailto"><b>{{verteiler.gruppe_kurzbz}}</b></a>
</div>
</div>
</div>
<div class="col-11 offset-1 ">{{verteiler.beschreibung}}</div>
</div>
</div>
</div>`,
};
@@ -1,62 +1,38 @@
export default {
data() {
return {};
},
props: {
title: {
type: String,
required: true,
},
data: {
type: Array,
},
},
template: /*html*/ `
<div class="card ">
data() {
return {};
},
props: {
title: {
type: String,
required: true,
},
data: {
type: Array,
},
},
template: /*html*/ `
<div class="card ">
<div class="card-header">
{{title}}
{{title}}
</div>
<div class="card-body">
<!-- HIER SIND DIE EMAILS -->
<div class="gy-3 row justify-content-center ">
<div v-for="email in data" class="col-12 ">
<div class="row align-items-center">
<div class="col-1 text-center">
<i class="fa-solid fa-envelope" style="color:rgb(0, 100, 156)"></i>
</div>
<div class="col-11">
<div class="form-underline">
<div class="form-underline-titel">{{email.type}}</div>
<a :href="'mailto:'+email.email" class="form-underline-content">{{email.email}} </a>
</div>
</div>
</div>
</div>
</div>
<!-- HIER SIND DIE EMAILS -->
<div class="gy-3 row justify-content-center ">
<div v-for="email in data" class="col-12 ">
<div class="row align-items-center">
<div class="col-1 text-center">
<i class="fa-solid fa-envelope" style="color:rgb(0, 100, 156)"></i>
</div>
<div class="col-11">
<div class="form-underline">
<div class="form-underline-titel">{{email.type}}</div>
<a :href="'mailto:'+email.email" class="form-underline-content">{{email.email}} </a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>`,
</div>`,
};
@@ -1,150 +1,115 @@
export default {
props: {
title: {
type: String,
},
data: {
type: Object,
},
editable: {
type: Boolean,
default: false
}
},
data() {
return {
FotoSperre: this.data.foto_sperre,
};
},
emits: ["showEditProfilModal"],
props: {
title: {
type: String,
},
data: {
type: Object,
},
editable: {
type: Boolean,
default: false
}
},
data() {
return {
FotoSperre: this.data.foto_sperre,
};
},
emits: ["showEditProfilModal"],
methods: {
sperre_foto_function() {
//TODO: refactor
if (!this.data) {
return;
}
this.$fhcApi.factory.profil.fotoSperre(!this.FotoSperre).then(res =>{
this.FotoSperre = res.data.foto_sperre;
})
},
},
computed: {
get_image_base64_src: function () {
if (!this.data.foto) {
return "";
}
return "data:image/jpeg;base64," + this.data.foto;
},
name: function () {
return { vorname: this.data.Vorname, nachname: this.data.Nachname };
},
profilInfo: function () {
let res = {};
let notIncludedProperties = [
"Vorname",
"Nachname",
"foto_sperre",
"foto",
];
Object.keys(this.data).forEach((key) => {
if (!notIncludedProperties.includes(key)) {
res[key] = this.data[key];
}
});
return res;
},
},
template: /*html*/ `
<div class="card h-100">
methods: {
sperre_foto_function() {
//TODO: refactor
if (!this.data) {
return;
}
this.$fhcApi.factory.profil.fotoSperre(!this.FotoSperre).then(res => {
this.FotoSperre = res.data.foto_sperre;
})
},
},
computed: {
get_image_base64_src: function () {
if (!this.data.foto) {
return "";
}
return "data:image/jpeg;base64," + this.data.foto;
},
name: function () {
return {vorname: this.data.Vorname, nachname: this.data.Nachname};
},
profilInfo: function () {
let res = {};
let notIncludedProperties = [
"Vorname",
"Nachname",
"foto_sperre",
"foto",
];
Object.keys(this.data).forEach((key) => {
if (!notIncludedProperties.includes(key)) {
res[key] = this.data[key];
}
});
return res;
},
},
template: /*html*/ `
<div class="card h-100">
<div class="card-header">
<div class="row">
<div v-if="editable" @click="$emit('showEditProfilModal','Personen_Informationen')" class="col-auto" type="button">
<i class="fa fa-edit"></i>
<div class="row">
<div v-if="editable" @click="$emit('showEditProfilModal','Personen_Informationen')" class="col-auto" type="button">
<i class="fa fa-edit"></i>
</div>
<div class="col">
<span>{{title}}</span>
</div>
</div>
<div class="col">
<span>{{title}}</span>
</div>
</div>
</div>
<div class="card-body">
<div class="gy-3 row justify-content-center align-items-center">
<!-- SQUEEZING THE IMAGE INSIDE THE FIRST INFORMATION COLUMN -->
<!-- START OF THE FIRST ROW WITH THE PROFIL IMAGE -->
<div class="col-12 col-sm-6 mb-2">
<div class="row justify-content-center">
<div class="col-auto " style="position:relative">
<img class=" img-thumbnail " style=" max-height:150px; " :src="get_image_base64_src">
<!-- LOCKING IMAGE FUNCTIONALITY -->
<div v-if="editable" role="button" @click.prevent="sperre_foto_function" class="image-lock" >
<i :class="{'fa':true, ...(FotoSperre?{'fa-lock':true}:{'fa-lock-open':true})} " ></i>
<div class="gy-3 row justify-content-center align-items-center">
<!-- SQUEEZING THE IMAGE INSIDE THE FIRST INFORMATION COLUMN -->
<!-- START OF THE FIRST ROW WITH THE PROFIL IMAGE -->
<div class="col-12 col-sm-6 mb-2">
<div class="row justify-content-center">
<div class="col-auto " style="position:relative">
<img class=" img-thumbnail " style=" max-height:150px; " :src="get_image_base64_src">
<!-- LOCKING IMAGE FUNCTIONALITY -->
<div v-if="editable" role="button" @click.prevent="sperre_foto_function" class="image-lock">
<i :class="{'fa':true, ...(FotoSperre?{'fa-lock':true}:{'fa-lock-open':true})} "></i>
</div>
</div>
</div>
<!-- END OF THE ROW WITH THE IMAGE -->
</div>
<!-- END OF SQUEEZE -->
<!-- COLUMNS WITH MULTIPLE ROWS NEXT TO PROFIL PICTURE -->
<div class="col-12 col-sm-6">
<div class="row gy-4">
<div class="col-12">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('profilUpdate','vorname')}}</div>
<span class="form-underline-content">{{name.vorname}} </span>
</div>
</div>
<div class="col-12">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('profilUpdate','nachname')}}</div>
<span class="form-underline-content">{{name.nachname}} </span>
</div>
</div>
</div>
</div>
<div v-for="(wert,bez) in profilInfo" class="col-md-6 col-sm-12">
<div class="form-underline">
<div class="form-underline-titel">{{$p.t('profil',bez)}}</div>
<span class="form-underline-content">{{wert?wert:'-'}} </span>
</div>
</div>
</div>
</div>
</div>
<!-- END OF THE ROW WITH THE IMAGE -->
</div>
<!-- END OF SQUEEZE -->
<!-- COLUMNS WITH MULTIPLE ROWS NEXT TO PROFIL PICTURE -->
<div class="col-12 col-sm-6">
<div class="row gy-4">
<div class="col-12">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('profilUpdate','vorname')}}</div>
<span class="form-underline-content">{{name.vorname}} </span>
</div>
</div>
<div class="col-12">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('profilUpdate','nachname')}}</div>
<span class="form-underline-content">{{name.nachname}} </span>
</div>
</div>
</div>
</div>
<div v-for="(wert,bez) in profilInfo" class="col-md-6 col-sm-12 ">
<div class="form-underline ">
<div class="form-underline-titel">{{$p.t('profil',bez)}}</div>
<span class="form-underline-content">{{wert?wert:'-'}} </span>
</div>
</div>
</div>
</div>
</div>`,
`,
};
@@ -1,66 +1,53 @@
export default {
//TODO: To be implemented
props: {
data: {
type: String,
},
title: {
type: String,
required: true,
},
mobile: {
type: Boolean,
default: false,
},
},
methods: {
hideCollapse: function () {
this.collapseOpen = false;
},
showCollapse: function () {
this.collapseOpen = true;
},
},
data() {
return {
collapseOpen: false,
};
},
template: /*html*/ `
<div class="card">
//TODO: To be implemented
props: {
data: {
type: String,
},
title: {
type: String,
required: true,
},
mobile: {
type: Boolean,
default: false,
},
},
methods: {
hideCollapse: function () {
this.collapseOpen = false;
},
showCollapse: function () {
this.collapseOpen = true;
},
},
data() {
return {
collapseOpen: false,
};
},
template: /*html*/ `
<div class="card">
<template v-if="mobile">
<button class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#quickLinks" :aria-expanded="collapseOpen" aria-controls="quickLinks" >
{{title}}
<i class="fa " :class="collapseOpen?'fa-chevron-up':'fa-chevron-down'"></i>
</button>
<div @[\`show.bs.collapse\`]="collapseOpen=true;" @[\`hide.bs.collapse\`]="collapseOpen=false;" class="mt-1 collapse" id="quickLinks">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','zeitwuensche')}}</a>
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','lehrveranstaltungen')}}</a>
<a href="#" class="list-group-item list-group-item-action ">{{$p.t('profil','zeitsperren')}}</a>
</div>
</div>
<button class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#quickLinks" :aria-expanded="collapseOpen" aria-controls="quickLinks" >
{{title}}
<i class="fa " :class="collapseOpen?'fa-chevron-up':'fa-chevron-down'"></i>
</button>
<div @[\`show.bs.collapse\`]="collapseOpen=true;" @[\`hide.bs.collapse\`]="collapseOpen=false;" class="mt-1 collapse" id="quickLinks">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','zeitwuensche')}}</a>
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','lehrveranstaltungen')}}</a>
<a href="#" class="list-group-item list-group-item-action ">{{$p.t('profil','zeitsperren')}}</a>
</div>
</div>
</template>
<template v-else>
<div class="card-header">
{{title}}
</div>
<div class="card-body">
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitwuensche')}}</a>
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','lehrveranstaltungen')}}</a>
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitsperren')}}</a>
</div>
</template>
</div>`,
<div class="card-header">{{title}}</div>
<div class="card-body">
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitwuensche')}}</a>
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','lehrveranstaltungen')}}</a>
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitsperren')}}</a>
</div>
</template>
</div>`,
};
@@ -1,49 +1,37 @@
export default {
data(){
return {
}
},
props:{
data:{
type:Object,
},
title:{
type:String,
}
},
computed:{
data() {
return {}
},
props: {
data: {
type: Object,
},
title: {
type: String,
}
},
computed: {},
created() {
//TODO: check if data.Telefon is a valid telefon number to call before using it as a tel: link
},
template: `
<div class="card">
<div class="card-header">{{title}}</div>
<div class="card-body">
<div class="gy-3 row">
<div v-for="(wert,bez) in data" class="col-md-6 col-sm-12 ">
<div class="form-underline">
<div class="form-underline-titel">{{bez }}</div>
},
created(){
//TODO: check if data.Telefon is a valid telefon number to call before using it as a tel: link
},
template:`
<div class="card">
<div class="card-header">
{{title}}
<!-- print Telefon link -->
<a v-if="bez=='Telefon'" :href="data.Telefon?'tel:'+data.Telefon:null" :class="{'form-underline-content':true,'text-decoration-none':!data.Telefon,'text-body':!data.Telefon}">{{wert?wert:'-'}}</a>
<!-- else print information -->
<span v-else class="form-underline-content">{{wert?wert:'-'}}</span>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="gy-3 row">
<div v-for="(wert,bez) in data" class="col-md-6 col-sm-12 ">
<div class="form-underline">
<div class="form-underline-titel">{{bez }}</div>
<!-- print Telefon link -->
<a v-if="bez=='Telefon'" :href="data.Telefon?'tel:'+data.Telefon:null" :class="{'form-underline-content':true,'text-decoration-none':!data.Telefon,'text-body':!data.Telefon}">{{wert?wert:'-'}}</a>
<!-- else print information -->
<span v-else class="form-underline-content">{{wert?wert:'-'}}</span>
</div>
</div>
</div>
</div>
</div>`
};
@@ -4,204 +4,201 @@ import EditProfilSelect from "./EditProfilSelect.js";
import Loader from "../../../Loader.js";
export default {
components: {
BsModal,
Alert,
EditProfilSelect,
Loader,
},
mixins: [BsModal],
props: {
value: Object,
title: String,
zustelladressenCount: Function,
zustellkontakteCount: Function,
/*
* NOTE(chris):
* Hack to expose in "emits" declared events to $props which we use
* in the v-bind directive to forward all events.
* @see: https://github.com/vuejs/core/issues/3432
*/
onHideBsModal: Function,
onHiddenBsModal: Function,
onHidePreventedBsModal: Function,
onShowBsModal: Function,
onShownBsModal: Function,
},
data() {
return {
topic: null,
profilUpdate: null,
editData: this.value,
fileID: null,
breadcrumb: null,
loading: false,
result: false,
info: null,
};
},
inject: ["setLoading"],
provide() {
return {
updateFileID: this.updateFileIDFunction,
};
},
methods: {
updateFileIDFunction: function (newFileID) {
this.fileID = newFileID;
},
handleFailedError: function (err) {
console.error(err);
this.loading = false;
this.setLoading(false);
this.result = false;
this.hide();
components: {
BsModal,
Alert,
EditProfilSelect,
Loader,
},
mixins: [BsModal],
props: {
value: Object,
title: String,
zustelladressenCount: Function,
zustellkontakteCount: Function,
/*
* NOTE(chris):
* Hack to expose in "emits" declared events to $props which we use
* in the v-bind directive to forward all events.
* @see: https://github.com/vuejs/core/issues/3432
*/
onHideBsModal: Function,
onHiddenBsModal: Function,
onHidePreventedBsModal: Function,
onShowBsModal: Function,
onShownBsModal: Function,
},
async submitProfilChange() {
data() {
return {
topic: null,
profilUpdate: null,
editData: this.value,
fileID: null,
breadcrumb: null,
loading: false,
result: false,
info: null,
};
},
inject: ["setLoading"],
provide() {
return {
updateFileID: this.updateFileIDFunction,
};
},
//? check if data is valid before making a request
if (this.topic && this.profilUpdate) {
//? if profil update contains any attachment
if (this.fileID) {
const fileData = await this.uploadFiles(this.fileID);
methods: {
updateFileIDFunction: function (newFileID) {
this.fileID = newFileID;
},
this.fileID = fileData ? fileData : null;
}
handleFailedError: function (err) {
console.error(err);
this.loading = false;
this.setLoading(false);
this.result = false;
this.hide();
},
//? inserts new row in public.tbl_cis_profil_update
//* calls the update api call if an update field is present in the data that was passed to the modal
const handleApiResponse = (res) => {
//? toggles the loading to false and closes the loading modal
if (res.data.error) {
this.result = false;
Alert.popup(
"Ein Fehler ist aufgetreten: " + JSON.stringify(res.data.retval)
);
} else {
this.result = true;
Alert.popup(
"Ihre Anfrage wurde erfolgreich gesendet. Bitte warten Sie, während sich das Team um Ihre Anfrage kümmert."
);
}
};
async submitProfilChange() {
//* v-show on EditProfil modal binded to this.loading
//? hides the EditProfil modal and shows the loading modal by calling a callback that was passed as prop from the parent component
this.loading = true;
this.setLoading(true);
//? check if data is valid before making a request
if (this.topic && this.profilUpdate) {
//? if profil update contains any attachment
if (this.fileID) {
const fileData = await this.uploadFiles(this.fileID);
//? if an updateID is present, updateProfilRequest is called, else insertProfilRequest is called
this.editData.updateID?
this.$fhcApi.factory.profilUpdate.updateProfilRequest(
this.topic,
this.profilUpdate,
this.editData.updateID,
this.fileID ? this.fileID[0] : null
)
.then((res) => {
handleApiResponse(res);
})
.catch((err) => this.$fhcAlert.handleSystemError)
.finally(()=>{
this.loading = false;
this.setLoading(false);
this.hide();
})
:
this.$fhcApi.factory.profilUpdate.insertProfilRequest(
this.topic,
this.profilUpdate,
this.fileID ? this.fileID[0] : null
)
.then((res) => {
handleApiResponse(res);
})
.catch((err) => this.$fhcAlert.handleSystemError)
.finally(() => {
this.loading = false;
this.setLoading(false);
this.hide();
});
}
},
this.fileID = fileData ? fileData : null;
}
uploadFiles: async function (files) {
if (files[0].type !== "application/x.fhc-dms+json") {
let formData = new FormData();
formData.append("files[]", files[0]);
const result = this.editData.updateID
? //? updating old attachment by replacing
//* second parameter of api request insertFile checks if the file has to be replaced or not
await this.$fhcApi.factory.profilUpdate.insertFile(
formData,
this.editData.updateID
).then((res) => {
return res.data?.map((file) => file.dms_id);
})
: //? fresh insert of new attachment
await this.$fhcApi.factory.profilUpdate.insertFile(formData).then((res) => {
return res.data?.map((file) => file.dms_id);
});
return result;
} else {
//? attachment hasn't been replaced
return false;
}
},
},
computed: {
showFooter: function () {
switch(this.value.view){
case 'Status': return false;
case 'Kontakt':return false;
case 'Adresse':return false;
default: return true;
}
},
},
created() {
if (this.editData.topic) {
//? if the topic was passed through the prop add it to the component
this.topic = this.editData.topic;
}
},
mounted() {
this.modal = this.$refs.modalContainer.modal;
},
popup(options) {
BsModal.popup.bind(this);
return BsModal.popup(null, options);
},
template: /*html*/ `
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" backdrop="false" >
//? inserts new row in public.tbl_cis_profil_update
//* calls the update api call if an update field is present in the data that was passed to the modal
const handleApiResponse = (res) => {
//? toggles the loading to false and closes the loading modal
if (res.data.error) {
this.result = false;
Alert.popup(
"Ein Fehler ist aufgetreten: " + JSON.stringify(res.data.retval)
);
} else {
this.result = true;
Alert.popup(
"Ihre Anfrage wurde erfolgreich gesendet. Bitte warten Sie, während sich das Team um Ihre Anfrage kümmert."
);
}
};
<template v-if="title" v-slot:title>
{{title }}
</template>
<template v-slot:default>
<div >
<nav aria-label="breadcrumb" class="ps-2 ">
<ol class="breadcrumb ">
<li class="breadcrumb-item" v-for="element in breadcrumb">{{element}}</li>
</ol>
</nav>
//* v-show on EditProfil modal binded to this.loading
//? hides the EditProfil modal and shows the loading modal by calling a callback that was passed as prop from the parent component
this.loading = true;
this.setLoading(true);
<edit-profil-select @submit="submitProfilChange" v-model:breadcrumb="breadcrumb" v-model:topic="topic" v-model:profilUpdate="profilUpdate" ariaLabel="select" :list="editData"></edit-profil-select>
</div>
//? if an updateID is present, updateProfilRequest is called, else insertProfilRequest is called
this.editData.updateID ?
this.$fhcApi.factory.profilUpdate.updateProfilRequest(
this.topic,
this.profilUpdate,
this.editData.updateID,
this.fileID ? this.fileID[0] : null
)
.then((res) => {
handleApiResponse(res);
})
.catch((err) => this.$fhcAlert.handleSystemError)
.finally(() => {
this.loading = false;
this.setLoading(false);
this.hide();
})
:
this.$fhcApi.factory.profilUpdate.insertProfilRequest(
this.topic,
this.profilUpdate,
this.fileID ? this.fileID[0] : null
)
.then((res) => {
handleApiResponse(res);
})
.catch((err) => this.$fhcAlert.handleSystemError)
.finally(() => {
this.loading = false;
this.setLoading(false);
this.hide();
});
}
},
</template>
<!-- optional footer -->
<template v-slot:footer v-if="showFooter">
<loader ref="loaderRef" :timeout="0"></loader>
<button class="btn btn-outline-danger " @click="hide">{{$p.t('ui','cancel')}}</button>
<button :disabled="!profilUpdate" @click="submitProfilChange" role="button" class="btn btn-primary">{{$p.t('ui','senden')}}</button>
</template>
<!-- end of optional footer -->
</bs-modal>`,
uploadFiles: async function (files) {
if (files[0].type !== "application/x.fhc-dms+json") {
let formData = new FormData();
formData.append("files[]", files[0]);
const result = this.editData.updateID
? //? updating old attachment by replacing
//* second parameter of api request insertFile checks if the file has to be replaced or not
await this.$fhcApi.factory.profilUpdate.insertFile(
formData,
this.editData.updateID
).then((res) => {
return res.data?.map((file) => file.dms_id);
})
: //? fresh insert of new attachment
await this.$fhcApi.factory.profilUpdate.insertFile(formData).then((res) => {
return res.data?.map((file) => file.dms_id);
});
return result;
} else {
//? attachment hasn't been replaced
return false;
}
},
},
computed: {
showFooter: function () {
switch (this.value.view) {
case 'Status':
return false;
case 'Kontakt':
return false;
case 'Adresse':
return false;
default:
return true;
}
},
},
created() {
if (this.editData.topic) {
//? if the topic was passed through the prop add it to the component
this.topic = this.editData.topic;
}
},
mounted() {
this.modal = this.$refs.modalContainer.modal;
},
popup(options) {
BsModal.popup.bind(this);
return BsModal.popup(null, options);
},
template: /*html*/ `
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" backdrop="false" >
<template v-if="title" v-slot:title>{{title }}</template>
<template v-slot:default>
<div >
<nav aria-label="breadcrumb" class="ps-2 ">
<ol class="breadcrumb ">
<li class="breadcrumb-item" v-for="element in breadcrumb">{{element}}</li>
</ol>
</nav>
<edit-profil-select @submit="submitProfilChange" v-model:breadcrumb="breadcrumb" v-model:topic="topic" v-model:profilUpdate="profilUpdate" ariaLabel="select" :list="editData"></edit-profil-select>
</div>
</template>
<!-- optional footer -->
<template v-slot:footer v-if="showFooter">
<loader ref="loaderRef" :timeout="0"></loader>
<button class="btn btn-outline-danger " @click="hide">{{$p.t('ui','cancel')}}</button>
<button :disabled="!profilUpdate" @click="submitProfilChange" role="button" class="btn btn-primary">{{$p.t('ui','senden')}}</button>
</template>
<!-- end of optional footer -->
</bs-modal>`,
};
@@ -6,164 +6,164 @@ import Status from "./EditProfilComponents/Status.js";
import TextInputDokument from "./EditProfilComponents/TextInputDokument.js";
export default {
components: {
Kontakt,
EditKontakt,
Adresse,
EditAdresse,
Status,
TextInputDokument,
},
inject: ["profilUpdateTopic"],
props: {
list: Object,
components: {
Kontakt,
EditKontakt,
Adresse,
EditAdresse,
Status,
TextInputDokument,
},
inject: ["profilUpdateTopic"],
props: {
list: Object,
//? Prop used to determine how many options the select should initially show
size: {
type: Number,
default: null,
},
//? Content for the aria label of the select
ariaLabel: {
type: String,
required: true,
},
profilUpdate: String,
topic: String,
breadcrumb: String,
},
emits: {
//? update:modelValue event is needed to notify the v-model when the value has changed
["update:profilUpdate"]: null,
["update:topic"]: null,
["update:breadcrumb"]: null,
submit: null,
select: null,
},
data() {
return {
view: null,
data: null,
breadcrumbItems: [],
modal_topic: this.topic,
properties: null,
};
},
//? Prop used to determine how many options the select should initially show
size: {
type: Number,
default: null,
},
//? Content for the aria label of the select
ariaLabel: {
type: String,
required: true,
},
profilUpdate: String,
topic: String,
breadcrumb: String,
},
emits: {
//? update:modelValue event is needed to notify the v-model when the value has changed
["update:profilUpdate"]: null,
["update:topic"]: null,
["update:breadcrumb"]: null,
submit: null,
select: null,
},
data() {
return {
view: null,
data: null,
breadcrumbItems: [],
modal_topic: this.topic,
properties: null,
};
},
methods: {
addItem: function () {
this.view =
this.modal_topic == this.profilUpdateTopic["Private Kontakte"]
? "EditKontakt"
: "EditAdresse";
//? updates the topic when a Kontakt or an Address should be added
this.modal_topic =
this.modal_topic == this.profilUpdateTopic["Private Kontakte"]
? this.profilUpdateTopic["Add Kontakt"]
: this.profilUpdateTopic["Add Adresse"];
this.$emit("update:topic", this.modal_topic);
this.breadcrumbItems.push(this.modal_topic);
this.$emit("update:breadcrumb", this.breadcrumbItems);
methods: {
addItem: function () {
this.view =
this.modal_topic == this.profilUpdateTopic["Private Kontakte"]
? "EditKontakt"
: "EditAdresse";
this.data =
this.view == "EditAdresse"
? {
//? add flag
add: true,
adresse_id: null,
strasse: null,
typ: null,
plz: null,
ort: null,
zustelladresse: false,
}
: {
//? add flag
add: true,
kontakt_id: null,
kontakttyp: null,
kontakt: null,
anmerkung: null,
zustellung: false,
};
},
//? updates the topic when a Kontakt or an Address should be added
this.modal_topic =
this.modal_topic == this.profilUpdateTopic["Private Kontakte"]
? this.profilUpdateTopic["Add Kontakt"]
: this.profilUpdateTopic["Add Adresse"];
this.$emit("update:topic", this.modal_topic);
this.breadcrumbItems.push(this.modal_topic);
this.$emit("update:breadcrumb", this.breadcrumbItems);
deleteItem: function (item) {
//? delete flag
item.data.delete = true;
this.$emit("update:profilUpdate", item.data);
//? updates the topic when a Kontakt or an Address should be deleted
this.modal_topic = this.modal_topic == this.profilUpdateTopic["Private Adressen"]
? this.profilUpdateTopic["Delete Adresse"]
: this.profilUpdateTopic["Delete Kontakt"];
this.$emit("update:topic", this.modal_topic);
this.$emit("submit");
},
this.data =
this.view == "EditAdresse"
? {
//? add flag
add: true,
adresse_id: null,
strasse: null,
typ: null,
plz: null,
ort: null,
zustelladresse: false,
}
: {
//? add flag
add: true,
kontakt_id: null,
kontakttyp: null,
kontakt: null,
anmerkung: null,
zustellung: false,
};
},
profilUpdateEmit: function (event) {
//? passes the updated profil information to the parent component
this.$emit("update:profilUpdate", event);
},
deleteItem: function (item) {
//? delete flag
item.data.delete = true;
this.$emit("update:profilUpdate", item.data);
//? updates the topic when a Kontakt or an Address should be deleted
updateOptions: function (event, item) {
this.properties = item;
this.data = item.data;
this.view = item.view;
if (item.title) {
//? emits the selected topic to the parent component
this.modal_topic = item.topic;
this.$emit("update:topic", this.modal_topic);
this.modal_topic = this.modal_topic == this.profilUpdateTopic["Private Adressen"]
? this.profilUpdateTopic["Delete Adresse"]
: this.profilUpdateTopic["Delete Kontakt"];
this.$emit("update:topic", this.modal_topic);
this.$emit("submit");
},
//? emits the new item for the breadcrumb in the parent component
this.breadcrumbItems.push(item.title);
} else {
if (item.data.kontakttyp) {
this.breadcrumbItems.push(item.data.kontakttyp);
this.breadcrumbItems.push(item.data.kontakt);
} else if (item.data.strasse) {
this.breadcrumbItems.push(item.data.strasse);
}
}
this.$emit("update:breadcrumb", this.breadcrumbItems);
},
},
computed: {},
created() {
//? JSON parse and stringify are used to deep clone the objects
this.properties = { ...this.list };
this.data = this.list.data
? JSON.parse(JSON.stringify(this.list.data))
: null;
this.view = this.list.view
? JSON.parse(JSON.stringify(this.list.view))
: null;
},
mounted() {},
profilUpdateEmit: function (event) {
//? passes the updated profil information to the parent component
this.$emit("update:profilUpdate", event);
},
template: /*html*/ `
updateOptions: function (event, item) {
this.properties = item;
this.data = item.data;
this.view = item.view;
if (item.title) {
//? emits the selected topic to the parent component
this.modal_topic = item.topic;
this.$emit("update:topic", this.modal_topic);
//? emits the new item for the breadcrumb in the parent component
this.breadcrumbItems.push(item.title);
} else {
if (item.data.kontakttyp) {
this.breadcrumbItems.push(item.data.kontakttyp);
this.breadcrumbItems.push(item.data.kontakt);
} else if (item.data.strasse) {
this.breadcrumbItems.push(item.data.strasse);
}
}
this.$emit("update:breadcrumb", this.breadcrumbItems);
},
},
computed: {},
created() {
//? JSON parse and stringify are used to deep clone the objects
this.properties = {...this.list};
this.data = this.list.data
? JSON.parse(JSON.stringify(this.list.data))
: null;
this.view = this.list.view
? JSON.parse(JSON.stringify(this.list.view))
: null;
},
mounted() {
},
template: /*html*/ `
<template v-if="!view">
<div class="list-group">
<template v-for="item in data">
<div class="d-flex flex-row align-items-center">
<button style="position:relative" type="button" class=" list-group-item list-group-item-action" @click="updateOptions($event,item)" >
<!-- render title of options -->
<p v-if="item.title" class="my-1" >{{item.title}}</p>
<!-- else render list view of items -->
<div v-else class="my-2 me-4" >
<component :is="item.listview" v-bind="item"></component>
</div>
</button>
<button v-if="item.listview" @click="deleteItem(item)" type="button" class="mx-3 btn btn-danger btn-circle"><i class="fa fa-trash"></i>
</div>
</template>
</div>
<div v-if="Array.isArray(data)" class="mt-4 d-flex justify-content-center">
<button @click="addItem" type="button" class="btn btn-primary btn-circle"><i class="fa fa-plus"></i>
</div>
<div class="list-group">
<template v-for="item in data">
<div class="d-flex flex-row align-items-center">
<button style="position:relative" type="button" class=" list-group-item list-group-item-action" @click="updateOptions($event,item)" >
<!-- render title of options -->
<p v-if="item.title" class="my-1" >{{item.title}}</p>
<!-- else render list view of items -->
<div v-else class="my-2 me-4" >
<component :is="item.listview" v-bind="item"></component>
</div>
</button>
<button v-if="item.listview" @click="deleteItem(item)" type="button" class="mx-3 btn btn-danger btn-circle"><i class="fa fa-trash"></i>
</div>
</template>
</div>
<div v-if="Array.isArray(data)" class="mt-4 d-flex justify-content-center">
<button @click="addItem" type="button" class="btn btn-primary btn-circle"><i class="fa fa-plus"></i>
</div>
</template>
<template v-else>
<component @profilUpdate="profilUpdateEmit" :is="view" v-bind="properties" :data="data" ></component>
</template>
+334 -421
View File
@@ -1,4 +1,4 @@
import { CoreFilterCmpt } from "../../../components/filter/Filter.js";
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
import AusweisStatus from "./ProfilComponents/FhAusweisStatus.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
@@ -11,439 +11,352 @@ import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js";
import EditProfil from "./ProfilModal/EditProfil.js";
export default {
components: {
CoreFilterCmpt,
Mailverteiler,
AusweisStatus,
QuickLinks,
Adresse,
Kontakt,
ProfilEmails,
RoleInformation,
ProfilInformation,
FetchProfilUpdates,
EditProfil,
},
inject: ["sortProfilUpdates", "collapseFunction"],
data() {
return {
showModal: false,
collapseIconBetriebsmittel: true,
editDataFilter: null,
components: {
CoreFilterCmpt,
Mailverteiler,
AusweisStatus,
QuickLinks,
Adresse,
Kontakt,
ProfilEmails,
RoleInformation,
ProfilInformation,
FetchProfilUpdates,
EditProfil,
},
inject: ["sortProfilUpdates", "collapseFunction"],
data() {
return {
showModal: false,
collapseIconBetriebsmittel: true,
editDataFilter: null,
// tabulator options
zutrittsgruppen_table_options: {
height: 200,
layout: "fitColumns",
data: [{ bezeichnung: "" }],
columns: [{ title: "Zutritt", field: "bezeichnung" }],
},
betriebsmittel_table_options: {
height: 300,
layout: "fitColumns",
responsiveLayout: "collapse",
responsiveLayoutCollapseUseFormatters: false,
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
data: [{ betriebsmittel: "", Nummer: "", Ausgegeben_am: "" }],
columns: [
{
title:
"<i id='collapseIconBetriebsmittel' role='button' class='fa-solid fa-angle-down '></i>",
field: "collapse",
headerSort: false,
headerFilter: false,
formatter: "responsiveCollapse",
maxWidth: 40,
headerClick: this.collapseFunction,
},
{
title: "Betriebsmittel",
field: "betriebsmittel",
headerFilter: true,
minWidth: 200,
},
{
title: "Nummer",
field: "Nummer",
headerFilter: true,
resizable: true,
minWidth: 200,
},
{
title: "Ausgegeben_am",
field: "Ausgegeben_am",
headerFilter: true,
minWidth: 200,
},
],
},
};
},
// tabulator options
zutrittsgruppen_table_options: {
height: 200,
layout: "fitColumns",
data: [{bezeichnung: ""}],
columns: [{title: "Zutritt", field: "bezeichnung"}],
},
betriebsmittel_table_options: {
height: 300,
layout: "fitColumns",
responsiveLayout: "collapse",
responsiveLayoutCollapseUseFormatters: false,
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
data: [{betriebsmittel: "", Nummer: "", Ausgegeben_am: ""}],
columns: [
{
title:
"<i id='collapseIconBetriebsmittel' role='button' class='fa-solid fa-angle-down '></i>",
field: "collapse",
headerSort: false,
headerFilter: false,
formatter: "responsiveCollapse",
maxWidth: 40,
headerClick: this.collapseFunction,
},
{
title: "Betriebsmittel",
field: "betriebsmittel",
headerFilter: true,
minWidth: 200,
},
{
title: "Nummer",
field: "Nummer",
headerFilter: true,
resizable: true,
minWidth: 200,
},
{
title: "Ausgegeben_am",
field: "Ausgegeben_am",
headerFilter: true,
minWidth: 200,
},
],
},
};
},
props: {
data: Object,
editData: Object,
},
methods: {
betriebsmittelTableBuilt: function () {
this.$refs.betriebsmittelTable.tabulator.setData(this.data.mittel);
},
zutrittsgruppenTableBuilt: function () {
this.$refs.zutrittsgruppenTable.tabulator.setData(
this.data.zuttritsgruppen
);
},
fetchProfilUpdates: function () {
this.$fhcApi.factory.profilUpdate.selectProfilRequest().then((res) => {
if (!res.error && res) {
this.data.profilUpdates = res.data?.length
? res.data.sort(this.sortProfilUpdates)
: null;
}
});
},
props: {
data: Object,
editData: Object,
},
methods: {
hideEditProfilModal: function () {
//? checks the editModal component property result, if the user made a successful request or not
if (this.$refs.editModal.result) {
this.$fhcApi.factory.profilUpdate.selectProfilRequest()
.then((request) => {
if (!request.error && res) {
this.data.profilUpdates = request.data;
this.data.profilUpdates.sort(this.sortProfilUpdates);
} else {
console.error("Error when fetching profile updates: " + res.data);
}
})
.catch((err) => {
console.error(err);
});
} else {
// when modal was closed without submitting request
}
this.showModal = false;
this.editDataFilter = null;
},
betriebsmittelTableBuilt: function () {
this.$refs.betriebsmittelTable.tabulator.setData(this.data.mittel);
},
zutrittsgruppenTableBuilt: function () {
this.$refs.zutrittsgruppenTable.tabulator.setData(
this.data.zuttritsgruppen
);
},
fetchProfilUpdates: function () {
this.$fhcApi.factory.profilUpdate.selectProfilRequest().then((res) => {
if (!res.error && res) {
this.data.profilUpdates = res.data?.length
? res.data.sort(this.sortProfilUpdates)
: null;
}
});
},
showEditProfilModal(view) {
if (view) {
this.editDataFilter = view;
}
this.showModal = true;
// after a state change, wait for the DOM updates to complete
Vue.nextTick(() => {
this.$refs.editModal.show();
});
},
},
hideEditProfilModal: function () {
//? checks the editModal component property result, if the user made a successful request or not
if (this.$refs.editModal.result) {
this.$fhcApi.factory.profilUpdate.selectProfilRequest()
.then((request) => {
if (!request.error && res) {
this.data.profilUpdates = request.data;
this.data.profilUpdates.sort(this.sortProfilUpdates);
} else {
console.error("Error when fetching profile updates: " + res.data);
}
})
.catch((err) => {
console.error(err);
});
} else {
// when modal was closed without submitting request
}
this.showModal = false;
this.editDataFilter = null;
},
computed: {
editable() {
return this.data?.editAllowed ?? false;
},
filteredEditData() {
return this.editDataFilter
? this.editData.data[this.editDataFilter]
: this.editData;
},
showEditProfilModal(view) {
if (view) {
this.editDataFilter = view;
}
this.showModal = true;
// after a state change, wait for the DOM updates to complete
Vue.nextTick(() => {
this.$refs.editModal.show();
});
},
},
profilInformation() {
if (!this.data) {
return {};
}
computed: {
editable() {
return this.data?.editAllowed ?? false;
},
return {
Vorname: this.data.vorname,
Nachname: this.data.nachname,
Username: this.data.username,
Anrede: this.data.anrede,
Titel: this.data.titel,
Postnomen: this.data.postnomen,
foto_sperre: this.data.foto_sperre,
foto: this.data.foto,
};
},
filteredEditData() {
return this.editDataFilter
? this.editData.data[this.editDataFilter]
: this.editData;
},
roleInformation() {
if (!this.data) {
return {};
}
profilInformation() {
if (!this.data) {
return {};
}
return {
Geburtsdatum: this.data.gebdatum,
Geburtsort: this.data.gebort,
Personenkennzeichen: this.data.personenkennzeichen,
Studiengang: this.data.studiengang,
Semester: this.data.semester,
Verband: this.data.verband,
Gruppe: this.data.gruppe.trim(),
};
},
},
return {
Vorname: this.data.vorname,
Nachname: this.data.nachname,
Username: this.data.username,
Anrede: this.data.anrede,
Titel: this.data.titel,
Postnomen: this.data.postnomen,
foto_sperre: this.data.foto_sperre,
foto: this.data.foto,
};
},
created() {
//? sorts the profil Updates: pending -> accepted -> rejected
this.data.profilUpdates?.sort(this.sortProfilUpdates);
},
roleInformation() {
if (!this.data) {
return {};
}
template: /*html*/ `
<div class="container-fluid text-break fhc-form" >
<edit-profil v-if="showModal" ref="editModal" @hideBsModal="hideEditProfilModal" :value="JSON.parse(JSON.stringify(filteredEditData))" :title="$p.t('profil','profilBearbeiten')" ></edit-profil>
return {
Geburtsdatum: this.data.gebdatum,
Geburtsort: this.data.gebort,
Personenkennzeichen: this.data.personenkennzeichen,
Studiengang: this.data.studiengang,
Semester: this.data.semester,
Verband: this.data.verband,
Gruppe: this.data.gruppe.trim(),
};
},
},
created() {
//? sorts the profil Updates: pending -> accepted -> rejected
this.data.profilUpdates?.sort(this.sortProfilUpdates);
},
template: /*html*/ `
<div class="container-fluid text-break fhc-form">
<edit-profil v-if="showModal" ref="editModal" @hideBsModal="hideEditProfilModal"
:value="JSON.parse(JSON.stringify(filteredEditData))" :title="$p.t('profil','profilBearbeiten')"></edit-profil>
<!-- ROW -->
<div class="row">
<!-- HIDDEN QUICK LINKS -->
<div class="d-md-none col-12 ">
<div class="row py-2">
<div class="row">
<!-- HIDDEN QUICK LINKS -->
<div class="d-md-none col-12 ">
<div class="row py-2">
<div class="col">
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
</div>
</div>
<!-- Bearbeiten Button -->
<div v-if="editable" class="row ">
<div class="col mb-3">
<button @click="showEditProfilModal" type="button" class="text-start w-100 btn btn-outline-secondary" >
<div class="row">
<div class="col-2">
<i class="fa fa-edit"></i>
</div>
<div class="col-10">{{$p.t('ui','bearbeiten')}}</div>
</div>
</button>
</div>
</div>
<div v-if="data.profilUpdates" class="row mb-3">
<div class="col">
<!-- MOBILE PROFIL UPDATES -->
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates"></fetch-profil-updates>
</div>
</div>
</div>
<!-- END OF HIDDEN QUCK LINKS -->
</div>
<!-- MAIN PANNEL -->
<div class="col-sm-12 col-md-8 col-xxl-9 ">
<!-- ROW WITH PROFIL IMAGE AND INFORMATION -->
<!-- INFORMATION CONTENT START -->
<!-- ROW WITH THE PROFIL INFORMATION -->
<div class="row mb-4 ">
<div class="col-lg-12 col-xl-6 ">
<div class="row mb-4">
<div class="col">
<!-- PROFIL INFORMATION -->
<profil-information @showEditProfilModal="showEditProfilModal" :title="$p.t('profil','studentIn')" :data="profilInformation" :editable="editable"></profil-information>
</div>
</div>
<div class="row mb-4">
<div class=" col-lg-12">
<!-- STUDENT INFO -->
<role-information :title="$p.t('profil','studentInformation')" :data="roleInformation"></role-information>
</div>
</div>
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
</div>
<div class="col-xl-6 col-lg-12 ">
<div class="row mb-4">
<div class="col">
<!-- EMAILS -->
<profil-emails :title="this.$p.t('person','email')" :data="data.emails" ></profil-emails>
</div>
</div>
<div class="row mb-4 ">
<div class="col">
<!-- PRIVATE KONTAKTE-->
<div class="card">
<div class="card-header">
<div class="row">
<div @click="showEditProfilModal('Private_Kontakte')" class="col-auto" type="button">
<i class="fa fa-edit"></i>
</div>
<div class="col">
<span>{{$p.t('profil','privateKontakte')}}</span>
</div>
</div>
</div>
<div class="card-body ">
<div class="gy-3 row ">
<div v-for="element in data.kontakte" class="col-12">
<Kontakt :data="element"></Kontakt>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col">
<!-- PRIVATE ADRESSEN-->
<div class="card">
<div class="card-header">
<div class="row">
<div @click="showEditProfilModal('Private_Adressen')" class="col-auto" type="button">
<i class="fa fa-edit"></i>
</div>
<div class="col">
<span>{{$p.t('profil','privateAdressen')}}</span>
</div>
</div>
</div>
<div class="card-body">
<div class="gy-3 row ">
<div v-for="element in data.adressen" class="col-12">
<Adresse :data="element"></Adresse>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div >
<!-- SECOND ROW UNDER THE PROFIL IMAGE AND INFORMATION WITH THE TABLES -->
<div class="row">
<div class="col-12 mb-4" >
<core-filter-cmpt @tableBuilt="betriebsmittelTableBuilt" :title="$p.t('profil','entlehnteBetriebsmittel')" ref="betriebsmittelTable" :tabulator-options="betriebsmittel_table_options" tableOnly :sideMenu="false" />
</div>
<div class="col-12 mb-4" >
<core-filter-cmpt @tableBuilt="zutrittsgruppenTableBuilt" :title="$p.t('profil','zutrittsGruppen')" ref="zutrittsgruppenTable" :tabulator-options="zutrittsgruppen_table_options" tableOnly :sideMenu="false" noColumnFilter />
</div>
</div>
<!-- END OF MAIN CONTENT COL -->
</div>
<!-- START OF SIDE PANEL -->
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
<div class="row d-none d-md-block mb-3">
<div class="col">
<!-- QUICK LINKS -->
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
</div>
</div>
<!-- Bearbeiten Button -->
<div class="row d-none d-md-block">
<div class="col mb-3">
<button @click="()=>showEditProfilModal()" type="button" class="text-start w-100 btn btn-outline-secondary" >
<div class="row">
<div class="col-2">
<i class="fa fa-edit"></i>
</div>
<div class="col-10">{{$p.t('ui','bearbeiten')}}</div>
</div>
</button>
</div>
</div>
<div v-if="data.profilUpdates" class="row d-none d-md-block mb-3">
<div class="col mb-3">
<!-- PROFIL UPDATES -->
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates"></fetch-profil-updates>
</div>
</div>
<div class="row mb-3" >
<div class="col-12">
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
</div>
</div>
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
<div class="row">
<div class="col">
<!-- HIER SIND DIE MAILVERTEILER -->
<mailverteiler :title="$p.t('profil','mailverteiler')" :data="data?.mailverteiler"></mailverteiler>
</div>
<!-- END OF THE SECOND ROW IN THE SIDE PANEL -->
</div>
<!-- END OF SIDE PANEL -->
</div>
<!-- END OF CONTAINER ROW-->
</div>
<!-- END OF CONTAINER -->
</div>
`,
<!-- Bearbeiten Button -->
<div v-if="editable" class="row ">
<div class="col mb-3">
<button @click="showEditProfilModal" type="button" class="text-start w-100 btn btn-outline-secondary" >
<div class="row">
<div class="col-2">
<i class="fa fa-edit"></i>
</div>
<div class="col-10">{{$p.t('ui','bearbeiten')}}</div>
</div>
</button>
</div>
</div>
<div v-if="data.profilUpdates" class="row mb-3">
<div class="col">
<!-- MOBILE PROFIL UPDATES -->
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates"></fetch-profil-updates>
</div>
</div>
</div>
<!-- END OF HIDDEN QUCK LINKS -->
<!-- MAIN PANNEL -->
<div class="col-sm-12 col-md-8 col-xxl-9 ">
<!-- ROW WITH PROFIL IMAGE AND INFORMATION -->
<!-- INFORMATION CONTENT START -->
<!-- ROW WITH THE PROFIL INFORMATION -->
<div class="row mb-4 ">
<div class="col-lg-12 col-xl-6 ">
<div class="row mb-4">
<div class="col">
<!-- PROFIL INFORMATION -->
<profil-information @showEditProfilModal="showEditProfilModal" :title="$p.t('profil','studentIn')" :data="profilInformation" :editable="editable"></profil-information>
</div>
</div>
<div class="row mb-4">
<div class=" col-lg-12">
<!-- STUDENT INFO -->
<role-information :title="$p.t('profil','studentInformation')" :data="roleInformation"></role-information>
</div>
</div>
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
</div>
<div class="col-xl-6 col-lg-12 ">
<div class="row mb-4">
<div class="col">
<!-- EMAILS -->
<profil-emails :title="this.$p.t('person','email')" :data="data.emails" ></profil-emails>
</div>
</div>
<div class="row mb-4 ">
<div class="col">
<!-- PRIVATE KONTAKTE-->
<div class="card">
<div class="card-header">
<div class="row">
<div @click="showEditProfilModal('Private_Kontakte')" class="col-auto" type="button">
<i class="fa fa-edit"></i>
</div>
<div class="col">
<span>{{$p.t('profil','privateKontakte')}}</span>
</div>
</div>
</div>
<div class="card-body ">
<div class="gy-3 row">
<div v-for="element in data.kontakte" class="col-12">
<Kontakt :data="element"></Kontakt>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col">
<!-- PRIVATE ADRESSEN-->
<div class="card">
<div class="card-header">
<div class="row">
<div @click="showEditProfilModal('Private_Adressen')" class="col-auto" type="button">
<i class="fa fa-edit"></i>
</div>
<div class="col">
<span>{{$p.t('profil','privateAdressen')}}</span>
</div>
</div>
</div>
<div class="card-body">
<div class="gy-3 row ">
<div v-for="element in data.adressen" class="col-12">
<Adresse :data="element"></Adresse>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div >
<!-- SECOND ROW UNDER THE PROFIL IMAGE AND INFORMATION WITH THE TABLES -->
<div class="row">
<div class="col-12 mb-4" >
<core-filter-cmpt @tableBuilt="betriebsmittelTableBuilt" :title="$p.t('profil','entlehnteBetriebsmittel')" ref="betriebsmittelTable" :tabulator-options="betriebsmittel_table_options" tableOnly :sideMenu="false" />
</div>
<div class="col-12 mb-4" >
<core-filter-cmpt @tableBuilt="zutrittsgruppenTableBuilt" :title="$p.t('profil','zutrittsGruppen')" ref="zutrittsgruppenTable" :tabulator-options="zutrittsgruppen_table_options" tableOnly :sideMenu="false" noColumnFilter />
</div>
</div>
<!-- END OF MAIN CONTENT COL -->
</div>
<!-- START OF SIDE PANEL -->
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
<div class="row d-none d-md-block mb-3">
<div class="col">
<!-- QUICK LINKS -->
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
</div>
</div>
<!-- Bearbeiten Button -->
<div class="row d-none d-md-block">
<div class="col mb-3">
<button @click="()=>showEditProfilModal()" type="button" class="text-start w-100 btn btn-outline-secondary" >
<div class="row">
<div class="col-2">
<i class="fa fa-edit"></i>
</div>
<div class="col-10">{{$p.t('ui','bearbeiten')}}</div>
</div>
</button>
</div>
</div>
<div v-if="data.profilUpdates" class="row d-none d-md-block mb-3">
<div class="col mb-3">
<!-- PROFIL UPDATES -->
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates"></fetch-profil-updates>
</div>
</div>
<div class="row mb-3" >
<div class="col-12">
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
</div>
</div>
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
<div class="row">
<div class="col">
<!-- HIER SIND DIE MAILVERTEILER -->
<mailverteiler :title="$p.t('profil','mailverteiler')" :data="data?.mailverteiler"></mailverteiler>
</div>
<!-- END OF THE SECOND ROW IN THE SIDE PANEL -->
</div>
<!-- END OF SIDE PANEL -->
</div>
<!-- END OF CONTAINER ROW-->
</div>
<!-- END OF CONTAINER -->
</div>
`,
};