From 2bfec337cd14fbd22bd6ea1e909ab83b90299552 Mon Sep 17 00:00:00 2001 From: hywznn Date: Sat, 15 Aug 2026 19:15:45 +0900 Subject: [PATCH] =?UTF-8?q?fix(document):=20=EB=AC=B8=EC=84=9C=ED=95=A8=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/documents.ts | 28 +++++++++ .../DocumentListPage.test.tsx | 59 ++++++++++++++++--- .../DocumentListPage/DocumentListPage.tsx | 11 +--- 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/api/documents.ts b/src/api/documents.ts index 51a0293..988eb81 100644 --- a/src/api/documents.ts +++ b/src/api/documents.ts @@ -59,6 +59,34 @@ export function fetchDocuments(params: FetchDocumentsParams = {}): Promise(`/documents?${query.toString()}`) } +const DOCUMENT_PAGE_SIZE = 100 + +export async function fetchAllDocuments( + params: Omit = {}, +): Promise { + const firstPage = await fetchDocuments({ ...params, page: 0, size: DOCUMENT_PAGE_SIZE }) + const pageCount = Math.ceil(firstPage.total_elements / DOCUMENT_PAGE_SIZE) + if (pageCount <= 1) return firstPage + + const remainingPages = await Promise.all( + Array.from({ length: pageCount - 1 }, (_, index) => + fetchDocuments({ ...params, page: index + 1, size: DOCUMENT_PAGE_SIZE }), + ), + ) + const itemsById = new Map( + [firstPage, ...remainingPages] + .flatMap((page) => page.items) + .map((document) => [document.worker_document_id, document]), + ) + + return { + items: [...itemsById.values()], + page: 0, + size: itemsById.size, + total_elements: firstPage.total_elements, + } +} + export function fetchDocument(documentId: string): Promise { return apiFetch(`/documents/${encodeURIComponent(documentId)}`) } diff --git a/src/pages/DocumentListPage/DocumentListPage.test.tsx b/src/pages/DocumentListPage/DocumentListPage.test.tsx index b32f375..1ed7d06 100644 --- a/src/pages/DocumentListPage/DocumentListPage.test.tsx +++ b/src/pages/DocumentListPage/DocumentListPage.test.tsx @@ -28,7 +28,12 @@ function document(overrides: Partial): DocumentItemRespons } const DOCUMENTS: DocumentItemResponse[] = [ - document({ worker_document_id: 'D-1', display_name: '수라즈C', document_type: 'ARC', submission_status: 'MISSING' }), + document({ + worker_document_id: 'D-1', + display_name: '수라즈C', + document_type: 'ARC', + submission_status: 'MISSING', + }), document({ worker_document_id: 'D-2', display_name: '쩐티B', @@ -56,12 +61,24 @@ const DOCUMENTS: DocumentItemResponse[] = [ ] function jsonResponse(body: unknown, init: ResponseInit = {}) { - return new Response(JSON.stringify(body), { status: 200, headers: { 'Content-Type': 'application/json' }, ...init }) + return new Response(JSON.stringify(body), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + ...init, + }) } function errorResponse(status: number, code: string, message: string) { return jsonResponse( - { timestamp: '2026-07-27T01:23:45Z', status, code, message, path: '/api/v1/documents', request_id: 'req-1', field_errors: [] }, + { + timestamp: '2026-07-27T01:23:45Z', + status, + code, + message, + path: '/api/v1/documents', + request_id: 'req-1', + field_errors: [], + }, { status }, ) } @@ -146,7 +163,9 @@ describe('DocumentListPage', () => { await waitFor(() => expect(vi.mocked(fetch)).toHaveBeenCalledTimes(2)) expect(String(vi.mocked(fetch).mock.calls[1][0])).not.toContain('workerId=') - expect(screen.queryByText('업무함에서 선택한 근로자의 문서만 표시합니다.')).not.toBeInTheDocument() + expect( + screen.queryByText('업무함에서 선택한 근로자의 문서만 표시합니다.'), + ).not.toBeInTheDocument() }) it('filters documents by tab', async () => { @@ -240,13 +259,37 @@ describe('DocumentListPage', () => { expect(await screen.findByText('등록된 서류가 없습니다')).toBeInTheDocument() }) - it('shows a cap notice when the server has more documents than the fetched page', async () => { - vi.mocked(fetch).mockResolvedValueOnce( - jsonResponse({ items: DOCUMENTS, page: 0, size: 100, total_elements: 150 }), + it('loads every document page before computing the list and metrics', async () => { + const paginatedDocuments = Array.from({ length: 102 }, (_, index) => + document({ + worker_document_id: `D-PAGE-${index + 1}`, + display_name: `페이지 근로자 ${index + 1}`, + submission_status: 'VERIFIED', + }), ) + vi.mocked(fetch) + .mockResolvedValueOnce( + jsonResponse({ + items: paginatedDocuments.slice(0, 100), + page: 0, + size: 100, + total_elements: 102, + }), + ) + .mockResolvedValueOnce( + jsonResponse({ + items: paginatedDocuments.slice(100), + page: 1, + size: 100, + total_elements: 102, + }), + ) renderPage() - expect(await screen.findByText(/전체 150건 중 4건만 불러왔습니다/)).toBeInTheDocument() + expect(await screen.findByRole('tab', { name: '전체 102' })).toBeInTheDocument() + expect(screen.getByText('페이지 근로자 102')).toBeInTheDocument() + expect(vi.mocked(fetch)).toHaveBeenCalledTimes(2) + expect(String(vi.mocked(fetch).mock.calls[1][0])).toContain('page=1') }) it('opens the HWP/HWPX upload modal', async () => { diff --git a/src/pages/DocumentListPage/DocumentListPage.tsx b/src/pages/DocumentListPage/DocumentListPage.tsx index 518c3c4..a02957e 100644 --- a/src/pages/DocumentListPage/DocumentListPage.tsx +++ b/src/pages/DocumentListPage/DocumentListPage.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo, useState } from 'react' import { useNavigate, useSearchParams } from 'react-router-dom' -import { fetchDocuments, type DocumentItemResponse } from '../../api/documents' +import { fetchAllDocuments, type DocumentItemResponse } from '../../api/documents' import { getErrorMessage } from '../../api/errors' import { EmptyState } from '../../components/ui/EmptyState/EmptyState' import { ListRow } from '../../components/ui/ListRow/ListRow' @@ -50,7 +50,7 @@ export function DocumentListPage() { const debouncedQuery = useDebouncedValue(query) const { status, data, error, refetch } = useApiQuery( - useCallback(() => fetchDocuments({ workerId: workerId ?? undefined, size: 100 }), [workerId]), + useCallback(() => fetchAllDocuments({ workerId: workerId ?? undefined }), [workerId]), useCallback((page: { items: unknown[] }) => page.items.length === 0, []), ) const documents = useMemo(() => data?.items ?? [], [data]) @@ -200,13 +200,6 @@ export function DocumentListPage() { - {data && data.total_elements > data.items.length && ( -

- 전체 {data.total_elements}건 중 {data.items.length}건만 불러왔습니다. 찾는 서류가 안 - 보이면 검색어를 바꿔보세요. -

- )} - {visibleDocuments.length === 0 ? (