Files
harheimertc/pages/cms/kontaktanfragen.vue

256 lines
8.2 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' ? 'Erledigt' : '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 gap-2">
<button
type="button"
class="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50"
:disabled="togglingId === request.id"
@click="toggleStatus(request)"
>
{{ togglingId === request.id ? '…' : (request.status === 'beantwortet' ? 'Wieder öffnen' : 'Als erledigt markieren') }}
</button>
<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 togglingId = ref(null)
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 toggleStatus = async (request) => {
togglingId.value = request.id
try {
await $fetch(`/api/cms/contact-requests/${request.id}/toggle-status`, {
method: 'PATCH'
})
await loadRequests()
} catch (error) {
console.error('Fehler beim Umschalten des Status:', error)
if (window.showErrorModal) {
window.showErrorModal('Fehler', error?.data?.statusMessage || 'Status konnte nicht geändert werden.')
}
} finally {
togglingId.value = null
}
}
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>