import axios from 'axios'; import { API_BASE_URL } from '@/config'; import apiClient from '@/lib/api/axios-instance'; import { createHeaders } from '@/lib/api'; export interface Task { id: number; type: string; priority?: string; severity?: string; description: string; sprintName: string | null; estimates: string; esUnit: number | null; stateDate: string; endDate: string | null; assignedTo: number | null; remainingHr: number | null; status: string; issuesId: number | null; title: string; createdAt?: string; createdBy?: number | null; createdByName?: string; updatedAt?: string | null; updatedBy?: number | null; updatedByName?: string | null; taskCode?: string; version?: string; url?: string; stepsToReproduce?: string; inputs?: string; environment?: string; expectedBehavior?: string; actualBehavior?: string; resolution?: string; rootCause?: string; projectId?: number; projectName?: string; issueName?: string; deliverableName?: string; relatedUrl?: string; } export interface TaskCreateRequest { id?: number; type: string; priority?: string; severity?: string; description: string; sprintName: string | null; estimates: string; esUnit: number | null; stateDate: string; endDate: string | null; assignedTo: number | null; remainingHr: number | null; status: string; issuesId: number | null; title: string; taskCode?: string; version?: string; url?: string; stepsToReproduce?: string; inputs?: string; environment?: string; expectedBehavior?: string; actualBehavior?: string; resolution?: string; rootCause?: string; } export interface TaskUpdateRequest extends TaskCreateRequest { id: number; } // Helper to create a safe task object with default values for null/undefined fields const createSafeTask = (data: any): Task => { return { id: data.id || 0, title: data.title || '', description: data.description || '', type: data.type || 'Medium', status: data.status || 'New', sprintName: data.sprintName, estimates: data.estimates || '', esUnit: data.esUnit, stateDate: data.stateDate || new Date().toISOString().split('T')[0], endDate: data.endDate, assignedTo: data.assignedTo, remainingHr: data.remainingHr, issuesId: data.issuesId, createdAt: data.createdAt || new Date().toISOString(), // Default to current time if not provided createdBy: data.createdBy, createdByName: data.createdByName, updatedAt: data.updatedAt, updatedBy: data.updatedBy, updatedByName: data.updatedByName, taskCode: data.taskCode || '', version: data.version || '', url: data.url || '', stepsToReproduce: data.stepsToReproduce || '', inputs: data.inputs || '', environment: data.environment || '', expectedBehavior: data.expectedBehavior || '', actualBehavior: data.actualBehavior || '', resolution: data.resolution || '', rootCause: data.rootCause || '', priority: data.priority, severity: data.severity, projectId: data.projectId, projectName: data.projectName, issueName: data.issueName, deliverableName: data.deliverableName, relatedUrl: data.relatedUrl }; }; class TasksApi { private baseUrl = `/api/Tasks`; async getAll(): Promise { try { console.log('TasksApi: Fetching all tasks from', this.baseUrl); const response = await apiClient.get(this.baseUrl); console.log('TasksApi: Response status', response.status); if (!response.data) { console.warn('TasksApi: No data in response'); return []; } if (!Array.isArray(response.data)) { console.warn('TasksApi: Response is not an array', response.data); return []; } // Map each item to ensure it has all required fields return response.data.map(item => createSafeTask(item)); } catch (error) { console.error('Error fetching tasks:', error); // Return empty array instead of throwing to prevent UI crashes return []; } } async getByType(type: string): Promise { try { console.log(`TasksApi: Fetching tasks of type ${type}`); console.log(`TasksApi: Requesting from URL: ${this.baseUrl}/GetTaskByType?type=${type}`); const response = await apiClient.get(`${this.baseUrl}/GetTaskByType?type=${type}`); console.log('TasksApi: Response status:', response.status); console.log('TasksApi: Response headers:', response.headers); if (!response.data) { console.warn('TasksApi: No data in response'); return []; } // Log the raw response data console.log('TasksApi: Raw response data type:', typeof response.data); console.log('TasksApi: Raw response data sample:', Array.isArray(response.data) ? response.data.slice(0, 1) : response.data ); if (!Array.isArray(response.data)) { console.warn('TasksApi: Response is not an array', response.data); // Try to handle if the response is enclosed in another object if (response.data && typeof response.data === 'object' && response.data.items && Array.isArray(response.data.items)) { console.log('TasksApi: Found array in response.data.items'); response.data = response.data.items; } else { console.warn('TasksApi: Unable to extract array from response'); return []; } } console.log(`TasksApi: Found ${response.data.length} ${type} tasks`); // Map each item to ensure it has all required fields const mappedTasks = response.data.map(item => createSafeTask(item)); console.log(`TasksApi: Returning ${mappedTasks.length} mapped ${type} tasks`); if (mappedTasks.length > 0) { console.log('TasksApi: First mapped task sample:', mappedTasks[0]); } return mappedTasks; } catch (error) { console.error(`Error fetching tasks of type ${type}:`, error); // Return empty array instead of throwing to prevent UI crashes return []; } } async getById(id: number): Promise { try { const response = await apiClient.get(`${this.baseUrl}/${id}`); if (response.data) { return createSafeTask(response.data); } return null; } catch (error) { console.error(`Error fetching task ${id}:`, error); return null; } } async create(task: TaskCreateRequest): Promise { try { // Remove id if it's 0, undefined, or null const taskData = { ...task }; if (taskData.id === 0 || taskData.id === undefined || taskData.id === null) { delete taskData.id; } // Don't fill empty fields with default values // Instead, let null values pass through to the API // Only format dates that actually have values if (taskData.stateDate && typeof taskData.stateDate === 'string') { // Check if the date already has a time part if (!taskData.stateDate.includes('T')) { taskData.stateDate = taskData.stateDate + 'T12:00:00.000Z'; } } if (taskData.endDate && typeof taskData.endDate === 'string') { // Check if the date already has a time part if (!taskData.endDate.includes('T')) { taskData.endDate = taskData.endDate + 'T12:00:00.000Z'; } } console.log('TasksApi: Creating task with data', taskData); const response = await apiClient.post(this.baseUrl, taskData); console.log('TasksApi: Create response status', response.status); return createSafeTask(response.data); } catch (error) { console.error('Error creating task:', error); throw error; } } async update(id: number, task: TaskUpdateRequest): Promise { try { // Don't fill empty fields with default values // Instead, let null values pass through to the API const taskData = { ...task }; // Only format dates that actually have values if (taskData.stateDate && typeof taskData.stateDate === 'string') { // Check if the date already has a time part if (!taskData.stateDate.includes('T')) { taskData.stateDate = taskData.stateDate + 'T12:00:00.000Z'; } } if (taskData.endDate && typeof taskData.endDate === 'string') { // Check if the date already has a time part if (!taskData.endDate.includes('T')) { taskData.endDate = taskData.endDate + 'T12:00:00.000Z'; } } console.log(`TasksApi: Updating task ${id} with data`, taskData); const response = await apiClient.put(`${this.baseUrl}/${id}`, taskData); console.log(`TasksApi: Update response status for task ${id}`, response.status); return createSafeTask(response.data); } catch (error) { console.error(`Error updating task ${id}:`, error); throw error; } } async delete(id: number): Promise { try { console.log(`TasksApi: Deleting task ${id}`); await apiClient.delete(`${this.baseUrl}/${id}`); console.log(`TasksApi: Task ${id} deleted successfully`); } catch (error) { console.error(`Error deleting task ${id}:`, error); throw error; } } } export const tasksApi = new TasksApi();