test(11-01): add failing ResultsList spec + extend tender-radar-api client (RED)
Extends tender-radar-api.ts with the Tender type, listTenders(params) and fetchCoverage() (plain fetch, credentials:'include', matching the established fetchSourceConfig pattern — TanStack Query is not installed). Adds the RED-first ResultsList.test.tsx: render items with title/buyerName/deadline/value, "keine Wertangabe" for null estimatedValue (D-05), and an empty-state message. ResultsList.tsx does not exist yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,44 @@ export interface SaveSourceConfigPayload {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single tender row from the global `Tender` catalog (Plan 11-01).
|
||||
* `estimatedValue` arrives as a string (Prisma Decimal serializes via
|
||||
* `toJSON()` -> `toString()`), NOT a number — never `Number()`-coerce it
|
||||
* without a null check (D-05: 91.6% of rows are null, "keine Wertangabe").
|
||||
* Date fields arrive as ISO strings (JSON has no native Date type).
|
||||
*/
|
||||
export interface Tender {
|
||||
id: string;
|
||||
sourcePortal: string;
|
||||
title: string;
|
||||
buyerName: string | null;
|
||||
cpvCodes: string[];
|
||||
region: string | null;
|
||||
plz: string | null;
|
||||
bundesland: string | null;
|
||||
deadlineAt: string | null;
|
||||
estimatedValue: string | null;
|
||||
procedureType: string | null;
|
||||
status: string;
|
||||
sourceUrl: string | null;
|
||||
publishedAt: string;
|
||||
}
|
||||
|
||||
/** Response shape of GET /modules/tender-radar. */
|
||||
export interface ListTendersResponse {
|
||||
items: Tender[];
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
/** Response shape of GET /modules/tender-radar/coverage (D-12, UI-05). */
|
||||
export interface CoverageResponse {
|
||||
total: number;
|
||||
sources: Array<{ sourcePortal: string; count: number }>;
|
||||
}
|
||||
|
||||
// --- API functions ---
|
||||
|
||||
/**
|
||||
@@ -65,3 +103,36 @@ export async function saveSourceConfig(
|
||||
if (!res.ok) throw new Error('Failed to save tender-radar source config');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* List the global tender catalog with the given filter/sort/pagination
|
||||
* params (Plan 11-01, FILTER-01/04/05, UI-01). `params` mirrors the
|
||||
* TenderQueryDto query-string contract exactly — the URL is the single
|
||||
* source of truth for filter state (Research: URL-Param-getriebener Client).
|
||||
* GET /modules/tender-radar?<params>
|
||||
*/
|
||||
export async function listTenders(
|
||||
params: URLSearchParams,
|
||||
): Promise<ListTendersResponse> {
|
||||
const query = params.toString();
|
||||
const res = await fetch(
|
||||
`${API_URL}/modules/tender-radar${query ? `?${query}` : ''}`,
|
||||
{ credentials: 'include' },
|
||||
);
|
||||
if (!res.ok) throw new Error('Failed to fetch tenders');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the active-tender distribution by sourcePortal (D-12, UI-05) —
|
||||
* feeds CoverageBanner so a thin/single-source result list is not
|
||||
* misread as a defect.
|
||||
* GET /modules/tender-radar/coverage
|
||||
*/
|
||||
export async function fetchCoverage(): Promise<CoverageResponse> {
|
||||
const res = await fetch(`${API_URL}/modules/tender-radar/coverage`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!res.ok) throw new Error('Failed to fetch tender-radar coverage');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user