feat: ajout de la pagination et des filtres dans le store d'activités, mise à jour des composants pour gérer l'affichage des jobs, et amélioration de la gestion des états des jobs. Intégration d'une nouvelle composante de pagination pour une navigation optimisée.
This commit is contained in:
parent
b456f9304d
commit
b4bfa48d00
202
assets/vue/app/shared/components/ui/Pagination.vue
Normal file
202
assets/vue/app/shared/components/ui/Pagination.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div v-if="totalPages > 1" class="flex items-center justify-between px-4 py-3 bg-white border-t border-gray-200">
|
||||
<!-- Informations de pagination -->
|
||||
<div class="flex items-center text-sm text-gray-700">
|
||||
<span>
|
||||
Affichage de
|
||||
<span class="font-medium">{{ startItem }}</span>
|
||||
à
|
||||
<span class="font-medium">{{ endItem }}</span>
|
||||
sur
|
||||
<span class="font-medium">{{ total }}</span>
|
||||
résultats
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Contrôles de pagination -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<!-- Bouton précédent -->
|
||||
<button
|
||||
@click="goToPage(currentPage - 1)"
|
||||
:disabled="!hasPreviousPage"
|
||||
:class="[
|
||||
'relative inline-flex items-center px-2 py-2 text-sm font-medium rounded-md',
|
||||
hasPreviousPage
|
||||
? 'text-gray-500 bg-white border border-gray-300 hover:bg-gray-50'
|
||||
: 'text-gray-300 bg-gray-100 border border-gray-200 cursor-not-allowed'
|
||||
]">
|
||||
<span class="sr-only">Précédent</span>
|
||||
<ChevronLeftIcon class="h-5 w-5" />
|
||||
</button>
|
||||
|
||||
<!-- Numéros de page -->
|
||||
<div class="hidden md:flex space-x-1">
|
||||
<!-- Première page -->
|
||||
<button
|
||||
v-if="showFirstPage"
|
||||
@click="goToPage(1)"
|
||||
:class="[
|
||||
'relative inline-flex items-center px-3 py-2 text-sm font-medium rounded-md',
|
||||
currentPage === 1
|
||||
? 'z-10 bg-indigo-50 border-indigo-500 text-indigo-600'
|
||||
: 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50'
|
||||
]">
|
||||
1
|
||||
</button>
|
||||
|
||||
<!-- Points de suspension gauche -->
|
||||
<span v-if="showLeftDots" class="relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700">
|
||||
...
|
||||
</span>
|
||||
|
||||
<!-- Pages visibles -->
|
||||
<button
|
||||
v-for="page in visiblePages"
|
||||
:key="page"
|
||||
@click="goToPage(page)"
|
||||
:class="[
|
||||
'relative inline-flex items-center px-3 py-2 text-sm font-medium rounded-md',
|
||||
currentPage === page
|
||||
? 'z-10 bg-indigo-50 border-indigo-500 text-indigo-600'
|
||||
: 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50'
|
||||
]">
|
||||
{{ page }}
|
||||
</button>
|
||||
|
||||
<!-- Points de suspension droite -->
|
||||
<span v-if="showRightDots" class="relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700">
|
||||
...
|
||||
</span>
|
||||
|
||||
<!-- Dernière page -->
|
||||
<button
|
||||
v-if="showLastPage"
|
||||
@click="goToPage(totalPages)"
|
||||
:class="[
|
||||
'relative inline-flex items-center px-3 py-2 text-sm font-medium rounded-md',
|
||||
currentPage === totalPages
|
||||
? 'z-10 bg-indigo-50 border-indigo-500 text-indigo-600'
|
||||
: 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50'
|
||||
]">
|
||||
{{ totalPages }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Pagination mobile -->
|
||||
<div class="md:hidden flex items-center space-x-2">
|
||||
<span class="text-sm text-gray-700">
|
||||
{{ currentPage }} / {{ totalPages }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Bouton suivant -->
|
||||
<button
|
||||
@click="goToPage(currentPage + 1)"
|
||||
:disabled="!hasNextPage"
|
||||
:class="[
|
||||
'relative inline-flex items-center px-2 py-2 text-sm font-medium rounded-md',
|
||||
hasNextPage
|
||||
? 'text-gray-500 bg-white border border-gray-300 hover:bg-gray-50'
|
||||
: 'text-gray-300 bg-gray-100 border border-gray-200 cursor-not-allowed'
|
||||
]">
|
||||
<span class="sr-only">Suivant</span>
|
||||
<ChevronRightIcon class="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/24/outline';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
currentPage: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
totalPages: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
hasNextPage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasPreviousPage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['page-change']);
|
||||
|
||||
// Calculs pour l'affichage des informations
|
||||
const startItem = computed(() => {
|
||||
return Math.max(1, (props.currentPage - 1) * props.limit + 1);
|
||||
});
|
||||
|
||||
const endItem = computed(() => {
|
||||
return Math.min(props.total, props.currentPage * props.limit);
|
||||
});
|
||||
|
||||
// Logique pour afficher les numéros de page
|
||||
const visiblePages = computed(() => {
|
||||
const pages = [];
|
||||
const maxVisible = 5; // Nombre maximum de pages visibles
|
||||
const half = Math.floor(maxVisible / 2);
|
||||
|
||||
let start = Math.max(1, props.currentPage - half);
|
||||
let end = Math.min(props.totalPages, start + maxVisible - 1);
|
||||
|
||||
// Ajuster le début si on est près de la fin
|
||||
if (end - start + 1 < maxVisible) {
|
||||
start = Math.max(1, end - maxVisible + 1);
|
||||
}
|
||||
|
||||
// Ne pas inclure la première et dernière page dans visiblePages
|
||||
// si elles sont déjà affichées séparément
|
||||
const actualStart = start === 1 && props.totalPages > maxVisible ? 2 : start;
|
||||
const actualEnd = end === props.totalPages && props.totalPages > maxVisible ? props.totalPages - 1 : end;
|
||||
|
||||
for (let i = actualStart; i <= actualEnd; i++) {
|
||||
if (i !== 1 && i !== props.totalPages) {
|
||||
pages.push(i);
|
||||
} else if (props.totalPages <= maxVisible) {
|
||||
pages.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
});
|
||||
|
||||
const showFirstPage = computed(() => {
|
||||
return props.totalPages > 5 && props.currentPage > 3;
|
||||
});
|
||||
|
||||
const showLastPage = computed(() => {
|
||||
return props.totalPages > 5 && props.currentPage < props.totalPages - 2;
|
||||
});
|
||||
|
||||
const showLeftDots = computed(() => {
|
||||
return props.totalPages > 5 && props.currentPage > 4;
|
||||
});
|
||||
|
||||
const showRightDots = computed(() => {
|
||||
return props.totalPages > 5 && props.currentPage < props.totalPages - 3;
|
||||
});
|
||||
|
||||
const goToPage = (page) => {
|
||||
if (page >= 1 && page <= props.totalPages && page !== props.currentPage) {
|
||||
emit('page-change', page);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user