import { apiFetch, apiPath, type ApiSettings } from "@govoplan/core-webui"; export type ViewPolicyScope = "system" | "tenant" | "group" | "user"; export type ViewPolicyItem = { allow_view?: boolean; allow_select?: boolean; allow_assign?: boolean; allow_edit?: boolean; allow_derive?: boolean; allow_workflow_activate?: boolean; allowed_view_ids?: string[]; visible_surface_ids?: string[]; }; export type EffectiveViewPolicy = { allow_view: boolean; allow_select: boolean; allow_assign: boolean; allow_edit: boolean; allow_derive: boolean; allow_workflow_activate: boolean; allowed_view_ids?: string[] | null; visible_surface_ids?: string[] | null; }; export type ViewPolicyScopeResponse = { scope_type: ViewPolicyScope; scope_id?: string | null; id?: string | null; revision?: number | null; policy: ViewPolicyItem; effective_policy: EffectiveViewPolicy; parent_policy: EffectiveViewPolicy; source_path: Array<{ scope_type: string; scope_id?: string | null; source_id?: string | null; fields?: string[]; }>; diagnostics: Array>; }; export type ViewPolicyReferenceData = { views: Array<{ id: string; name: string; scope_type?: string }>; surfaces: Array<{ id: string; label: string; module_id: string; kind: string }>; }; export type PolicyImpactCategory = "newly_allowed" | "newly_denied" | "unchanged" | "indeterminate"; export type PolicyImpactPreviewResponse = { preview_id: string; proposal_hash: string; policy_family: string; scope_type: string; scope_id?: string | null; base_revision?: number | null; counts: Record; effects: Array<{ category: PolicyImpactCategory; subject: { module_id: string; resource_type: string; resource_id: string; action: string; label?: string | null; scope_type?: string | null; scope_id?: string | null; }; current_allowed?: boolean | null; proposed_allowed?: boolean | null; rule: string; current_sources: Array<{ path: string; label: string }>; proposed_sources: Array<{ path: string; label: string }>; explanation?: string | null; }>; populations: Array<{ provider_id: string; state: "complete" | "sampled" | "truncated" | "unavailable"; returned: number; total_available?: number | null; explanation?: string | null; }>; details_hidden: boolean; details_explanation?: string | null; high_impact: boolean; }; export function fetchViewPolicy( settings: ApiSettings, scope: ViewPolicyScope, scopeId?: string | null ): Promise { return apiFetch(settings, apiPath(`/api/v1/admin/view-policies/${scope}`, { scope_id: scopeId || undefined })); } export function updateViewPolicy( settings: ApiSettings, scope: ViewPolicyScope, scopeId: string | null | undefined, policy: ViewPolicyItem, impactPreview?: Pick | null ): Promise { return apiFetch(settings, apiPath(`/api/v1/admin/view-policies/${scope}`, { scope_id: scopeId || undefined }), { method: "PUT", body: JSON.stringify({ policy, impact_preview_id: impactPreview?.preview_id, impact_proposal_hash: impactPreview?.proposal_hash }) }); } export function deleteViewPolicy( settings: ApiSettings, scope: ViewPolicyScope, scopeId?: string | null, impactPreview?: Pick | null ): Promise { return apiFetch(settings, apiPath(`/api/v1/admin/view-policies/${scope}`, { scope_id: scopeId || undefined, impact_preview_id: impactPreview?.preview_id, impact_proposal_hash: impactPreview?.proposal_hash }), { method: "DELETE" }); } export function previewViewPolicyImpact( settings: ApiSettings, scope: ViewPolicyScope, scopeId: string | null | undefined, policy: ViewPolicyItem, population: { viewIds: string[]; surfaceIds: string[] } ): Promise { return apiFetch(settings, "/api/v1/admin/policy-impact/preview", { method: "POST", body: JSON.stringify({ policy_family: "view", scope_type: scope, scope_id: scopeId || null, proposed_policy: policy, populations: [ { provider_id: "views", selector: { include_views: true, include_surfaces: false, view_ids: population.viewIds.slice(0, 500) }, limit: 500 }, { provider_id: "views", selector: { include_views: false, include_surfaces: true, surface_ids: population.surfaceIds.slice(0, 500) }, limit: 500 } ], include_details: true }) }); } export async function fetchViewPolicyReferences(settings: ApiSettings): Promise { const [definitionResult, surfaceResult] = await Promise.allSettled([ apiFetch<{ definitions: Array<{ id: string; name: string; scope_type?: string }> }>( settings, apiPath("/api/v1/views/definitions", { scope_type: "tenant", include_inherited: true }) ), apiFetch<{ surfaces: Array<{ id: string; label: string; module_id: string; kind: string }> }>( settings, "/api/v1/views/surfaces" ) ]); return { views: definitionResult.status === "fulfilled" ? definitionResult.value.definitions : [], surfaces: surfaceResult.status === "fulfilled" ? surfaceResult.value.surfaces : [] }; }