Files
govoplan-views/webui/src/api/views.ts

281 lines
6.8 KiB
TypeScript

import {
apiFetch,
apiPath,
type ApiSettings,
type EffectiveViewProjection
} from "@govoplan/core-webui";
export type ViewScopeType = "system" | "tenant";
export type ViewAssignmentScopeType = "system" | "tenant" | "group" | "user";
export type ViewAssignmentMode = "available" | "default" | "required";
export type ViewRevision = {
id: string;
definition_id: string;
revision: number;
surface_contract_version: string;
visible_surface_ids: string[];
content_hash: string;
created_by?: string | null;
created_at: string;
};
export type ViewDefinition = {
id: string;
tenant_id?: string | null;
scope_type: ViewScopeType;
scope_id?: string | null;
definition_key: string;
name: string;
description?: string | null;
status: "draft" | "published" | "archived";
current_revision: number;
latest_revision: ViewRevision;
published_revision?: ViewRevision | null;
readonly: boolean;
stale_surface_ids: string[];
created_by?: string | null;
updated_by?: string | null;
created_at: string;
updated_at: string;
};
export type ViewAssignment = {
id: string;
tenant_id?: string | null;
scope_type: ViewAssignmentScopeType;
scope_id?: string | null;
definition_id: string;
revision_id?: string | null;
mode: ViewAssignmentMode;
priority: number;
is_active: boolean;
metadata: Record<string, unknown>;
created_by?: string | null;
updated_by?: string | null;
created_at: string;
updated_at: string;
};
type EffectiveViewApiResponse = {
active_view_id?: string | null;
active_revision_id?: string | null;
active_view_name?: string | null;
visible_surface_ids: string[];
locked: boolean;
available_views: Array<{
id: string;
name: string;
description?: string | null;
revision_id: string;
}>;
provenance: Array<{
source: string;
scope_type?: string | null;
scope_id?: string | null;
detail?: string | null;
}>;
diagnostics?: Array<{
severity: "warning" | "error";
code: string;
message: string;
surface_ids: string[];
}>;
};
function jsonBody(payload: unknown): RequestInit {
return {
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
};
}
function projection(response: EffectiveViewApiResponse): EffectiveViewProjection {
return {
activeViewId: response.active_view_id ?? null,
activeRevisionId: response.active_revision_id ?? null,
activeViewName: response.active_view_name ?? null,
visibleSurfaceIds: response.visible_surface_ids,
locked: response.locked,
availableViews: response.available_views.map((view) => ({
id: view.id,
name: view.name,
description: view.description,
revisionId: view.revision_id
})),
provenance: response.provenance.map((item) => ({
source: item.source,
scopeType: item.scope_type,
scopeId: item.scope_id,
detail: item.detail
})),
diagnostics: (response.diagnostics ?? []).map((item) => ({
severity: item.severity,
code: item.code,
message: item.message,
surfaceIds: item.surface_ids
}))
};
}
export async function fetchEffectiveView(
settings: ApiSettings
): Promise<EffectiveViewProjection> {
return projection(
await apiFetch<EffectiveViewApiResponse>(
settings,
"/api/v1/views/effective",
{ cache: "no-store" }
)
);
}
export async function selectEffectiveView(
settings: ApiSettings,
viewId: string | null
): Promise<EffectiveViewProjection> {
return projection(
await apiFetch<EffectiveViewApiResponse>(
settings,
"/api/v1/views/selection",
{
method: "PUT",
...jsonBody({ view_id: viewId })
}
)
);
}
export async function fetchViewDefinitions(
settings: ApiSettings,
scopeType: ViewScopeType
): Promise<ViewDefinition[]> {
const response = await apiFetch<{ definitions: ViewDefinition[] }>(
settings,
apiPath("/api/v1/views/definitions", {
scope_type: scopeType,
include_inherited: scopeType === "tenant"
})
);
return response.definitions;
}
export function createViewDefinition(
settings: ApiSettings,
payload: {
scope_type: ViewScopeType;
name: string;
description?: string | null;
visible_surface_ids: string[];
}
): Promise<ViewDefinition> {
return apiFetch(settings, "/api/v1/views/definitions", {
method: "POST",
...jsonBody(payload)
});
}
export function updateViewDefinition(
settings: ApiSettings,
definitionId: string,
payload: { name: string; description?: string | null }
): Promise<ViewDefinition> {
return apiFetch(settings, `/api/v1/views/definitions/${definitionId}`, {
method: "PATCH",
...jsonBody(payload)
});
}
export function createViewRevision(
settings: ApiSettings,
definitionId: string,
visibleSurfaceIds: string[]
): Promise<ViewDefinition> {
return apiFetch(
settings,
`/api/v1/views/definitions/${definitionId}/revisions`,
{
method: "POST",
...jsonBody({ visible_surface_ids: visibleSurfaceIds })
}
);
}
export function publishViewRevision(
settings: ApiSettings,
definitionId: string,
revisionId: string
): Promise<ViewDefinition> {
return apiFetch(
settings,
`/api/v1/views/definitions/${definitionId}/revisions/${revisionId}/publish`,
{ method: "POST" }
);
}
export function archiveViewDefinition(
settings: ApiSettings,
definitionId: string
): Promise<ViewDefinition> {
return apiFetch(settings, `/api/v1/views/definitions/${definitionId}`, {
method: "DELETE"
});
}
export async function fetchViewAssignments(
settings: ApiSettings,
scopeType: ViewScopeType
): Promise<ViewAssignment[]> {
const response = await apiFetch<{ assignments: ViewAssignment[] }>(
settings,
apiPath("/api/v1/views/assignments", {
scope_type: scopeType,
include_inherited: scopeType === "tenant"
})
);
return response.assignments;
}
export function createViewAssignment(
settings: ApiSettings,
payload: {
scope_type: ViewAssignmentScopeType;
scope_id?: string | null;
definition_id: string;
revision_id?: string | null;
mode: ViewAssignmentMode;
priority: number;
is_active: boolean;
}
): Promise<ViewAssignment> {
return apiFetch(settings, "/api/v1/views/assignments", {
method: "POST",
...jsonBody(payload)
});
}
export function updateViewAssignment(
settings: ApiSettings,
assignmentId: string,
payload: {
revision_id?: string | null;
mode?: ViewAssignmentMode;
priority?: number;
is_active?: boolean;
}
): Promise<ViewAssignment> {
return apiFetch(settings, `/api/v1/views/assignments/${assignmentId}`, {
method: "PATCH",
...jsonBody(payload)
});
}
export function deleteViewAssignment(
settings: ApiSettings,
assignmentId: string
): Promise<void> {
return apiFetch(settings, `/api/v1/views/assignments/${assignmentId}`, {
method: "DELETE"
});
}