mandant-crm/frontend/components/history/HistoryEntry.vue

91 lines
3.3 KiB
Vue
Raw Permalink Normal View History

<script setup lang="ts">
import type { HistoryEntry } from '~/types'
const props = defineProps<{ entry: HistoryEntry }>()
const emit = defineEmits<{
delete: [id: number]
update: [id: number, payload: { body: string; occurred_at: string | null }]
}>()
const { user } = useAuth()
// Auto-generated change entries are read-only audit records; only the author
// may edit their own manual notes.
const canEdit = computed(() => !props.entry.is_field_change && props.entry.author_id === user.value?.id)
// Only admins may delete Verlauf entries (manual notes and change entries alike).
const canDelete = computed(() => user.value?.role === 'admin')
const editing = ref(false)
const editBody = ref(props.entry.body)
// datetime-local wants "YYYY-MM-DDTHH:MM" (no seconds); occurred_at carries seconds.
const editOccurredAt = ref(props.entry.occurred_at?.slice(0, 16) ?? '')
const saveEdit = () => {
const occurred_at = props.entry.requires_datetime && editOccurredAt.value
? `${editOccurredAt.value}:00`
: null
emit('update', props.entry.id, { body: editBody.value, occurred_at })
editing.value = false
}
const formatDate = (iso: string) =>
new Date(iso).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' })
</script>
<template>
<div class="border border-gray-200 bg-white p-4">
<div class="flex items-start justify-between gap-2 mb-2">
<div class="flex items-center gap-2">
<span class="text-xs font-medium bg-gray-100 text-gray-600 px-2 py-0.5">
{{ entry.type_name ?? '—' }}<template v-if="entry.occurred_at"> {{ formatEventDateTime(entry.occurred_at) }}</template>
</span>
<span class="text-xs text-gray-400">{{ formatDate(entry.created_at) }}</span>
<span class="text-xs text-gray-400">{{ entry.author_name }}</span>
<span v-if="entry.updated_by_name" class="text-xs text-gray-400 italic">
(bearb. {{ entry.updated_by_name }})
</span>
</div>
<div class="flex gap-1 shrink-0">
<button
v-if="canEdit"
@click="editing = !editing"
:title="editing ? 'Abbrechen' : 'Bearbeiten'"
:aria-label="editing ? 'Abbrechen' : 'Bearbeiten'"
class="p-1 text-gray-400 hover:text-blue-600"
>
<IconClose v-if="editing" />
<IconEdit v-else />
</button>
<button
v-if="canDelete"
@click="emit('delete', entry.id)"
title="Löschen"
aria-label="Löschen"
class="p-1 text-gray-400 hover:text-red-600"
>
<IconTrash />
</button>
</div>
</div>
<div v-if="!editing" class="text-sm text-gray-800 whitespace-pre-wrap">{{ entry.body }}</div>
<div v-else class="space-y-2">
<div v-if="entry.requires_datetime">
<label class="block text-xs font-medium text-gray-600 mb-1">Datum/Uhrzeit</label>
<input
v-model="editOccurredAt"
type="datetime-local"
class="border border-gray-300 px-2 py-1.5 text-sm"
/>
</div>
<textarea
v-model="editBody"
rows="4"
class="w-full border border-gray-300 px-3 py-2 text-sm resize-y"
/>
<button @click="saveEdit" class="bg-blue-600 text-white px-3 py-1 text-sm hover:bg-blue-700">
Speichern
</button>
</div>
</div>
</template>