generated from Flycro/laravel-nuxt
feat: BR Overview + Modal for New Recommendations
parent
923e41b396
commit
fffe1b4717
|
|
@ -19,6 +19,10 @@ const columns = [
|
|||
key: 'author',
|
||||
label: 'Autor',
|
||||
},
|
||||
{
|
||||
key: 'published_at',
|
||||
label: 'Erstveröffentlichung',
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: 'Beschreibung',
|
||||
|
|
@ -42,27 +46,46 @@ const columns = [
|
|||
{
|
||||
key: 'votes',
|
||||
label: 'Votes',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
label: '',
|
||||
},
|
||||
]
|
||||
const sort = ref({
|
||||
column: 'votes',
|
||||
direction: 'desc',
|
||||
})
|
||||
|
||||
function resolveStatus(status: string) {
|
||||
return bookRecommendationStore.statusOptions.find(option => option.value === status)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<NewBookRecommendation />
|
||||
<UTable :loading="bookRecommendationStore.fetchRecommendationsStatus === 'pending'" :columns="columns" :rows="bookRecommendationStore.recommendations">
|
||||
<UTable :sort="sort" :loading="bookRecommendationStore.fetchRecommendationsStatus === 'pending'" :columns="columns" :rows="bookRecommendationStore.recommendations">
|
||||
<template #created_at-data="{ row }">
|
||||
<div>{{ dayjs(row.created_at).format('DD.MM.YYYY') }}</div>
|
||||
</template>
|
||||
<template #published_at-data="{ row }">
|
||||
<div>{{ dayjs(row.published_at).format('DD.MM.YYYY') }}</div>
|
||||
</template>
|
||||
<template #description-data="{ row }">
|
||||
<div v-if="row.description">
|
||||
{{ `${row.description.substring(0, 50)}...` }}
|
||||
</div>
|
||||
</template>
|
||||
<template #votes-data="{ row }">
|
||||
{{ row.votes.length }}
|
||||
</template>
|
||||
<template #status-data="{ row }">
|
||||
<UBadge :color="resolveStatus(row.status)?.color">
|
||||
{{ resolveStatus(row.status)?.name }}
|
||||
</UBadge>
|
||||
</template>
|
||||
<template #actions-data="{ row }">
|
||||
<div class="flex space-x-2">
|
||||
<CastVote :row="row" />
|
||||
|
|
|
|||
|
|
@ -2,50 +2,95 @@
|
|||
import { useBookRecommendationStore } from '~/stores/book-recommendations'
|
||||
|
||||
const isOpen = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const dayjs = useDayjs()
|
||||
|
||||
const form = ref()
|
||||
|
||||
const state = reactive({
|
||||
book_name: null,
|
||||
author: null,
|
||||
description: null,
|
||||
isbn: null,
|
||||
pages: null,
|
||||
cover_image: null,
|
||||
status: null,
|
||||
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 | undefined
|
||||
}
|
||||
|
||||
const state: State = reactive({
|
||||
book_name: '',
|
||||
author: '',
|
||||
description: '',
|
||||
isbn: '',
|
||||
pages: 0,
|
||||
cover_image: '',
|
||||
status: 'PENDING',
|
||||
published_at: dayjs().format('YYYY-MM-DD'),
|
||||
})
|
||||
|
||||
const bookRecommendationStore = useBookRecommendationStore()
|
||||
|
||||
const { refresh: onSubmit, status } = useFetch<any>(`book-recommendations`, {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
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 {
|
||||
if (key === 'cover_image') {
|
||||
continue
|
||||
}
|
||||
const value = typeof item === 'string' ? item : String(item)
|
||||
formData.append(key, value)
|
||||
}
|
||||
}
|
||||
await $fetch<any>(`book-recommendations`, {
|
||||
method: 'POST',
|
||||
body: state,
|
||||
immediate: false,
|
||||
watch: false,
|
||||
body: formData,
|
||||
async onResponse({ response }) {
|
||||
loading.value = false
|
||||
if (response?.status === 422) {
|
||||
form.value.setErrors(response._data?.errors)
|
||||
}
|
||||
else if (response.ok) {
|
||||
useToast().add({
|
||||
icon: 'i-heroicons-check-circle-20-solid',
|
||||
title: 'Buchempfehlung wurde erfolgreich aktualisiert.',
|
||||
title: 'Buchempfehlung wurde erfolgreich angelegt.',
|
||||
color: 'emerald',
|
||||
})
|
||||
await bookRecommendationStore.fetchRecommendations()
|
||||
|
||||
state.book_name = null
|
||||
state.author = null
|
||||
state.description = null
|
||||
state.isbn = null
|
||||
state.pages = null
|
||||
state.cover_image = null
|
||||
state.status = null
|
||||
state.book_name = ''
|
||||
state.author = ''
|
||||
state.description = ''
|
||||
state.isbn = ''
|
||||
state.pages = 0
|
||||
state.cover_image = ''
|
||||
state.status = 'PENDING'
|
||||
state.published_at = dayjs().format('YYYY-MM-DD')
|
||||
|
||||
isOpen.value = false
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -72,9 +117,15 @@ const { refresh: onSubmit, status } = useFetch<any>(`book-recommendations`, {
|
|||
<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>
|
||||
<UFormGroup label="Cover" name="cover_image">
|
||||
<UInput type="file" @change="handleCoverImageInput" />
|
||||
</UFormGroup>
|
||||
<UFormGroup label="ISBN" name="isbn">
|
||||
<UInput v-model="state.isbn" />
|
||||
</UFormGroup>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
enum BookRecommendationStatusEnum {
|
||||
export enum BookRecommendationStatusEnum {
|
||||
PENDING = 'PENDING',
|
||||
REJECTED = 'REJECTED',
|
||||
ACTIVE = 'ACTIVE',
|
||||
|
|
@ -23,6 +23,7 @@ export interface BookRecommendation {
|
|||
}
|
||||
status: BookRecommendationStatusEnum
|
||||
cover_image?: string
|
||||
published_at?: string
|
||||
}
|
||||
|
||||
export const useBookRecommendationStore = defineStore('bookRecommendations', () => {
|
||||
|
|
@ -30,20 +31,25 @@ export const useBookRecommendationStore = defineStore('bookRecommendations', ()
|
|||
|
||||
const statusOptions = [
|
||||
{
|
||||
name: 'Pending',
|
||||
name: 'Ausstehend',
|
||||
value: BookRecommendationStatusEnum.PENDING,
|
||||
color: 'orange',
|
||||
},
|
||||
{
|
||||
name: 'Rejected',
|
||||
name: 'Abgelehnt',
|
||||
value: BookRecommendationStatusEnum.REJECTED,
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
name: 'Active',
|
||||
name: 'Aktiv',
|
||||
value: BookRecommendationStatusEnum.ACTIVE,
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
name: 'Completed',
|
||||
name: 'Abgeschlossen',
|
||||
value: BookRecommendationStatusEnum.COMPLETED,
|
||||
color: 'primary',
|
||||
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -57,29 +63,30 @@ export const useBookRecommendationStore = defineStore('bookRecommendations', ()
|
|||
},
|
||||
})
|
||||
|
||||
const deleteRecommendation = async (id: number) => {
|
||||
try {
|
||||
const { error } = await useFetch(`book-recommendations/${id}`, {
|
||||
method: 'DELETE',
|
||||
const { refresh: fetchActiveRecommendations, status: fetchActiveRecommendationsStatus } = useFetch<BookRecommendation[]>('book-recommendations?with=recommender,votes&status=ACTIVE', {
|
||||
immediate: false,
|
||||
onResponse({ response }) {
|
||||
if (response.status === 200) {
|
||||
recommendations.value = response._data
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
if (error.value) {
|
||||
console.error('Failed to delete book recommendation:', error.value)
|
||||
}
|
||||
else {
|
||||
recommendations.value = recommendations.value.filter(rec => rec.id !== id)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('An error occurred while deleting a book recommendation:', e)
|
||||
}
|
||||
function resetRecommendations() {
|
||||
recommendations.value = []
|
||||
}
|
||||
|
||||
return {
|
||||
recommendations,
|
||||
resetRecommendations,
|
||||
statusOptions,
|
||||
fetchRecommendations,
|
||||
fetchRecommendationsStatus,
|
||||
deleteRecommendation,
|
||||
fetchActiveRecommendations,
|
||||
fetchActiveRecommendationsStatus,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useBookRecommendationStore, import.meta.hot))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue