generated from Flycro/laravel-nuxt
feat: Modal to Edit BookRecommendations
parent
3857ff70d2
commit
1a836b71f0
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useBookRecommendationStore } from '~/stores/book-recommendations'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const props = defineProps<{
|
||||
row: {
|
||||
|
|
@ -9,16 +10,39 @@ const props = defineProps<{
|
|||
description: string
|
||||
isbn: string
|
||||
pages: number
|
||||
cover_image?: string
|
||||
cover_image?: string | File
|
||||
status: string
|
||||
published_at: string
|
||||
recommender: {
|
||||
ulid: string
|
||||
}
|
||||
}
|
||||
}>()
|
||||
|
||||
const dayjs = useDayjs()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const { $storage } = useNuxtApp()
|
||||
|
||||
const isOpen = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const form = ref()
|
||||
|
||||
const state = reactive({
|
||||
interface State {
|
||||
book_name: string
|
||||
author: string
|
||||
description: string
|
||||
isbn: string
|
||||
pages: number
|
||||
cover_image?: File | string
|
||||
status: string
|
||||
published_at: string
|
||||
// Index signature
|
||||
[key: string]: string | number | File | Date | undefined
|
||||
}
|
||||
|
||||
const state: State = reactive({
|
||||
book_name: props.row.book_name,
|
||||
author: props.row.author,
|
||||
description: props.row.description,
|
||||
|
|
@ -26,16 +50,55 @@ const state = reactive({
|
|||
pages: props.row.pages,
|
||||
cover_image: props.row.cover_image,
|
||||
status: props.row.status,
|
||||
published_at: dayjs(props.row.published_at).format('YYYY-MM-DD'),
|
||||
})
|
||||
|
||||
watch(() => props.row, (newRow) => {
|
||||
state.book_name = newRow.book_name
|
||||
state.author = newRow.author
|
||||
state.description = newRow.description
|
||||
state.isbn = newRow.isbn
|
||||
state.pages = newRow.pages
|
||||
state.cover_image = newRow.cover_image
|
||||
state.status = newRow.status
|
||||
state.published_at = dayjs(newRow.published_at).format('YYYY-MM-DD')
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
function handleCoverImageInput(event: Event) {
|
||||
const file = (event.target as HTMLInputElement).files?.[0]
|
||||
if (file) {
|
||||
// Update the state with the selected file
|
||||
state.cover_image = file
|
||||
}
|
||||
}
|
||||
|
||||
const bookRecommendationStore = useBookRecommendationStore()
|
||||
|
||||
const { refresh: onSubmit, status } = useFetch<any>(`book-recommendations/${props.row.id}`, {
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
form.value.clear()
|
||||
loading.value = true
|
||||
const formData = new FormData()
|
||||
for (const key in state) {
|
||||
const item = state[key]
|
||||
if (item === undefined) {
|
||||
continue
|
||||
}
|
||||
if (key === 'cover_image' && state[key] instanceof File) {
|
||||
formData.append(key, item as Blob, (state[key] as File).name)
|
||||
}
|
||||
else {
|
||||
const value = typeof item === 'string' ? item : String(item)
|
||||
if (key === 'cover_image') {
|
||||
continue
|
||||
}
|
||||
formData.append(key, value)
|
||||
}
|
||||
}
|
||||
const response = await $fetch<any>(`book-recommendations/${props.row.id}`, {
|
||||
method: 'PUT',
|
||||
body: state,
|
||||
immediate: false,
|
||||
watch: false,
|
||||
body: formData,
|
||||
async onResponse({ response }) {
|
||||
loading.value = false
|
||||
if (response?.status === 422) {
|
||||
form.value.setErrors(response._data?.errors)
|
||||
}
|
||||
|
|
@ -45,16 +108,17 @@ const { refresh: onSubmit, status } = useFetch<any>(`book-recommendations/${prop
|
|||
title: 'Buchempfehlung wurde erfolgreich aktualisiert.',
|
||||
color: 'emerald',
|
||||
})
|
||||
await bookRecommendationStore.fetchRecommendations()
|
||||
isOpen.value = false
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
await bookRecommendationStore.fetchRecommendations()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<UButton icon="i-heroicons-pencil-square" size="sm" variant="solid" square @click="isOpen = true" />
|
||||
<UButton v-if="authStore.user.ulid === props.row.recommender.ulid || authStore.user.roles.includes('admin')" class="transition-150 transform-gpu hover:scale-110" icon="i-heroicons-pencil-square" size="sm" variant="solid" square @click="isOpen = true" />
|
||||
|
||||
<UModal v-model="isOpen">
|
||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
|
|
@ -67,16 +131,23 @@ const { refresh: onSubmit, status } = useFetch<any>(`book-recommendations/${prop
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<UForm ref="form" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UForm ref="form" :state="state" class="space-y-4" enctype="multipart/form-data" @submit="onSubmit">
|
||||
<UFormGroup label="Name" name="book_name">
|
||||
<UInput v-model="state.book_name" />
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Autor" name="author">
|
||||
<UInput v-model="state.author" />
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Erstveröffentlichung">
|
||||
<UInput v-model="state.published_at" type="date" />
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Beschreibung" name="description">
|
||||
<UTextarea v-model="state.description" />
|
||||
</UFormGroup>
|
||||
<img v-if="state.cover_image" :src="$storage(state.cover_image)" alt="Cover" class="size-1/3 content-center rounded-lg">
|
||||
<UFormGroup label="Cover" name="cover_image">
|
||||
<UInput type="file" @change="handleCoverImageInput" />
|
||||
</UFormGroup>
|
||||
<UFormGroup label="ISBN" name="isbn">
|
||||
<UInput v-model="state.isbn" />
|
||||
</UFormGroup>
|
||||
|
|
@ -86,7 +157,7 @@ const { refresh: onSubmit, status } = useFetch<any>(`book-recommendations/${prop
|
|||
<UFormGroup label="Status" name="status">
|
||||
<USelect v-model="state.status" :options="bookRecommendationStore.statusOptions" option-attribute="name" />
|
||||
</UFormGroup>
|
||||
<UButton size="md" type="submit" :loading="status === 'pending'">
|
||||
<UButton size="md" type="submit">
|
||||
Speichern
|
||||
</UButton>
|
||||
<UButton size="md" class="mx-4" color="white" label="Abbrechen" @click="isOpen = false" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue