1
0
mirror of https://github.com/TandoorRecipes/recipes.git synced 2026-01-11 17:16:59 -05:00
Files
recipes/vue3/src/pages/BookViewPage.vue
2025-05-13 16:24:23 +02:00

186 lines
6.5 KiB
Vue

<template>
<v-container>
<v-row>
<v-col>
<v-card>
<v-card-title>{{ book.name }}
<v-btn class="float-right" variant="flat" :to="{name: 'BooksPage'}" prepend-icon="$books" v-if="mdAndUp">{{ $t('Books') }}</v-btn>
</v-card-title>
<v-card-text v-if="book.shared && book.shared.length > 0">
<v-chip-group>
<v-label class="me-2">{{ $t('shared_with') }}</v-label>
<v-chip v-for="u in book.shared">{{ u.displayName }}</v-chip>
</v-chip-group>
</v-card-text>
<v-card-text class="text-disabled">
{{ book.description }}
</v-card-text>
<v-expansion-panels v-model="toc">
<v-expansion-panel>
<v-expansion-panel-title>{{ $t('Table_of_Contents') }}</v-expansion-panel-title>
<v-expansion-panel-text>
<v-list>
<v-list-item v-for="(entry, i) in recipes" :key="entry.id" @click="page = i; toc = false">
{{ entry.name }}
</v-list-item>
</v-list>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col class="text-center">
<v-pagination :model-value="page + 1"
@update:model-value="value => page = Math.max(value - 1, 0)"
:length="totalItems"
@next="page = Math.min(page + (mdAndUp ? 1 : 0), totalItems - 1)"
@prev="page = Math.max(page - (mdAndUp ? 1 : 0), 0)"
></v-pagination>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-window v-model="page" show-arrows>
<template #next>
<v-btn icon="fa-solid fa-chevron-right" variant="plain" @click="page = page + (mdAndUp ? 2 : 1)"></v-btn>
</template>
<template #prev>
<v-btn icon="fa-solid fa-chevron-left" variant="plain" @click="page = page - (mdAndUp ? 2 : 1)"></v-btn>
</template>
<v-window-item v-for="(entry, i) in recipes" :key="entry.id">
<v-row>
<v-col cols="12" md="6">
<book-entry-card :recipe-overview="recipes[i]"></book-entry-card>
<div class="text-center mt-1">
<span class="text-disabled">{{ i + 1 }}</span>
</div>
</v-col>
<v-col cols="6" v-if="mdAndUp && recipes.length > i + 1">
<book-entry-card :recipe-overview="recipes[i + 1]"></book-entry-card>
<div class="text-center mt-1">
<span class="text-disabled">{{ i + 2 }}</span>
</div>
</v-col>
</v-row>
</v-window-item>
</v-window>
</v-col>
</v-row>
</v-container>
</template>
<script setup lang="ts">
import {computed, onMounted, ref} from "vue";
import {ApiApi, RecipeBook, RecipeBookEntry, RecipeOverview} from "@/openapi";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {useRouter} from "vue-router";
import RecipeImage from "@/components/display/RecipeImage.vue";
import {useDisplay} from "vuetify";
import BookEntryCard from "@/components/display/BookEntryCard.vue";
const props = defineProps({
bookId: {type: String, required: true},
})
const {mdAndUp} = useDisplay()
const router = useRouter()
const loading = ref(false)
const loadingEntries = ref(false)
const toc = ref(false)
const page = ref(0)
const manualItems = ref(0)
const filterItems = ref(0)
const totalItems = computed(() => {
return manualItems.value + filterItems.value
})
const book = ref({} as RecipeBook)
const entries = ref([] as RecipeBookEntry[])
const recipes = ref([] as RecipeOverview[])
onMounted(() => {
loadBook()
})
/**
* load the given book and trigger loading its entries
*/
function loadBook() {
const api = new ApiApi()
loading.value = true
api.apiRecipeBookRetrieve({id: props.bookId}).then(r => {
book.value = r
entries.value = []
recLoadEntries(1)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
loading.value = false
})
}
/**
* recursively load the book entries and trigger loading all entries from a saved custom filter
* @param page
*/
function recLoadEntries(page: number) {
const api = new ApiApi()
loadingEntries.value = true
api.apiRecipeBookEntryList({book: props.bookId, page: page, pageSize: 50}).then(r => {
r.results.forEach(rBE => {
recipes.value.push(rBE.recipeContent)
})
manualItems.value = r.count
if (r.next) {
recLoadEntries(page + 1)
} else {
if (book.value.filter) {
recLoadFilter(book.value.filter.id, 1)
} else {
loadingEntries.value = false
}
}
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
loadingEntries.value = false
})
}
/**
* recursively load the recipes matched by the custom filter configured in the book
* @param filterId filter id to look for
* @param page page to load
*/
function recLoadFilter(filterId: number, page: number) {
let api = new ApiApi()
api.apiRecipeList({filter: filterId, page: page, pageSize: 50}).then(r => {
recipes.value = recipes.value.concat(r.results)
manualItems.value = r.count
if (r.next) {
recLoadFilter(filterId, page + 1)
} else {
loadingEntries.value = false
}
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
loadingEntries.value = false
})
}
</script>
<style scoped></style>