feat(search): read sort/dir/tagQ from URL and unwrap DocumentSearchResult envelope

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-06 13:53:54 +02:00
parent 4fe10e1316
commit 769937e03d
2 changed files with 66 additions and 6 deletions

View File

@@ -13,6 +13,9 @@ export async function load({ url, fetch }) {
const senderId = url.searchParams.get('senderId') || '';
const receiverId = url.searchParams.get('receiverId') || '';
const tags = url.searchParams.getAll('tag');
const sort = url.searchParams.get('sort') || 'DATE';
const dir = url.searchParams.get('dir') || 'desc';
const tagQ = url.searchParams.get('tagQ') || '';
const isDashboard = !q && !from && !to && !senderId && !receiverId && !tags.length;
@@ -30,7 +33,10 @@ export async function load({ url, fetch }) {
to: to || undefined,
senderId: senderId || undefined,
receiverId: receiverId || undefined,
tag: tags.length ? tags : undefined
tag: tags.length ? tags : undefined,
tagQ: tagQ || undefined,
sort: sort as 'DATE' | 'TITLE' | 'SENDER' | 'RECEIVER' | 'UPLOAD_DATE',
dir: dir || undefined
}
}
}),
@@ -44,7 +50,9 @@ export async function load({ url, fetch }) {
throw redirect(302, '/login');
}
const documents: Document[] = docsResult?.data ?? [];
const searchResult = docsResult?.data as { documents?: Document[]; total?: number } | null;
const documents: Document[] = searchResult?.documents ?? [];
const total: number = searchResult?.total ?? 0;
const allPersons = (personsResult.data ?? []) as {
id: string;
firstName: string;
@@ -80,6 +88,7 @@ export async function load({ url, fetch }) {
return {
isDashboard,
documents,
total,
stats,
incompleteDocs,
recentDocs,
@@ -87,7 +96,7 @@ export async function load({ url, fetch }) {
senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '',
receiverName: receiverObj ? `${receiverObj.firstName} ${receiverObj.lastName}` : ''
},
filters: { q, from, to, senderId, receiverId, tags },
filters: { q, from, to, senderId, receiverId, tags, sort, dir, tagQ },
error: null as string | null
};
} catch (e) {
@@ -96,11 +105,12 @@ export async function load({ url, fetch }) {
return {
isDashboard,
documents: [],
total: 0,
stats: null,
incompleteDocs: [],
recentDocs: [],
initialValues: { senderName: '', receiverName: '' },
filters: { q, from, to, senderId, receiverId, tags },
filters: { q, from, to, senderId, receiverId, tags, sort, dir, tagQ },
error: 'Daten konnten nicht geladen werden.' as string | null
};
}

View File

@@ -123,7 +123,10 @@ describe('home page load — search mode', () => {
it('sets isDashboard false and skips widget APIs when q is set', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [{ id: 'd1' }] }) // search docs
.mockResolvedValueOnce({
response: { ok: true, status: 200 },
data: { documents: [{ id: 'd1' }], total: 1 }
}) // search docs
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }); // persons
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
typeof createApiClient
@@ -146,7 +149,10 @@ describe('home page load — search mode', () => {
it('passes search params from the URL to the documents API', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] })
.mockResolvedValueOnce({
response: { ok: true, status: 200 },
data: { documents: [], total: 0 }
})
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] });
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
typeof createApiClient
@@ -163,6 +169,50 @@ describe('home page load — search mode', () => {
});
});
it('passes sort, dir, and tagQ params to the documents API', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({
response: { ok: true, status: 200 },
data: { documents: [], total: 0 }
})
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] });
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
typeof createApiClient
>);
await load({
url: makeUrl({ q: 'test', sort: 'TITLE', dir: 'asc', tagQ: 'fam' }),
fetch: vi.fn() as unknown as typeof fetch
});
const firstCall = mockGet.mock.calls[0];
expect(firstCall[1].params.query.sort).toBe('TITLE');
expect(firstCall[1].params.query.dir).toBe('asc');
expect(firstCall[1].params.query.tagQ).toBe('fam');
});
it('returns total from the DocumentSearchResult envelope', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({
response: { ok: true, status: 200 },
data: { documents: [{ id: 'd1' }], total: 42 }
})
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] });
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
typeof createApiClient
>);
const result = await load({
url: makeUrl({ q: 'test' }),
fetch: vi.fn() as unknown as typeof fetch
});
expect(result.documents).toHaveLength(1);
expect(result.total).toBe(42);
});
// ─── 401 redirect ─────────────────────────────────────────────────────────────
describe('home page load — auth redirect', () => {