Files
website/layers/diary/app/components/ReviewForm.vue
T
2026-09-30 19:15:57 +02:00

63 lines
2.0 KiB
Vue

<script setup lang="ts">
import type { FormSubmitEvent } from '@nuxt/ui'
const emit = defineEmits<{ saved: [] }>()
const { data: books, status } = useFetch<DiaryBook[]>('/api/diary/books', { key: 'diary-books', default: () => [], lazy: true })
// Recently read books first, then the rest by title
const bookItems = computed(() => books.value
.toSorted((a, b) => (b.readDate ?? '').localeCompare(a.readDate ?? '') || a.title.localeCompare(b.title, 'de'))
.map(book => ({
label: book.title,
description: book.authors.join('; '),
originalTitle: book.originalTitle,
value: book.id
})))
const state = reactive({ bookId: undefined as number | undefined, date: todayIso(), text: '' })
const { saving, save } = useSaveEntry({
url: '/api/diary/reviews',
dataKey: ['diary-reviews', 'diary-books'],
noun: 'review',
onSaved: () => emit('saved')
})
watch(() => state.bookId, (id) => {
state.date = books.value.find(book => book.id === id)?.readDate ?? todayIso()
})
function onSubmit(event: FormSubmitEvent<NewReviewInput>) {
return save(event.data)
}
</script>
<template>
<UForm :schema="newReviewSchema" :state="state" class="space-y-5" @submit="onSubmit">
<UFormField label="Book" name="bookId" required>
<USelectMenu
v-model="state.bookId"
:items="bookItems"
value-key="value"
:filter-fields="['label', 'description', 'originalTitle']"
:loading="status === 'pending'"
:search-input="{ placeholder: 'Title or author' }"
placeholder="Pick a book"
icon="i-lucide-book-open"
virtualize
class="w-full"
/>
</UFormField>
<UFormField label="Read on" name="date" help="Saved as the book's read date" required>
<UInput v-model="state.date" type="date" />
</UFormField>
<UFormField label="Review" name="text" required>
<UTextarea v-model="state.text" :rows="8" autoresize :maxrows="18" class="w-full" />
</UFormField>
<UButton type="submit" label="Save review" :icon="REVIEW_ICON" :loading="saving" />
</UForm>
</template>