mirror of
https://github.com/Floriansylvain/snippets-manager-front.git
synced 2026-08-19 11:43:16 +02:00
feat: categories pagination & sort
This commit is contained in:
@@ -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,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 = ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user