Files
L-Ami-Fiduciaire/resources/js/components/Pagination.vue
Saad Ibn-Ezzoubayr c89d1879bf feat: complete Epic 1 — team management & permission system
- Story 1.1: Permission enum, config, AuthorizesPermissions & HasWorkspaceScope traits, member→worker migration
- Story 1.2: Team page with member list, invitation system with queued email
- Story 1.3: Role assignment (Manager/Worker) and member removal with activity logging
- Story 1.4: Owner-only permission toggle matrix for Managers (manage team, view logs, configure portal)
- Story 1.5: Role-based access enforcement — Workers see only assigned declarations/clients, sidebar scoping
- Story 1.6: Workspace switcher dropdown for multi-workspace users with session-based switching
- 83 new/modified files, 182 tests passing with zero regressions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 00:12:50 +00:00

163 lines
4.8 KiB
Vue

<script setup lang="ts">
import { router } from '@inertiajs/vue3';
import {
ChevronFirst,
ChevronLast,
ChevronLeft,
ChevronRight,
} from 'lucide-vue-next';
import { computed } from 'vue';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
interface PaginationData {
current_page: number;
per_page: number;
total: number;
last_page: number;
from: number;
to: number;
}
interface Props {
pagination: PaginationData;
selectedCount?: number;
perPageOptions?: number[];
}
const props = withDefaults(defineProps<Props>(), {
selectedCount: 0,
perPageOptions: () => [10, 25, 50, 100],
});
const canGoPrevious = computed(() => props.pagination.current_page > 1);
const canGoNext = computed(
() => props.pagination.current_page < props.pagination.last_page,
);
const handlePerPageChange = (value: unknown): void => {
const str = value != null ? String(value) : null;
if (str == null) return;
const perPage = Number.parseInt(str, 10);
const url = new URL(window.location.href);
url.searchParams.set('per_page', perPage.toString());
url.searchParams.set('page', '1');
router.get(
url.pathname + url.search,
{},
{
preserveState: true,
preserveScroll: true,
},
);
};
const goToPage = (page: number): void => {
if (page < 1 || page > props.pagination.last_page) {
return;
}
const url = new URL(window.location.href);
url.searchParams.set('page', page.toString());
router.get(
url.pathname + url.search,
{},
{
preserveState: true,
preserveScroll: true,
},
);
};
</script>
<template>
<div
class="flex flex-col gap-4 px-4 sm:flex-row sm:items-center sm:justify-between"
>
<div class="text-sm text-muted-foreground">
<span v-if="selectedCount > 0">
{{ selectedCount }} sur {{ pagination.total }} ligne(s)
sélectionnée(s).
</span>
<span v-else> {{ pagination.total }} ligne(s) au total </span>
</div>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:gap-6">
<div class="flex items-center gap-2">
<span class="hidden text-sm text-muted-foreground sm:inline"
>Lignes par page</span
>
<Select
:model-value="pagination.per_page.toString()"
@update:model-value="handlePerPageChange"
>
<SelectTrigger class="h-8 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="option in perPageOptions"
:key="option"
:value="option.toString()"
>
{{ option }}
</SelectItem>
</SelectContent>
</Select>
</div>
<div class="hidden text-sm text-muted-foreground md:inline">
Page {{ pagination.current_page }} sur
{{ pagination.last_page }}
</div>
<div class="flex items-center justify-center gap-2">
<Button
variant="outline"
size="sm"
:disabled="!canGoPrevious"
@click="goToPage(1)"
>
<ChevronFirst class="size-4" />
</Button>
<Button
variant="outline"
size="sm"
:disabled="!canGoPrevious"
@click="goToPage(pagination.current_page - 1)"
>
<ChevronLeft class="size-4" />
</Button>
<div class="mx-2 text-sm text-muted-foreground md:hidden">
{{ pagination.current_page }}/{{ pagination.last_page }}
</div>
<Button
variant="outline"
size="sm"
:disabled="!canGoNext"
@click="goToPage(pagination.current_page + 1)"
>
<ChevronRight class="size-4" />
</Button>
<Button
variant="outline"
size="sm"
:disabled="!canGoNext"
@click="goToPage(pagination.last_page)"
>
<ChevronLast class="size-4" />
</Button>
</div>
</div>
</div>
</template>