Initialize governed Views module
This commit is contained in:
280
webui/src/api/views.ts
Normal file
280
webui/src/api/views.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
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"
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user