refactor(search): adapt UI to use ResourceList
This commit is contained in:
@@ -2,16 +2,16 @@ import { it, expect, vi, beforeEach } from 'vitest';
|
||||
vi.mock('$lib/api/client', () => ({ apiFetch: vi.fn(), apiJson: vi.fn() }));
|
||||
vi.mock('$lib/api/csrf', () => ({ getCsrfHeaders: () => ({}) }));
|
||||
import { apiFetch, apiJson } from '$lib/api/client';
|
||||
import { searchFiles, searchSuggest, clearSearchCache } from './search';
|
||||
import { searchResources, searchSuggest, clearSearchCache } from './search';
|
||||
const f = apiFetch as unknown as ReturnType<typeof vi.fn>;
|
||||
const j = apiJson as unknown as ReturnType<typeof vi.fn>;
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
f.mockResolvedValue({ ok: true, status: 200, json: async () => ({}) });
|
||||
j.mockResolvedValue({ files: [], folders: [] });
|
||||
j.mockResolvedValue({ items: [], query_time_ms: 0 });
|
||||
});
|
||||
it('builds search requests including filters', async () => {
|
||||
await searchFiles('q', {
|
||||
await searchResources('q', {
|
||||
recursive: true,
|
||||
fileTypes: ['mp3', 'wav'],
|
||||
minSize: 1,
|
||||
@@ -19,7 +19,22 @@ it('builds search requests including filters', async () => {
|
||||
sortBy: 'date'
|
||||
}).catch(() => {});
|
||||
expect(j).toHaveBeenCalledWith(expect.stringContaining('type=mp3%2Cwav'), expect.anything());
|
||||
// Sort dimension is sent on the wire as `order_by`, matching the
|
||||
// backend's `SearchResourcesQuery` (post-normalization).
|
||||
expect(j).toHaveBeenCalledWith(expect.stringContaining('order_by=date'), expect.anything());
|
||||
await searchSuggest('q').catch(() => {});
|
||||
await clearSearchCache().catch(() => {});
|
||||
expect(f.mock.calls.length + j.mock.calls.length).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it('forwards cursor pagination and resource-type filter', async () => {
|
||||
await searchResources('q', {
|
||||
cursor: 'abc',
|
||||
resourceTypes: ['file'],
|
||||
limit: 25
|
||||
}).catch(() => {});
|
||||
const call = j.mock.calls.at(-1)?.[0] as string;
|
||||
expect(call).toContain('cursor=abc');
|
||||
expect(call).toContain('resource_types=file');
|
||||
expect(call).toContain('limit=25');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Search endpoint — ported from features/files/search.js. */
|
||||
// Search endpoint — hits the normalized `/*/resources` envelope shape.
|
||||
import { apiFetch, apiJson } from '$lib/api/client';
|
||||
import type { SearchResults, SortBy } from '$lib/api/types';
|
||||
import type { ItemType, SearchResourcesResponse, SortBy } from '$lib/api/types';
|
||||
|
||||
export interface SearchOptions {
|
||||
folderId?: string;
|
||||
@@ -16,14 +16,31 @@ export interface SearchOptions {
|
||||
modifiedAfter?: number;
|
||||
/** Unix-seconds upper bound on modified time. */
|
||||
modifiedBefore?: number;
|
||||
/** Page size (1–200 server-side; default 50). */
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
/**
|
||||
* Cursor from a previous response's `next_cursor`. Absent → first page.
|
||||
* The wire uses cursor pagination now; the old `offset` param is gone.
|
||||
*/
|
||||
cursor?: string;
|
||||
/** Sort dimension; maps to backend `order_by`. */
|
||||
sortBy?: SortBy;
|
||||
/** Restrict to files, folders, or both (default). */
|
||||
resourceTypes?: ItemType[];
|
||||
/** Abort the request when a newer search supersedes it. */
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export function searchFiles(query: string, opts: SearchOptions = {}): Promise<SearchResults> {
|
||||
/**
|
||||
* Cursor-paginated search. Returns the shared envelope
|
||||
* `{ items[], next_cursor?, query_time_ms, total? }` — same shape as
|
||||
* favorites / recent / trash / folder listings so `ResourceList`
|
||||
* consumes the items without a demux step.
|
||||
*/
|
||||
export function searchResources(
|
||||
query: string,
|
||||
opts: SearchOptions = {}
|
||||
): Promise<SearchResourcesResponse> {
|
||||
const params = new URLSearchParams();
|
||||
params.append('query', query);
|
||||
if (opts.folderId) params.append('folder_id', opts.folderId);
|
||||
@@ -37,10 +54,11 @@ export function searchFiles(query: string, opts: SearchOptions = {}): Promise<Se
|
||||
if (opts.createdBefore != null) params.append('created_before', String(opts.createdBefore));
|
||||
if (opts.modifiedAfter != null) params.append('modified_after', String(opts.modifiedAfter));
|
||||
if (opts.modifiedBefore != null) params.append('modified_before', String(opts.modifiedBefore));
|
||||
params.append('limit', String(opts.limit ?? 100));
|
||||
params.append('offset', String(opts.offset ?? 0));
|
||||
params.append('sort_by', opts.sortBy ?? 'relevance');
|
||||
return apiJson<SearchResults>(`/api/search?${params.toString()}`, {
|
||||
if (opts.resourceTypes?.length) params.append('resource_types', opts.resourceTypes.join(','));
|
||||
if (opts.limit != null) params.append('limit', String(opts.limit));
|
||||
if (opts.cursor) params.append('cursor', opts.cursor);
|
||||
if (opts.sortBy) params.append('order_by', opts.sortBy);
|
||||
return apiJson<SearchResourcesResponse>(`/api/search?${params.toString()}`, {
|
||||
credentials: 'same-origin',
|
||||
signal: opts.signal
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user