feat: categories pagination & sort

This commit is contained in:
Florian Sylvain
2024-10-13 20:50:38 +02:00
parent 6e085a34ab
commit 57d431a0ba
4 changed files with 79 additions and 15 deletions
+34 -5
View File
@@ -10,10 +10,12 @@ interface CategoryItem {
const props = defineProps<{ const props = defineProps<{
fetchedCategories: Category | undefined fetchedCategories: Category | undefined
loading: boolean
itemsTotal: number
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
categoryDeleted: [id: number] categoriesUpdated: []
}>() }>()
const categoriesStore = useCategoriesStore() const categoriesStore = useCategoriesStore()
@@ -21,6 +23,9 @@ const categoriesStore = useCategoriesStore()
const deleteDialog: Ref<boolean> = ref(false) const deleteDialog: Ref<boolean> = ref(false)
const deleteDialogCategory: Ref<CategoryItem | undefined> = ref() const deleteDialogCategory: Ref<CategoryItem | undefined> = ref()
const itemsPerPageGlob: Ref<number> = ref(10)
const itemsPage: Ref<number> = ref(0)
function onDeleteClick(category: CategoryItem) { function onDeleteClick(category: CategoryItem) {
deleteDialogCategory.value = category deleteDialogCategory.value = category
deleteDialog.value = true deleteDialog.value = true
@@ -28,25 +33,49 @@ function onDeleteClick(category: CategoryItem) {
async function deleteCategory() { async function deleteCategory() {
const categoryPromise = await categoriesStore.deleteCategory(deleteDialogCategory.value?.id ?? -1) const categoryPromise = await categoriesStore.deleteCategory(deleteDialogCategory.value?.id ?? -1)
const response = await categoryPromise.json() await categoryPromise.json()
emit('categoryDeleted', response.id) const skip = itemsPage.value === 1 ? 0 : (itemsPage.value - 1) * itemsPerPageGlob.value
categoriesStore.updateSkipTake(skip, itemsPerPageGlob.value)
emit('categoriesUpdated')
deleteDialog.value = false deleteDialog.value = false
} }
async function loadItems({
page,
itemsPerPage,
sortBy
}: {
page: number
itemsPerPage: number
sortBy: { key: string; order: 'asc' | 'desc' }[]
}) {
itemsPage.value = page
itemsPerPageGlob.value = itemsPerPage
const skip = page === 1 ? 0 : (page - 1) * itemsPerPage
categoriesStore.updateSkipTake(skip, itemsPerPage)
if (sortBy[0]) categoriesStore.updateSort(sortBy[0].key, sortBy[0].order)
else categoriesStore.updateSort(undefined, undefined)
emit('categoriesUpdated')
}
</script> </script>
<template> <template>
<VDataTable <VDataTableServer
:headers="[ :headers="[
{ title: 'Name', key: 'name', sortable: true }, { title: 'Name', key: 'name', sortable: true },
{ title: 'Actions', key: 'actions', align: 'end', sortable: false } { title: 'Actions', key: 'actions', align: 'end', sortable: false }
]" ]"
:items="props.fetchedCategories?.categories" :items="props.fetchedCategories?.categories"
:loading="props.loading"
:items-length="props.itemsTotal"
v-model:items-per-page="itemsPerPageGlob"
@update:options="loadItems"
> >
<template v-slot:[`item.actions`]="{ item }"> <template v-slot:[`item.actions`]="{ item }">
<VBtn icon="mdi-pencil" variant="flat" size="small"></VBtn> <VBtn icon="mdi-pencil" variant="flat" size="small"></VBtn>
<VBtn icon="mdi-delete" variant="flat" size="small" @click="onDeleteClick(item)" /> <VBtn icon="mdi-delete" variant="flat" size="small" @click="onDeleteClick(item)" />
</template> </template>
</VDataTable> </VDataTableServer>
<VDialog max-width="340" v-model="deleteDialog"> <VDialog max-width="340" v-model="deleteDialog">
<VCard prepend-icon="mdi-delete" title="Delete Category"> <VCard prepend-icon="mdi-delete" title="Delete Category">
+3 -4
View File
@@ -3,7 +3,7 @@ import { useCategoriesStore } from '@/stores/categories'
import { ref, type Ref } from 'vue' import { ref, type Ref } from 'vue'
const emit = defineEmits<{ const emit = defineEmits<{
categoryCreated: [id: number] categoryCreated: []
}>() }>()
const dialog: Ref<boolean> = ref(false) const dialog: Ref<boolean> = ref(false)
@@ -20,9 +20,8 @@ const nameRules: Ref<Array<(v: string) => boolean | string>> = ref([
const categoriesStore = useCategoriesStore() const categoriesStore = useCategoriesStore()
async function onSubmit(): Promise<void> { async function onSubmit(): Promise<void> {
const createdCategoryPromise = await categoriesStore.postCategory(name.value) await categoriesStore.postCategory(name.value)
const createdCategory: { id: number } = await createdCategoryPromise.json() emit('categoryCreated')
emit('categoryCreated', createdCategory.id)
dialog.value = false dialog.value = false
name.value = '' name.value = ''
} }
+31 -2
View File
@@ -1,11 +1,34 @@
import { useApi } from '@/composables/api' import { useApi } from '@/composables/api'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useCategoriesStore = defineStore('categories', () => { export const useCategoriesStore = defineStore('categories', () => {
const api = useApi() const api = useApi()
const skip = ref(0)
const take = ref(10)
const orderBy = ref(undefined as string | undefined)
const direction = ref(undefined as string | undefined)
const updateSkipTake = (_skip: number, _take: number) => {
skip.value = _skip
take.value = _take
}
const updateSort = (_orderBy?: string, _direction?: 'asc' | 'desc') => {
orderBy.value = _orderBy
direction.value = _direction
}
const getCategories = async (): Promise<Response> => { const getCategories = async (): Promise<Response> => {
return await fetch(`${api.url}/category`, api.options) const sort =
orderBy.value && direction.value
? `&orderBy=${orderBy.value}&direction=${direction.value}`
: ''
return await fetch(
`${api.url}/category?skip=${skip.value}&take=${take.value}${sort}`,
api.options
)
} }
const postCategory = async (name: string): Promise<Response> => { const postCategory = async (name: string): Promise<Response> => {
@@ -23,5 +46,11 @@ export const useCategoriesStore = defineStore('categories', () => {
}) })
} }
return { getCategories, postCategory, deleteCategory } return {
getCategories,
postCategory,
deleteCategory,
updateSkipTake,
updateSort
}
}) })
+11 -4
View File
@@ -7,21 +7,28 @@ import { onMounted, ref, type Ref } from 'vue'
const categoriesStore = useCategoriesStore() const categoriesStore = useCategoriesStore()
const fetchedCategories: Ref<Category | undefined> = ref() const fetchedCategories: Ref<Category | undefined> = ref()
const loading: Ref<boolean> = ref(true)
async function updateCategories() { async function fetchCategories() {
const categoriesPromise = await categoriesStore.getCategories() const categoriesPromise = await categoriesStore.getCategories()
fetchedCategories.value = await categoriesPromise.json() fetchedCategories.value = await categoriesPromise.json()
loading.value = false
} }
onMounted(async () => { onMounted(async () => {
await updateCategories() await fetchCategories()
}) })
</script> </script>
<template> <template>
<header class="d-flex align-center justify-space-between"> <header class="d-flex align-center justify-space-between">
<h1 class="text-h3">Categories</h1> <h1 class="text-h3">Categories</h1>
<CategoryCreate @categoryCreated="updateCategories" /> <CategoryCreate @categoryCreated="fetchCategories" />
</header> </header>
<CategoriesTable :fetchedCategories="fetchedCategories" @categoryDeleted="updateCategories" /> <CategoriesTable
@categoriesUpdated="fetchCategories"
:fetchedCategories="fetchedCategories"
:loading="loading"
:itemsTotal="fetchedCategories?.total ?? 0"
/>
</template> </template>