Spaces:
Sleeping
Sleeping
| 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 <T>( | |
| operation: () => Promise<T>, | |
| maxRetries: number = 3, | |
| delay: number = 1000 | |
| ): Promise<T> => { | |
| 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<SystemDetails[]> { | |
| 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<SystemDetails | null> { | |
| 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<SystemDetails | null> { | |
| 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<SystemDetails> { | |
| 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<SystemDetails> { | |
| 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<void> { | |
| 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(); |