Enhance news component functionality and UI; implement dynamic grid layout in PublicNews.vue, add visibility and expiration options in news management, and update API to handle new fields for improved news filtering and display.

This commit is contained in:
Torsten Schulz (local)
2025-10-24 12:47:27 +02:00
parent bf97cfd508
commit 75e6919f13
5 changed files with 167 additions and 35 deletions

View File

@@ -43,6 +43,20 @@
<Globe :size="14" class="mr-1" />
Öffentlich
</span>
<span
v-if="item.isHidden"
class="px-3 py-1 bg-yellow-100 text-yellow-800 text-xs font-semibold rounded-full flex items-center"
>
<EyeOff :size="14" class="mr-1" />
Ausgeblendet
</span>
<span
v-if="item.expiresAt && isExpired(item.expiresAt)"
class="px-3 py-1 bg-red-100 text-red-800 text-xs font-semibold rounded-full flex items-center"
>
<Calendar :size="14" class="mr-1" />
Abgelaufen
</span>
</div>
<div class="flex items-center text-sm text-gray-500 space-x-4">
<div class="flex items-center">
@@ -145,6 +159,40 @@
</label>
</div>
<div v-if="formData.isPublic" class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Ablaufdatum (optional)</label>
<input
v-model="formData.expiresAt"
type="datetime-local"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
:disabled="isSaving"
/>
<p class="text-xs text-gray-600 mt-1">
Nach diesem Datum wird die News automatisch nicht mehr auf der Startseite angezeigt.
</p>
</div>
<div class="flex items-center space-x-3 p-4 bg-yellow-50 rounded-lg border border-yellow-200">
<input
id="isHidden"
v-model="formData.isHidden"
type="checkbox"
class="w-5 h-5 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
:disabled="isSaving"
/>
<label for="isHidden" class="text-sm font-medium text-gray-900 cursor-pointer flex-1">
<div class="flex items-center">
<EyeOff :size="18" class="mr-2 text-yellow-600" />
<span>News ausblenden</span>
</div>
<p class="text-xs text-gray-600 mt-1 ml-6">
News wird nicht auf der Startseite angezeigt, bleibt aber im internen Bereich sichtbar.
</p>
</label>
</div>
</div>
<div v-if="errorMessage" class="flex items-center p-3 rounded-md bg-red-50 text-red-700 text-sm">
<AlertCircle :size="20" class="mr-2" />
{{ errorMessage }}
@@ -177,7 +225,7 @@
<script setup>
import { ref, computed, onMounted } from 'vue'
import { Newspaper, Plus, User, Calendar, Edit, Trash2, Loader2, AlertCircle, Globe } from 'lucide-vue-next'
import { Newspaper, Plus, User, Calendar, Edit, Trash2, Loader2, AlertCircle, Globe, EyeOff } from 'lucide-vue-next'
const authStore = useAuthStore()
@@ -191,7 +239,9 @@ const errorMessage = ref('')
const formData = ref({
title: '',
content: '',
isPublic: false
isPublic: false,
expiresAt: '',
isHidden: false
})
const canWrite = computed(() => {
@@ -215,7 +265,9 @@ const openAddModal = () => {
formData.value = {
title: '',
content: '',
isPublic: false
isPublic: false,
expiresAt: '',
isHidden: false
}
showModal.value = true
errorMessage.value = ''
@@ -226,12 +278,30 @@ const openEditModal = (item) => {
formData.value = {
title: item.title,
content: item.content,
isPublic: item.isPublic || false
isPublic: item.isPublic || false,
expiresAt: item.expiresAt ? convertUTCToLocal(item.expiresAt) : '',
isHidden: item.isHidden || false
}
showModal.value = true
errorMessage.value = ''
}
// Convert UTC ISO string to datetime-local format
const convertUTCToLocal = (utcDateTime) => {
// UTC ISO format: "2025-10-24T12:39:00.000Z"
// Convert to local datetime-local format: "2025-10-24T12:39"
const utcDate = new Date(utcDateTime)
// Get local date components
const year = utcDate.getFullYear()
const month = String(utcDate.getMonth() + 1).padStart(2, '0')
const day = String(utcDate.getDate()).padStart(2, '0')
const hours = String(utcDate.getHours()).padStart(2, '0')
const minutes = String(utcDate.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day}T${hours}:${minutes}`
}
const closeModal = () => {
showModal.value = false
editingNews.value = null
@@ -243,12 +313,19 @@ const saveNews = async () => {
errorMessage.value = ''
try {
// Convert datetime-local to ISO string (local time to UTC)
const dataToSend = {
id: editingNews.value?.id,
title: formData.value.title,
content: formData.value.content,
isPublic: formData.value.isPublic,
isHidden: formData.value.isHidden,
expiresAt: formData.value.expiresAt ? convertLocalToUTC(formData.value.expiresAt) : undefined
}
await $fetch('/api/news', {
method: 'POST',
body: {
id: editingNews.value?.id,
...formData.value
}
body: dataToSend
})
closeModal()
@@ -260,10 +337,17 @@ const saveNews = async () => {
}
}
const confirmDelete = async (item) => {
console.log('Delete item:', item)
console.log('Delete item.id:', item.id)
// Convert datetime-local to UTC ISO string
const convertLocalToUTC = (localDateTime) => {
// datetime-local format: "2025-10-24T12:39"
// Create Date object in local timezone
const localDate = new Date(localDateTime)
// Convert to UTC ISO string
return localDate.toISOString()
}
const confirmDelete = async (item) => {
window.showConfirmModal('News löschen', `Möchten Sie die News "${item.title}" wirklich löschen?`, async () => {
if (!item.id) {
window.showErrorModal('Fehler', 'News-ID fehlt!')
@@ -271,7 +355,6 @@ const confirmDelete = async (item) => {
}
try {
console.log('Deleting with ID:', item.id)
await $fetch(`/api/news?id=${encodeURIComponent(item.id)}`, {
method: 'DELETE'
})
@@ -279,7 +362,6 @@ const confirmDelete = async (item) => {
await loadNews()
window.showSuccessModal('Erfolg', 'News wurde erfolgreich gelöscht')
} catch (error) {
console.error('Delete error:', error)
window.showErrorModal('Fehler', 'Fehler beim Löschen der News: ' + (error.data?.message || error.message))
}
})
@@ -297,6 +379,11 @@ const formatDate = (dateString) => {
})
}
const isExpired = (expiresAt) => {
if (!expiresAt) return false
return new Date(expiresAt) <= new Date()
}
onMounted(() => {
loadNews()
})