// Copyright (c) 2026 PowerOn AG // All rights reserved. /** * Features API * * API-Schicht für das Multi-Tenant Feature-System. * Hauptendpoint: GET /features/my - Lädt alle Mandate + Features + Instanzen + Permissions */ import api from '../api'; import type { FeaturesMyResponse, Mandate, MandateFeature, FeatureInstance, InstancePermissions, AccessLevel, } from '../types/mandate'; // ============================================================================= // MOCK DATA (Temporär bis Backend bereit) // ============================================================================= const MOCK_PERMISSIONS: InstancePermissions = { tables: { TrusteeOrganisation: { view: true, read: 'g', create: 'g', update: 'g', delete: 'n' }, TrusteeContract: { view: true, read: 'g', create: 'g', update: 'm', delete: 'n' }, TrusteeDocument: { view: true, read: 'g', create: 'g', update: 'm', delete: 'm' }, TrusteePosition: { view: true, read: 'g', create: 'g', update: 'm', delete: 'n' }, }, views: { 'trustee-dashboard': true, 'trustee-organisations': true, 'trustee-contracts': true, 'trustee-documents': true, 'trustee-positions': true, 'trustee-roles': true, 'trustee-access': true, }, }; const MOCK_CUSTOMER_PERMISSIONS: InstancePermissions = { tables: { TrusteeOrganisation: { view: true, read: 'm', create: 'n', update: 'n', delete: 'n' }, TrusteeContract: { view: true, read: 'm', create: 'n', update: 'n', delete: 'n' }, TrusteeDocument: { view: true, read: 'm', create: 'm', update: 'm', delete: 'n' }, TrusteePosition: { view: true, read: 'm', create: 'n', update: 'n', delete: 'n' }, }, views: { 'trustee-dashboard': true, 'trustee-contracts': true, 'trustee-documents': true, 'trustee-positions': true, 'trustee-organisations': false, 'trustee-roles': false, 'trustee-access': false, }, }; const MOCK_WORKFLOW_PERMISSIONS: InstancePermissions = { tables: { WorkflowRun: { view: true, read: 'g', create: 'g', update: 'm', delete: 'n' }, WorkflowFile: { view: true, read: 'g', create: 'g', update: 'm', delete: 'm' }, }, views: { 'chatworkflow-dashboard': true, 'chatworkflow-runs': true, 'chatworkflow-files': true, }, }; const MOCK_RESPONSE: FeaturesMyResponse = { mandates: [ { id: 'mand-soha', name: 'soha-treuhand', label: 'Soha Treuhand', code: 'soha', features: [ { code: 'trustee', label: 'Treuhand', icon: 'briefcase', instances: [ { id: 'inst-soha-pamo', featureCode: 'trustee', mandateId: 'mand-soha', mandateName: 'Soha Treuhand', instanceLabel: 'PamoCreate AG', userRoles: ['admin'], permissions: MOCK_PERMISSIONS, }, { id: 'inst-soha-valueon', featureCode: 'trustee', mandateId: 'mand-soha', mandateName: 'Soha Treuhand', instanceLabel: 'ValueOn AG', userRoles: ['customer'], permissions: MOCK_CUSTOMER_PERMISSIONS, }, ], }, { code: 'chatworkflow', label: 'Workflow', icon: 'play_circle', instances: [ { id: 'inst-soha-workflow', featureCode: 'chatworkflow', mandateId: 'mand-soha', mandateName: 'Soha Treuhand', instanceLabel: 'Beratung Dynamic', userRoles: ['user'], permissions: MOCK_WORKFLOW_PERMISSIONS, }, ], }, ], }, { id: 'mand-swiss', name: 'swisstreu', label: 'SwissTreu', code: 'swisstreu', features: [ { code: 'trustee', label: 'Treuhand', icon: 'briefcase', instances: [ { id: 'inst-swiss-firma-x', featureCode: 'trustee', mandateId: 'mand-swiss', mandateName: 'SwissTreu', instanceLabel: 'Firma X', userRoles: ['customer'], permissions: MOCK_CUSTOMER_PERMISSIONS, }, ], }, ], }, ], }; // Flag für Mock-Modus (auf false setzen wenn Backend bereit) const USE_MOCK = false; // ============================================================================= // API FUNCTIONS // ============================================================================= /** * Lädt alle Mandate + Features + Instanzen + Permissions für den aktuellen User * * Endpoint: GET /api/features/my * * Response enthält: * - Alle Mandanten zu denen der User Zugriff hat * - Pro Mandant: Alle Features mit deren Instanzen * - Pro Instanz: Summarische Berechtigungen (tables, views) */ export async function fetchMyFeatures(): Promise { if (USE_MOCK) { console.log('📦 featuresApi: Using MOCK data'); // Simuliere Netzwerk-Latenz await new Promise(resolve => setTimeout(resolve, 300)); return MOCK_RESPONSE; } try { const response = await api.get('/api/features/my'); // Get the actual data (response.data contains the FeaturesMyResponse) const data = response.data; return data; } catch (error) { console.error('❌ featuresApi: Error fetching features:', error); throw error; } } /** * Lädt die verfügbaren Features (für Admin - Feature-Instanz erstellen) * * Endpoint: GET /api/features/available */ export async function fetchAvailableFeatures(): Promise { if (USE_MOCK) { return [ { code: 'trustee', label: 'Treuhand', icon: 'briefcase', instances: [] }, { code: 'chatworkflow', label: 'Workflow', icon: 'play_circle', instances: [] }, ]; } const response = await api.get('/api/features/available'); return response.data; } // ============================================================================= // TYPE GUARDS // ============================================================================= export function isValidAccessLevel(value: string): value is AccessLevel { return ['n', 'm', 'g', 'a'].includes(value); } export function isValidMandate(obj: unknown): obj is Mandate { if (!obj || typeof obj !== 'object') return false; const mandate = obj as Record; return ( typeof mandate.id === 'string' && typeof mandate.name === 'string' && Array.isArray(mandate.features) ); } export function isValidFeatureInstance(obj: unknown): obj is FeatureInstance { if (!obj || typeof obj !== 'object') return false; const instance = obj as Record; return ( typeof instance.id === 'string' && typeof instance.featureCode === 'string' && typeof instance.mandateId === 'string' && typeof instance.instanceLabel === 'string' ); }