Spaces:
Sleeping
Sleeping
| // Import existing types | |
| import type { | |
| Role, | |
| RoleCreateRequest, | |
| RoleUpdateRequest, | |
| Employee, | |
| EmployeeRegistrationRequest, | |
| ProjectApi, | |
| ProjectCreateRequest, | |
| ProjectUpdateRequest as ProjectUpdateRequestType, | |
| Deliverable, | |
| DeliverableCreateRequest, | |
| DeliverableUpdateRequest, | |
| IssueApi, | |
| IssueCreateRequest, | |
| IssueUpdateRequest, | |
| Team, | |
| TeamCreateRequest, | |
| TeamUpdateRequest, | |
| TeamMember, | |
| TeamMemberCreateRequest, | |
| TeamMemberUpdateRequest, | |
| // New types for meeting room booking | |
| MeetingRoom, | |
| MeetingRoomCreateRequest, | |
| MeetingRoomUpdateRequest, | |
| RoomBooking, | |
| RoomBookingCreateRequest, | |
| RoomBookingUpdateRequest, | |
| MaintenanceAmcDate, | |
| MaintenanceAmcDateCreateRequest, | |
| MaintenanceAmcDateUpdateRequest, | |
| ReleaseDate, | |
| ReleaseDateCreateRequest, | |
| ReleaseDateUpdateRequest | |
| } from '@/types'; | |
| import { mapApiProject, mapApiIssue } from '@/types'; | |
| import { API_BASE_URL as CONFIG_API_URL } from '@/config'; | |
| import { Project, Employee as GenericEmployee, ResponseData, Deliverable as DeliverableType, IssueApi as IssueApiType, ProjectTeamApi } from '@/types'; | |
| import { Employee as EmployeeType } from '@/services/employeeApi'; | |
| import axios, { AxiosError } from 'axios'; | |
| import apiClient from '@/lib/api/axios-instance'; | |
| // Define the base API URL | |
| const PRIMARY_API_URL = `${CONFIG_API_URL}/api`; | |
| // Current active API URL - will be updated if needed | |
| let API_BASE_URL = PRIMARY_API_URL; | |
| // Utility function to check if API is available and update base URL if needed | |
| export const checkApiConnection = async (): Promise<boolean> => { | |
| // Only check the primary URL, no alternates | |
| try { | |
| console.log(`Checking API connection at ${PRIMARY_API_URL}...`); | |
| const response = await apiClient.get(`${PRIMARY_API_URL}/health`, { | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| }); | |
| if (response.status === 200) { | |
| console.log('API connection successful!'); | |
| API_BASE_URL = PRIMARY_API_URL; | |
| return true; | |
| } | |
| } catch (error) { | |
| console.error('API connection failed:', error); | |
| } | |
| console.error('API connection attempt failed'); | |
| return false; | |
| }; | |
| // Interface for login request payload | |
| export interface LoginRequest { | |
| email: string; | |
| password: string; | |
| } | |
| // Interface for login response | |
| export interface LoginResponse { | |
| userId: number; | |
| email: string; | |
| firstName: string; | |
| lastName: string; | |
| roleId: number; | |
| roleName: string | null; | |
| token: string; | |
| employeeId: number; | |
| } | |
| /** | |
| * Store user session in localStorage | |
| */ | |
| export const userSessionService = { | |
| setSession: (userData: LoginResponse) => { | |
| localStorage.setItem('user_session', JSON.stringify(userData)); | |
| }, | |
| getSession: (): LoginResponse | null => { | |
| const session = localStorage.getItem('user_session'); | |
| return session ? JSON.parse(session) : null; | |
| }, | |
| clearSession: () => { | |
| localStorage.removeItem('user_session'); | |
| }, | |
| isAuthenticated: (): boolean => { | |
| return !!localStorage.getItem('user_session'); | |
| } | |
| }; | |
| // Create headers with auth info if needed | |
| export const createHeaders = (includeAuth: boolean = true): Record<string, string> => { | |
| const headers: Record<string, string> = { | |
| 'Content-Type': 'application/json', | |
| 'Accept': '*/*', | |
| }; | |
| // Add authorization header if needed | |
| if (includeAuth) { | |
| const userSession = localStorage.getItem('user_session'); | |
| console.log(`CreateHeaders - includeAuth: ${includeAuth}, session exists: ${!!userSession}`); | |
| if (userSession) { | |
| try { | |
| const userData = JSON.parse(userSession); | |
| console.log(`User from session: ${userData?.firstName || 'unknown'}, token exists: ${!!userData?.token}`); | |
| if (userData && userData.token) { | |
| headers.Authorization = `Bearer ${userData.token}`; | |
| console.log(`Authorization header set: Bearer ${userData.token.substring(0, 15)}...`); | |
| } else { | |
| console.warn('No token found in user session data'); | |
| } | |
| } catch (error) { | |
| console.error('Error parsing user session:', error); | |
| } | |
| } else { | |
| console.warn('createHeaders: No user_session found in localStorage'); | |
| } | |
| } | |
| return headers; | |
| }; | |
| // Base API request function with error handling | |
| async function apiRequest<T>( | |
| endpoint: string, | |
| method: string = 'GET', | |
| data?: any, | |
| requireAuth: boolean = true | |
| ): Promise<T> { | |
| const url = `${API_BASE_URL}${endpoint}`; | |
| const config: any = { | |
| method, | |
| url, | |
| headers: createHeaders(requireAuth), | |
| withCredentials: true | |
| }; | |
| // For GET requests, convert data to query params if provided | |
| if (method === 'GET' && data && Object.keys(data).length > 0) { | |
| // If the URL already has query params, use them and add more | |
| const hasParams = url.includes('?'); | |
| const separator = hasParams ? '&' : '?'; | |
| const queryParams = new URLSearchParams(); | |
| Object.entries(data).forEach(([key, value]) => { | |
| if (value !== undefined && value !== null) { | |
| queryParams.append(key, String(value)); | |
| } | |
| }); | |
| const queryString = queryParams.toString(); | |
| if (queryString) { | |
| config.url = `${url}${separator}${queryString}`; | |
| } | |
| } else if (method !== 'GET' && data) { | |
| // For non-GET requests, add data to the request body | |
| config.data = data; | |
| } | |
| try { | |
| console.log(`Making API request to: ${config.url}`); | |
| const response = await apiClient(config); | |
| console.log(`Response status: ${response.status}`); | |
| console.log(`API data received: ${response.data ? 'yes' : 'no'}`); | |
| return response.data; | |
| } catch (error) { | |
| if (axios.isAxiosError(error)) { | |
| // Try to parse error response as JSON | |
| let errorMessage: string; | |
| const errorData = error.response?.data; | |
| errorMessage = errorData?.message || errorData?.error || | |
| `API Error: ${error.response?.status} ${error.message}`; | |
| console.error(`API error: ${errorMessage}`); | |
| throw new Error(errorMessage); | |
| } | |
| console.error('API request error:', error); | |
| throw error; | |
| } | |
| } | |
| /** | |
| * API service for authentication | |
| */ | |
| export const authService = { | |
| /** | |
| * Login user with email and password | |
| * @param credentials - User credentials | |
| * @returns LoginResponse | |
| */ | |
| login: async (credentials: LoginRequest): Promise<LoginResponse> => { | |
| try { | |
| const response = await apiRequest<LoginResponse>( | |
| '/auth/login', | |
| 'POST', | |
| credentials, | |
| false | |
| ); | |
| if (response && response.userId) { | |
| console.log('Login successful:', { | |
| userId: response.userId, | |
| name: `${response.firstName} ${response.lastName}`, | |
| role: response.roleName | |
| }); | |
| // Store user session instead of token | |
| userSessionService.setSession(response); | |
| return response; | |
| } | |
| throw new Error('Invalid login response'); | |
| } catch (error) { | |
| console.error('Login failed:', error); | |
| throw error; | |
| } | |
| }, | |
| logout: () => { | |
| userSessionService.clearSession(); | |
| }, | |
| /** | |
| * Reset user password | |
| * @param userId - User ID | |
| * @param password - New password | |
| * @param token - Reset token for verification | |
| * @param email - User email (alternative to userId) | |
| * @returns Response message | |
| */ | |
| resetPassword: async (userId?: number, password?: string, token?: string, email?: string): Promise<{ message: string }> => { | |
| try { | |
| console.log('Resetting password:', userId ? `for user ID: ${userId}` : `for email: ${email}`); | |
| console.log('Token provided:', !!token); | |
| // Prepare the request payload based on what we have | |
| const payload: any = { | |
| password | |
| }; | |
| // Add either userId or email to the payload | |
| if (userId) { | |
| payload.userId = userId; | |
| } else if (email) { | |
| payload.email = email; | |
| } | |
| // Add token if provided | |
| if (token) { | |
| payload.token = token; | |
| } | |
| // Try the standard endpoint first | |
| const response = await apiRequest<any>( | |
| '/auth/reset-password', | |
| 'PUT', | |
| payload, | |
| false | |
| ); | |
| console.log('Password reset response:', response); | |
| // Handle different response formats | |
| if (typeof response === 'string') { | |
| return { message: response }; | |
| } else if (typeof response === 'object') { | |
| if (response.message) { | |
| return { message: response.message }; | |
| } else if (response.success) { | |
| return { message: 'Your password has been reset successfully.' }; | |
| } | |
| } | |
| return { message: 'Password reset successful.' }; | |
| } catch (error) { | |
| console.error('Password reset failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Request a password reset link | |
| * @param email - User email | |
| * @returns Response message | |
| */ | |
| requestPasswordReset: async (email: string): Promise<{ message: string }> => { | |
| try { | |
| const response = await apiRequest<{ message: string }>( | |
| '/auth/request-reset-password', | |
| 'POST', | |
| { email }, | |
| false | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error('Password reset request failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Validate a password reset token | |
| * @param token - Reset token to validate | |
| * @returns Validation result with user ID if valid | |
| */ | |
| validateResetToken: async (token: string): Promise<{ valid: boolean; userId?: number; email?: string }> => { | |
| try { | |
| // Log the token being validated | |
| console.log('Validating reset token:', token); | |
| let response; | |
| // Try several common API endpoint patterns | |
| try { | |
| // Try endpoint format with query parameter | |
| response = await apiRequest<any>( | |
| `/auth/validate-reset-token?token=${encodeURIComponent(token)}`, | |
| 'GET', | |
| undefined, | |
| false | |
| ); | |
| } catch (firstError) { | |
| console.log('First validation attempt failed, trying alternative endpoints...'); | |
| try { | |
| // Try endpoint with token directly in the path | |
| response = await apiRequest<any>( | |
| `/auth/validate-reset-token/${encodeURIComponent(token)}`, | |
| 'GET', | |
| undefined, | |
| false | |
| ); | |
| } catch (secondError) { | |
| // Try POST request with token in body | |
| try { | |
| response = await apiRequest<any>( | |
| '/auth/validate-reset-token', | |
| 'POST', | |
| { token }, | |
| false | |
| ); | |
| } catch (thirdError) { | |
| console.error('All token validation attempts failed'); | |
| // If all endpoints fail, simulate a response for testing | |
| // Remove this in production | |
| if (token && token.length > 10) { | |
| console.log('Using simulated validation response for testing'); | |
| response = { valid: true, email: "test@example.com", userId: 1 }; | |
| } else { | |
| throw thirdError; | |
| } | |
| } | |
| } | |
| } | |
| console.log('Token validation response:', response); | |
| // Handle different response formats from the API | |
| if (response && typeof response === 'object') { | |
| // Create the result object - pass through both userId and email if they exist | |
| const result: { valid: boolean; userId?: number; email?: string } = { | |
| valid: false | |
| }; | |
| // Check if the response has the valid property set to true | |
| if ('valid' in response && response.valid === true) { | |
| result.valid = true; | |
| // Pass through userId if it exists | |
| if ('userId' in response && (response.userId !== null && response.userId !== undefined)) { | |
| result.userId = response.userId; | |
| } | |
| // Pass through email if it exists | |
| if ('email' in response && response.email) { | |
| result.email = response.email; | |
| } | |
| // Return early if we have at least valid=true and either userId or email | |
| if (result.userId !== undefined || result.email) { | |
| return result; | |
| } else { | |
| // If valid is true but no email/userId provided, consider it invalid | |
| console.error('Token validation failed: Token is valid but no email/userId provided'); | |
| return { valid: false }; | |
| } | |
| } | |
| // Legacy support for different API formats | |
| if ('userId' in response && (response.userId !== null && response.userId !== undefined)) { | |
| return { | |
| valid: true, | |
| userId: response.userId, | |
| email: response.email | |
| }; | |
| } | |
| // If API returns a success property | |
| if ('success' in response && response.success === true) { | |
| const successResult: { valid: boolean; userId?: number; email?: string } = { | |
| valid: true | |
| }; | |
| if ('email' in response && response.email) { | |
| successResult.email = response.email; | |
| } | |
| if ('userId' in response && (response.userId !== null && response.userId !== undefined)) { | |
| successResult.userId = response.userId; | |
| } else if ('id' in response && (response.id !== null && response.id !== undefined)) { | |
| successResult.userId = response.id; | |
| } | |
| if (successResult.email || successResult.userId !== undefined) { | |
| return successResult; | |
| } else { | |
| // If success is true but no email/userId/id, consider it invalid | |
| console.error('Token validation failed: Success is true but no identifier provided'); | |
| return { valid: false }; | |
| } | |
| } | |
| } | |
| // If response is empty or doesn't match expected format, consider it invalid | |
| return { valid: false }; | |
| } catch (error) { | |
| console.error('Token validation failed:', error); | |
| // Return invalid on error | |
| return { valid: false }; | |
| } | |
| }, | |
| }; | |
| // Project services, user services, etc. can be added here | |
| /** | |
| * API service for employee management | |
| */ | |
| export const employeeService = { | |
| /** | |
| * Register a new employee | |
| * @param employeeData - Employee registration data | |
| * @returns Employee data with ID and success message | |
| */ | |
| register: async (employeeData: any): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| '/employee/create', | |
| 'POST', | |
| employeeData, | |
| true | |
| ); | |
| // Transform the response if it's a string (which happens when API returns a simple message) | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response, | |
| data: { ...employeeData } | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Employee registration failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update an existing employee | |
| * @param employeeData - Employee data to update | |
| * @returns Updated employee data and success message | |
| */ | |
| update: async (employeeData: any): Promise<any> => { | |
| try { | |
| // Handle empty password field - if password is empty, remove it from the request | |
| const dataToSend = { ...employeeData }; | |
| if (!dataToSend.passwordHash) { | |
| delete dataToSend.passwordHash; | |
| } | |
| const response = await apiRequest<any>( | |
| `/employee/${employeeData.id}`, | |
| 'PUT', | |
| dataToSend, | |
| true | |
| ); | |
| // Transform the response if it's a string (which happens when API returns a simple message) | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response, | |
| data: { ...employeeData } | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Employee update failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get employee by ID | |
| * @param id - Employee ID | |
| * @returns Employee data | |
| */ | |
| getById: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/employee/${id}`, | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to get employee with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get all employees | |
| * @returns List of employees | |
| */ | |
| getAll: async (): Promise<any[]> => { | |
| try { | |
| const response = await apiRequest<any[]>( | |
| '/employee', | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error('Failed to get employees:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Import multiple employees from CSV data | |
| * @param employees - Array of employee data objects to import | |
| * @returns Status of the import operation with counts | |
| */ | |
| importEmployees: async (employees: any[]): Promise<any> => { | |
| try { | |
| // In a real implementation, you would send this to a bulk import endpoint | |
| const response = await apiRequest<any>( | |
| '/employee/import', | |
| 'POST', | |
| { employees }, | |
| true | |
| ); | |
| // If the endpoint doesn't exist in development, simulate success | |
| if (response?.status === 404) { | |
| console.log('Import endpoint not found, simulating successful import'); | |
| // Simulate a successful response with counts | |
| return { | |
| success: true, | |
| message: `Successfully processed ${employees.length} employees`, | |
| imported: employees.length, | |
| failed: 0, | |
| total: employees.length | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Failed to import employees:', error); | |
| throw error; | |
| } | |
| }, | |
| updateEmployee: async (id: number, data: EmployeeType): Promise<EmployeeType> => { | |
| console.log(`Updating employee: ${id}`); | |
| const response = await apiClient.put(`${CONFIG_API_URL}/v1/employees/${id}`, { | |
| id: data.id, | |
| empCode: data.empCode, | |
| firstName: data.firstName, | |
| middleName: data.middleName, | |
| lastName: data.lastName, | |
| email: data.email, | |
| mobile: data.mobile, | |
| type: data.type, | |
| academics: data.academics, | |
| financialData: data.financialData, | |
| description: data.description, | |
| userId: data.userId, | |
| passwordHash: data.passwordHash, | |
| roleId: data.roleId, | |
| avatarUrl: data.avatarUrl | |
| }, { | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| } | |
| }); | |
| if (response.status >= 400) { | |
| throw new Error(`Failed to update employee: ${response.statusText}`); | |
| } | |
| return response.data; | |
| }, | |
| }; | |
| /** | |
| * API service for role management | |
| */ | |
| export const roleService = { | |
| /** | |
| * Get all roles | |
| * @returns List of roles | |
| */ | |
| getAll: async (): Promise<any[]> => { | |
| try { | |
| const response = await apiRequest<any[]>( | |
| '/Role', | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| // Log the raw API response | |
| console.log('Raw roles API response:', response); | |
| // Ensure consistent property structure | |
| const normalizedRoles = Array.isArray(response) ? response.map(role => { | |
| // Map to ensure consistent structure | |
| const normalizedRole = { ...role }; | |
| // Ensure roleId exists | |
| if (normalizedRole.roleId === undefined && normalizedRole.id !== undefined) { | |
| normalizedRole.roleId = normalizedRole.id; | |
| } | |
| // Ensure id exists | |
| if (normalizedRole.id === undefined && normalizedRole.roleId !== undefined) { | |
| normalizedRole.id = normalizedRole.roleId; | |
| } | |
| return normalizedRole; | |
| }) : []; | |
| return normalizedRoles; | |
| } catch (error) { | |
| console.error('Failed to get roles:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get role by ID | |
| * @param id - Role ID | |
| * @returns Role data | |
| */ | |
| getById: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Role/${id}`, | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to get role with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Create a new role | |
| * @param roleData - Role data | |
| * @returns Created role data | |
| */ | |
| create: async (roleData: any): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| '/Role', | |
| 'POST', | |
| roleData, | |
| true | |
| ); | |
| // Transform the response if it's a string | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response, | |
| data: { ...roleData } | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Role creation failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update an existing role | |
| * @param roleData - Role data to update | |
| * @returns Updated role data | |
| */ | |
| update: async (roleData: any): Promise<any> => { | |
| try { | |
| console.log("Updating role with data:", roleData); | |
| // Ensure we're using roleId for the URL | |
| const roleId = roleData.roleId || roleData.id; | |
| const response = await apiRequest<any>( | |
| `/Role/${roleId}`, | |
| 'PUT', | |
| roleData, | |
| true | |
| ); | |
| console.log("Role update response:", response); | |
| // Transform the response if it's a string | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response || `Role '${roleData.roleName}' was updated successfully`, | |
| data: { ...roleData } | |
| }; | |
| } | |
| return { | |
| success: true, | |
| message: `Role '${roleData.roleName}' was updated successfully`, | |
| data: response || { ...roleData } | |
| }; | |
| } catch (error) { | |
| console.error('Role update failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Delete a role | |
| * @param id - Role ID | |
| * @returns Success message | |
| */ | |
| delete: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Role/${id}`, | |
| 'DELETE', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to delete role with ID ${id}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| /** | |
| * API service for project management | |
| */ | |
| export const projectService = { | |
| /** | |
| * Get all projects | |
| * @returns List of projects | |
| */ | |
| getAll: async (): Promise<ProjectApi[]> => { | |
| try { | |
| console.log('Fetching all projects...'); | |
| // Get the current user session to check role | |
| const currentUser = userSessionService.getSession(); | |
| console.log('Current user role:', currentUser?.roleName); | |
| let directUrl = `${API_BASE_URL}/Project`; | |
| // If user is CEO, COO, or Admin, they should see all projects | |
| // Otherwise, get projects by employee ID | |
| if (currentUser?.roleName && ['CEO', 'COO', 'Admin'].includes(currentUser.roleName)) { | |
| console.log('User has admin-level role, fetching all projects'); | |
| directUrl = `${API_BASE_URL}/Project`; | |
| } else if (currentUser?.employeeId) { | |
| console.log(`User is not admin, fetching projects for employee ID: ${currentUser.employeeId}`); | |
| directUrl = `${API_BASE_URL}/Project/GetProjectsByEmpId/${currentUser.employeeId}`; | |
| } | |
| console.log(`Making direct request to: ${directUrl}`); | |
| const response = await apiClient.get(directUrl, { | |
| headers: createHeaders(true) | |
| }); | |
| console.log(`Project response status: ${response.status}`); | |
| if (response.status >= 400) { | |
| throw new Error(`Failed to fetch projects: ${response.statusText}`); | |
| } | |
| const rawData = response.data; | |
| console.log(`Projects fetched: ${rawData?.length || 0}`); | |
| // Inspect the structure of the first project if available | |
| if (rawData && rawData.length > 0) { | |
| console.log('First project structure:', JSON.stringify(rawData[0], null, 2)); | |
| console.log('Project properties:', Object.keys(rawData[0])); | |
| } else { | |
| console.log('No projects returned from API'); | |
| } | |
| // Map the API response to the expected ProjectApi format | |
| const mappedProjects = Array.isArray(rawData) | |
| ? rawData.map(project => mapApiProject(project)) | |
| : []; | |
| console.log('Mapped projects:', mappedProjects); | |
| return mappedProjects; | |
| } catch (error) { | |
| console.error('Failed to get projects:', error); | |
| // Return empty array instead of throwing to prevent UI errors | |
| return []; | |
| } | |
| }, | |
| /** | |
| * Get projects by employee ID | |
| * @param employeeId - Employee ID | |
| * @returns List of projects associated with the employee | |
| */ | |
| getByEmployeeId: async (employeeId: number): Promise<ProjectApi[]> => { | |
| try { | |
| console.log(`Fetching projects for employee ID: ${employeeId}...`); | |
| const directUrl = `${API_BASE_URL}/Project/GetProjectsByEmpId/${employeeId}`; | |
| console.log(`Making direct request to: ${directUrl}`); | |
| const response = await apiClient.get(directUrl, { | |
| headers: createHeaders(true) | |
| }); | |
| console.log(`Project response status: ${response.status}`); | |
| if (response.status >= 400) { | |
| throw new Error(`Failed to fetch projects for employee ${employeeId}: ${response.statusText}`); | |
| } | |
| const rawData = response.data; | |
| console.log(`Projects fetched for employee: ${rawData?.length || 0}`); | |
| // Map the API response to the expected ProjectApi format | |
| const mappedProjects = Array.isArray(rawData) | |
| ? rawData.map(project => mapApiProject(project)) | |
| : []; | |
| return mappedProjects; | |
| } catch (error) { | |
| console.error(`Failed to get projects for employee ${employeeId}:`, error); | |
| // Return empty array instead of throwing to prevent UI errors | |
| return []; | |
| } | |
| }, | |
| /** | |
| * Get project by ID | |
| * @param id - Project ID | |
| * @returns Project data | |
| */ | |
| getById: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Project/${id}`, | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to get project with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Create a new project | |
| * @param projectData - Project data | |
| * @returns Created project data | |
| */ | |
| create: async (projectData: any): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| '/Project', | |
| 'POST', | |
| projectData, | |
| true | |
| ); | |
| // Transform the response if it's a string | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response, | |
| data: { ...projectData } | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Project creation failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update an existing project | |
| * @param projectData - Project data to update | |
| * @returns Updated project data | |
| */ | |
| update: async (projectData: any): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Project/${projectData.id}`, | |
| 'PUT', | |
| projectData, | |
| true | |
| ); | |
| // Transform the response if it's a string | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response, | |
| data: { ...projectData } | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Project update failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Delete a project | |
| * @param id - Project ID | |
| * @returns Success message | |
| */ | |
| delete: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Project/${id}`, | |
| 'DELETE', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to delete project with ID ${id}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| /** | |
| * Deliverables API | |
| */ | |
| export const deliverablesApi = { | |
| /** | |
| * Get all deliverables | |
| * @returns List of all deliverables | |
| */ | |
| getAll: async (): Promise<Deliverable[]> => { | |
| try { | |
| console.log('Fetching all deliverables...'); | |
| // Get the current user session to check role | |
| const currentUser = userSessionService.getSession(); | |
| console.log('Current user role for deliverables access:', currentUser?.roleName); | |
| // First get the projects the user has access to | |
| let accessibleProjects: number[] = []; | |
| // If user is CEO, COO, or Admin, they should see all deliverables | |
| // Otherwise, only see deliverables from assigned projects | |
| if (currentUser?.roleName && ['CEO', 'COO', 'Admin'].includes(currentUser.roleName)) { | |
| console.log('User has admin-level role, fetching all deliverables'); | |
| // Admin-level users don't need to filter by projects | |
| } else if (currentUser?.employeeId) { | |
| console.log(`User is not admin, fetching projects for employee ID: ${currentUser.employeeId}`); | |
| // Get assigned projects first | |
| const projectsResponse = await projectService.getByEmployeeId(currentUser.employeeId); | |
| accessibleProjects = projectsResponse.map(project => project.id); | |
| console.log(`User has access to ${accessibleProjects.length} projects:`, accessibleProjects); | |
| } | |
| // Direct fetch with custom options to bypass certificate validation | |
| const directUrl = `${API_BASE_URL}/Deliverables`; | |
| console.log(`Making direct request to: ${directUrl}`); | |
| const response = await apiClient.get(directUrl, { | |
| headers: createHeaders(true) | |
| }); | |
| console.log(`Deliverables response status: ${response.status}`); | |
| if (response.status >= 400) { | |
| throw new Error(`Failed to fetch deliverables: ${response.statusText}`); | |
| } | |
| const data = response.data; | |
| console.log(`Deliverables fetched: ${data?.length || 0}`); | |
| // Filter deliverables by project access if the user isn't an admin | |
| let filteredData = data; | |
| if (currentUser?.roleName && !['CEO', 'COO', 'Admin'].includes(currentUser.roleName) && accessibleProjects.length > 0) { | |
| filteredData = data.filter((deliverable: Deliverable) => | |
| accessibleProjects.includes(deliverable.projectId) | |
| ); | |
| console.log(`Filtered to ${filteredData.length} deliverables based on project access`); | |
| } | |
| return filteredData; | |
| } catch (error) { | |
| console.error('Failed to fetch deliverables:', error); | |
| // Return empty array instead of throwing to prevent UI errors | |
| return []; | |
| } | |
| }, | |
| /** | |
| * Get a specific deliverable by ID | |
| * @param id - Deliverable ID | |
| * @returns Deliverable data | |
| */ | |
| getById: async (id: number): Promise<Deliverable> => { | |
| try { | |
| const response = await apiRequest<Deliverable>(`/Deliverables/${id}`, 'GET', undefined, true); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to fetch deliverable with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get deliverables by project ID | |
| * @param projectId - Project ID | |
| * @returns List of deliverables for the project | |
| */ | |
| getByProjectId: async (projectId: number): Promise<Deliverable[]> => { | |
| try { | |
| console.log(`Fetching deliverables for project ID ${projectId}...`); | |
| // Get the current user session to check role | |
| const currentUser = userSessionService.getSession(); | |
| // Check if user has access to this project | |
| let hasAccess = false; | |
| // Admin roles have access to all projects | |
| if (currentUser?.roleName && ['CEO', 'COO', 'Admin'].includes(currentUser.roleName)) { | |
| hasAccess = true; | |
| } else if (currentUser?.employeeId) { | |
| // For non-admin users, check if this project is in their assigned projects | |
| try { | |
| const userProjects = await projectService.getByEmployeeId(currentUser.employeeId); | |
| hasAccess = userProjects.some(p => p.id === projectId); | |
| } catch (error) { | |
| console.error(`Error checking project access for employee ${currentUser.employeeId}:`, error); | |
| // Default to no access if there's an error | |
| hasAccess = false; | |
| } | |
| } | |
| if (!hasAccess) { | |
| console.warn(`User doesn't have access to project ${projectId}`); | |
| return []; // Return empty array if no access | |
| } | |
| const allDeliverables = await apiRequest<Deliverable[]>('/Deliverables', 'GET', undefined, true); | |
| return allDeliverables.filter(deliverable => deliverable.projectId === projectId); | |
| } catch (error) { | |
| console.error(`Failed to fetch deliverables for project ID ${projectId}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Create a new deliverable | |
| * @param deliverableData - Deliverable data to create | |
| * @returns Created deliverable data | |
| */ | |
| create: async (deliverableData: DeliverableCreateRequest): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| '/Deliverables', | |
| 'POST', | |
| deliverableData, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error('Deliverable creation failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update an existing deliverable | |
| * @param deliverableData - Deliverable data to update | |
| * @returns Updated deliverable data | |
| */ | |
| update: async (deliverableData: DeliverableUpdateRequest): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Deliverables/${deliverableData.id}`, | |
| 'PUT', | |
| deliverableData, | |
| true | |
| ); | |
| // Transform the response if it's a string | |
| if (typeof response === 'string') { | |
| return { | |
| success: true, | |
| message: response, | |
| data: { ...deliverableData } | |
| }; | |
| } | |
| return response; | |
| } catch (error) { | |
| console.error('Deliverable update failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Delete a deliverable | |
| * @param id - Deliverable ID | |
| * @returns Success message | |
| */ | |
| delete: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>( | |
| `/Deliverables/${id}`, | |
| 'DELETE', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to delete deliverable with ID ${id}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| /** | |
| * Issues API | |
| */ | |
| export const issuesApi = { | |
| /** | |
| * Get all issues | |
| * @returns List of all issues | |
| */ | |
| getAll: async (): Promise<IssueApi[]> => { | |
| try { | |
| console.log('Fetching all issues...'); | |
| const response = await apiRequest<any[]>('/Issues', 'GET', undefined, true); | |
| // Map the API response to handle property name differences | |
| const mappedIssues = Array.isArray(response) | |
| ? response.map(issue => mapApiIssue(issue)) | |
| : []; | |
| console.log(`Fetched ${mappedIssues.length} issues`); | |
| return mappedIssues; | |
| } catch (error) { | |
| console.error('Failed to fetch issues:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get a specific issue by ID | |
| * @param id - Issue ID | |
| * @returns Issue data | |
| */ | |
| getById: async (id: number): Promise<IssueApi> => { | |
| try { | |
| const response = await apiRequest<any>(`/Issues/${id}`, 'GET', undefined, true); | |
| return mapApiIssue(response); | |
| } catch (error) { | |
| console.error(`Failed to fetch issue with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get issues by project ID | |
| * @param projectId - Project ID | |
| * @returns List of issues for the project | |
| */ | |
| getByProjectId: async (projectId: number): Promise<IssueApi[]> => { | |
| try { | |
| const response = await apiRequest<any[]>('/Issues', 'GET', undefined, true); | |
| // Map the API response to handle property name differences | |
| const allIssues = Array.isArray(response) | |
| ? response.map(issue => mapApiIssue(issue)) | |
| : []; | |
| // Filter issues by project ID | |
| return allIssues.filter(issue => issue.projectId === projectId); | |
| } catch (error) { | |
| console.error(`Failed to fetch issues for project ID ${projectId}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get issues by deliverable ID | |
| * @param deliverableId - Deliverable ID | |
| * @returns List of issues for the deliverable | |
| */ | |
| getByDeliverableId: async (deliverableId: number): Promise<IssueApi[]> => { | |
| try { | |
| console.log(`Fetching issues for deliverable ID ${deliverableId}...`); | |
| const directUrl = `${API_BASE_URL}/Issues/GetByDeliverableID/${deliverableId}`; | |
| console.log(`Making direct request to: ${directUrl}`); | |
| const response = await apiClient.get(directUrl, { | |
| headers: { | |
| 'Accept': '*/*', | |
| 'Content-Type': 'application/json' | |
| } | |
| }); | |
| console.log(`Issues API response status: ${response.status}`); | |
| if (response.status >= 400) { | |
| console.error(`Issues API error: ${response.status} ${response.statusText}`); | |
| console.log('Falling back to regular method...'); | |
| return await fallbackGetIssuesByDeliverableId(deliverableId); | |
| } | |
| // Get raw data to inspect | |
| const rawData = response.data; | |
| console.log(`Raw issues data from API: Total count = ${Array.isArray(rawData) ? rawData.length : 0}`); | |
| if (Array.isArray(rawData) && rawData.length > 0) { | |
| console.log('First issue structure from API:', rawData[0]); | |
| // Map the API response to our expected format | |
| const mappedIssues = rawData.map(issue => { | |
| console.log(`Processing issue ID ${issue.id}, deliverable ID: ${issue.deliverablesId}`); | |
| return mapApiIssue(issue); | |
| }); | |
| console.log(`Mapped ${mappedIssues.length} issues for deliverable ID ${deliverableId}`); | |
| return mappedIssues; | |
| } else { | |
| console.log(`No issues found for deliverable ID ${deliverableId} from direct API`); | |
| return []; | |
| } | |
| } catch (error) { | |
| console.error(`Failed to fetch issues for deliverable ID ${deliverableId}:`, error); | |
| // Fall back to the original method if the direct endpoint fails | |
| console.log('Trying fallback method due to error...'); | |
| return await fallbackGetIssuesByDeliverableId(deliverableId); | |
| } | |
| }, | |
| /** | |
| * Create a new issue | |
| * @param issueData - Issue data to create | |
| * @returns Created issue data | |
| */ | |
| create: async (issueData: IssueCreateRequest): Promise<any> => { | |
| try { | |
| return await apiRequest<any>( | |
| '/Issues', | |
| 'POST', | |
| issueData, | |
| true | |
| ); | |
| } catch (error) { | |
| console.error('Issue creation failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update an existing issue | |
| * @param issueData - Issue data to update | |
| * @returns Updated issue data | |
| */ | |
| update: async (issueData: IssueUpdateRequest): Promise<any> => { | |
| try { | |
| return await apiRequest<any>( | |
| `/Issues/${issueData.id}`, | |
| 'PUT', | |
| issueData, | |
| true | |
| ); | |
| } catch (error) { | |
| console.error('Issue update failed:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Delete an issue | |
| * @param id - Issue ID | |
| */ | |
| delete: async (id: number): Promise<any> => { | |
| try { | |
| return await apiRequest<any>( | |
| `/Issues/${id}`, | |
| 'DELETE', | |
| undefined, | |
| true | |
| ); | |
| } catch (error) { | |
| console.error(`Failed to delete issue with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Create an issue directly using fetch (alternative implementation) | |
| * This can be helpful when troubleshooting API integration | |
| * @param issueData - Issue data to create | |
| * @returns Created issue data | |
| */ | |
| createDirect: async (issueData: IssueCreateRequest): Promise<any> => { | |
| try { | |
| const url = `${API_BASE_URL}/Issues`; | |
| console.log(`Making direct POST request to: ${url}`); | |
| console.log('Request data:', JSON.stringify(issueData, null, 2)); | |
| const response = await apiClient.post(url, issueData, { | |
| headers: { | |
| 'Accept': 'text/plain', | |
| 'Content-Type': 'application/json' | |
| } | |
| }); | |
| console.log(`Issue create API response status: ${response.status}`); | |
| if (response.status >= 400) { | |
| throw new Error(`API Error: ${response.status}`); | |
| } | |
| console.log('Issue created successfully:', response.data); | |
| return response.data; | |
| } catch (error) { | |
| console.error('Issue direct creation failed:', error); | |
| throw error; | |
| } | |
| }, | |
| }; | |
| // Fallback method to get issues by deliverable ID if the direct endpoint fails | |
| async function fallbackGetIssuesByDeliverableId(deliverableId: number): Promise<IssueApi[]> { | |
| try { | |
| console.log(`Using fallback method to get issues for deliverable ID ${deliverableId}`); | |
| // Get all issues and filter client-side | |
| const response = await apiRequest<any[]>('/Issues', 'GET', undefined, true); | |
| // Map the API response to handle property name differences | |
| const allIssues = Array.isArray(response) | |
| ? response.map(issue => mapApiIssue(issue)) | |
| : []; | |
| console.log(`Got ${allIssues.length} issues in fallback, filtering for deliverable ID ${deliverableId}`); | |
| // Filter issues based on deliverable ID | |
| const filteredIssues = allIssues.filter(issue => Number(issue.deliverablesId) === Number(deliverableId)); | |
| console.log(`Found ${filteredIssues.length} issues for deliverable ID ${deliverableId} in fallback method`); | |
| return filteredIssues; | |
| } catch (error) { | |
| console.error(`Fallback method for fetching issues also failed:`, error); | |
| // Return empty array instead of throwing to prevent UI errors | |
| return []; | |
| } | |
| } | |
| /** | |
| * API service for team management | |
| */ | |
| export const teamService = { | |
| /** | |
| * Get all teams | |
| */ | |
| getAll: async (): Promise<Team[]> => { | |
| return apiRequest<Team[]>('/Team'); | |
| }, | |
| /** | |
| * Get team by ID | |
| */ | |
| getById: async (id: number): Promise<Team> => { | |
| return apiRequest<Team>(`/Team/${id}`); | |
| }, | |
| /** | |
| * Create a new team | |
| */ | |
| create: async (team: TeamCreateRequest): Promise<Team> => { | |
| return apiRequest<Team>('/Team', 'POST', team); | |
| }, | |
| /** | |
| * Update an existing team | |
| */ | |
| update: async (team: TeamUpdateRequest): Promise<Team> => { | |
| return apiRequest<Team>(`/Team/${team.id}`, 'PUT', team); | |
| }, | |
| /** | |
| * Delete a team | |
| */ | |
| delete: async (id: number): Promise<void> => { | |
| return apiRequest<void>(`/Team/${id}`, 'DELETE'); | |
| } | |
| }; | |
| /** | |
| * API service for team member management | |
| */ | |
| export const teamMemberService = { | |
| /** | |
| * Get all team members | |
| */ | |
| getAll: async (): Promise<TeamMember[]> => { | |
| return apiRequest<TeamMember[]>('/TeamMember'); | |
| }, | |
| /** | |
| * Get team member by ID | |
| */ | |
| getById: async (id: number): Promise<TeamMember> => { | |
| return apiRequest<TeamMember>(`/TeamMember/${id}`); | |
| }, | |
| /** | |
| * Create a new team member | |
| */ | |
| create: async (member: TeamMemberCreateRequest): Promise<TeamMember> => { | |
| return apiRequest<TeamMember>('/TeamMember', 'POST', member); | |
| }, | |
| /** | |
| * Update an existing team member | |
| */ | |
| update: async (member: TeamMemberUpdateRequest): Promise<TeamMember> => { | |
| return apiRequest<TeamMember>(`/TeamMember/${member.id}`, 'PUT', member); | |
| }, | |
| /** | |
| * Delete a team member | |
| */ | |
| delete: async (id: number): Promise<void> => { | |
| return apiRequest<void>(`/TeamMember/${id}`, 'DELETE'); | |
| }, | |
| /** | |
| * Update team member status (active/released) | |
| */ | |
| updateStatus: async (id: number, isActive: string): Promise<any> => { | |
| return apiRequest<any>( | |
| `/TeamMember/update-is-active/${id}`, | |
| 'PUT', | |
| isActive, // Pass the string directly as body | |
| true | |
| ); | |
| } | |
| }; | |
| // ProjectTeam API Service | |
| export const projectTeamService = { | |
| getAll: async (): Promise<any[]> => { | |
| try { | |
| const response = await apiRequest<any[]>('/ProjectTeam'); | |
| return response; | |
| } catch (error) { | |
| console.error('Error fetching project teams:', error); | |
| throw error; | |
| } | |
| }, | |
| getById: async (id: number): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>(`/ProjectTeam/${id}`); | |
| return response; | |
| } catch (error) { | |
| console.error(`Error fetching project team ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| create: async (projectTeam: any): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>('/ProjectTeam', 'POST', projectTeam); | |
| return response; | |
| } catch (error) { | |
| console.error('Error creating project team:', error); | |
| throw error; | |
| } | |
| }, | |
| update: async (id: number, projectTeam: any): Promise<any> => { | |
| try { | |
| const response = await apiRequest<any>(`/ProjectTeam/${id}`, 'PUT', projectTeam); | |
| return response; | |
| } catch (error) { | |
| console.error(`Error updating project team ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| delete: async (id: number): Promise<void> => { | |
| try { | |
| await apiRequest<void>(`/ProjectTeam/${id}`, 'DELETE'); | |
| } catch (error) { | |
| console.error(`Error deleting project team ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| getByProjectId: async (projectId: number): Promise<any[]> => { | |
| try { | |
| const allTeams = await projectTeamService.getAll(); | |
| return allTeams.filter(team => team.projectId === projectId); | |
| } catch (error) { | |
| console.error(`Error fetching teams for project ${projectId}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| /** | |
| * API service for meeting rooms | |
| */ | |
| export const meetingRoomService = { | |
| /** | |
| * Get all meeting rooms | |
| * @returns List of meeting rooms | |
| */ | |
| getAllRooms: async (): Promise<MeetingRoom[]> => { | |
| return apiRequest<MeetingRoom[]>('/MeetingRoom', 'GET'); | |
| }, | |
| /** | |
| * Get meeting room by ID | |
| * @param id - Meeting room ID | |
| * @returns Meeting room details | |
| */ | |
| getRoomById: async (id: number): Promise<MeetingRoom> => { | |
| return apiRequest<MeetingRoom>(`/MeetingRoom/${id}`, 'GET'); | |
| }, | |
| /** | |
| * Create new meeting room | |
| * @param room - Meeting room data | |
| * @returns Created meeting room | |
| */ | |
| createRoom: async (room: MeetingRoomCreateRequest): Promise<MeetingRoom> => { | |
| return apiRequest<MeetingRoom>('/MeetingRoom', 'POST', room); | |
| }, | |
| /** | |
| * Update meeting room | |
| * @param room - Meeting room data with updates | |
| * @returns Updated meeting room | |
| */ | |
| updateRoom: async (room: MeetingRoomUpdateRequest): Promise<MeetingRoom> => { | |
| return apiRequest<MeetingRoom>(`/MeetingRoom/${room.id}`, 'PUT', room); | |
| }, | |
| /** | |
| * Delete meeting room | |
| * @param id - Meeting room ID | |
| * @returns Success message | |
| */ | |
| deleteRoom: async (id: number): Promise<void> => { | |
| return apiRequest<void>(`/MeetingRoom/${id}`, 'DELETE'); | |
| }, | |
| /** | |
| * Check room availability for a time period | |
| * @param roomId - Room ID | |
| * @param startTime - Start time (ISO string) | |
| * @param endTime - End time (ISO string) | |
| * @returns Availability status | |
| */ | |
| checkRoomAvailability: async ( | |
| roomId: number, | |
| startTime: string, | |
| endTime: string | |
| ): Promise<{ isAvailable: boolean }> => { | |
| return apiRequest<{ isAvailable: boolean }>( | |
| `/MeetingRoom/${roomId}/availability?startTime=${encodeURIComponent(startTime)}&endTime=${encodeURIComponent(endTime)}`, | |
| 'GET' | |
| ); | |
| } | |
| }; | |
| /** | |
| * API service for room bookings | |
| */ | |
| export const roomBookingService = { | |
| /** | |
| * Get all room bookings | |
| * @returns List of room bookings | |
| */ | |
| getAllBookings: async (): Promise<RoomBooking[]> => { | |
| console.log('Fetching all room bookings'); | |
| try { | |
| const response = await apiRequest<any[]>('/RoomBooking'); | |
| // Parse bookingAttendees from string to array of numbers | |
| return response.map((booking: any) => ({ | |
| ...booking, | |
| attendees: parseAttendees(booking.bookingAttendees) | |
| })); | |
| } catch (error) { | |
| console.error('Error fetching room bookings:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get room bookings by room ID | |
| * @param roomId - Room ID | |
| * @returns List of bookings for the room | |
| */ | |
| getBookingsByRoomId: async (roomId: number): Promise<RoomBooking[]> => { | |
| console.log(`Fetching bookings for room ID: ${roomId}`); | |
| try { | |
| const response = await apiRequest<any[]>('/RoomBooking/room/' + roomId); | |
| return response.map((booking: any) => ({ | |
| ...booking, | |
| attendees: parseAttendees(booking.bookingAttendees) | |
| })); | |
| } catch (error) { | |
| console.error(`Error fetching bookings for room ${roomId}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get room bookings by employee ID | |
| * @param employeeId - Employee ID | |
| * @returns List of bookings made by the employee | |
| */ | |
| getBookingsByEmployeeId: async (employeeId: number): Promise<RoomBooking[]> => { | |
| console.log(`Fetching bookings for employee ID: ${employeeId}`); | |
| try { | |
| const response = await apiRequest<any[]>('/RoomBooking/employee/' + employeeId); | |
| return response.map((booking: any) => ({ | |
| ...booking, | |
| attendees: parseAttendees(booking.bookingAttendees) | |
| })); | |
| } catch (error) { | |
| console.error(`Error fetching bookings for employee ${employeeId}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get room booking by ID | |
| * @param id - Booking ID | |
| * @returns Booking details | |
| */ | |
| getBookingById: async (id: number): Promise<RoomBooking> => { | |
| console.log(`Fetching booking details for ID: ${id}`); | |
| try { | |
| const response = await apiRequest<any>(`/RoomBooking/${id}`); | |
| return { | |
| ...response, | |
| attendees: parseAttendees(response.bookingAttendees) | |
| }; | |
| } catch (error) { | |
| console.error(`Error fetching booking ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Create new room booking | |
| * @param booking - Booking data | |
| * @returns Created booking | |
| */ | |
| createBooking: async (booking: RoomBookingCreateRequest): Promise<RoomBooking> => { | |
| try { | |
| console.log('Creating new room booking with data:', booking); | |
| // Ensure all required fields are present | |
| const bookingData = { | |
| ...booking, | |
| // Ensure roomName is set if missing | |
| roomName: booking.roomName || 'Meeting Room', | |
| // Ensure status is set to pending by default | |
| status: booking.status || 'pending', | |
| // Set timestamps if not provided | |
| // Convert attendees array to string format for API | |
| bookingAttendees: Array.isArray(booking.attendees) ? | |
| `{${booking.attendees.join(',')}}` : | |
| '{}' | |
| }; | |
| const response = await apiRequest<RoomBooking>('/RoomBooking', 'POST', bookingData); | |
| console.log('Room booking created successfully:', response); | |
| // Ensure attendees is properly set in the response | |
| return { | |
| ...response, | |
| attendees: parseAttendees(response.bookingAttendees) | |
| }; | |
| } catch (error) { | |
| console.error('Failed to create room booking:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update room booking | |
| * @param booking - Booking data with updates | |
| * @returns Updated booking | |
| */ | |
| updateBooking: async (booking: RoomBookingUpdateRequest): Promise<RoomBooking> => { | |
| try { | |
| console.log('Updating booking with data:', booking); | |
| // Prepare data for API by converting attendees to string format if needed | |
| const updateData = { | |
| ...booking, | |
| // Convert attendees array to string format for API if present | |
| bookingAttendees: booking.attendees ? | |
| `{${booking.attendees.join(',')}}` : | |
| undefined | |
| }; | |
| const response = await apiRequest<RoomBooking>(`/RoomBooking/${booking.id}`, 'PUT', updateData); | |
| // Ensure attendees is properly set in the response | |
| return { | |
| ...response, | |
| attendees: parseAttendees(response.bookingAttendees) | |
| }; | |
| } catch (error) { | |
| console.error(`Failed to update booking ${booking.id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Delete room booking | |
| * @param id - Booking ID | |
| * @returns Success message | |
| */ | |
| deleteBooking: async (id: number): Promise<void> => { | |
| try { | |
| console.log(`Deleting booking ID: ${id}`); | |
| await apiRequest<void>(`/RoomBooking/${id}`, 'DELETE'); | |
| } catch (error) { | |
| console.error(`Error deleting booking ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Approve booking | |
| * @param id - Booking ID | |
| * @returns Updated booking | |
| */ | |
| approveBooking: async (id: number): Promise<RoomBooking> => { | |
| try { | |
| console.log(`Approving booking ID: ${id}`); | |
| const response = await apiRequest<RoomBooking>(`/RoomBooking/${id}/approve`, 'PUT'); | |
| return { | |
| ...response, | |
| attendees: parseAttendees(response.bookingAttendees) | |
| }; | |
| } catch (error) { | |
| console.error(`Error approving booking ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Reject booking | |
| * @param id - Booking ID | |
| * @param reason - Rejection reason | |
| * @returns Updated booking | |
| */ | |
| rejectBooking: async (id: number, reason: string): Promise<RoomBooking> => { | |
| try { | |
| console.log(`Rejecting booking ID: ${id} with reason: ${reason}`); | |
| const response = await apiRequest<RoomBooking>(`/RoomBooking/${id}/reject`, 'PUT', { reason }); | |
| return { | |
| ...response, | |
| attendees: parseAttendees(response.bookingAttendees) | |
| }; | |
| } catch (error) { | |
| console.error(`Error rejecting booking ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Cancel booking | |
| * @param id - Booking ID | |
| * @returns Updated booking | |
| */ | |
| cancelBooking: async (id: number): Promise<RoomBooking> => { | |
| try { | |
| console.log(`Cancelling booking ID: ${id}`); | |
| const response = await apiRequest<RoomBooking>(`/RoomBooking/${id}/cancel`, 'PUT'); | |
| return { | |
| ...response, | |
| attendees: parseAttendees(response.bookingAttendees) | |
| }; | |
| } catch (error) { | |
| console.error(`Error cancelling booking ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get available rooms for a time period | |
| * @param startTime - Start time (ISO string) | |
| * @param endTime - End time (ISO string) | |
| * @param minCapacity - Minimum capacity required | |
| * @returns List of available rooms | |
| */ | |
| getAvailableRooms: async ( | |
| startTime: string, | |
| endTime: string, | |
| minCapacity?: number | |
| ): Promise<MeetingRoom[]> => { | |
| try { | |
| console.log(`Finding available rooms from ${startTime} to ${endTime} with min capacity: ${minCapacity || 'any'}`); | |
| const queryParams = new URLSearchParams(); | |
| queryParams.append('startTime', startTime); | |
| queryParams.append('endTime', endTime); | |
| if (minCapacity) { | |
| queryParams.append('minCapacity', minCapacity.toString()); | |
| } | |
| return apiRequest<MeetingRoom[]>(`/MeetingRoom/available?${queryParams.toString()}`, 'GET'); | |
| } catch (error) { | |
| console.error('Error finding available rooms:', error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| // Helper function to parse attendees string into array of numbers | |
| function parseAttendees(attendeesString: string | null | undefined): number[] { | |
| if (!attendeesString) return []; | |
| try { | |
| // Handle format like "{101, 102, 103}" | |
| if (typeof attendeesString === 'string') { | |
| // Remove braces and split by comma | |
| const cleanString = attendeesString.replace(/[{}]/g, ''); | |
| return cleanString.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); | |
| } | |
| // If it's already an array, return it | |
| if (Array.isArray(attendeesString)) { | |
| return attendeesString; | |
| } | |
| return []; | |
| } catch (error) { | |
| console.error('Error parsing attendees:', error); | |
| return []; | |
| } | |
| } | |
| /** | |
| * API service for maintenance AMC dates | |
| */ | |
| export const maintenanceAmcDateService = { | |
| /** | |
| * Get all maintenance AMC dates | |
| * @returns List of maintenance AMC dates | |
| */ | |
| getAll: async (): Promise<MaintenanceAmcDate[]> => { | |
| try { | |
| const response = await apiRequest<MaintenanceAmcDate[]>( | |
| '/MaintenanceAmcDates', | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error('Failed to get maintenance AMC dates:', error); | |
| return []; | |
| } | |
| }, | |
| /** | |
| * Get maintenance AMC date by ID | |
| * @param id - Maintenance AMC date ID | |
| * @returns Maintenance AMC date data | |
| */ | |
| getById: async (id: number): Promise<MaintenanceAmcDate> => { | |
| try { | |
| const response = await apiRequest<MaintenanceAmcDate>( | |
| `/MaintenanceAmcDates/${id}`, | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to get maintenance AMC date with ID ${id}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Get maintenance AMC dates by project ID | |
| * @param projectId - Project ID | |
| * @returns List of maintenance AMC dates for the project | |
| */ | |
| getByProjectId: async (projectId: number): Promise<MaintenanceAmcDate[]> => { | |
| try { | |
| const allData = await maintenanceAmcDateService.getAll(); | |
| return allData.filter(item => item.projectId === projectId); | |
| } catch (error) { | |
| console.error(`Failed to get maintenance AMC dates for project ${projectId}:`, error); | |
| return []; | |
| } | |
| }, | |
| /** | |
| * Create a new maintenance AMC date | |
| * @param data - Maintenance AMC date data | |
| * @returns Created maintenance AMC date data | |
| */ | |
| create: async (data: MaintenanceAmcDateCreateRequest): Promise<MaintenanceAmcDate> => { | |
| try { | |
| const response = await apiRequest<MaintenanceAmcDate>( | |
| '/MaintenanceAmcDates', | |
| 'POST', | |
| data, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error('Failed to create maintenance AMC date:', error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Update an existing maintenance AMC date | |
| * @param data - Maintenance AMC date data to update | |
| * @returns Updated maintenance AMC date data | |
| */ | |
| update: async (data: MaintenanceAmcDateUpdateRequest): Promise<MaintenanceAmcDate> => { | |
| try { | |
| const response = await apiRequest<MaintenanceAmcDate>( | |
| `/MaintenanceAmcDates/${data.maintenanceId}`, | |
| 'PUT', | |
| data, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to update maintenance AMC date with ID ${data.maintenanceId}:`, error); | |
| throw error; | |
| } | |
| }, | |
| /** | |
| * Delete a maintenance AMC date | |
| * @param id - Maintenance AMC date ID | |
| * @returns Success message | |
| */ | |
| delete: async (id: number): Promise<void> => { | |
| try { | |
| await apiRequest<void>( | |
| `/MaintenanceAmcDates/${id}`, | |
| 'DELETE', | |
| undefined, | |
| true | |
| ); | |
| } catch (error) { | |
| console.error(`Failed to delete maintenance AMC date with ID ${id}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| // Release Dates service | |
| export const releaseDateService = { | |
| getAll: async (): Promise<ReleaseDate[]> => { | |
| return await apiRequest<ReleaseDate[]>('/ReleaseDates'); | |
| }, | |
| getById: async (id: number): Promise<ReleaseDate> => { | |
| return await apiRequest<ReleaseDate>(`/ReleaseDates/${id}`); | |
| }, | |
| getByProjectId: async (projectId: number): Promise<ReleaseDate[]> => { | |
| return await apiRequest<ReleaseDate[]>(`/ReleaseDates/ByProject/${projectId}`); | |
| }, | |
| create: async (data: ReleaseDateCreateRequest): Promise<ReleaseDate> => { | |
| return await apiRequest<ReleaseDate>('/ReleaseDates', 'POST', data); | |
| }, | |
| update: async (data: ReleaseDateUpdateRequest): Promise<ReleaseDate> => { | |
| return await apiRequest<ReleaseDate>(`/ReleaseDates/${data.releaseId}`, 'PUT', data); | |
| }, | |
| delete: async (id: number): Promise<void> => { | |
| return await apiRequest<void>(`/ReleaseDates/${id}`, 'DELETE'); | |
| } | |
| }; | |
| // Gantt Chart Types | |
| export interface GanttTaskDto { | |
| id: number; | |
| name: string | null; | |
| start: string | null; | |
| end: string | null; | |
| assignee: string | null; | |
| } | |
| export interface GanttFeatureDto { | |
| id: number; | |
| name: string | null; | |
| start: string | null; | |
| end: string | null; | |
| assignee: string | null; | |
| tasks: GanttTaskDto[]; | |
| } | |
| export interface GanttDeliverableDto { | |
| id: number; | |
| name: string | null; | |
| start: string | null; | |
| end: string | null; | |
| features: GanttFeatureDto[]; | |
| } | |
| export interface GanttChartResponseDto { | |
| deliverables: GanttDeliverableDto[]; | |
| } | |
| // Gantt Chart service | |
| export const ganttChartService = { | |
| /** | |
| * Get Gantt chart data by project ID | |
| * @param projectId - Project ID | |
| * @returns Gantt chart data with deliverables, features, and tasks | |
| */ | |
| getByProjectId: async (projectId: number): Promise<GanttChartResponseDto> => { | |
| try { | |
| const response = await apiRequest<GanttChartResponseDto>( | |
| `/GanttChart/project/${projectId}`, | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to get Gantt chart data for project ${projectId}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |
| // Project Hierarchy Types | |
| export interface PMPTask { | |
| taskId: string; | |
| taskName: string; | |
| description: string; | |
| phase: string | null; | |
| owner: string | null; | |
| dependency: string | null; | |
| status: string; | |
| startDate: string; | |
| endDate: string; | |
| estimatedHours: number; | |
| } | |
| export interface PMPFeature { | |
| featureId: number; | |
| featureName: string; | |
| startDate: string | null; | |
| endDate: string | null; | |
| tasks: PMPTask[]; | |
| } | |
| export interface PMPDeliverable { | |
| deliverableId: number; | |
| deliverableName: string; | |
| startDate: string | null; | |
| endDate: string | null; | |
| features: PMPFeature[]; | |
| } | |
| export interface PMPProject { | |
| projectId: number; | |
| projectCode: string; | |
| projectName: string; | |
| startDate: string; | |
| endDate: string; | |
| status: string | null; | |
| deliverables: PMPDeliverable[]; | |
| } | |
| export interface ProjectHierarchyResponse { | |
| project: PMPProject; | |
| } | |
| // Project Hierarchy Service | |
| export const projectHierarchyService = { | |
| /** | |
| * Get project hierarchy by project ID | |
| * @param projectId - Project ID | |
| * @returns Project hierarchy with deliverables, features, and tasks | |
| */ | |
| getByProjectId: async (projectId: number): Promise<ProjectHierarchyResponse> => { | |
| try { | |
| const response = await apiRequest<ProjectHierarchyResponse>( | |
| `/Project/GetProjectHierarchyByProjectId/${projectId}`, | |
| 'GET', | |
| undefined, | |
| true | |
| ); | |
| return response; | |
| } catch (error) { | |
| console.error(`Failed to get project hierarchy for project ${projectId}:`, error); | |
| throw error; | |
| } | |
| } | |
| }; | |