1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 13:49:36 +01:00

feat: Paginierung für alle GET-List-Endpoints (#61)

Einheitliches Paginierungs-Pattern mit page, size und Multi-Field sort
für alle 14 List-Endpoints. Response-Format ändert sich von [...] zu
{ content: [...], page: { number, size, totalElements, totalPages } }.

Backend:
- Shared Kernel: Page<T>, PageRequest, SortField, SortDirection
- PaginationHelper (SQL ORDER BY mit Whitelist), PageResponse DTO
- Paginated Methoden in allen 14 Domain-Repos + JDBC-Implementierungen
- Safety-Limit (500) für findAllBelowMinimumLevel/ExpiryRelevantBatches
- Alle List-Use-Cases akzeptieren PageRequest, liefern Page<T>
- Alle Controller mit page/size/sort Query-Params + PageResponse

Frontend:
- PagedResponse<T> Type auf nested page-Format aktualisiert
- Alle 14 API-Client-Resourcen liefern PagedResponse mit PaginationParams
- Alle Hooks mit Pagination-State (currentPage, totalPages, pageSize)
- Alle List-Screens mit Seiten-Navigation (Pfeiltasten) und Footer

Loadtest:
- Podman-Support im justfile (DOCKER_HOST auto-detect)
- Verschärfte Performance-Schwellwerte basierend auf Ist-Werten
This commit is contained in:
Sebastian Frick 2026-03-20 16:33:20 +01:00
parent fc4faafd57
commit 72979c9537
151 changed files with 2880 additions and 1120 deletions

View file

@ -1,5 +1,6 @@
import { useState, useCallback } from 'react';
import type { UserDTO, CreateUserRequest } from '@effigenix/api-client';
import type { PaginationParams } from '@effigenix/types';
import { client } from '../utils/api-client.js';
type RoleName = CreateUserRequest['roleNames'][number];
@ -8,6 +9,10 @@ interface UsersState {
users: UserDTO[];
loading: boolean;
error: string | null;
currentPage: number;
totalElements: number;
totalPages: number;
pageSize: number;
}
function errorMessage(err: unknown): string {
@ -19,13 +24,26 @@ export function useUsers() {
users: [],
loading: false,
error: null,
currentPage: 0,
totalElements: 0,
totalPages: 0,
pageSize: 20,
});
const fetchUsers = useCallback(async () => {
const fetchUsers = useCallback(async (pagination?: PaginationParams) => {
setState((s) => ({ ...s, loading: true, error: null }));
try {
const users = await client.users.list();
setState({ users, loading: false, error: null });
const res = await client.users.list(pagination);
setState((s) => ({
...s,
users: res.content,
currentPage: res.page.number,
totalElements: res.page.totalElements,
totalPages: res.page.totalPages,
pageSize: res.page.size,
loading: false,
error: null,
}));
} catch (err) {
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
}
@ -41,7 +59,7 @@ export function useUsers() {
password,
roleNames: roleName ? [roleName as RoleName] : [],
});
setState((s) => ({ users: [...s.users, user], loading: false, error: null }));
setState((s) => ({ ...s, users: [...s.users, user], loading: false, error: null }));
return user;
} catch (err) {
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
@ -121,6 +139,22 @@ export function useUsers() {
[],
);
const nextPage = useCallback(() => {
if (state.currentPage < state.totalPages - 1) {
// Caller should re-fetch with new page
}
}, [state.currentPage, state.totalPages]);
const prevPage = useCallback(() => {
if (state.currentPage > 0) {
// Caller should re-fetch with new page
}
}, [state.currentPage]);
const goToPage = useCallback((_page: number) => {
// Caller should re-fetch with new page
}, []);
const clearError = useCallback(() => {
setState((s) => ({ ...s, error: null }));
}, []);
@ -134,6 +168,9 @@ export function useUsers() {
assignRole,
removeRole,
changePassword,
nextPage,
prevPage,
goToPage,
clearError,
};
}