feat(stamdata): admin stamdata maintenance editor (beheer)
Realizes ADR-0004's "future low-code editor that commits a PR": an
admin-only stamdata maintenance editor built on the stamdata-as-code
foundation.
Backend: `professions` moves from a hardcoded C# dictionary to an embedded
`professions.json` data-file (typed as `ProfessionMapping`) with valid-time
(geldigVan/geldigTot, half-open). A generic, reflection-driven
StamdataCatalog/StamdataTable/StamdataFile describes every table so one
endpoint pair + one grid editor serve all of them; add a table in one line.
Two read-only, admin-gated endpoints (GET /stamdata, GET /stamdata/{table}
?peildatum=) — no runtime write path. Generic build gate
`Every_catalog_table_is_valid` (keys non-blank, no overlapping validity,
well-formed windows).
Frontend: new `beheer` context (route beheer/stamdata, capabilityGuard
'stamdata:edit'). A schema-driven grid editor edits rows locally; download()
emits {table}.json for the admin to commit as a reviewed PR (no mutation
command — the CI build + StamdataValidationTests stay the authority).
Full gate GREEN both sides; gen:api leaves no drift; new stamdata story
passes axe. See WP-29 + ADR-0004.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -263,6 +263,102 @@ export class ApiClient {
|
||||
return Promise.resolve<IntakePolicyDto>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OK
|
||||
*/
|
||||
stamdataTables(): Promise<StamdataTableSummaryDto[]> {
|
||||
let url_ = this.baseUrl + "/api/v1/stamdata";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processStamdataTables(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processStamdataTables(response: Response): Promise<StamdataTableSummaryDto[]> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as StamdataTableSummaryDto[];
|
||||
return result200;
|
||||
});
|
||||
} else if (status === 403) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result403: any = null;
|
||||
result403 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;
|
||||
return throwException("Forbidden", status, _responseText, _headers, result403);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<StamdataTableSummaryDto[]>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param peildatum (optional)
|
||||
* @return OK
|
||||
*/
|
||||
stamdataTable(table: string, peildatum?: string | undefined): Promise<StamdataTableDto> {
|
||||
let url_ = this.baseUrl + "/api/v1/stamdata/{table}?";
|
||||
if (table === undefined || table === null)
|
||||
throw new globalThis.Error("The parameter 'table' must be defined.");
|
||||
url_ = url_.replace("{table}", encodeURIComponent("" + table));
|
||||
if (peildatum === null)
|
||||
throw new globalThis.Error("The parameter 'peildatum' cannot be null.");
|
||||
else if (peildatum !== undefined)
|
||||
url_ += "peildatum=" + encodeURIComponent("" + peildatum) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processStamdataTable(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processStamdataTable(response: Response): Promise<StamdataTableDto> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as StamdataTableDto;
|
||||
return result200;
|
||||
});
|
||||
} else if (status === 403) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result403: any = null;
|
||||
result403 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;
|
||||
return throwException("Forbidden", status, _responseText, _headers, result403);
|
||||
});
|
||||
} else if (status === 404) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("Not Found", status, _responseText, _headers);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<StamdataTableDto>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OK
|
||||
*/
|
||||
@@ -1858,6 +1954,28 @@ export interface SaveOrgTemplateRequest {
|
||||
draft?: OrgTemplateDto;
|
||||
}
|
||||
|
||||
export interface StamdataColumnDto {
|
||||
name?: string | undefined;
|
||||
type?: string | undefined;
|
||||
isKey?: boolean;
|
||||
options?: string[] | undefined;
|
||||
}
|
||||
|
||||
export interface StamdataTableDto {
|
||||
id?: string | undefined;
|
||||
label?: string | undefined;
|
||||
columns?: StamdataColumnDto[] | undefined;
|
||||
temporal?: boolean;
|
||||
rows?: any[] | undefined;
|
||||
}
|
||||
|
||||
export interface StamdataTableSummaryDto {
|
||||
id?: string | undefined;
|
||||
label?: string | undefined;
|
||||
columns?: StamdataColumnDto[] | undefined;
|
||||
temporal?: boolean;
|
||||
}
|
||||
|
||||
export interface SubOrgSummaryDto {
|
||||
subOrgId?: string | undefined;
|
||||
orgName?: string | undefined;
|
||||
|
||||
Reference in New Issue
Block a user