import toast from "react-hot-toast"; interface ApiResponse { message: string; data: T; state: number; time: number; errorCode: number; resultType: number; } class ApiService { apiUrl = import.meta.env.VITE_API_URL; private baseURL = this.apiUrl; private token: string | null = null; constructor() { // Initialize token from localStorage this.initializeToken(); } private initializeToken() { try { const savedToken = localStorage.getItem("auth_token"); if (savedToken) { const tokenData = JSON.parse(savedToken); this.token = tokenData.accessToken; } } catch (error) { console.error("Error initializing token:", error); } } public setToken(token: string) { this.token = token; } public clearToken() { this.token = null; } private async request( endpoint: string, options: RequestInit = {}, ): Promise> { const url = `${this.baseURL}${endpoint}`; const defaultHeaders: HeadersInit = { "Content-Type": "application/json", }; // Add authorization header if token exists if (this.token) { defaultHeaders.Authorization = `Bearer ${this.token}`; } const config: RequestInit = { ...options, headers: { ...defaultHeaders, ...options.headers, }, }; try { const response = await fetch(url, config); const data: ApiResponse = await response.json(); // Handle different response states if (!response.ok) { throw new Error( data.message || `HTTP error! status: ${response.status}`, ); } if (data.state !== 0) { throw new Error(data.message || "API error occurred"); } return data; } catch (error) { console.error("API request failed:", error); // Handle network errors (propagate up; UI decides how to toast) if (error instanceof TypeError && error.message.includes("fetch")) { const err = Object.assign(new Error("شبکه در دسترس نیست"), { code: "NETWORK_ERROR", }); throw err; } // Handle authentication errors if (error instanceof Error && error.message.includes("401")) { this.clearToken(); localStorage.removeItem("auth_token"); localStorage.removeItem("auth_user"); try { sessionStorage.setItem("sessionExpired", "1"); } catch {} window.location.href = "/login"; throw error; } throw error; } } // POST to external absolute endpoint while reusing token handling public async postAbsolute( fullUrl: string, data?: any, ): Promise> { const defaultHeaders: HeadersInit = { "Content-Type": "application/json;charset=UTF-8", }; if (this.token) { defaultHeaders.Authorization = `Bearer ${this.token}`; } const config: RequestInit = { method: "POST", headers: defaultHeaders, body: data ? JSON.stringify(data) : undefined, }; try { const response = await fetch(fullUrl, config); const apiData: ApiResponse = await response.json(); if (!response.ok) { throw new Error(apiData?.message || `HTTP error! status: ${response.status}`); } if (typeof apiData?.state !== "undefined" && apiData.state !== 0) { throw new Error(apiData.message || "API error occurred"); } return apiData; } catch (error) { console.error("API absolute POST failed:", error); throw error as Error; } } // Innovation process function call wrapper public async call(payload: any) { const url = "https://inogen-back.pelekan.org/api/call"; return this.postAbsolute(url, payload); } // GET request public async get(endpoint: string): Promise> { return this.request(endpoint, { method: "GET", }); } // POST request public async post( endpoint: string, data?: any, ): Promise> { return this.request(endpoint, { method: "POST", body: data ? JSON.stringify(data) : undefined, }); } // PUT request public async put( endpoint: string, data?: any, ): Promise> { return this.request(endpoint, { method: "PUT", body: data ? JSON.stringify(data) : undefined, }); } // DELETE request public async delete(endpoint: string): Promise> { return this.request(endpoint, { method: "DELETE", }); } // PATCH request public async patch( endpoint: string, data?: any, ): Promise> { return this.request(endpoint, { method: "PATCH", body: data ? JSON.stringify(data) : undefined, }); } // Authentication methods public async login(username: string, password: string) { try { const response = await this.post("/login", { username, password, }); if (response.state === 0) { const parsedData = JSON.parse(response.data); this.setToken(parsedData.Token.AccessToken); return { success: true, data: parsedData, message: response.message, }; } return { success: false, message: response.message || "ورود ناموفق", }; } catch (error) { return { success: false, message: error instanceof Error ? error.message : "خطای غیرمنتظره", }; } } public async logout() { try { // Call logout endpoint if it exists await this.post("/logout"); } catch (error) { console.error("Logout API call failed:", error); } finally { // Clear token regardless of API call success this.clearToken(); localStorage.removeItem("auth_token"); localStorage.removeItem("auth_user"); } } // Profile methods public async getProfile() { return this.get("/profile"); } public async updateProfile(data: any) { return this.put("/profile", data); } // Select method for dynamic data queries public async select( data: | { ProcessName: string; OutputFields: string[]; Pagination: { PageNumber: number; PageSize: number }; Sorts: [string, string][]; Conditions: any[]; } | any, ) { return this.post("/select", data); } // Projects methods public async getProjects() { return this.get("/projects"); } // Dashboard methods public async getDashboardStats() { return this.get("/dashboard/stats"); } public async getDashboardRecentActivity() { return this.get("/dashboard/recent-activity"); } // File upload method public async uploadFile(file: File, endpoint: string = "/upload") { const formData = new FormData(); formData.append("file", file); const config: RequestInit = { method: "POST", headers: this.token ? { Authorization: `Bearer ${this.token}` } : {}, body: formData, }; try { const response = await fetch(`${this.baseURL}${endpoint}`, config); const data = await response.json(); if (!response.ok) { throw new Error( data.message || `HTTP error! status: ${response.status}`, ); } return data; } catch (error) { console.error("File upload failed:", error); throw error; } } } // Create and export a singleton instance const apiService = new ApiService(); export default apiService; // Export types for use in components export type { ApiResponse };