@burdenoff/microfe-adaptercloud 2026.628.1 → 2026.706.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"useAdapterCloudApi.js","names":[],"sources":["../../../src/adaptercloud/hooks/useAdapterCloudApi.ts"],"sourcesContent":["/**\n * AdapterCloud API Hooks\n * @module adaptercloud/hooks\n *\n * Real, typed data layer for AdapterCloud (InfraVerse). Every hook talks to the\n * deployed `wspace-adaptercloud-svc` subgraph through the workspace GraphQL\n * gateway via `graphqlFetch`. Operations, fields, inputs, enums and the\n * Connection/pageInfo shapes are taken verbatim from the service's\n * `.hive-schema.graphql`. No mocks — every screen reads/writes real data.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useAdapterCloud } from '../context';\nimport type {\n AdaAdapter,\n AdaAdapterConnectionPage,\n AdaAdapterKind,\n AdaAdapterStatus,\n AdaConnection,\n AdaConnectionPage,\n AdaConnectionStatus,\n AdaCostRecord,\n AdaCostSummary,\n AdaDriftRecord,\n AdaDriftStatus,\n AdaPolicy,\n AdaPolicyKind,\n AdaPolicyPage,\n AdaPolicyViolation,\n AdaPolicyViolationPage,\n AdaResource,\n AdaResourcePage,\n AdaResourceState,\n AdaSyncRun,\n AdaSyncRunPage,\n AdaSyncStatus,\n AdaViolationStatus,\n PaginationInput,\n AdaCreateAdapterInput,\n AdaUpdateAdapterInput,\n AdaCreateConnectionInput,\n AdaUpdateConnectionInput,\n AdaCreatePolicyInput,\n AdaStartSyncRunInput,\n} from '../types';\n\n// ============================================================================\n// Query keys\n// ============================================================================\n\nexport const adaKeys = {\n all: ['adaptercloud'] as const,\n health: () => [...adaKeys.all, 'health'] as const,\n costSummary: (connectionId?: string) =>\n [...adaKeys.all, 'cost-summary', connectionId ?? null] as const,\n costRecords: (filter: Record<string, unknown>) =>\n [...adaKeys.all, 'cost-records', filter] as const,\n adapters: (filter: Record<string, unknown>) => [...adaKeys.all, 'adapters', filter] as const,\n adapter: (id: string) => [...adaKeys.all, 'adapter', id] as const,\n connections: (filter: Record<string, unknown>) =>\n [...adaKeys.all, 'connections', filter] as const,\n connection: (id: string) => [...adaKeys.all, 'connection', id] as const,\n resources: (filter: Record<string, unknown>) => [...adaKeys.all, 'resources', filter] as const,\n resource: (id: string) => [...adaKeys.all, 'resource', id] as const,\n policies: (filter: Record<string, unknown>) => [...adaKeys.all, 'policies', filter] as const,\n policy: (id: string) => [...adaKeys.all, 'policy', id] as const,\n violations: (filter: Record<string, unknown>) => [...adaKeys.all, 'violations', filter] as const,\n drift: (filter: Record<string, unknown>) => [...adaKeys.all, 'drift', filter] as const,\n syncRuns: (filter: Record<string, unknown>) => [...adaKeys.all, 'sync-runs', filter] as const,\n syncRun: (id: string) => [...adaKeys.all, 'sync-run', id] as const,\n};\n\n// ============================================================================\n// Low-level query wrapper\n// ============================================================================\n\nconst adaQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n return response.data;\n};\n\nconst STALE = 30 * 1000;\n\n// ============================================================================\n// Reusable GraphQL field selections (inline strings)\n// ============================================================================\n\nconst ADAPTER_FIELDS = `\n id name slug kind status vendor version description capabilities tags\n configSchema workspaceId createdAt updatedAt\n`;\n\nconst CONNECTION_FIELDS = `\n id name adapterId authMethod status environment region resourceCount\n secretRef targetRef config tags lastError lastSyncedAt workspaceId createdAt updatedAt\n`;\n\nconst RESOURCE_FIELDS = `\n id connectionId externalId resourceType name region state monthlyCost\n providerTags attributes tags lastSeenAt workspaceId createdAt updatedAt\n`;\n\nconst POLICY_FIELDS = `\n id name description kind enforcement severity expression appliesTo isActive\n tags workspaceId createdAt updatedAt\n`;\n\nconst VIOLATION_FIELDS = `\n id policyId resourceId message status severity evidence waiverReason\n detectedAt resolvedAt resolvedById workspaceId createdAt updatedAt\n`;\n\nconst DRIFT_FIELDS = `\n id resourceId attributePath expectedValue observedValue severity status\n detectedAt remediatedAt workspaceId createdAt updatedAt\n`;\n\nconst SYNC_RUN_FIELDS = `\n id connectionId kind status discoveredCount reconciledCount driftCount\n violationCount error summary startedAt finishedAt workspaceId createdAt updatedAt\n`;\n\n// ============================================================================\n// Health\n// ============================================================================\n\nexport const useAdaHealth = () => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.health(),\n queryFn: async () => {\n const result = await adaQuery<{ adaHealth: string }>(\n `query AdaHealth { adaHealth }`,\n {},\n apiGatewayUrl\n );\n return result.adaHealth;\n },\n staleTime: STALE,\n });\n};\n\n// ============================================================================\n// Adapters\n// ============================================================================\n\nexport interface AdapterListFilter {\n kind?: AdaAdapterKind;\n status?: AdaAdapterStatus;\n pagination?: PaginationInput;\n}\n\nexport const useAdapters = (filter: AdapterListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.adapters(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListAdapters: AdaAdapterConnectionPage }>(\n `query AdaListAdapters($kind: AdaAdapterKind, $status: AdaAdapterStatus, $pagination: PaginationInput) {\n adaListAdapters(kind: $kind, status: $status, pagination: $pagination) {\n items { ${ADAPTER_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n kind: filter.kind,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListAdapters;\n },\n staleTime: STALE,\n });\n};\n\nexport const useAdapter = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.adapter(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaAdapter: AdaAdapter | null }>(\n `query AdaAdapter($id: ID!) {\n adaAdapter(id: $id) {\n ${ADAPTER_FIELDS}\n connections { ${CONNECTION_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaAdapter;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\nexport const useCreateAdapter = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaCreateAdapterInput) => {\n const result = await adaQuery<{ adaCreateAdapter: AdaAdapter }>(\n `mutation AdaCreateAdapter($input: AdaCreateAdapterInput!) {\n adaCreateAdapter(input: $input) { ${ADAPTER_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaCreateAdapter;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'adapters'] });\n },\n });\n};\n\nexport const useUpdateAdapter = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AdaUpdateAdapterInput }) => {\n const result = await adaQuery<{ adaUpdateAdapter: AdaAdapter }>(\n `mutation AdaUpdateAdapter($id: ID!, $input: AdaUpdateAdapterInput!) {\n adaUpdateAdapter(id: $id, input: $input) { ${ADAPTER_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.adaUpdateAdapter;\n },\n onSuccess: (adapter) => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'adapters'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.adapter(adapter.id) });\n },\n });\n};\n\nexport const useArchiveAdapter = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaArchiveAdapter: boolean }>(\n `mutation AdaArchiveAdapter($id: ID!) { adaArchiveAdapter(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.adaArchiveAdapter;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'adapters'] });\n },\n });\n};\n\n// ============================================================================\n// Connections\n// ============================================================================\n\nexport interface ConnectionListFilter {\n adapterId?: string;\n status?: AdaConnectionStatus;\n pagination?: PaginationInput;\n}\n\nexport const useConnections = (filter: ConnectionListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.connections(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListConnections: AdaConnectionPage }>(\n `query AdaListConnections($adapterId: ID, $status: AdaConnectionStatus, $pagination: PaginationInput) {\n adaListConnections(adapterId: $adapterId, status: $status, pagination: $pagination) {\n items {\n ${CONNECTION_FIELDS}\n adapter { id name slug kind }\n }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n adapterId: filter.adapterId,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListConnections;\n },\n staleTime: STALE,\n });\n};\n\nexport const useConnection = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.connection(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaConnection: AdaConnection | null }>(\n `query AdaConnection($id: ID!) {\n adaConnection(id: $id) {\n ${CONNECTION_FIELDS}\n adapter { ${ADAPTER_FIELDS} }\n resources { ${RESOURCE_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaConnection;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\nexport const useCreateConnection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaCreateConnectionInput) => {\n const result = await adaQuery<{ adaCreateConnection: AdaConnection }>(\n `mutation AdaCreateConnection($input: AdaCreateConnectionInput!) {\n adaCreateConnection(input: $input) { ${CONNECTION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaCreateConnection;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n },\n });\n};\n\nexport const useUpdateConnection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AdaUpdateConnectionInput }) => {\n const result = await adaQuery<{ adaUpdateConnection: AdaConnection }>(\n `mutation AdaUpdateConnection($id: ID!, $input: AdaUpdateConnectionInput!) {\n adaUpdateConnection(id: $id, input: $input) { ${CONNECTION_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.adaUpdateConnection;\n },\n onSuccess: (connection) => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.connection(connection.id) });\n },\n });\n};\n\nexport const useSetConnectionStatus = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: AdaConnectionStatus }) => {\n const result = await adaQuery<{ adaSetConnectionStatus: AdaConnection }>(\n `mutation AdaSetConnectionStatus($id: ID!, $status: AdaConnectionStatus!) {\n adaSetConnectionStatus(id: $id, status: $status) { ${CONNECTION_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.adaSetConnectionStatus;\n },\n onSuccess: (connection) => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.connection(connection.id) });\n },\n });\n};\n\nexport const useDisableConnection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaDisableConnection: AdaConnection }>(\n `mutation AdaDisableConnection($id: ID!) {\n adaDisableConnection(id: $id) { ${CONNECTION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaDisableConnection;\n },\n onSuccess: (connection) => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.connection(connection.id) });\n },\n });\n};\n\n// ============================================================================\n// Resources\n// ============================================================================\n\nexport interface ResourceListFilter {\n connectionId?: string;\n resourceType?: string;\n state?: AdaResourceState;\n pagination?: PaginationInput;\n}\n\nexport const useResources = (filter: ResourceListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.resources(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListResources: AdaResourcePage }>(\n `query AdaListResources($connectionId: ID, $resourceType: String, $state: AdaResourceState, $pagination: PaginationInput) {\n adaListResources(connectionId: $connectionId, resourceType: $resourceType, state: $state, pagination: $pagination) {\n items {\n ${RESOURCE_FIELDS}\n connection { id name }\n }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n connectionId: filter.connectionId,\n resourceType: filter.resourceType,\n state: filter.state,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListResources;\n },\n staleTime: STALE,\n });\n};\n\nexport const useResource = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.resource(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaResource: AdaResource | null }>(\n `query AdaResource($id: ID!) {\n adaResource(id: $id) {\n ${RESOURCE_FIELDS}\n connection { ${CONNECTION_FIELDS} adapter { id name slug kind } }\n dependsOn { id name resourceType region state monthlyCost }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaResource;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\n// ============================================================================\n// Policies\n// ============================================================================\n\nexport interface PolicyListFilter {\n isActive?: boolean;\n kind?: AdaPolicyKind;\n pagination?: PaginationInput;\n}\n\nexport const usePolicies = (filter: PolicyListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.policies(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListPolicies: AdaPolicyPage }>(\n `query AdaListPolicies($isActive: Boolean, $kind: AdaPolicyKind, $pagination: PaginationInput) {\n adaListPolicies(isActive: $isActive, kind: $kind, pagination: $pagination) {\n items { ${POLICY_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n isActive: filter.isActive,\n kind: filter.kind,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListPolicies;\n },\n staleTime: STALE,\n });\n};\n\nexport const usePolicy = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.policy(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaPolicy: AdaPolicy | null }>(\n `query AdaPolicy($id: ID!) {\n adaPolicy(id: $id) {\n ${POLICY_FIELDS}\n violations { ${VIOLATION_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaPolicy;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\nexport const useCreatePolicy = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaCreatePolicyInput) => {\n const result = await adaQuery<{ adaCreatePolicy: AdaPolicy }>(\n `mutation AdaCreatePolicy($input: AdaCreatePolicyInput!) {\n adaCreatePolicy(input: $input) { ${POLICY_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaCreatePolicy;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'policies'] });\n },\n });\n};\n\nexport const useArchivePolicy = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaArchivePolicy: boolean }>(\n `mutation AdaArchivePolicy($id: ID!) { adaArchivePolicy(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.adaArchivePolicy;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'policies'] });\n },\n });\n};\n\n// ============================================================================\n// Violations\n// ============================================================================\n\nexport interface ViolationListFilter {\n policyId?: string;\n resourceId?: string;\n status?: AdaViolationStatus;\n pagination?: PaginationInput;\n}\n\nexport const useViolations = (filter: ViolationListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.violations(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListViolations: AdaPolicyViolationPage }>(\n `query AdaListViolations($policyId: ID, $resourceId: ID, $status: AdaViolationStatus, $pagination: PaginationInput) {\n adaListViolations(policyId: $policyId, resourceId: $resourceId, status: $status, pagination: $pagination) {\n items {\n ${VIOLATION_FIELDS}\n policy { id name kind severity }\n }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n policyId: filter.policyId,\n resourceId: filter.resourceId,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListViolations;\n },\n staleTime: STALE,\n });\n};\n\nexport const useResolveViolation = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({\n id,\n status,\n waiverReason,\n }: {\n id: string;\n status: AdaViolationStatus;\n waiverReason?: string;\n }) => {\n const result = await adaQuery<{ adaResolveViolation: AdaPolicyViolation }>(\n `mutation AdaResolveViolation($id: ID!, $status: AdaViolationStatus!, $waiverReason: String) {\n adaResolveViolation(id: $id, status: $status, waiverReason: $waiverReason) {\n ${VIOLATION_FIELDS}\n }\n }`,\n { id, status, waiverReason },\n apiGatewayUrl\n );\n return result.adaResolveViolation;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'violations'] });\n },\n });\n};\n\n// ============================================================================\n// Drift\n// ============================================================================\n\nexport interface DriftListFilter {\n resourceId?: string;\n status?: AdaDriftStatus;\n}\n\nexport const useDriftRecords = (filter: DriftListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.drift(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListDriftRecords: AdaDriftRecord[] }>(\n `query AdaListDriftRecords($resourceId: ID, $status: AdaDriftStatus) {\n adaListDriftRecords(resourceId: $resourceId, status: $status) { ${DRIFT_FIELDS} }\n }`,\n { resourceId: filter.resourceId, status: filter.status },\n apiGatewayUrl\n );\n return result.adaListDriftRecords;\n },\n staleTime: STALE,\n });\n};\n\nexport const useRemediateDrift = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaRemediateDrift: AdaDriftRecord }>(\n `mutation AdaRemediateDrift($id: ID!) {\n adaRemediateDrift(id: $id) { ${DRIFT_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaRemediateDrift;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'drift'] });\n },\n });\n};\n\n// ============================================================================\n// Sync runs\n// ============================================================================\n\nexport interface SyncRunListFilter {\n connectionId?: string;\n status?: AdaSyncStatus;\n pagination?: PaginationInput;\n}\n\nexport const useSyncRuns = (filter: SyncRunListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.syncRuns(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListSyncRuns: AdaSyncRunPage }>(\n `query AdaListSyncRuns($connectionId: ID, $status: AdaSyncStatus, $pagination: PaginationInput) {\n adaListSyncRuns(connectionId: $connectionId, status: $status, pagination: $pagination) {\n items { ${SYNC_RUN_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n connectionId: filter.connectionId,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListSyncRuns;\n },\n staleTime: STALE,\n });\n};\n\nexport const useStartSyncRun = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaStartSyncRunInput) => {\n const result = await adaQuery<{ adaStartSyncRun: AdaSyncRun }>(\n `mutation AdaStartSyncRun($input: AdaStartSyncRunInput!) {\n adaStartSyncRun(input: $input) { ${SYNC_RUN_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaStartSyncRun;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'sync-runs'] });\n },\n });\n};\n\nexport const useCancelSyncRun = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaCancelSyncRun: AdaSyncRun }>(\n `mutation AdaCancelSyncRun($id: ID!) {\n adaCancelSyncRun(id: $id) { ${SYNC_RUN_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaCancelSyncRun;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'sync-runs'] });\n },\n });\n};\n\n// ============================================================================\n// FinOps — cost\n// ============================================================================\n\nexport const useCostSummary = (connectionId?: string) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.costSummary(connectionId),\n queryFn: async () => {\n const result = await adaQuery<{ adaCostSummary: AdaCostSummary }>(\n `query AdaCostSummary($connectionId: ID) {\n adaCostSummary(connectionId: $connectionId) {\n currency total\n lines { service total }\n }\n }`,\n { connectionId },\n apiGatewayUrl\n );\n return result.adaCostSummary;\n },\n staleTime: STALE,\n });\n};\n\nexport interface CostRecordListFilter {\n connectionId?: string;\n resourceId?: string;\n service?: string;\n}\n\nexport const useCostRecords = (filter: CostRecordListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.costRecords(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListCostRecords: AdaCostRecord[] }>(\n `query AdaListCostRecords($connectionId: ID, $resourceId: ID, $service: String) {\n adaListCostRecords(connectionId: $connectionId, resourceId: $resourceId, service: $service) {\n id service amount currency periodStart periodEnd breakdown\n connectionId resourceId workspaceId createdAt updatedAt\n }\n }`,\n {\n connectionId: filter.connectionId,\n resourceId: filter.resourceId,\n service: filter.service,\n },\n apiGatewayUrl\n );\n return result.adaListCostRecords;\n },\n staleTime: STALE,\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useAdapterCloudApi.js","names":[],"sources":["../../../src/adaptercloud/hooks/useAdapterCloudApi.ts"],"sourcesContent":["/**\n * AdapterCloud API Hooks\n * @module adaptercloud/hooks\n *\n * Real, typed data layer for AdapterCloud (InfraVerse). Every hook talks to the\n * deployed `wspace-adaptercloud-svc` subgraph through the workspace GraphQL\n * gateway via `graphqlFetch`. Operations, fields, inputs, enums and the\n * Connection/pageInfo shapes are taken verbatim from the service's\n * `.hive-schema.graphql`. No mocks — every screen reads/writes real data.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nimport { useAdapterCloud } from '../context';\nimport { notify } from '../lib/toast';\nimport type {\n AdaAdapter,\n AdaAdapterConnectionPage,\n AdaAdapterKind,\n AdaAdapterStatus,\n AdaConnection,\n AdaConnectionPage,\n AdaConnectionStatus,\n AdaCostRecord,\n AdaCostSummary,\n AdaDriftRecord,\n AdaDriftStatus,\n AdaPolicy,\n AdaPolicyKind,\n AdaPolicyPage,\n AdaPolicyViolation,\n AdaPolicyViolationPage,\n AdaResource,\n AdaResourcePage,\n AdaResourceState,\n AdaSyncRun,\n AdaSyncRunPage,\n AdaSyncStatus,\n AdaViolationStatus,\n PaginationInput,\n AdaCreateAdapterInput,\n AdaUpdateAdapterInput,\n AdaCreateConnectionInput,\n AdaUpdateConnectionInput,\n AdaCreatePolicyInput,\n AdaStartSyncRunInput,\n} from '../types';\n\n// ============================================================================\n// Query keys\n// ============================================================================\n\nexport const adaKeys = {\n all: ['adaptercloud'] as const,\n health: () => [...adaKeys.all, 'health'] as const,\n costSummary: (connectionId?: string) =>\n [...adaKeys.all, 'cost-summary', connectionId ?? null] as const,\n costRecords: (filter: Record<string, unknown>) =>\n [...adaKeys.all, 'cost-records', filter] as const,\n adapters: (filter: Record<string, unknown>) => [...adaKeys.all, 'adapters', filter] as const,\n adapter: (id: string) => [...adaKeys.all, 'adapter', id] as const,\n connections: (filter: Record<string, unknown>) =>\n [...adaKeys.all, 'connections', filter] as const,\n connection: (id: string) => [...adaKeys.all, 'connection', id] as const,\n resources: (filter: Record<string, unknown>) => [...adaKeys.all, 'resources', filter] as const,\n resource: (id: string) => [...adaKeys.all, 'resource', id] as const,\n policies: (filter: Record<string, unknown>) => [...adaKeys.all, 'policies', filter] as const,\n policy: (id: string) => [...adaKeys.all, 'policy', id] as const,\n violations: (filter: Record<string, unknown>) => [...adaKeys.all, 'violations', filter] as const,\n drift: (filter: Record<string, unknown>) => [...adaKeys.all, 'drift', filter] as const,\n syncRuns: (filter: Record<string, unknown>) => [...adaKeys.all, 'sync-runs', filter] as const,\n syncRun: (id: string) => [...adaKeys.all, 'sync-run', id] as const,\n};\n\n// ============================================================================\n// Low-level query wrapper\n// ============================================================================\n\nconst adaQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n // After a successful mutation, drop the fe-libs graphqlFetch response cache\n // (≤30s TTL) so the follow-up React-Query refetch reads fresh data instead of\n // a stale cached response — otherwise writes appear to \"not take effect\" for\n // up to 30s. (fe-libs' own Apollo client does the same on every mutation.)\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlFetchResponseCache();\n }\n\n return response.data;\n};\n\nconst STALE = 30 * 1000;\n\n// ============================================================================\n// Reusable GraphQL field selections (inline strings)\n// ============================================================================\n\nconst ADAPTER_FIELDS = `\n id name slug kind status vendor version description capabilities tags\n configSchema workspaceId createdAt updatedAt\n`;\n\nconst CONNECTION_FIELDS = `\n id name adapterId authMethod status environment region resourceCount\n secretRef targetRef config tags lastError lastSyncedAt workspaceId createdAt updatedAt\n`;\n\nconst RESOURCE_FIELDS = `\n id connectionId externalId resourceType name region state monthlyCost\n providerTags attributes tags lastSeenAt workspaceId createdAt updatedAt\n`;\n\nconst POLICY_FIELDS = `\n id name description kind enforcement severity expression appliesTo isActive\n tags workspaceId createdAt updatedAt\n`;\n\nconst VIOLATION_FIELDS = `\n id policyId resourceId message status severity evidence waiverReason\n detectedAt resolvedAt resolvedById workspaceId createdAt updatedAt\n`;\n\nconst DRIFT_FIELDS = `\n id resourceId attributePath expectedValue observedValue severity status\n detectedAt remediatedAt workspaceId createdAt updatedAt\n`;\n\nconst SYNC_RUN_FIELDS = `\n id connectionId kind status discoveredCount reconciledCount driftCount\n violationCount error summary startedAt finishedAt workspaceId createdAt updatedAt\n`;\n\n// ============================================================================\n// Health\n// ============================================================================\n\nexport const useAdaHealth = () => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.health(),\n queryFn: async () => {\n const result = await adaQuery<{ adaHealth: string }>(\n `query AdaHealth { adaHealth }`,\n {},\n apiGatewayUrl\n );\n return result.adaHealth;\n },\n staleTime: STALE,\n });\n};\n\n// ============================================================================\n// Adapters\n// ============================================================================\n\nexport interface AdapterListFilter {\n kind?: AdaAdapterKind;\n status?: AdaAdapterStatus;\n pagination?: PaginationInput;\n}\n\nexport const useAdapters = (filter: AdapterListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.adapters(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListAdapters: AdaAdapterConnectionPage }>(\n `query AdaListAdapters($kind: AdaAdapterKind, $status: AdaAdapterStatus, $pagination: PaginationInput) {\n adaListAdapters(kind: $kind, status: $status, pagination: $pagination) {\n items { ${ADAPTER_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n kind: filter.kind,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListAdapters;\n },\n staleTime: STALE,\n });\n};\n\nexport const useAdapter = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.adapter(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaAdapter: AdaAdapter | null }>(\n `query AdaAdapter($id: ID!) {\n adaAdapter(id: $id) {\n ${ADAPTER_FIELDS}\n connections { ${CONNECTION_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaAdapter;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\nexport const useCreateAdapter = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaCreateAdapterInput) => {\n const result = await adaQuery<{ adaCreateAdapter: AdaAdapter }>(\n `mutation AdaCreateAdapter($input: AdaCreateAdapterInput!) {\n adaCreateAdapter(input: $input) { ${ADAPTER_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaCreateAdapter;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'adapters'] });\n },\n });\n};\n\nexport const useUpdateAdapter = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AdaUpdateAdapterInput }) => {\n const result = await adaQuery<{ adaUpdateAdapter: AdaAdapter }>(\n `mutation AdaUpdateAdapter($id: ID!, $input: AdaUpdateAdapterInput!) {\n adaUpdateAdapter(id: $id, input: $input) { ${ADAPTER_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.adaUpdateAdapter;\n },\n onSuccess: (adapter) => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'adapters'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.adapter(adapter.id) });\n },\n });\n};\n\nexport const useArchiveAdapter = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaArchiveAdapter: boolean }>(\n `mutation AdaArchiveAdapter($id: ID!) { adaArchiveAdapter(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.adaArchiveAdapter;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'adapters'] });\n },\n });\n};\n\n// ============================================================================\n// Connections\n// ============================================================================\n\nexport interface ConnectionListFilter {\n adapterId?: string;\n status?: AdaConnectionStatus;\n pagination?: PaginationInput;\n}\n\nexport const useConnections = (filter: ConnectionListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.connections(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListConnections: AdaConnectionPage }>(\n `query AdaListConnections($adapterId: ID, $status: AdaConnectionStatus, $pagination: PaginationInput) {\n adaListConnections(adapterId: $adapterId, status: $status, pagination: $pagination) {\n items {\n ${CONNECTION_FIELDS}\n adapter { id name slug kind }\n }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n adapterId: filter.adapterId,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListConnections;\n },\n staleTime: STALE,\n });\n};\n\nexport const useConnection = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.connection(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaConnection: AdaConnection | null }>(\n `query AdaConnection($id: ID!) {\n adaConnection(id: $id) {\n ${CONNECTION_FIELDS}\n adapter { ${ADAPTER_FIELDS} }\n resources { ${RESOURCE_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaConnection;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\nexport const useCreateConnection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaCreateConnectionInput) => {\n const result = await adaQuery<{ adaCreateConnection: AdaConnection }>(\n `mutation AdaCreateConnection($input: AdaCreateConnectionInput!) {\n adaCreateConnection(input: $input) { ${CONNECTION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaCreateConnection;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n },\n });\n};\n\nexport const useUpdateConnection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AdaUpdateConnectionInput }) => {\n const result = await adaQuery<{ adaUpdateConnection: AdaConnection }>(\n `mutation AdaUpdateConnection($id: ID!, $input: AdaUpdateConnectionInput!) {\n adaUpdateConnection(id: $id, input: $input) { ${CONNECTION_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.adaUpdateConnection;\n },\n onSuccess: (connection) => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.connection(connection.id) });\n },\n });\n};\n\nexport const useSetConnectionStatus = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: AdaConnectionStatus }) => {\n const result = await adaQuery<{ adaSetConnectionStatus: AdaConnection }>(\n `mutation AdaSetConnectionStatus($id: ID!, $status: AdaConnectionStatus!) {\n adaSetConnectionStatus(id: $id, status: $status) { ${CONNECTION_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.adaSetConnectionStatus;\n },\n onSuccess: (connection) => {\n notify.success('Connection health updated');\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.connection(connection.id) });\n },\n });\n};\n\nexport const useDisableConnection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaDisableConnection: AdaConnection }>(\n `mutation AdaDisableConnection($id: ID!) {\n adaDisableConnection(id: $id) { ${CONNECTION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaDisableConnection;\n },\n onSuccess: (connection) => {\n notify.success('Connection disabled');\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n void queryClient.invalidateQueries({ queryKey: adaKeys.connection(connection.id) });\n },\n });\n};\n\n// ============================================================================\n// Resources\n// ============================================================================\n\nexport interface ResourceListFilter {\n connectionId?: string;\n resourceType?: string;\n state?: AdaResourceState;\n pagination?: PaginationInput;\n}\n\nexport const useResources = (filter: ResourceListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.resources(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListResources: AdaResourcePage }>(\n `query AdaListResources($connectionId: ID, $resourceType: String, $state: AdaResourceState, $pagination: PaginationInput) {\n adaListResources(connectionId: $connectionId, resourceType: $resourceType, state: $state, pagination: $pagination) {\n items {\n ${RESOURCE_FIELDS}\n connection { id name }\n }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n connectionId: filter.connectionId,\n resourceType: filter.resourceType,\n state: filter.state,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListResources;\n },\n staleTime: STALE,\n });\n};\n\nexport const useResource = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.resource(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaResource: AdaResource | null }>(\n `query AdaResource($id: ID!) {\n adaResource(id: $id) {\n ${RESOURCE_FIELDS}\n connection { ${CONNECTION_FIELDS} adapter { id name slug kind } }\n dependsOn { id name resourceType region state monthlyCost }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaResource;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\n// ============================================================================\n// Policies\n// ============================================================================\n\nexport interface PolicyListFilter {\n isActive?: boolean;\n kind?: AdaPolicyKind;\n pagination?: PaginationInput;\n}\n\nexport const usePolicies = (filter: PolicyListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.policies(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListPolicies: AdaPolicyPage }>(\n `query AdaListPolicies($isActive: Boolean, $kind: AdaPolicyKind, $pagination: PaginationInput) {\n adaListPolicies(isActive: $isActive, kind: $kind, pagination: $pagination) {\n items { ${POLICY_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n isActive: filter.isActive,\n kind: filter.kind,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListPolicies;\n },\n staleTime: STALE,\n });\n};\n\nexport const usePolicy = (id: string | undefined) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.policy(id ?? ''),\n queryFn: async () => {\n const result = await adaQuery<{ adaPolicy: AdaPolicy | null }>(\n `query AdaPolicy($id: ID!) {\n adaPolicy(id: $id) {\n ${POLICY_FIELDS}\n violations { ${VIOLATION_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaPolicy;\n },\n enabled: Boolean(id),\n staleTime: STALE,\n });\n};\n\nexport const useCreatePolicy = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaCreatePolicyInput) => {\n const result = await adaQuery<{ adaCreatePolicy: AdaPolicy }>(\n `mutation AdaCreatePolicy($input: AdaCreatePolicyInput!) {\n adaCreatePolicy(input: $input) { ${POLICY_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaCreatePolicy;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'policies'] });\n },\n });\n};\n\nexport const useArchivePolicy = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaArchivePolicy: boolean }>(\n `mutation AdaArchivePolicy($id: ID!) { adaArchivePolicy(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.adaArchivePolicy;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'policies'] });\n },\n });\n};\n\n// ============================================================================\n// Violations\n// ============================================================================\n\nexport interface ViolationListFilter {\n policyId?: string;\n resourceId?: string;\n status?: AdaViolationStatus;\n pagination?: PaginationInput;\n}\n\nexport const useViolations = (filter: ViolationListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.violations(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListViolations: AdaPolicyViolationPage }>(\n `query AdaListViolations($policyId: ID, $resourceId: ID, $status: AdaViolationStatus, $pagination: PaginationInput) {\n adaListViolations(policyId: $policyId, resourceId: $resourceId, status: $status, pagination: $pagination) {\n items {\n ${VIOLATION_FIELDS}\n policy { id name kind severity }\n }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n policyId: filter.policyId,\n resourceId: filter.resourceId,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListViolations;\n },\n staleTime: STALE,\n });\n};\n\nexport const useResolveViolation = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async ({\n id,\n status,\n waiverReason,\n }: {\n id: string;\n status: AdaViolationStatus;\n waiverReason?: string;\n }) => {\n const result = await adaQuery<{ adaResolveViolation: AdaPolicyViolation }>(\n `mutation AdaResolveViolation($id: ID!, $status: AdaViolationStatus!, $waiverReason: String) {\n adaResolveViolation(id: $id, status: $status, waiverReason: $waiverReason) {\n ${VIOLATION_FIELDS}\n }\n }`,\n { id, status, waiverReason },\n apiGatewayUrl\n );\n return result.adaResolveViolation;\n },\n onSuccess: (violation) => {\n const label: Record<string, string> = {\n ACKNOWLEDGED: 'Violation acknowledged',\n RESOLVED: 'Violation resolved',\n WAIVED: 'Violation waived',\n OPEN: 'Violation reopened',\n };\n notify.success(label[violation.status] ?? 'Violation updated');\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'violations'] });\n },\n });\n};\n\n// ============================================================================\n// Drift\n// ============================================================================\n\nexport interface DriftListFilter {\n resourceId?: string;\n status?: AdaDriftStatus;\n}\n\nexport const useDriftRecords = (filter: DriftListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.drift(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListDriftRecords: AdaDriftRecord[] }>(\n `query AdaListDriftRecords($resourceId: ID, $status: AdaDriftStatus) {\n adaListDriftRecords(resourceId: $resourceId, status: $status) { ${DRIFT_FIELDS} }\n }`,\n { resourceId: filter.resourceId, status: filter.status },\n apiGatewayUrl\n );\n return result.adaListDriftRecords;\n },\n staleTime: STALE,\n });\n};\n\nexport const useRemediateDrift = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaRemediateDrift: AdaDriftRecord }>(\n `mutation AdaRemediateDrift($id: ID!) {\n adaRemediateDrift(id: $id) { ${DRIFT_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaRemediateDrift;\n },\n onSuccess: () => {\n notify.success('Drift marked remediated');\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'drift'] });\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'resources'] });\n },\n });\n};\n\n// ============================================================================\n// Sync runs\n// ============================================================================\n\nexport interface SyncRunListFilter {\n connectionId?: string;\n status?: AdaSyncStatus;\n pagination?: PaginationInput;\n}\n\nexport const useSyncRuns = (filter: SyncRunListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.syncRuns(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListSyncRuns: AdaSyncRunPage }>(\n `query AdaListSyncRuns($connectionId: ID, $status: AdaSyncStatus, $pagination: PaginationInput) {\n adaListSyncRuns(connectionId: $connectionId, status: $status, pagination: $pagination) {\n items { ${SYNC_RUN_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n connectionId: filter.connectionId,\n status: filter.status,\n pagination: filter.pagination,\n },\n apiGatewayUrl\n );\n return result.adaListSyncRuns;\n },\n staleTime: STALE,\n });\n};\n\nexport const useStartSyncRun = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (input: AdaStartSyncRunInput) => {\n const result = await adaQuery<{ adaStartSyncRun: AdaSyncRun }>(\n `mutation AdaStartSyncRun($input: AdaStartSyncRunInput!) {\n adaStartSyncRun(input: $input) { ${SYNC_RUN_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.adaStartSyncRun;\n },\n onSuccess: () => {\n notify.success('Sync run started');\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'sync-runs'] });\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'connections'] });\n },\n });\n};\n\nexport const useCancelSyncRun = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAdapterCloud();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await adaQuery<{ adaCancelSyncRun: AdaSyncRun }>(\n `mutation AdaCancelSyncRun($id: ID!) {\n adaCancelSyncRun(id: $id) { ${SYNC_RUN_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.adaCancelSyncRun;\n },\n onSuccess: () => {\n notify.success('Sync run cancelled');\n void queryClient.invalidateQueries({ queryKey: [...adaKeys.all, 'sync-runs'] });\n },\n });\n};\n\n// ============================================================================\n// FinOps — cost\n// ============================================================================\n\nexport const useCostSummary = (connectionId?: string) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.costSummary(connectionId),\n queryFn: async () => {\n const result = await adaQuery<{ adaCostSummary: AdaCostSummary }>(\n `query AdaCostSummary($connectionId: ID) {\n adaCostSummary(connectionId: $connectionId) {\n currency total\n lines { service total }\n }\n }`,\n { connectionId },\n apiGatewayUrl\n );\n return result.adaCostSummary;\n },\n staleTime: STALE,\n });\n};\n\nexport interface CostRecordListFilter {\n connectionId?: string;\n resourceId?: string;\n service?: string;\n}\n\nexport const useCostRecords = (filter: CostRecordListFilter = {}) => {\n const { apiGatewayUrl } = useAdapterCloud();\n return useQuery({\n queryKey: adaKeys.costRecords(filter as Record<string, unknown>),\n queryFn: async () => {\n const result = await adaQuery<{ adaListCostRecords: AdaCostRecord[] }>(\n `query AdaListCostRecords($connectionId: ID, $resourceId: ID, $service: String) {\n adaListCostRecords(connectionId: $connectionId, resourceId: $resourceId, service: $service) {\n id service amount currency periodStart periodEnd breakdown\n connectionId resourceId workspaceId createdAt updatedAt\n }\n }`,\n {\n connectionId: filter.connectionId,\n resourceId: filter.resourceId,\n service: filter.service,\n },\n apiGatewayUrl\n );\n return result.adaListCostRecords;\n },\n staleTime: STALE,\n });\n};\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { toast as e } from "sonner";
2
+ //#region src/adaptercloud/lib/toast.ts
3
+ var t = {
4
+ success(t, n) {
5
+ e.success(t, {
6
+ description: n,
7
+ duration: 4e3
8
+ });
9
+ },
10
+ info(t, n) {
11
+ e.info(t, {
12
+ description: n,
13
+ duration: 4e3
14
+ });
15
+ },
16
+ warning(t, n) {
17
+ e.warning(t, {
18
+ description: n,
19
+ duration: 8e3
20
+ });
21
+ },
22
+ error(t, n) {
23
+ e.error(t, {
24
+ description: n,
25
+ duration: Infinity,
26
+ closeButton: !0
27
+ });
28
+ },
29
+ dismiss(t) {
30
+ e.dismiss(t);
31
+ }
32
+ };
33
+ //#endregion
34
+ export { t as notify };
35
+
36
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","names":[],"sources":["../../../src/adaptercloud/lib/toast.ts"],"sourcesContent":["/**\n * Module-wide toast API for AdapterCloud.\n *\n * sonner is a global singleton and the matching <Toaster> is owned by the HOST:\n * the production app shell (adaptercloud-app) mounts a single one via\n * fe-libs `AppShellProvider`. This module therefore only EMITS toasts — it must\n * never mount its own <Toaster> in production or every toast renders twice.\n * (sonner is pinned to the same `^2.0.7` as fe-libs so bun dedupes to one\n * instance shared with the shell.)\n *\n * Durations follow the platform notification spec: success/info auto-dismiss;\n * errors persist with a close button and are announced via aria-live.\n */\nimport { toast } from 'sonner';\n\nexport const notify = {\n success(message: string, description?: string): void {\n toast.success(message, { description, duration: 4000 });\n },\n info(message: string, description?: string): void {\n toast.info(message, { description, duration: 4000 });\n },\n warning(message: string, description?: string): void {\n toast.warning(message, { description, duration: 8000 });\n },\n error(message: string, description?: string): void {\n toast.error(message, { description, duration: Infinity, closeButton: true });\n },\n dismiss(id?: string | number): void {\n toast.dismiss(id);\n },\n};\n\nexport { toast };\n"],"mappings":";;AAeA,IAAa,IAAS;CACpB,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,KAAK,GAAiB,GAA4B;AAChD,IAAM,KAAK,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEtD,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,MAAM,GAAiB,GAA4B;AACjD,IAAM,MAAM,GAAS;GAAE;GAAa,UAAU;GAAU,aAAa;GAAM,CAAC;;CAE9E,QAAQ,GAA4B;AAClC,IAAM,QAAQ,EAAG;;CAEpB"}
@@ -17,7 +17,7 @@ function F() {
17
17
  let { navigateTo: F, currentUser: I } = e(), { t: L } = v(), R = g((e, t) => {
18
18
  let n = L(e);
19
19
  return n === e ? t : n;
20
- }, [L]), z = c({ pagination: { limit: 1 } }), B = l({ pagination: { limit: 100 } }), V = f({ pagination: { limit: 1 } }), H = m({
20
+ }, [L]), z = c({ pagination: { limit: 100 } }), B = l({ pagination: { limit: 100 } }), V = f({ pagination: { limit: 100 } }), H = m({
21
21
  status: "OPEN",
22
22
  pagination: { limit: 50 }
23
23
  }), U = d({ status: "DETECTED" }), W = p({ pagination: { limit: 5 } }), G = u(), K = I?.firstName ?? I?.name ?? "there", q = _(() => {
@@ -307,8 +307,8 @@ function F() {
307
307
  e.discoveredCount,
308
308
  " ",
309
309
  R("adaptercloud.overview.discovered", "discovered"),
310
- " ·",
311
310
  " ",
311
+ "· ",
312
312
  h(e.createdAt)
313
313
  ]
314
314
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../src/adaptercloud/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Overview / Dashboard Page\n * @module adaptercloud/pages\n *\n * The AdapterCloud landing page: a single-pane view of the estate — fabric\n * counts (adapters, connections, resources), governance posture (open\n * violations, unremediated drift), operations (recent sync runs) and FinOps\n * (workspace cost summary by service). All data is live from the deployed\n * `wspace-adaptercloud-svc` subgraph.\n */\n\nimport { useMemo, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Boxes,\n Cable,\n Server,\n ShieldAlert,\n GitCompareArrows,\n RefreshCw,\n DollarSign,\n ArrowRight,\n Activity,\n AlertCircle,\n Plug,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { SeverityBadge } from '../components/SeverityBadge';\nimport { DataState } from '../components/DataState';\nimport { useAdapterCloud } from '../context/AdapterCloudContext';\nimport {\n useAdapters,\n useConnections,\n useResources,\n useViolations,\n useDriftRecords,\n useSyncRuns,\n useCostSummary,\n} from '../hooks/useAdapterCloudApi';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { formatMoney, severityRank, syncKindLabels } from '../utils/domain';\nimport type { AdaSeverity } from '../types';\n\nexport function OverviewPage() {\n const { navigateTo, currentUser } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const adapters = useAdapters({ pagination: { limit: 1 } });\n const connections = useConnections({ pagination: { limit: 100 } });\n const resources = useResources({ pagination: { limit: 1 } });\n const openViolations = useViolations({ status: 'OPEN', pagination: { limit: 50 } });\n const drift = useDriftRecords({ status: 'DETECTED' });\n const syncRuns = useSyncRuns({ pagination: { limit: 5 } });\n const costSummary = useCostSummary();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n // Connection health breakdown\n const connectionHealth = useMemo(() => {\n const items = connections.data?.items ?? [];\n const counts = { CONNECTED: 0, DEGRADED: 0, ERROR: 0, PENDING: 0, DISABLED: 0 };\n for (const c of items) counts[c.status] += 1;\n return counts;\n }, [connections.data]);\n\n const topViolations = useMemo(() => {\n return [...(openViolations.data?.items ?? [])]\n .sort((a, b) => severityRank[b.severity] - severityRank[a.severity])\n .slice(0, 5);\n }, [openViolations.data]);\n\n const recentRuns = syncRuns.data?.items ?? [];\n\n const topCostLines = useMemo(() => {\n return [...(costSummary.data?.lines ?? [])].sort((a, b) => b.total - a.total).slice(0, 6);\n }, [costSummary.data]);\n\n const isLoading =\n adapters.isLoading ||\n connections.isLoading ||\n resources.isLoading ||\n openViolations.isLoading ||\n drift.isLoading ||\n costSummary.isLoading;\n\n const isError =\n adapters.isError ||\n connections.isError ||\n resources.isError ||\n openViolations.isError ||\n drift.isError ||\n costSummary.isError;\n\n const refetchAll = useCallback(() => {\n void adapters.refetch();\n void connections.refetch();\n void resources.refetch();\n void openViolations.refetch();\n void drift.refetch();\n void syncRuns.refetch();\n void costSummary.refetch();\n }, [adapters, connections, resources, openViolations, drift, syncRuns, costSummary]);\n\n const stats = [\n {\n icon: <Boxes className=\"h-5 w-5\" />,\n value: adapters.data?.items.length\n ? String(adapters.data.items.length)\n : adapters.data\n ? '0'\n : '—',\n label: t('adaptercloud.overview.adapters', 'Adapters'),\n onClick: () => navigateTo('/adapters'),\n },\n {\n icon: <Cable className=\"h-5 w-5\" />,\n value: connections.data ? String(connections.data.items.length) : '—',\n label: t('adaptercloud.overview.connections', 'Connections'),\n onClick: () => navigateTo('/connections'),\n },\n {\n icon: <Server className=\"h-5 w-5\" />,\n value: resources.data ? String(resources.data.items.length) : '—',\n label: t('adaptercloud.overview.resources', 'Resources'),\n onClick: () => navigateTo('/resources'),\n },\n {\n icon: <ShieldAlert className=\"h-5 w-5\" />,\n value: openViolations.data ? String(openViolations.data.items.length) : '—',\n label: t('adaptercloud.overview.openViolations', 'Open Violations'),\n onClick: () => navigateTo('/violations'),\n },\n ];\n\n return (\n <PageLayout\n title={`${t('adaptercloud.overview.hello', 'Welcome back')}, ${firstName}`}\n description={t(\n 'adaptercloud.overview.subtitle',\n 'Your infrastructure continuum at a glance — fabric, governance, operations and cost.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={refetchAll}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n {t('adaptercloud.common.refresh', 'Refresh')}\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n {t(\n 'adaptercloud.overview.purpose',\n 'AdapterCloud connects your fragmented infrastructure — multi-cloud, on-prem, edge and SaaS — into one governable fabric. This overview is your control plane: see what you run, how connected it is, what is out of policy, and what it costs.'\n )}\n </PagePurpose>\n\n <DataState isLoading={isLoading} isError={isError} error={adapters.error} onRetry={refetchAll}>\n {/* Top metrics — the one emphasis zone */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.estate', 'Estate Summary')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {stats.map((stat) => (\n <button key={stat.label} type=\"button\" onClick={stat.onClick} className=\"text-left\">\n <MetricsCard icon={stat.icon} value={stat.value} label={stat.label} />\n </button>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Connection health + Cost summary */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Connection health */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.connectionHealth', 'Connection Health')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n {connections.data && connections.data.items.length > 0 ? (\n <ul className=\"space-y-3\">\n {(\n [\n ['CONNECTED', connectionHealth.CONNECTED],\n ['DEGRADED', connectionHealth.DEGRADED],\n ['ERROR', connectionHealth.ERROR],\n ['PENDING', connectionHealth.PENDING],\n ['DISABLED', connectionHealth.DISABLED],\n ] as const\n ).map(([status, count]) => (\n <li key={status} className=\"flex items-center justify-between\">\n <StatusBadge status={status} />\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">\n {count}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"flex flex-col items-center gap-3 py-6 text-center\">\n <Plug className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noConnections',\n 'No connections yet. Connect a target to start discovering resources.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n {t('adaptercloud.overview.addConnection', 'Add connection')}\n </button>\n </div>\n )}\n </GlassCard>\n </section>\n\n {/* Cost summary */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.costByService', 'Cost by Service')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/costs')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.overview.finops', 'FinOps')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n <div className=\"mb-4 flex items-baseline gap-2\">\n <DollarSign className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {costSummary.data\n ? formatMoney(costSummary.data.total, costSummary.data.currency)\n : '—'}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.totalSpend', 'total spend')}\n </span>\n </div>\n {topCostLines.length > 0 ? (\n <ul className=\"space-y-2\">\n {topCostLines.map((line) => (\n <li key={line.service} className=\"flex items-center justify-between text-sm\">\n <span className=\"truncate text-text-primary\">{line.service}</span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatMoney(line.total, costSummary.data?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"py-4 text-center text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noCost',\n 'No cost records ingested yet. Run a cost-ingest sync to populate FinOps.'\n )}\n </p>\n )}\n </GlassCard>\n </section>\n </div>\n\n {/* Governance + Operations */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Top open violations */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.priorityViolations', 'Priority Violations')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.overview.queue', 'Queue')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {topViolations.length > 0 ? (\n topViolations.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-error-text\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{v.message}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {v.policy?.name ?? t('adaptercloud.overview.policy', 'Policy')} ·{' '}\n {formatRelativeTime(v.detectedAt)}\n </p>\n </div>\n <SeverityBadge severity={v.severity as AdaSeverity} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <ShieldAlert className=\"h-8 w-8 text-status-success-text\" />\n <p className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.noViolations', 'No open violations. Estate is clean.')}\n </p>\n </div>\n )}\n </div>\n {drift.data && drift.data.length > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/drift')}\n className=\"mt-3 flex w-full items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3 text-left text-sm transition-colors hover:bg-accent/50\"\n >\n <GitCompareArrows className=\"h-4 w-4 text-status-warning-text\" />\n <span className=\"text-text-primary\">\n {drift.data.length} {t('adaptercloud.overview.driftDetected', 'drift records detected')}\n </span>\n <ArrowRight className=\"ml-auto h-3.5 w-3.5 text-text-secondary\" />\n </button>\n )}\n </section>\n\n {/* Recent sync runs */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.recentRuns', 'Recent Sync Runs')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {recentRuns.length > 0 ? (\n recentRuns.map((run) => (\n <button\n key={run.id}\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken\">\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {syncKindLabels[run.kind]}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {run.discoveredCount} {t('adaptercloud.overview.discovered', 'discovered')} ·{' '}\n {formatRelativeTime(run.createdAt)}\n </p>\n </div>\n <StatusBadge status={run.status} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <RefreshCw className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.noRuns', 'No sync runs yet.')}\n </p>\n </div>\n )}\n </div>\n </section>\n </div>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../src/adaptercloud/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Overview / Dashboard Page\n * @module adaptercloud/pages\n *\n * The AdapterCloud landing page: a single-pane view of the estate — fabric\n * counts (adapters, connections, resources), governance posture (open\n * violations, unremediated drift), operations (recent sync runs) and FinOps\n * (workspace cost summary by service). All data is live from the deployed\n * `wspace-adaptercloud-svc` subgraph.\n */\n\nimport { useMemo, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Boxes,\n Cable,\n Server,\n ShieldAlert,\n GitCompareArrows,\n RefreshCw,\n DollarSign,\n ArrowRight,\n Activity,\n AlertCircle,\n Plug,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { SeverityBadge } from '../components/SeverityBadge';\nimport { DataState } from '../components/DataState';\nimport { useAdapterCloud } from '../context/AdapterCloudContext';\nimport {\n useAdapters,\n useConnections,\n useResources,\n useViolations,\n useDriftRecords,\n useSyncRuns,\n useCostSummary,\n} from '../hooks/useAdapterCloudApi';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { formatMoney, severityRank, syncKindLabels } from '../utils/domain';\nimport type { AdaSeverity } from '../types';\n\nexport function OverviewPage() {\n const { navigateTo, currentUser } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n // Estate count tiles read item counts from the page length, so the page must\n // be large enough to be meaningful (limit:1 would cap every count at 1).\n const adapters = useAdapters({ pagination: { limit: 100 } });\n const connections = useConnections({ pagination: { limit: 100 } });\n const resources = useResources({ pagination: { limit: 100 } });\n const openViolations = useViolations({ status: 'OPEN', pagination: { limit: 50 } });\n const drift = useDriftRecords({ status: 'DETECTED' });\n const syncRuns = useSyncRuns({ pagination: { limit: 5 } });\n const costSummary = useCostSummary();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n // Connection health breakdown\n const connectionHealth = useMemo(() => {\n const items = connections.data?.items ?? [];\n const counts = { CONNECTED: 0, DEGRADED: 0, ERROR: 0, PENDING: 0, DISABLED: 0 };\n for (const c of items) counts[c.status] += 1;\n return counts;\n }, [connections.data]);\n\n const topViolations = useMemo(() => {\n return [...(openViolations.data?.items ?? [])]\n .sort((a, b) => severityRank[b.severity] - severityRank[a.severity])\n .slice(0, 5);\n }, [openViolations.data]);\n\n const recentRuns = syncRuns.data?.items ?? [];\n\n const topCostLines = useMemo(() => {\n return [...(costSummary.data?.lines ?? [])].sort((a, b) => b.total - a.total).slice(0, 6);\n }, [costSummary.data]);\n\n const isLoading =\n adapters.isLoading ||\n connections.isLoading ||\n resources.isLoading ||\n openViolations.isLoading ||\n drift.isLoading ||\n costSummary.isLoading;\n\n const isError =\n adapters.isError ||\n connections.isError ||\n resources.isError ||\n openViolations.isError ||\n drift.isError ||\n costSummary.isError;\n\n const refetchAll = useCallback(() => {\n void adapters.refetch();\n void connections.refetch();\n void resources.refetch();\n void openViolations.refetch();\n void drift.refetch();\n void syncRuns.refetch();\n void costSummary.refetch();\n }, [adapters, connections, resources, openViolations, drift, syncRuns, costSummary]);\n\n const stats = [\n {\n icon: <Boxes className=\"h-5 w-5\" />,\n value: adapters.data?.items.length\n ? String(adapters.data.items.length)\n : adapters.data\n ? '0'\n : '—',\n label: t('adaptercloud.overview.adapters', 'Adapters'),\n onClick: () => navigateTo('/adapters'),\n },\n {\n icon: <Cable className=\"h-5 w-5\" />,\n value: connections.data ? String(connections.data.items.length) : '—',\n label: t('adaptercloud.overview.connections', 'Connections'),\n onClick: () => navigateTo('/connections'),\n },\n {\n icon: <Server className=\"h-5 w-5\" />,\n value: resources.data ? String(resources.data.items.length) : '—',\n label: t('adaptercloud.overview.resources', 'Resources'),\n onClick: () => navigateTo('/resources'),\n },\n {\n icon: <ShieldAlert className=\"h-5 w-5\" />,\n value: openViolations.data ? String(openViolations.data.items.length) : '—',\n label: t('adaptercloud.overview.openViolations', 'Open Violations'),\n onClick: () => navigateTo('/violations'),\n },\n ];\n\n return (\n <PageLayout\n title={`${t('adaptercloud.overview.hello', 'Welcome back')}, ${firstName}`}\n description={t(\n 'adaptercloud.overview.subtitle',\n 'Your infrastructure continuum at a glance — fabric, governance, operations and cost.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={refetchAll}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n {t('adaptercloud.common.refresh', 'Refresh')}\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n {t(\n 'adaptercloud.overview.purpose',\n 'AdapterCloud connects your fragmented infrastructure — multi-cloud, on-prem, edge and SaaS — into one governable fabric. This overview is your control plane: see what you run, how connected it is, what is out of policy, and what it costs.'\n )}\n </PagePurpose>\n\n <DataState\n isLoading={isLoading}\n isError={isError}\n error={adapters.error}\n onRetry={refetchAll}\n >\n {/* Top metrics — the one emphasis zone */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.estate', 'Estate Summary')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {stats.map((stat) => (\n <button key={stat.label} type=\"button\" onClick={stat.onClick} className=\"text-left\">\n <MetricsCard icon={stat.icon} value={stat.value} label={stat.label} />\n </button>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Connection health + Cost summary */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Connection health */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.connectionHealth', 'Connection Health')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n {connections.data && connections.data.items.length > 0 ? (\n <ul className=\"space-y-3\">\n {(\n [\n ['CONNECTED', connectionHealth.CONNECTED],\n ['DEGRADED', connectionHealth.DEGRADED],\n ['ERROR', connectionHealth.ERROR],\n ['PENDING', connectionHealth.PENDING],\n ['DISABLED', connectionHealth.DISABLED],\n ] as const\n ).map(([status, count]) => (\n <li key={status} className=\"flex items-center justify-between\">\n <StatusBadge status={status} />\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">\n {count}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"flex flex-col items-center gap-3 py-6 text-center\">\n <Plug className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noConnections',\n 'No connections yet. Connect a target to start discovering resources.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n {t('adaptercloud.overview.addConnection', 'Add connection')}\n </button>\n </div>\n )}\n </GlassCard>\n </section>\n\n {/* Cost summary */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.costByService', 'Cost by Service')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/costs')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.overview.finops', 'FinOps')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n <div className=\"mb-4 flex items-baseline gap-2\">\n <DollarSign className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {costSummary.data\n ? formatMoney(costSummary.data.total, costSummary.data.currency)\n : '—'}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.totalSpend', 'total spend')}\n </span>\n </div>\n {topCostLines.length > 0 ? (\n <ul className=\"space-y-2\">\n {topCostLines.map((line) => (\n <li key={line.service} className=\"flex items-center justify-between text-sm\">\n <span className=\"truncate text-text-primary\">{line.service}</span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatMoney(line.total, costSummary.data?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"py-4 text-center text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noCost',\n 'No cost records ingested yet. Run a cost-ingest sync to populate FinOps.'\n )}\n </p>\n )}\n </GlassCard>\n </section>\n </div>\n\n {/* Governance + Operations */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Top open violations */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.priorityViolations', 'Priority Violations')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.overview.queue', 'Queue')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {topViolations.length > 0 ? (\n topViolations.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-error-text\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{v.message}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {v.policy?.name ?? t('adaptercloud.overview.policy', 'Policy')} ·{' '}\n {formatRelativeTime(v.detectedAt)}\n </p>\n </div>\n <SeverityBadge severity={v.severity as AdaSeverity} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <ShieldAlert className=\"h-8 w-8 text-status-success-text\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noViolations',\n 'No open violations. Estate is clean.'\n )}\n </p>\n </div>\n )}\n </div>\n {drift.data && drift.data.length > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/drift')}\n className=\"mt-3 flex w-full items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3 text-left text-sm transition-colors hover:bg-accent/50\"\n >\n <GitCompareArrows className=\"h-4 w-4 text-status-warning-text\" />\n <span className=\"text-text-primary\">\n {drift.data.length}{' '}\n {t('adaptercloud.overview.driftDetected', 'drift records detected')}\n </span>\n <ArrowRight className=\"ml-auto h-3.5 w-3.5 text-text-secondary\" />\n </button>\n )}\n </section>\n\n {/* Recent sync runs */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.recentRuns', 'Recent Sync Runs')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {recentRuns.length > 0 ? (\n recentRuns.map((run) => (\n <button\n key={run.id}\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken\">\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {syncKindLabels[run.kind]}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {run.discoveredCount} {t('adaptercloud.overview.discovered', 'discovered')}{' '}\n · {formatRelativeTime(run.createdAt)}\n </p>\n </div>\n <StatusBadge status={run.status} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <RefreshCw className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.noRuns', 'No sync runs yet.')}\n </p>\n </div>\n )}\n </div>\n </section>\n </div>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AdapterDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/adapters/AdapterDetailPage.tsx"],"sourcesContent":["/**\n * Adapter Detail Page\n * @module adaptercloud/pages/adapters\n *\n * Full view of one adapter (`adaAdapter`) — metadata, capabilities, config\n * schema and its connections. Supports archiving via `adaArchiveAdapter`.\n */\n\nimport { useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Archive, Cable, Pencil } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useAdapter, useArchiveAdapter } from '../../hooks/useAdapterCloudApi';\nimport { adapterKindLabels, authMethodLabels } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\n\nexport function AdapterDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const query = useAdapter(id);\n const archive = useArchiveAdapter();\n const adapter = query.data;\n\n const handleArchive = useCallback(() => {\n if (!id) return;\n archive.mutate(id, {\n onSuccess: () => navigateTo('/adapters'),\n });\n }, [archive, id, navigateTo]);\n\n return (\n <PageLayout\n title={adapter?.name ?? t('adaptercloud.adapters.detailTitle', 'Adapter')}\n description={adapter?.slug}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/adapters')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.back', 'Back')}\n </button>\n {adapter && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/adapters/${adapter.id}/edit`)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <Pencil className=\"h-4 w-4\" />\n {t('adaptercloud.common.edit', 'Edit')}\n </button>\n {adapter.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archive.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Archive className=\"h-4 w-4\" />\n {t('adaptercloud.common.archive', 'Archive')}\n </button>\n )}\n </>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !adapter}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.adapters.notFound', 'Adapter not found')}\n emptyDescription={t(\n 'adaptercloud.adapters.notFoundDescription',\n 'This adapter does not exist or belongs to another workspace.'\n )}\n >\n {adapter && (\n <div className=\"space-y-6\">\n {/* Metadata */}\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-3\">\n <StatusBadge status={adapter.status} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {adapterKindLabels[adapter.kind]}\n </span>\n <span className=\"text-xs text-text-secondary\">v{adapter.version}</span>\n </div>\n\n {adapter.description && (\n <p className=\"mb-4 text-sm text-text-secondary\">{adapter.description}</p>\n )}\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <dt className=\"text-text-secondary\">{t('adaptercloud.adapters.vendor', 'Vendor')}</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">{adapter.vendor ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.created', 'Created')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.updatedAt)}\n </dd>\n </div>\n </dl>\n\n {adapter.capabilities.length > 0 && (\n <div className=\"mt-5\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.capabilities', 'Capabilities')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.capabilities.map((cap) => (\n <span\n key={cap}\n className=\"inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 text-xs text-accent-blue\"\n >\n {cap}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {adapter.tags.length > 0 && (\n <div className=\"mt-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.common.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </GlassCard>\n\n {/* Config schema */}\n {adapter.configSchema != null && (\n <GlassCard className=\"p-6\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.configSchema', 'Config Schema')}\n </h3>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(adapter.configSchema, null, 2)}\n </pre>\n </GlassCard>\n )}\n\n {/* Connections */}\n <section>\n <h3 className=\"mb-3 text-base font-semibold text-text-primary\">\n {t('adaptercloud.adapters.connections', 'Connections')}\n </h3>\n {adapter.connections && adapter.connections.length > 0 ? (\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {adapter.connections.map((conn) => (\n <button\n key={conn.id}\n type=\"button\"\n onClick={() => navigateTo(`/connections/${conn.id}`)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <Cable className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {conn.name}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {authMethodLabels[conn.authMethod]} · {conn.resourceCount}{' '}\n {t('adaptercloud.adapters.resourcesLower', 'resources')}\n </p>\n </div>\n <StatusBadge status={conn.status} />\n </button>\n ))}\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t('adaptercloud.adapters.noConnections', 'No connections use this adapter yet.')}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdapterDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/adapters/AdapterDetailPage.tsx"],"sourcesContent":["/**\n * Adapter Detail Page\n * @module adaptercloud/pages/adapters\n *\n * Full view of one adapter (`adaAdapter`) — metadata, capabilities, config\n * schema and its connections. Supports archiving via `adaArchiveAdapter`.\n */\n\nimport { useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Archive, Cable, Pencil } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useAdapter, useArchiveAdapter } from '../../hooks/useAdapterCloudApi';\nimport { adapterKindLabels, authMethodLabels } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\n\nexport function AdapterDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const query = useAdapter(id);\n const archive = useArchiveAdapter();\n const adapter = query.data;\n\n const handleArchive = useCallback(() => {\n if (!id) return;\n archive.mutate(id, {\n onSuccess: () => navigateTo('/adapters'),\n });\n }, [archive, id, navigateTo]);\n\n return (\n <PageLayout\n title={adapter?.name ?? t('adaptercloud.adapters.detailTitle', 'Adapter')}\n description={adapter?.slug}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/adapters')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.back', 'Back')}\n </button>\n {adapter && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/adapters/${adapter.id}/edit`)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <Pencil className=\"h-4 w-4\" />\n {t('adaptercloud.common.edit', 'Edit')}\n </button>\n {adapter.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archive.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Archive className=\"h-4 w-4\" />\n {t('adaptercloud.common.archive', 'Archive')}\n </button>\n )}\n </>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !adapter}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.adapters.notFound', 'Adapter not found')}\n emptyDescription={t(\n 'adaptercloud.adapters.notFoundDescription',\n 'This adapter does not exist or belongs to another workspace.'\n )}\n >\n {adapter && (\n <div className=\"space-y-6\">\n {/* Metadata */}\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-3\">\n <StatusBadge status={adapter.status} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {adapterKindLabels[adapter.kind]}\n </span>\n <span className=\"text-xs text-text-secondary\">v{adapter.version}</span>\n </div>\n\n {adapter.description && (\n <p className=\"mb-4 text-sm text-text-secondary\">{adapter.description}</p>\n )}\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.adapters.vendor', 'Vendor')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">{adapter.vendor ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.created', 'Created')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.updatedAt)}\n </dd>\n </div>\n </dl>\n\n {adapter.capabilities.length > 0 && (\n <div className=\"mt-5\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.capabilities', 'Capabilities')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.capabilities.map((cap) => (\n <span\n key={cap}\n className=\"inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 text-xs text-accent-blue\"\n >\n {cap}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {adapter.tags.length > 0 && (\n <div className=\"mt-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.common.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </GlassCard>\n\n {/* Config schema */}\n {adapter.configSchema != null && (\n <GlassCard className=\"p-6\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.configSchema', 'Config Schema')}\n </h3>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(adapter.configSchema, null, 2)}\n </pre>\n </GlassCard>\n )}\n\n {/* Connections */}\n <section>\n <h3 className=\"mb-3 text-base font-semibold text-text-primary\">\n {t('adaptercloud.adapters.connections', 'Connections')}\n </h3>\n {adapter.connections && adapter.connections.length > 0 ? (\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {adapter.connections.map((conn) => (\n <button\n key={conn.id}\n type=\"button\"\n onClick={() => navigateTo(`/connections/${conn.id}`)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <Cable className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {conn.name}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {authMethodLabels[conn.authMethod]} · {conn.resourceCount}{' '}\n {t('adaptercloud.adapters.resourcesLower', 'resources')}\n </p>\n </div>\n <StatusBadge status={conn.status} />\n </button>\n ))}\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t('adaptercloud.adapters.noConnections', 'No connections use this adapter yet.')}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AdapterFormPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/adapters/AdapterFormPage.tsx"],"sourcesContent":["/**\n * Adapter Create / Edit Form Page\n * @module adaptercloud/pages/adapters\n *\n * Creates a new adapter (`adaCreateAdapter`) or edits an existing one\n * (`adaUpdateAdapter`). The presence of a route `:id` switches to edit mode and\n * pre-fills from `adaAdapter`.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport {\n useAdapter,\n useCreateAdapter,\n useUpdateAdapter,\n} from '../../hooks/useAdapterCloudApi';\nimport { adapterKindLabels, adapterStatusLabels } from '../../utils/domain';\nimport type { AdaAdapterKind, AdaAdapterStatus } from '../../types';\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nconst parseList = (value: string): string[] =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\ninterface FormState {\n name: string;\n slug: string;\n kind: AdaAdapterKind;\n status: AdaAdapterStatus;\n vendor: string;\n version: string;\n description: string;\n capabilities: string;\n tags: string;\n}\n\nconst EMPTY: FormState = {\n name: '',\n slug: '',\n kind: 'CUSTOM',\n status: 'DRAFT',\n vendor: '',\n version: '1.0.0',\n description: '',\n capabilities: '',\n tags: '',\n};\n\nexport function AdapterFormPage() {\n const { id } = useParams<{ id: string }>();\n const isEdit = Boolean(id);\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const existing = useAdapter(isEdit ? id : undefined);\n const create = useCreateAdapter();\n const update = useUpdateAdapter();\n\n const [form, setForm] = useState<FormState>(EMPTY);\n const [slugTouched, setSlugTouched] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Pre-fill in edit mode\n useEffect(() => {\n const a = existing.data;\n if (isEdit && a) {\n setForm({\n name: a.name,\n slug: a.slug,\n kind: a.kind,\n status: a.status,\n vendor: a.vendor ?? '',\n version: a.version,\n description: a.description ?? '',\n capabilities: a.capabilities.join(', '),\n tags: a.tags.join(', '),\n });\n setSlugTouched(true);\n }\n }, [isEdit, existing.data]);\n\n const setField = useCallback(\n <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n },\n []\n );\n\n const onNameChange = useCallback(\n (value: string) => {\n setForm((prev) => ({\n ...prev,\n name: value,\n slug: slugTouched ? prev.slug : slugify(value),\n }));\n },\n [slugTouched]\n );\n\n const isPending = create.isPending || update.isPending;\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!form.name.trim()) {\n setError(t('adaptercloud.adapters.form.nameRequired', 'Name is required.'));\n return;\n }\n if (!isEdit && !form.slug.trim()) {\n setError(t('adaptercloud.adapters.form.slugRequired', 'Slug is required.'));\n return;\n }\n\n const common = {\n name: form.name.trim(),\n kind: form.kind,\n status: form.status,\n vendor: form.vendor.trim() || undefined,\n version: form.version.trim() || undefined,\n description: form.description.trim() || undefined,\n capabilities: parseList(form.capabilities),\n tags: parseList(form.tags),\n };\n\n const onError = (err: unknown) =>\n setError(err instanceof Error ? err.message : 'Failed to save adapter.');\n\n if (isEdit && id) {\n update.mutate(\n { id, input: common },\n { onSuccess: (a) => navigateTo(`/adapters/${a.id}`), onError }\n );\n } else {\n create.mutate(\n { ...common, slug: form.slug.trim() },\n { onSuccess: (a) => navigateTo(`/adapters/${a.id}`), onError }\n );\n }\n },\n [create, update, form, id, isEdit, navigateTo, t]\n );\n\n const fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n const labelClass = 'mb-1.5 block text-sm font-medium text-text-primary';\n\n return (\n <PageLayout\n title={\n isEdit\n ? t('adaptercloud.adapters.editTitle', 'Edit Adapter')\n : t('adaptercloud.adapters.newTitle', 'New Adapter')\n }\n description={t(\n 'adaptercloud.adapters.formSubtitle',\n 'Define how AdapterCloud connects to a class of system.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && id ? `/adapters/${id}` : '/adapters')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n }\n >\n <DataState\n isLoading={isEdit && existing.isLoading}\n isError={isEdit && existing.isError}\n isEmpty={isEdit && !existing.isLoading && !existing.data}\n error={existing.error}\n onRetry={() => void existing.refetch()}\n emptyTitle={t('adaptercloud.adapters.notFound', 'Adapter not found')}\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl\">\n <GlassCard className=\"space-y-5 p-6\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ada-name\" className={labelClass}>\n {t('adaptercloud.adapters.form.name', 'Name')} *\n </label>\n <input\n id=\"ada-name\"\n type=\"text\"\n value={form.name}\n onChange={(e) => onNameChange(e.target.value)}\n className={fieldClass}\n placeholder=\"AWS Production\"\n />\n </div>\n <div>\n <label htmlFor=\"ada-slug\" className={labelClass}>\n {t('adaptercloud.adapters.form.slug', 'Slug')} {isEdit ? '' : '*'}\n </label>\n <input\n id=\"ada-slug\"\n type=\"text\"\n value={form.slug}\n disabled={isEdit}\n onChange={(e) => {\n setSlugTouched(true);\n setField('slug', slugify(e.target.value));\n }}\n className={`${fieldClass} disabled:cursor-not-allowed disabled:opacity-60`}\n placeholder=\"aws-production\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ada-kind\" className={labelClass}>\n {t('adaptercloud.adapters.form.kind', 'Kind')}\n </label>\n <select\n id=\"ada-kind\"\n value={form.kind}\n onChange={(e) => setField('kind', e.target.value as AdaAdapterKind)}\n className={fieldClass}\n >\n {(Object.keys(adapterKindLabels) as AdaAdapterKind[]).map((k) => (\n <option key={k} value={k}>\n {adapterKindLabels[k]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-status\" className={labelClass}>\n {t('adaptercloud.adapters.form.status', 'Status')}\n </label>\n <select\n id=\"ada-status\"\n value={form.status}\n onChange={(e) => setField('status', e.target.value as AdaAdapterStatus)}\n className={fieldClass}\n >\n {(Object.keys(adapterStatusLabels) as AdaAdapterStatus[]).map((s) => (\n <option key={s} value={s}>\n {adapterStatusLabels[s]}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ada-vendor\" className={labelClass}>\n {t('adaptercloud.adapters.form.vendor', 'Vendor')}\n </label>\n <input\n id=\"ada-vendor\"\n type=\"text\"\n value={form.vendor}\n onChange={(e) => setField('vendor', e.target.value)}\n className={fieldClass}\n placeholder=\"Amazon Web Services\"\n />\n </div>\n <div>\n <label htmlFor=\"ada-version\" className={labelClass}>\n {t('adaptercloud.adapters.form.version', 'Version')}\n </label>\n <input\n id=\"ada-version\"\n type=\"text\"\n value={form.version}\n onChange={(e) => setField('version', e.target.value)}\n className={fieldClass}\n placeholder=\"1.0.0\"\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"ada-description\" className={labelClass}>\n {t('adaptercloud.adapters.form.description', 'Description')}\n </label>\n <textarea\n id=\"ada-description\"\n value={form.description}\n onChange={(e) => setField('description', e.target.value)}\n rows={3}\n className={fieldClass}\n placeholder={t(\n 'adaptercloud.adapters.form.descriptionPlaceholder',\n 'What this adapter connects to and how.'\n )}\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-capabilities\" className={labelClass}>\n {t('adaptercloud.adapters.form.capabilities', 'Capabilities')}\n </label>\n <input\n id=\"ada-capabilities\"\n type=\"text\"\n value={form.capabilities}\n onChange={(e) => setField('capabilities', e.target.value)}\n className={fieldClass}\n placeholder=\"discovery, cost, reconcile\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n <div>\n <label htmlFor=\"ada-tags\" className={labelClass}>\n {t('adaptercloud.common.tags', 'Tags')}\n </label>\n <input\n id=\"ada-tags\"\n type=\"text\"\n value={form.tags}\n onChange={(e) => setField('tags', e.target.value)}\n className={fieldClass}\n placeholder=\"production, cloud\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit\n ? t('adaptercloud.common.saveChanges', 'Save changes')\n : t('adaptercloud.adapters.create', 'Create adapter')}\n </button>\n </div>\n </GlassCard>\n </form>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdapterFormPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/adapters/AdapterFormPage.tsx"],"sourcesContent":["/**\n * Adapter Create / Edit Form Page\n * @module adaptercloud/pages/adapters\n *\n * Creates a new adapter (`adaCreateAdapter`) or edits an existing one\n * (`adaUpdateAdapter`). The presence of a route `:id` switches to edit mode and\n * pre-fills from `adaAdapter`.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useAdapter, useCreateAdapter, useUpdateAdapter } from '../../hooks/useAdapterCloudApi';\nimport { adapterKindLabels, adapterStatusLabels } from '../../utils/domain';\nimport type { AdaAdapterKind, AdaAdapterStatus } from '../../types';\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nconst parseList = (value: string): string[] =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\ninterface FormState {\n name: string;\n slug: string;\n kind: AdaAdapterKind;\n status: AdaAdapterStatus;\n vendor: string;\n version: string;\n description: string;\n capabilities: string;\n tags: string;\n}\n\nconst EMPTY: FormState = {\n name: '',\n slug: '',\n kind: 'CUSTOM',\n status: 'DRAFT',\n vendor: '',\n version: '1.0.0',\n description: '',\n capabilities: '',\n tags: '',\n};\n\nexport function AdapterFormPage() {\n const { id } = useParams<{ id: string }>();\n const isEdit = Boolean(id);\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const existing = useAdapter(isEdit ? id : undefined);\n const create = useCreateAdapter();\n const update = useUpdateAdapter();\n\n const [form, setForm] = useState<FormState>(EMPTY);\n const [slugTouched, setSlugTouched] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Pre-fill in edit mode\n useEffect(() => {\n const a = existing.data;\n if (isEdit && a) {\n setForm({\n name: a.name,\n slug: a.slug,\n kind: a.kind,\n status: a.status,\n vendor: a.vendor ?? '',\n version: a.version,\n description: a.description ?? '',\n capabilities: a.capabilities.join(', '),\n tags: a.tags.join(', '),\n });\n setSlugTouched(true);\n }\n }, [isEdit, existing.data]);\n\n const setField = useCallback(<K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n }, []);\n\n const onNameChange = useCallback(\n (value: string) => {\n setForm((prev) => ({\n ...prev,\n name: value,\n slug: slugTouched ? prev.slug : slugify(value),\n }));\n },\n [slugTouched]\n );\n\n const isPending = create.isPending || update.isPending;\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!form.name.trim()) {\n setError(t('adaptercloud.adapters.form.nameRequired', 'Name is required.'));\n return;\n }\n if (!isEdit && !form.slug.trim()) {\n setError(t('adaptercloud.adapters.form.slugRequired', 'Slug is required.'));\n return;\n }\n\n const common = {\n name: form.name.trim(),\n kind: form.kind,\n status: form.status,\n vendor: form.vendor.trim() || undefined,\n version: form.version.trim() || undefined,\n description: form.description.trim() || undefined,\n capabilities: parseList(form.capabilities),\n tags: parseList(form.tags),\n };\n\n const onError = (err: unknown) =>\n setError(err instanceof Error ? err.message : 'Failed to save adapter.');\n\n if (isEdit && id) {\n update.mutate(\n { id, input: common },\n { onSuccess: (a) => navigateTo(`/adapters/${a.id}`), onError }\n );\n } else {\n create.mutate(\n { ...common, slug: form.slug.trim() },\n { onSuccess: (a) => navigateTo(`/adapters/${a.id}`), onError }\n );\n }\n },\n [create, update, form, id, isEdit, navigateTo, t]\n );\n\n const fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n const labelClass = 'mb-1.5 block text-sm font-medium text-text-primary';\n\n return (\n <PageLayout\n title={\n isEdit\n ? t('adaptercloud.adapters.editTitle', 'Edit Adapter')\n : t('adaptercloud.adapters.newTitle', 'New Adapter')\n }\n description={t(\n 'adaptercloud.adapters.formSubtitle',\n 'Define how AdapterCloud connects to a class of system.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && id ? `/adapters/${id}` : '/adapters')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n }\n >\n <DataState\n isLoading={isEdit && existing.isLoading}\n isError={isEdit && existing.isError}\n isEmpty={isEdit && !existing.isLoading && !existing.data}\n error={existing.error}\n onRetry={() => void existing.refetch()}\n emptyTitle={t('adaptercloud.adapters.notFound', 'Adapter not found')}\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl\">\n <GlassCard className=\"space-y-5 p-6\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ada-name\" className={labelClass}>\n {t('adaptercloud.adapters.form.name', 'Name')} *\n </label>\n <input\n id=\"ada-name\"\n type=\"text\"\n value={form.name}\n onChange={(e) => onNameChange(e.target.value)}\n className={fieldClass}\n placeholder=\"AWS Production\"\n />\n </div>\n <div>\n <label htmlFor=\"ada-slug\" className={labelClass}>\n {t('adaptercloud.adapters.form.slug', 'Slug')} {isEdit ? '' : '*'}\n </label>\n <input\n id=\"ada-slug\"\n type=\"text\"\n value={form.slug}\n disabled={isEdit}\n onChange={(e) => {\n setSlugTouched(true);\n setField('slug', slugify(e.target.value));\n }}\n className={`${fieldClass} disabled:cursor-not-allowed disabled:opacity-60`}\n placeholder=\"aws-production\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ada-kind\" className={labelClass}>\n {t('adaptercloud.adapters.form.kind', 'Kind')}\n </label>\n <select\n id=\"ada-kind\"\n value={form.kind}\n onChange={(e) => setField('kind', e.target.value as AdaAdapterKind)}\n className={fieldClass}\n >\n {(Object.keys(adapterKindLabels) as AdaAdapterKind[]).map((k) => (\n <option key={k} value={k}>\n {adapterKindLabels[k]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-status\" className={labelClass}>\n {t('adaptercloud.adapters.form.status', 'Status')}\n </label>\n <select\n id=\"ada-status\"\n value={form.status}\n onChange={(e) => setField('status', e.target.value as AdaAdapterStatus)}\n className={fieldClass}\n >\n {(Object.keys(adapterStatusLabels) as AdaAdapterStatus[]).map((s) => (\n <option key={s} value={s}>\n {adapterStatusLabels[s]}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ada-vendor\" className={labelClass}>\n {t('adaptercloud.adapters.form.vendor', 'Vendor')}\n </label>\n <input\n id=\"ada-vendor\"\n type=\"text\"\n value={form.vendor}\n onChange={(e) => setField('vendor', e.target.value)}\n className={fieldClass}\n placeholder=\"Amazon Web Services\"\n />\n </div>\n <div>\n <label htmlFor=\"ada-version\" className={labelClass}>\n {t('adaptercloud.adapters.form.version', 'Version')}\n </label>\n <input\n id=\"ada-version\"\n type=\"text\"\n value={form.version}\n onChange={(e) => setField('version', e.target.value)}\n className={fieldClass}\n placeholder=\"1.0.0\"\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"ada-description\" className={labelClass}>\n {t('adaptercloud.adapters.form.description', 'Description')}\n </label>\n <textarea\n id=\"ada-description\"\n value={form.description}\n onChange={(e) => setField('description', e.target.value)}\n rows={3}\n className={fieldClass}\n placeholder={t(\n 'adaptercloud.adapters.form.descriptionPlaceholder',\n 'What this adapter connects to and how.'\n )}\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-capabilities\" className={labelClass}>\n {t('adaptercloud.adapters.form.capabilities', 'Capabilities')}\n </label>\n <input\n id=\"ada-capabilities\"\n type=\"text\"\n value={form.capabilities}\n onChange={(e) => setField('capabilities', e.target.value)}\n className={fieldClass}\n placeholder=\"discovery, cost, reconcile\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n <div>\n <label htmlFor=\"ada-tags\" className={labelClass}>\n {t('adaptercloud.common.tags', 'Tags')}\n </label>\n <input\n id=\"ada-tags\"\n type=\"text\"\n value={form.tags}\n onChange={(e) => setField('tags', e.target.value)}\n className={fieldClass}\n placeholder=\"production, cloud\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit\n ? t('adaptercloud.common.saveChanges', 'Save changes')\n : t('adaptercloud.adapters.create', 'Create adapter')}\n </button>\n </div>\n </GlassCard>\n </form>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}