230 lines
7.3 KiB
Vue
230 lines
7.3 KiB
Vue
<template>
|
|
<div class="min-h-full py-16 bg-gray-50">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h1 class="text-4xl font-display font-bold text-gray-900">
|
|
Kontaktanfragen
|
|
</h1>
|
|
<div class="w-24 h-1 bg-primary-600 mt-4" />
|
|
</div>
|
|
<button
|
|
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700"
|
|
:disabled="isLoading"
|
|
@click="loadRequests"
|
|
>
|
|
{{ isLoading ? 'Lädt...' : 'Aktualisieren' }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mb-4 flex items-center justify-end">
|
|
<label class="inline-flex items-center gap-2 text-sm text-gray-700">
|
|
<input
|
|
v-model="showAnswered"
|
|
type="checkbox"
|
|
class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500"
|
|
>
|
|
Bearbeitete Anfragen anzeigen
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="isLoading" class="text-center py-12 text-gray-600">
|
|
Lade Kontaktanfragen...
|
|
</div>
|
|
|
|
<div v-else-if="filteredRequests.length === 0" class="bg-white rounded-xl shadow p-8 text-center text-gray-600">
|
|
{{ showAnswered ? 'Aktuell liegen keine Kontaktanfragen vor.' : 'Aktuell liegen keine offenen Kontaktanfragen vor.' }}
|
|
</div>
|
|
|
|
<div v-else class="space-y-4">
|
|
<div
|
|
v-for="request in filteredRequests"
|
|
:key="request.id"
|
|
class="bg-white rounded-xl shadow border border-gray-100"
|
|
>
|
|
<div class="p-5 border-b border-gray-100 flex items-start justify-between gap-4">
|
|
<div>
|
|
<p class="text-lg font-semibold text-gray-900">
|
|
{{ request.subject }}
|
|
</p>
|
|
<p class="text-sm text-gray-600">
|
|
Von {{ request.name }} ({{ request.email }}){{ request.phone ? ` · ${request.phone}` : '' }}
|
|
</p>
|
|
<p class="text-xs text-gray-500 mt-1">
|
|
Eingegangen: {{ formatDate(request.createdAt) }}
|
|
</p>
|
|
</div>
|
|
<span
|
|
class="px-2.5 py-1 rounded-full text-xs font-semibold"
|
|
:class="request.status === 'beantwortet' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'"
|
|
>
|
|
{{ request.status === 'beantwortet' ? 'Beantwortet' : 'Offen' }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="p-5">
|
|
<p class="text-gray-800 whitespace-pre-wrap">
|
|
{{ request.message }}
|
|
</p>
|
|
|
|
<div v-if="Array.isArray(request.replies) && request.replies.length > 0" class="mt-5 border-t border-gray-100 pt-4">
|
|
<h3 class="text-sm font-semibold text-gray-700 mb-2">
|
|
Antworten
|
|
</h3>
|
|
<div class="space-y-2">
|
|
<div
|
|
v-for="reply in request.replies"
|
|
:key="reply.id"
|
|
class="bg-gray-50 rounded-lg p-3"
|
|
>
|
|
<p class="text-xs text-gray-500 mb-1">
|
|
{{ formatDate(reply.createdAt) }}{{ reply.responderEmail ? ` · ${reply.responderEmail}` : '' }}
|
|
</p>
|
|
<p class="text-sm text-gray-800 whitespace-pre-wrap">
|
|
{{ reply.message }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 flex justify-end">
|
|
<button
|
|
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700"
|
|
@click="openReplyModal(request)"
|
|
>
|
|
Antworten
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="replyModalOpen && selectedRequest"
|
|
class="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4"
|
|
@click.self="closeReplyModal"
|
|
>
|
|
<div class="bg-white rounded-xl shadow-2xl max-w-2xl w-full p-6">
|
|
<h2 class="text-2xl font-display font-bold text-gray-900 mb-2">
|
|
Antwort senden
|
|
</h2>
|
|
<p class="text-sm text-gray-600 mb-4">
|
|
An: {{ selectedRequest.email }}<br>
|
|
Betreff: <strong>Aw: {{ selectedRequest.subject }}</strong>
|
|
</p>
|
|
<textarea
|
|
v-model="replyText"
|
|
rows="8"
|
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-600"
|
|
placeholder="Ihre Antwort..."
|
|
/>
|
|
<div v-if="errorMessage" class="mt-3 text-sm text-red-600">
|
|
{{ errorMessage }}
|
|
</div>
|
|
<div class="mt-5 flex justify-end gap-3">
|
|
<button
|
|
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200"
|
|
:disabled="isSendingReply"
|
|
@click="closeReplyModal"
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
<button
|
|
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 disabled:opacity-50"
|
|
:disabled="isSendingReply || !replyText.trim()"
|
|
@click="sendReply"
|
|
>
|
|
{{ isSendingReply ? 'Sende...' : 'Antwort senden' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
|
const requests = ref([])
|
|
const isLoading = ref(false)
|
|
const replyModalOpen = ref(false)
|
|
const selectedRequest = ref(null)
|
|
const replyText = ref('')
|
|
const isSendingReply = ref(false)
|
|
const errorMessage = ref('')
|
|
const showAnswered = ref(false)
|
|
|
|
const filteredRequests = computed(() => {
|
|
if (showAnswered.value) return requests.value
|
|
return requests.value.filter((request) => request.status !== 'beantwortet')
|
|
})
|
|
|
|
const formatDate = (value) => {
|
|
if (!value) return '-'
|
|
return new Date(value).toLocaleString('de-DE')
|
|
}
|
|
|
|
const loadRequests = async () => {
|
|
isLoading.value = true
|
|
try {
|
|
requests.value = await $fetch('/api/cms/contact-requests')
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Kontaktanfragen:', error)
|
|
requests.value = []
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
const openReplyModal = (request) => {
|
|
selectedRequest.value = request
|
|
replyText.value = ''
|
|
errorMessage.value = ''
|
|
replyModalOpen.value = true
|
|
}
|
|
|
|
const closeReplyModal = () => {
|
|
replyModalOpen.value = false
|
|
selectedRequest.value = null
|
|
replyText.value = ''
|
|
errorMessage.value = ''
|
|
}
|
|
|
|
const sendReply = async () => {
|
|
if (!selectedRequest.value) return
|
|
const text = replyText.value.trim()
|
|
if (!text) return
|
|
|
|
isSendingReply.value = true
|
|
errorMessage.value = ''
|
|
try {
|
|
await $fetch(`/api/cms/contact-requests/${selectedRequest.value.id}/reply`, {
|
|
method: 'POST',
|
|
body: { message: text }
|
|
})
|
|
closeReplyModal()
|
|
await loadRequests()
|
|
if (window.showSuccessModal) {
|
|
window.showSuccessModal('Erfolg', 'Antwort wurde erfolgreich versendet.')
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Senden der Antwort:', error)
|
|
errorMessage.value = error?.data?.statusMessage || error?.data?.message || 'Antwort konnte nicht gesendet werden.'
|
|
} finally {
|
|
isSendingReply.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(loadRequests)
|
|
|
|
definePageMeta({
|
|
middleware: 'auth',
|
|
layout: 'default'
|
|
})
|
|
|
|
useHead({
|
|
title: 'Kontaktanfragen - CMS - Harheimer TC'
|
|
})
|
|
</script>
|