From 3c187411a83082f381ab7b2895cc767429352c25 Mon Sep 17 00:00:00 2001 From: razyd Date: Mon, 12 Oct 2026 01:36:20 +0200 Subject: [PATCH] perf(home): load the important logbook entries with one request per period - Add logbooksApi.getLogbooksInRange, which reads /api/logbooks/all for a date range, 500 entries per page. - Use it in the home panel instead of one /day request per date, sorting the dates first (the week comes newest first). With the month period the panel sent up to 31 requests in parallel; /all filters by the same entry day, so the result is the same with one. --- backend/tests/frontend/home-logbooks.test.ts | 39 ++++++++++++++++++++ frontend/app/dashboard/page.tsx | 10 ++--- frontend/app/lib/logbooks/queries.ts | 16 ++++++++ 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 backend/tests/frontend/home-logbooks.test.ts diff --git a/backend/tests/frontend/home-logbooks.test.ts b/backend/tests/frontend/home-logbooks.test.ts new file mode 100644 index 00000000..d4fedec4 --- /dev/null +++ b/backend/tests/frontend/home-logbooks.test.ts @@ -0,0 +1,39 @@ +// tests/frontend/home-logbooks.test.ts +// The home panel asked for the important logbook entries one day at a time: +// up to 31 requests to /api/logbooks/day/ for the month. It now asks /all for +// the whole range, page by page. Reads the frontend sources; no browser. + +import { describe, it, expect } from 'vitest' +import { readFileSync } from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const frontend = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'frontend') +const read = (...p: string[]) => readFileSync(join(frontend, ...p), 'utf8') +const page = read('app', 'dashboard', 'page.tsx') +const queries = read('app', 'lib', 'logbooks', 'queries.ts') + +describe('home panel logbook entries', () => { + it('asks for the whole period at once, not one request per day', () => { + expect(page).not.toContain('getLogbooksByDay') + expect(page).not.toMatch(/dates\.map\(/) + expect(page).toMatch( + /logbooksApi\s*\.getLogbooksInRange\(range\[0\], range\[range\.length - 1\]\)/ + ) + }) + + it('sorts the dates first: the week comes newest first', () => { + expect(page).toContain('const range = [...dates].sort()') + }) + + it('reads every page of /all for that range', () => { + const start = queries.indexOf('getLogbooksInRange: async (') + expect(start).toBeGreaterThan(-1) + const body = queries.slice(start, queries.indexOf('\n },', start)) + expect(body).toContain('/api/logbooks/all?date_from=${dateFrom}&date_to=${dateTo}') + expect(body).toContain('&limit=${RANGE_PAGE}&offset=${offset}') + expect(body).toMatch(/if \(page\.length < RANGE_PAGE\) return entries/) + // The backend accepts up to 500 per page + expect(queries).toContain('const RANGE_PAGE = 500') + }) +}) diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx index 32e1339c..8e8b5c26 100644 --- a/frontend/app/dashboard/page.tsx +++ b/frontend/app/dashboard/page.tsx @@ -109,12 +109,12 @@ export default function DashboardHome() { } } - const responses = await Promise.all( - dates.map((date) => logbooksApi.getLogbooksByDay(date).catch(() => [])) - ) - + // One request for the whole period (one per page), not one per day + const range = [...dates].sort() // eslint-disable-next-line @typescript-eslint/no-explicit-any - const allLogbooks: any[] = responses.flat().filter(Boolean) + const allLogbooks: any[] = await logbooksApi + .getLogbooksInRange(range[0], range[range.length - 1]) + .catch(() => []) // eslint-disable-next-line @typescript-eslint/no-explicit-any const logbooksInPeriod = allLogbooks.filter((entry: any) => { diff --git a/frontend/app/lib/logbooks/queries.ts b/frontend/app/lib/logbooks/queries.ts index ac00c917..bd640023 100644 --- a/frontend/app/lib/logbooks/queries.ts +++ b/frontend/app/lib/logbooks/queries.ts @@ -3,10 +3,14 @@ import apiClient from '@/app/lib/apiClient' import { API_BASE_URL } from '@/app/lib/env' +import type { LogbookEntry } from './types' // Types available in ./types if needed: LogbookEntry, LogbookComment const API_URL = API_BASE_URL +// Largest page /api/logbooks/all accepts +const RANGE_PAGE = 500 + // =============== LOGBOOKS API =============== export const logbooksApi = { @@ -63,6 +67,18 @@ export const logbooksApi = { } }, + // Entries whose day falls between two dates (YYYY-MM-DD), every page + getLogbooksInRange: async (dateFrom: string, dateTo: string): Promise => { + const entries: LogbookEntry[] = [] + for (let offset = 0; ; offset += RANGE_PAGE) { + const page = await apiClient.get( + `${API_URL}/api/logbooks/all?date_from=${dateFrom}&date_to=${dateTo}&limit=${RANGE_PAGE}&offset=${offset}` + ) + entries.push(...page) + if (page.length < RANGE_PAGE) return entries + } + }, + // Eliminar un logbook (soft-delete) deleteLogbook: async (id: number) => { return apiClient.delete(`${API_URL}/api/logbooks/${id}`)