import apiClient from '@/lib/api/axios-instance'; import { AxiosError } from 'axios'; import { SystemDetails, SystemDetailsCreateRequest, SystemDetailsUpdateRequest, validateSystemDetailsApiResponse } from '@/types/asset-management'; import { assetApi } from './assetApi'; // Helper to create a safe system details object with default values for null/undefined fields const createSafeSystemDetails = (data: any): SystemDetails => { return { systemDetailsID: data.systemDetailsID || 0, assetID: data.assetID || 0, systemModel: data.systemModel || '', processor: data.processor || '', installedRAM: data.installedRAM || '', graphics: data.graphics || '', screenResolution: data.screenResolution || '', diskModel: data.diskModel || '', diskSize: data.diskSize || '', manufacturer: data.manufacturer || '', }; }; // Helper to handle API errors with user-friendly messages const handleApiError = (error: unknown, operation: string): never => { console.error(`SystemDetailsApi: Error during ${operation}:`, error); if (error instanceof AxiosError) { const status = error.response?.status; const message = error.response?.data?.message || error.message; switch (status) { case 400: throw new Error(`Invalid request: ${message}`); case 401: throw new Error('You are not authorized to perform this action. Please log in again.'); case 403: throw new Error('You do not have permission to perform this action.'); case 404: throw new Error('The requested system details were not found.'); case 409: throw new Error('System details already exist for this asset or conflict with existing data.'); case 422: throw new Error(`Validation failed: ${message}`); case 500: throw new Error('Server error occurred. Please try again later.'); case 503: throw new Error('Service is temporarily unavailable. Please try again later.'); default: throw new Error(`${operation} failed: ${message}`); } } if (error instanceof Error) { throw new Error(`${operation} failed: ${error.message}`); } throw new Error(`${operation} failed: Unknown error occurred`); }; // Helper for retry logic const retryOperation = async ( operation: () => Promise, maxRetries: number = 3, delay: number = 1000 ): Promise => { let lastError: unknown; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error; // Don't retry on client errors (4xx) except for 408 (timeout) and 429 (rate limit) if (error instanceof AxiosError) { const status = error.response?.status; if (status && status >= 400 && status < 500 && status !== 408 && status !== 429) { throw error; } } if (attempt === maxRetries) { throw lastError; } // Exponential backoff const waitTime = delay * Math.pow(2, attempt - 1); console.log(`SystemDetailsApi: Retry attempt ${attempt}/${maxRetries} in ${waitTime}ms`); await new Promise(resolve => setTimeout(resolve, waitTime)); } } throw lastError; }; class SystemDetailsApi { private baseUrl = `/api/SystemsDetails`; async getAll(): Promise { return retryOperation(async () => { try { console.log('SystemDetailsApi: Fetching all system details from', this.baseUrl); const response = await apiClient.get(this.baseUrl); console.log('SystemDetailsApi: Response status', response.status); if (!response.data) { console.warn('SystemDetailsApi: No data in response'); return []; } if (!Array.isArray(response.data)) { console.warn('SystemDetailsApi: Response is not an array', response.data); return []; } console.log(`SystemDetailsApi: Found ${response.data.length} system details`); // Map each item to ensure it has all required fields and validate return response.data.map(item => { try { return validateSystemDetailsApiResponse(item); } catch (error) { console.warn('SystemDetailsApi: Invalid system details data, using safe fallback:', error); return createSafeSystemDetails(item); } }); } catch (error) { handleApiError(error, 'fetch system details'); } }); } async getById(id: number): Promise { try { console.log(`SystemDetailsApi: Fetching system details ${id}`); const response = await apiClient.get(`${this.baseUrl}/${id}`); console.log(`SystemDetailsApi: Response status for system details ${id}`, response.status); if (response.data) { try { return validateSystemDetailsApiResponse(response.data); } catch (error) { console.warn(`SystemDetailsApi: Invalid system details data for ID ${id}, using safe fallback:`, error); return createSafeSystemDetails(response.data); } } return null; } catch (error) { console.error(`SystemDetailsApi: Error fetching system details ${id}:`, error); return null; } } async getByAssetId(assetId: number): Promise { return retryOperation(async () => { try { console.log(`SystemDetailsApi: Fetching system details for asset ${assetId}`); const response = await apiClient.get(`${this.baseUrl}/by-asset/${assetId}`); console.log(`SystemDetailsApi: Response status for asset ${assetId} system details`, response.status); if (!response.data) { console.warn(`SystemDetailsApi: No system details found for asset ${assetId}`); return null; } // Handle both single object and array responses let systemDetailsData; if (Array.isArray(response.data)) { // If it's an array, take the first item (assuming one system details per asset) systemDetailsData = response.data.length > 0 ? response.data[0] : null; console.log(`SystemDetailsApi: Found ${response.data.length} system details for asset ${assetId}, using first one`); } else { // If it's a single object, use it directly systemDetailsData = response.data; console.log(`SystemDetailsApi: Found system details for asset ${assetId}`); } if (!systemDetailsData) { return null; } try { return validateSystemDetailsApiResponse(systemDetailsData); } catch (error) { console.warn(`SystemDetailsApi: Invalid system details data for asset ${assetId}, using safe fallback:`, error); return createSafeSystemDetails(systemDetailsData); } } catch (error) { if (error instanceof AxiosError && error.response?.status === 404) { console.log(`SystemDetailsApi: No system details found for asset ${assetId}`); return null; } handleApiError(error, `fetch system details for asset ${assetId}`); } }); } async create(systemDetails: SystemDetailsCreateRequest): Promise { return retryOperation(async () => { try { // Validate that the referenced asset exists const asset = await assetApi.getById(systemDetails.assetID); if (!asset) { throw new Error(`Asset with ID ${systemDetails.assetID} does not exist. Please select a valid asset.`); } const systemDetailsData = { ...systemDetails }; console.log('SystemDetailsApi: Creating system details with data', systemDetailsData); const response = await apiClient.post(this.baseUrl, systemDetailsData); console.log('SystemDetailsApi: Create response status', response.status); try { return validateSystemDetailsApiResponse(response.data); } catch (error) { console.warn('SystemDetailsApi: Invalid response data from create, using safe fallback:', error); return createSafeSystemDetails(response.data); } } catch (error) { handleApiError(error, 'create system details'); } }, 2); // Fewer retries for create operations } async update(id: number, systemDetails: SystemDetailsUpdateRequest): Promise { try { // Validate that the referenced asset exists const asset = await assetApi.getById(systemDetails.assetID); if (!asset) { const errorMessage = `Asset with ID ${systemDetails.assetID} does not exist`; console.error('SystemDetailsApi:', errorMessage); throw new Error(errorMessage); } const systemDetailsData = { ...systemDetails }; console.log(`SystemDetailsApi: Updating system details ${id} with data`, systemDetailsData); const response = await apiClient.put(`${this.baseUrl}/${id}`, systemDetailsData); console.log(`SystemDetailsApi: Update response status for system details ${id}`, response.status); try { return validateSystemDetailsApiResponse(response.data); } catch (error) { console.warn(`SystemDetailsApi: Invalid response data from update for ID ${id}, using safe fallback:`, error); return createSafeSystemDetails(response.data); } } catch (error) { console.error(`SystemDetailsApi: Error updating system details ${id}:`, error); throw error; } } async delete(id: number): Promise { try { console.log(`SystemDetailsApi: Deleting system details ${id}`); await apiClient.delete(`${this.baseUrl}/${id}`); console.log(`SystemDetailsApi: System details ${id} deleted successfully`); } catch (error) { console.error(`SystemDetailsApi: Error deleting system details ${id}:`, error); throw error; } } } export const systemDetailsApi = new SystemDetailsApi();