import { injectable } from 'inversify'; import { PolicyApiClient, type FeatureState, type PolicySnapshotDto } from '@/lib/api/policy/PolicyApiClient'; import { Result } from '@/lib/contracts/Result'; import { DomainError, Service } from '@/lib/contracts/services/Service'; import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl'; import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger'; import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter'; export interface CapabilityEvaluationResult { isLoading: boolean; isError: boolean; capabilityState: FeatureState | null; shouldShowChildren: boolean; shouldShowComingSoon: boolean; } @injectable() export class PolicyService implements Service { private readonly apiClient: PolicyApiClient; constructor() { const baseUrl = getWebsiteApiBaseUrl(); const logger = new ConsoleLogger(); const errorReporter = new EnhancedErrorReporter(logger); this.apiClient = new PolicyApiClient(baseUrl, errorReporter, logger); } async getSnapshot(): Promise> { try { const data = await this.apiClient.getSnapshot(); return Result.ok(data); } catch (error: any) { return Result.err({ type: 'serverError', message: error.message || 'Failed to get policy snapshot' }); } } getCapabilityState(snapshot: PolicySnapshotDto, capabilityKey: string): FeatureState { return snapshot.capabilities[capabilityKey] ?? 'hidden'; } isCapabilityEnabled(snapshot: PolicySnapshotDto, capabilityKey: string): boolean { return this.getCapabilityState(snapshot, capabilityKey) === 'enabled'; } /** * Evaluate capability state and determine what should be rendered * Centralizes the logic for capability-based UI rendering */ evaluateCapability( snapshot: PolicySnapshotDto | null, capabilityKey: string, isLoading: boolean, isError: boolean ): CapabilityEvaluationResult { if (isLoading || isError || !snapshot) { return { isLoading, isError, capabilityState: null, shouldShowChildren: false, shouldShowComingSoon: false, }; } const capabilityState = this.getCapabilityState(snapshot, capabilityKey); return { isLoading, isError, capabilityState, shouldShowChildren: capabilityState === 'enabled', shouldShowComingSoon: capabilityState === 'coming_soon', }; } /** * Get the appropriate content based on capability state * Handles fallback and coming soon logic */ getCapabilityContent( snapshot: PolicySnapshotDto | null, capabilityKey: string, isLoading: boolean, isError: boolean, children: React.ReactNode, fallback: React.ReactNode = null, comingSoon: React.ReactNode = null ): React.ReactNode { const evaluation = this.evaluateCapability(snapshot, capabilityKey, isLoading, isError); if (evaluation.shouldShowChildren) { return children; } if (evaluation.shouldShowComingSoon) { return comingSoon ?? fallback; } return fallback; } }