Two month of work
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, useTemplateRef, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/crm-button'
|
||||
import TextEditor from '@/components/TextEditor.vue';
|
||||
import { Trash2, CornerDownLeft, Pencil, X } from "lucide-vue-next"
|
||||
import { Note, NoteableType } from '@/types';
|
||||
import { bgColorForString, toDatetimeLocal, toDuration, toShortISOString } from '@/lib/utils';
|
||||
import { getInitials } from '@/composables/useInitials';
|
||||
import { alertStore } from '@/stores/alertStore';
|
||||
import { Kbd, KbdGroup } from '@/components/ui/kbd'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import { Input } from './ui/crm-input';
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { usePage } from '@inertiajs/vue3';
|
||||
import NotesService from '@/services/NotesService';
|
||||
|
||||
const props = defineProps<{
|
||||
notableId: number
|
||||
notableType: NoteableType
|
||||
title?: string
|
||||
modelValue?: Note[]
|
||||
}>()
|
||||
defineEmits(['update:modelValue'])
|
||||
const notes = ref(props.modelValue)
|
||||
const isTakingNote = ref(false)
|
||||
const noteEditor = useTemplateRef('note-editor')
|
||||
const noteDate = ref<string>(toDatetimeLocal(new Date())) //
|
||||
const alert = alertStore()
|
||||
const page = usePage();
|
||||
const auth = computed(() => page.props.auth);
|
||||
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
notes.value = newValue
|
||||
})
|
||||
|
||||
const toggleNoteEditor = () => {
|
||||
isTakingNote.value = !isTakingNote.value
|
||||
|
||||
if (isTakingNote.value) {
|
||||
noteDate.value = toDatetimeLocal(new Date())
|
||||
noteEditor.value?.editor?.commands.clearContent()
|
||||
noteEditor.value?.editor?.commands.focus()
|
||||
}
|
||||
}
|
||||
|
||||
const saveNote = async () => {
|
||||
if (!noteEditor.value?.getContent().trim()) {
|
||||
isTakingNote.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(`/api/notes`, {
|
||||
userId: auth.value.user.id,
|
||||
text: noteEditor.value?.getContent(),
|
||||
noteableId: props.notableId,
|
||||
noteableType: props.notableType,
|
||||
createdAt: new Date(noteDate.value).toISOString() || new Date().toISOString()
|
||||
});
|
||||
|
||||
// Add to notes array and sort by creation date
|
||||
notes.value?.unshift(response.data)
|
||||
notes.value?.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||
|
||||
// Clear editor and hide the note editor
|
||||
toggleNoteEditor()
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Speichern der Notiz:", error);
|
||||
toast.error("Fehler beim Speichern der Notiz", {
|
||||
description: (error as AxiosError).message || String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const deleteNote = async (id: number) => {
|
||||
alert.show(
|
||||
"Möchtest Du diese Notiz wirklich löschen?", null,
|
||||
{
|
||||
actionText: "Löschen",
|
||||
actionVariant: "destructive",
|
||||
onAction: async () => {
|
||||
const deleted = await NotesService.deleteNote(id)
|
||||
if (!deleted) return
|
||||
|
||||
// Remove from notes array
|
||||
const index = notes.value?.findIndex(note => note.id === id)
|
||||
if (index !== -1 && index !== undefined) {
|
||||
notes.value?.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="notes overflow-y-auto flex flex-col">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center gap-6 sticky top-0 bg-background z-1 mb-6">
|
||||
<h2 class="font-bold text-md">{{ title || 'Notizen' }}</h2>
|
||||
<Button variant="ghost" size="icon" @click="toggleNoteEditor">
|
||||
<X v-if="isTakingNote" />
|
||||
<Pencil v-else />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Note editor -->
|
||||
<div class="flex flex-col overflow-hidden transition-all min-h-40 bg-accent mb-8 rounded-lg p-4"
|
||||
:class="{ 'h-0! min-h-0! mb-0 py-0': !isTakingNote }">
|
||||
|
||||
<Input type="datetime-local" ref="note-date" class="mb-4" :model-value="noteDate"
|
||||
@update:model-value="value => noteDate = value as string" />
|
||||
|
||||
<TextEditor ref="note-editor" class="grow" />
|
||||
|
||||
<div class="flex gap-3 items-center justify-end">
|
||||
<KbdGroup class="ml-2">
|
||||
<Kbd class="visible-mac">⌘</Kbd>
|
||||
<Kbd class="visible-pc">Ctrl</Kbd>
|
||||
<Kbd>
|
||||
<CornerDownLeft class="h-3 w-3" />
|
||||
</Kbd>
|
||||
</KbdGroup>
|
||||
<Button variant="action" size="sm" @click="saveNote">
|
||||
Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notes -->
|
||||
<article v-for="note in notes" class="group">
|
||||
<div class="text-muted-foreground text-sm font-medium flex gap-3 items-center">
|
||||
|
||||
<Avatar class="size-7">
|
||||
<AvatarImage :src="'storage/uploads/users/' + (note.user.avatar || '')" loading="lazy" />
|
||||
<AvatarFallback :class="bgColorForString(getInitials(note.user.name))">
|
||||
{{ getInitials(note.user.name) }}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<span class="text-sm">{{ toDuration(note.createdAt) }}</span>
|
||||
|
||||
<div class="grow"></div>
|
||||
|
||||
<div class="transition-opacity opacity-0 group-hover:opacity-100">
|
||||
<Button variant="ghost" size="sm" @click="deleteNote(note.id)" class="text-muted-foreground">
|
||||
<Trash2 />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" @click="" class="text-muted-foreground">
|
||||
<Pencil />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-html="note.text" class="note-content ml-3.5 mt-3 pl-6.5 border-l" />
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="css">
|
||||
.notes {
|
||||
p:not(:last-child) {
|
||||
margin-bottom: calc(var(--spacing) * 1.333);
|
||||
}
|
||||
|
||||
article:not(:last-child) {
|
||||
margin-bottom: calc(var(--spacing) * 3);
|
||||
}
|
||||
|
||||
article:not(:last-child) .note-content {
|
||||
padding-bottom: calc(var(--spacing) * 3);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: calc(var(--spacing) * 4) 0;
|
||||
padding: calc(var(--spacing) * 4) calc(var(--spacing) * 6);
|
||||
color: var(--color-muted-foreground);
|
||||
background-color: var(--color-muted);
|
||||
border-radius: var(--radius-lg);
|
||||
border-left: 4px solid var(--color-border);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user