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:
@@ -13,6 +13,9 @@ export async function load({ url, fetch }) {
|
|||||||
const senderId = url.searchParams.get('senderId') || '';
|
const senderId = url.searchParams.get('senderId') || '';
|
||||||
const receiverId = url.searchParams.get('receiverId') || '';
|
const receiverId = url.searchParams.get('receiverId') || '';
|
||||||
const tags = url.searchParams.getAll('tag');
|
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;
|
const isDashboard = !q && !from && !to && !senderId && !receiverId && !tags.length;
|
||||||
|
|
||||||
@@ -30,7 +33,10 @@ export async function load({ url, fetch }) {
|
|||||||
to: to || undefined,
|
to: to || undefined,
|
||||||
senderId: senderId || undefined,
|
senderId: senderId || undefined,
|
||||||
receiverId: receiverId || 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');
|
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 {
|
const allPersons = (personsResult.data ?? []) as {
|
||||||
id: string;
|
id: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -80,6 +88,7 @@ export async function load({ url, fetch }) {
|
|||||||
return {
|
return {
|
||||||
isDashboard,
|
isDashboard,
|
||||||
documents,
|
documents,
|
||||||
|
total,
|
||||||
stats,
|
stats,
|
||||||
incompleteDocs,
|
incompleteDocs,
|
||||||
recentDocs,
|
recentDocs,
|
||||||
@@ -87,7 +96,7 @@ export async function load({ url, fetch }) {
|
|||||||
senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '',
|
senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '',
|
||||||
receiverName: receiverObj ? `${receiverObj.firstName} ${receiverObj.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
|
error: null as string | null
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -96,11 +105,12 @@ export async function load({ url, fetch }) {
|
|||||||
return {
|
return {
|
||||||
isDashboard,
|
isDashboard,
|
||||||
documents: [],
|
documents: [],
|
||||||
|
total: 0,
|
||||||
stats: null,
|
stats: null,
|
||||||
incompleteDocs: [],
|
incompleteDocs: [],
|
||||||
recentDocs: [],
|
recentDocs: [],
|
||||||
initialValues: { senderName: '', receiverName: '' },
|
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
|
error: 'Daten konnten nicht geladen werden.' as string | null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,10 @@ describe('home page load — search mode', () => {
|
|||||||
it('sets isDashboard false and skips widget APIs when q is set', async () => {
|
it('sets isDashboard false and skips widget APIs when q is set', async () => {
|
||||||
const mockGet = vi
|
const mockGet = vi
|
||||||
.fn()
|
.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
|
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }); // persons
|
||||||
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
|
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
|
||||||
typeof createApiClient
|
typeof createApiClient
|
||||||
@@ -146,7 +149,10 @@ describe('home page load — search mode', () => {
|
|||||||
it('passes search params from the URL to the documents API', async () => {
|
it('passes search params from the URL to the documents API', async () => {
|
||||||
const mockGet = vi
|
const mockGet = vi
|
||||||
.fn()
|
.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: [] });
|
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] });
|
||||||
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
|
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
|
||||||
typeof createApiClient
|
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 ─────────────────────────────────────────────────────────────
|
// ─── 401 redirect ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
describe('home page load — auth redirect', () => {
|
describe('home page load — auth redirect', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user