Bugs in settings fixed, profile added

This commit is contained in:
Torsten Schulz
2024-09-21 00:25:42 +02:00
parent c5a72d57d8
commit e494fe41db
65 changed files with 3121 additions and 7478 deletions

View File

@@ -0,0 +1,110 @@
<template>
<DialogWidget ref="dialog" :title="$t('admin.editcontactrequest.title')" :show-close="true" :buttons="buttons"
@close="closeDialog" name="AnswerContact" :modal="true" :isTitleTranslated="true">
<div class="contact-details">
<h3>Von: {{ contact.email }}</h3>
<p>{{ contact.message }}</p>
</div>
<div class="editor-container">
<Editor v-model="answer" :init="tinymceInitOptions" :api-key="apiKey" />
</div>
</DialogWidget>
<DialogWidget ref="errorDialog" :title="$t('error.title')" :show-close="true" :buttons="errorButtons"
@close="closeErrorDialog" name="ErrorDialog" :modal="true" :isTitleTranslated="false">
<div>
<p>{{ errorMessage }}</p>
</div>
</DialogWidget>
</template>
<script>
import { ref, onBeforeUnmount } from 'vue'
import Editor from '@tinymce/tinymce-vue'
import apiClient from '@/utils/axios.js'
import DialogWidget from '@/components/DialogWidget.vue'
export default {
name: 'AnswerContact',
components: {
DialogWidget,
Editor,
},
data() {
return {
apiKey: import.meta.env.VITE_TINYMCE_API_KEY,
dialog: null,
errorDialog: null,
contact: null,
answer: '',
errorMessage: '',
tinymceInitOptions: {
height: 300,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar:
'undo redo cut copy paste | bold italic forecolor fontfamily fontsize | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help'
},
buttons: [
{ text: 'OK', action: this.sendAnswer },
{ text: 'Cancel', action: this.closeDialog }
],
errorButtons: [
{ text: 'OK', action: this.closeErrorDialog }
]
}
},
methods: {
open(contactData) {
this.contact = contactData;
this.dialog.open();
this.answer = '';
},
closeDialog() {
this.dialog.close();
this.answer = '';
},
closeErrorDialog() {
this.errorDialog.close();
},
async sendAnswer() {
try {
await apiClient.post('/api/admin/contacts/answer', {
id: this.contact.id,
answer: this.answer,
});
this.dialog.close();
this.$emit('refresh');
this.answer = '';
} catch (error) {
const errorText = error.response?.data?.error || 'An unexpected error occurred.';
this.errorMessage = errorText;
this.errorDialog.open();
}
}
},
mounted() {
this.dialog = this.$refs.dialog;
this.errorDialog = this.$refs.errorDialog;
},
beforeUnmount() {
// Aufräumarbeiten falls nötig
}
}
</script>
<style scoped>
.contact-details {
margin-bottom: 20px;
}
.editor-container {
margin-top: 20px;
}
</style>

View File

@@ -0,0 +1,149 @@
<template>
<DialogWidget ref="dialog" :title="$t('socialnetwork.profile.pretitle')" :isTitleTranslated="isTitleTranslated"
:show-close="true" :buttons="[{ text: 'Ok', action: 'close' }]" :modal="false" @close="closeDialog">
<div class="dialog-body">
<div>
<ul class="tab-list">
<li v-for="tab in tabs" :key="tab.name" :class="{ active: activeTab === tab.name }"
@click="selectTab(tab.name)">
{{ tab.label }}
</li>
</ul>
<div class="tab-content" v-if="activeTab === 'general'">
<table>
<tr v-for="(value, key) in userProfile.params" :key="key">
<td>{{ $t(`socialnetwork.profile.${key}`) }}</td>
<td>{{ generateValue(key, value) }}</td>
</tr>
</table>
</div>
</div>
</div>
</DialogWidget>
</template>
<script>
import DialogWidget from '@/components/DialogWidget.vue';
import apiClient from '@/utils/axios.js';
export default {
name: 'UserProfileDialog',
components: {
DialogWidget
},
props: {
userId: {
type: String,
required: true
}
},
data() {
return {
isTitleTranslated: true,
userProfile: {},
activeTab: 'general',
userId: '',
tabs: [
{ name: 'general', label: this.$t('socialnetwork.profile.tab.general') },
{ name: 'images', label: this.$t('socialnetwork.profile.tab.images') },
{ name: 'guestbook', label: this.$t('socialnetwork.profile.tab.guestbook') }
],
};
},
methods: {
open() {
this.$refs.dialog.open();
this.loadUserProfile();
},
async loadUserProfile() {
try {
const response = await apiClient.get(`/api/socialnetwork/profile/${this.userId}`);
this.userProfile = response.data;
const newTitle = this.$t('socialnetwork.profile.title').replace('<username>', this.userProfile.username);
this.$refs.dialog.updateTitle(newTitle, false);
} catch (error) {
this.$refs.dialog.updateTitle('socialnetwork.profile.error_title', true);
console.error('Fehler beim Laden des Benutzerprofils:', error);
}
},
closeDialog() {
this.$refs.dialog.close();
},
selectTab(tabName) {
this.activeTab = tabName;
},
generateValue(key, value) {
if (Array.isArray(value.value)) {
const strings = [];
for (const val of value.value) {
strings.push(this.generateValue(key, {type: value.type, value: val}));
}
return strings.join(', ');
}
switch (value.type) {
case 'bool':
return this.$t(`socialnetwork.profile.values.bool.${value.value}`);
case 'multiselect':
case 'singleselect':
return this.$t(`socialnetwork.profile.values.${key}.${value.value}`);
case 'date':
const date = new Date(value.value);
return date.toLocaleDateString();
case 'string':
case 'int':
return value.value;
case 'float':
return new Intl.NumberFormat(navigator.language, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(parseFloat(value.value));
default:
return value.value;
}
}
}
};
</script>
<style scoped>
.tab-list {
list-style-type: none;
padding: 0;
display: flex;
margin-bottom: 20px;
border-bottom: 2px solid #ccc;
}
.tab-list li {
padding: 10px 20px;
cursor: pointer;
margin-right: 5px;
border: 1px solid #ccc;
border-bottom: none;
background: #f9f9f9;
}
.tab-list li.active {
background: #ffffff;
border-bottom: 2px solid #ffffff;
font-weight: bold;
}
.tab-content {
padding: 20px;
border: 1px solid #ccc;
background: #ffffff;
overflow: auto;
}
.dialog-body,
.dialog-body > div {
height: 100%;
}
.dialog-body > div {
display: flex;
flex-direction: column;
}
</style>