diff --git a/src/app/api/v1/grid/route.ts b/src/app/api/v1/grid/route.ts index 8534cdc..a60a0c7 100644 --- a/src/app/api/v1/grid/route.ts +++ b/src/app/api/v1/grid/route.ts @@ -93,10 +93,12 @@ export async function GET(req: NextRequest) { // Métriques Qlik et gamme serveur relues au moment de l'appel (voir api-enrich). const rows = q.enrich === "1" ? await enrichRows(result.rows) : result.rows; + const pagination = buildPagination(q.page, q.limit, result.total); + return ok( rows.map((r) => pickFields(r, q.fields)), { - pagination: buildPagination(q.page, q.limit, result.total), + pagination, meta: { fournisseur: q.fournisseur, computedAt: result.computedAt, @@ -105,6 +107,20 @@ export async function GET(req: NextRequest) { enrichi: q.enrich === "1", /** true = l'instantané n'existait pas et vient d'être calculé par cet appel. */ computedOnDemand, + /** + * true = cette réponse contient **toutes** les lignes du fournisseur. + * Dit explicitement à un appelant — en particulier un agent — s'il + * peut s'arrêter là, au lieu de le laisser déduire d'une pagination + * qu'il risque d'ignorer. + */ + complet: !pagination.hasMore, + ...(pagination.hasMore + ? { + avertissement: + `Réponse partielle : ${rows.length} lignes sur ${result.total}. ` + + `Relancez avec page=${q.page + 1}, ou augmentez limit (5000 au maximum) pour tout obtenir en un appel.`, + } + : {}), }, }, ); diff --git a/src/app/api/v1/openapi.json/route.ts b/src/app/api/v1/openapi.json/route.ts index e37bad3..1286104 100644 --- a/src/app/api/v1/openapi.json/route.ts +++ b/src/app/api/v1/openapi.json/route.ts @@ -22,7 +22,7 @@ export async function GET(req: NextRequest) { const paginationParams = [ { name: "page", in: "query", schema: { type: "integer", minimum: 1, default: 1 }, description: "Numéro de page." }, - { name: "limit", in: "query", schema: { type: "integer", minimum: 1, maximum: 500, default: 100 }, description: "Nombre de lignes par page (500 maximum)." }, + { name: "limit", in: "query", schema: { type: "integer", minimum: 1, maximum: 5000, default: 100 }, description: "Lignes par page. Jusqu'à 5000 sur /grid : un seul appel suffit pour un fournisseur entier. 500 maximum sur les autres endpoints." }, ]; const sortParams = [ { name: "sort", in: "query", schema: { type: "string", enum: GRID_SORT_KEYS }, description: "Colonne de tri." }, @@ -93,6 +93,7 @@ export async function GET(req: NextRequest) { limit: { type: "integer" }, total: { type: "integer" }, totalPages: { type: "integer" }, + hasMore: { type: "boolean", description: "true = il reste des pages. Sur /grid, meta.complet dit la même chose en positif." }, }, }, Fournisseur: { diff --git a/src/features/admin/components/api-connection-info.tsx b/src/features/admin/components/api-connection-info.tsx index 158e204..c7617d3 100644 --- a/src/features/admin/components/api-connection-info.tsx +++ b/src/features/admin/components/api-connection-info.tsx @@ -50,7 +50,7 @@ const ENDPOINTS: Array<{ method: string; path: string; desc: string }> = [ ]; const PARAMS: Array<{ name: string; desc: string }> = [ - { name: "page, limit", desc: "Pagination (limit ≤ 500, défaut 100)" }, + { name: "page, limit", desc: "Pagination. limit ≤ 5000 sur /grid — un seul appel suffit donc pour un fournisseur entier ; ≤ 500 ailleurs" }, { name: "sort, order", desc: "Tri, ex. sort=totalCa&order=desc" }, { name: "search", desc: "Libellé, codein, GTIN, référence ou code centrale" }, { name: "gamme, code1..code3", desc: "Filtres sur la gamme et la nomenclature" }, @@ -122,6 +122,11 @@ export function ApiConnectionInfo() {
Exemples
+ Tout un fournisseur en un seul appel — meta.complet confirme
+ qu'il ne reste rien à lire :
+