diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 4b2b414..c686cc6 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -5,6 +5,8 @@ api_platform: stateless: true cache_headers: vary: ['Content-Type', 'Authorization', 'Origin'] + pagination_client_items_per_page: true + pagination_maximum_items_per_page: 100 formats: json: ['application/json'] jsonld: ['application/ld+json'] diff --git a/frontend/components/ui/UiDataTable.vue b/frontend/components/ui/UiDataTable.vue new file mode 100644 index 0000000..fa3572e --- /dev/null +++ b/frontend/components/ui/UiDataTable.vue @@ -0,0 +1,230 @@ + + + + + + {{ col.label }} + + Actions + + + + + + + + {{ getNestedValue(item, col.key) }} + + + + + + + + + + Chargement… + + + {{ emptyMessage }} + + + + + + + Chargement… + + + + + + + Lignes : + + {{ n }} + + + + + + Prev + + + + … + + {{ entry }} + + + + + Next + + + + + + + diff --git a/frontend/composables/useDataTableServerState.ts b/frontend/composables/useDataTableServerState.ts new file mode 100644 index 0000000..36697fd --- /dev/null +++ b/frontend/composables/useDataTableServerState.ts @@ -0,0 +1,102 @@ +import { ref, watch } from 'vue' +import { useApi } from '~/composables/useApi' + +type FilterValue = string | number | boolean | null + +export interface UseDataTableServerStateOptions { + initialPerPage?: number + debounceMs?: number +} + +export function useDataTableServerState>( + endpoint: string, + initialFilters: Record = {}, + options: UseDataTableServerStateOptions = {} +) { + const api = useApi() + + const debounceMs = options.debounceMs ?? 300 + const initialPerPage = options.initialPerPage ?? 10 + + const items = ref([]) as { value: T[] } + const totalItems = ref(0) + const page = ref(1) + const perPage = ref(initialPerPage) + const filters = ref>({ ...initialFilters }) + const loading = ref(false) + + let debounceTimer: ReturnType | null = null + let requestToken = 0 + + const buildQueryParams = (): Record => { + const params: Record = { + page: page.value, + itemsPerPage: perPage.value + } + for (const [key, value] of Object.entries(filters.value)) { + if (value === '' || value === null) continue + params[key] = value as string | number | boolean + } + return params + } + + const fetchItems = async (): Promise => { + const currentToken = ++requestToken + loading.value = true + try { + const data = await api.get<{ member: T[]; totalItems: number }>( + endpoint, + buildQueryParams(), + { + toast: false, + headers: { Accept: 'application/ld+json' } + } + ) + if (currentToken !== requestToken) return + items.value = data?.member ?? [] + totalItems.value = data?.totalItems ?? 0 + } finally { + if (currentToken === requestToken) { + loading.value = false + } + } + } + + const reload = (): void => { + if (debounceTimer) { + clearTimeout(debounceTimer) + debounceTimer = null + } + void fetchItems() + } + + const scheduleReload = (): void => { + if (debounceTimer) clearTimeout(debounceTimer) + debounceTimer = setTimeout(() => { + debounceTimer = null + void fetchItems() + }, debounceMs) + } + + watch([page, perPage], () => { + reload() + }) + + watch(filters, () => { + if (page.value !== 1) { + page.value = 1 + return + } + scheduleReload() + }, { deep: true }) + + return { + items, + totalItems, + page, + perPage, + filters, + loading, + reload + } +} diff --git a/frontend/pages/reception/finish-reception.vue b/frontend/pages/reception/finish-reception.vue index abf79a0..999dead 100644 --- a/frontend/pages/reception/finish-reception.vue +++ b/frontend/pages/reception/finish-reception.vue @@ -5,42 +5,50 @@ - - - Numéro - Date et heure - Fournisseur - Adresse - Type réception - Poids - - + - {{ reception.identificationNumber}} - {{ formatDate(reception.receptionDate) }} - {{ reception.supplier?.name }} - {{ reception.address?.fullAddress }} - {{ reception.receptionType?.label }} - {{ formatWeighing(reception) }} - + + {{ formatDate(item.receptionDate) }} + + + {{ formatWeighing(item) }} + +