feat(10-06): tender-radar-api client + admin source-config settings form

- tender-radar-api.ts: fetchSourceConfig/saveSourceConfig hitting GET/PUT
  /modules/tender-radar/source-config (Plan 05 endpoint)
- SourceConfigForm: load-on-mount, numeric interval (5-1440 min, mirrors
  backend SourceConfigDto bounds) + isActive toggle, read-only sourceType/
  lastIngestedDay display
- settings/page.tsx: standard App Router route rendering the form,
  no module-loader whitelist change needed
This commit is contained in:
2026-07-21 11:24:32 +02:00
parent 6d6302294e
commit 4360bc0c6b
3 changed files with 324 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
/**
* Ausschreibungs-Radar (tender-radar) module API client.
* Consumes the /modules/tender-radar/* REST surface built in Plan 05.
* All calls use credentials: 'include' for cookie-based auth.
*
* Security: the DÖE source config carries no secrets (no username/password
* field, unlike DkvConfig) — T-10-18 accepts this as low-risk information
* disclosure since the config fetch response is auth-free by nature.
*/
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
// --- Types mirroring the Plan 05 backend contract (TenderSourcePollConfig) ---
/**
* Singleton `doe-opendata` poll config.
* `pollIntervalMin` is the cron-tick frequency (D-04 default 60) — NOT the
* DÖE fetch frequency, which is gated separately by the day-cursor inside
* the backend's TenderIngestionService (day-granular source).
*/
export interface SourceConfig {
/** Fixed slug identifying the source, e.g. 'doe-opendata'. Read-only display field. */
sourceType: string;
pollIntervalMin: number;
isActive: boolean;
/** Day-cursor of the last successfully ingested day. Read-only display field. */
lastIngestedDay?: string | null;
}
/** Payload accepted by PUT /modules/tender-radar/source-config. */
export interface SaveSourceConfigPayload {
pollIntervalMin: number;
isActive: boolean;
}
// --- API functions ---
/**
* Fetch the current shared DÖE source poll config.
* GET /modules/tender-radar/source-config
*/
export async function fetchSourceConfig(): Promise<SourceConfig> {
const res = await fetch(`${API_URL}/modules/tender-radar/source-config`, {
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to fetch tender-radar source config');
return res.json();
}
/**
* Save the shared DÖE source poll config.
* PUT /modules/tender-radar/source-config
* Saving live-applies the change to the running scheduler (INGEST-06,
* no restart required — see Plan 05).
*/
export async function saveSourceConfig(
payload: SaveSourceConfigPayload,
): Promise<SourceConfig> {
const res = await fetch(`${API_URL}/modules/tender-radar/source-config`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error('Failed to save tender-radar source config');
return res.json();
}