feat(11-06): SavedSearchBar UI — save/load/rename/delete profiles (FILTER-06)
GREEN phase — extends tender-radar-api.ts with listSavedSearches/ createSavedSearch/updateSavedSearch/deleteSavedSearch (plain fetch, credentials: include), adds SavedSearchBar with the serializeFiltersFromSearchParams/filtersToSearchParams round-trip helpers (URL searchParams <-> filters JSON, deliberately excluding page/tender — navigation state, not filter state), and mounts it above FilterPanel in page.tsx. Hardcoded German UI per phase convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -204,3 +204,94 @@ export async function setTriage(
|
||||
if (!res.ok) throw new Error('Failed to save tender triage');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* A single per-user saved search profile (Plan 11-06, FILTER-06). Per-user,
|
||||
* NOT tenant-wide (D-11) — the backend derives userId from the auth cookie,
|
||||
* this client never sends a userId. `filters` mirrors the FilterPanel's URL
|
||||
* searchParams contract exactly (q, plz, bundesland, region, cpv,
|
||||
* deadlineFrom/To, openOnly, valueMin/Max, includeNullValue, sort, favOnly)
|
||||
* — see SavedSearchBar.tsx's serialize/deserialize helpers for the
|
||||
* round-trip contract.
|
||||
*/
|
||||
export interface SavedSearch {
|
||||
id: string;
|
||||
name: string;
|
||||
filters: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** Payload accepted by POST /modules/tender-radar/saved-searches. */
|
||||
export interface CreateSavedSearchPayload {
|
||||
name: string;
|
||||
filters: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Payload accepted by PATCH /modules/tender-radar/saved-searches/:searchId. */
|
||||
export interface UpdateSavedSearchPayload {
|
||||
name?: string;
|
||||
filters?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* List this user's saved search profiles (Plan 11-06, FILTER-06).
|
||||
* GET /modules/tender-radar/saved-searches
|
||||
*/
|
||||
export async function listSavedSearches(): Promise<SavedSearch[]> {
|
||||
const res = await fetch(`${API_URL}/modules/tender-radar/saved-searches`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!res.ok) throw new Error('Failed to fetch saved searches');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new saved search profile from the currently active filters.
|
||||
* POST /modules/tender-radar/saved-searches
|
||||
*/
|
||||
export async function createSavedSearch(
|
||||
payload: CreateSavedSearchPayload,
|
||||
): Promise<SavedSearch> {
|
||||
const res = await fetch(`${API_URL}/modules/tender-radar/saved-searches`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
if (!res.ok) throw new Error('Failed to create saved search');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename and/or update the filters of an existing saved search profile.
|
||||
* PATCH /modules/tender-radar/saved-searches/:searchId
|
||||
*/
|
||||
export async function updateSavedSearch(
|
||||
id: string,
|
||||
payload: UpdateSavedSearchPayload,
|
||||
): Promise<SavedSearch> {
|
||||
const res = await fetch(
|
||||
`${API_URL}/modules/tender-radar/saved-searches/${id}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
);
|
||||
if (!res.ok) throw new Error('Failed to update saved search');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a saved search profile.
|
||||
* DELETE /modules/tender-radar/saved-searches/:searchId
|
||||
*/
|
||||
export async function deleteSavedSearch(id: string): Promise<void> {
|
||||
const res = await fetch(
|
||||
`${API_URL}/modules/tender-radar/saved-searches/${id}`,
|
||||
{ method: 'DELETE', credentials: 'include' },
|
||||
);
|
||||
if (!res.ok) throw new Error('Failed to delete saved search');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user