620 lines
19 KiB
TypeScript
620 lines
19 KiB
TypeScript
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000/api';
|
|
|
|
export interface LoginRequest {
|
|
email: string;
|
|
password: string;
|
|
twoFactorCode?: string;
|
|
}
|
|
|
|
export interface RegisterRequest {
|
|
email: string;
|
|
password: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
phone?: string;
|
|
company?: string;
|
|
// Vendor-specific fields
|
|
companyType?: 'corporation' | 'llc' | 'partnership' | 'sole_proprietorship' | 'other';
|
|
registrationNumber?: string;
|
|
gstNumber?: string;
|
|
panNumber?: string;
|
|
address?: string;
|
|
website?: string;
|
|
businessLicense?: string;
|
|
taxId?: string;
|
|
industry?: string;
|
|
yearsInBusiness?: string | number;
|
|
annualRevenue?: string | number;
|
|
employeeCount?: string | number;
|
|
role?: 'channel_partner_admin' | 'channel_partner_manager' | 'channel_partner_sales' | 'channel_partner_support' | 'channel_partner_finance' | 'channel_partner_analyst' | 'reseller_admin' | 'reseller_manager' | 'reseller_sales' | 'reseller_support' | 'reseller_finance' | 'reseller_analyst' | 'system_admin' | 'system_support' | 'system_analyst' | 'read_only';
|
|
userType?: 'channel_partner' | 'reseller' | 'system';
|
|
}
|
|
|
|
export interface AuthResponse {
|
|
success: boolean;
|
|
message: string;
|
|
data?: {
|
|
user: {
|
|
id: number;
|
|
email: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
phone?: string;
|
|
company?: string;
|
|
role: string;
|
|
status: string;
|
|
emailVerified: boolean;
|
|
twoFactorEnabled: boolean;
|
|
lastLogin?: string;
|
|
roles: Array<{
|
|
id: number;
|
|
name: string;
|
|
description: string;
|
|
permissions: string[];
|
|
}>;
|
|
};
|
|
accessToken: string;
|
|
refreshToken: string;
|
|
sessionId: string;
|
|
};
|
|
}
|
|
|
|
export interface User {
|
|
id: number;
|
|
email: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
phone?: string;
|
|
company?: string;
|
|
role: string;
|
|
status: string;
|
|
emailVerified: boolean;
|
|
twoFactorEnabled: boolean;
|
|
lastLogin?: string;
|
|
roles: Array<{
|
|
id: number;
|
|
name: string;
|
|
description: string;
|
|
permissions: string[];
|
|
}>;
|
|
}
|
|
|
|
export interface Product {
|
|
id: number;
|
|
name: string;
|
|
description?: string;
|
|
category: 'cloud_storage' | 'cloud_computing' | 'cybersecurity' | 'data_analytics' | 'ai_ml' | 'iot' | 'blockchain' | 'other';
|
|
subcategory?: string;
|
|
price: number;
|
|
currency: string;
|
|
commissionRate: number;
|
|
features?: string[];
|
|
specifications?: Record<string, any>;
|
|
images?: string[];
|
|
documents?: string[];
|
|
status: 'draft' | 'active' | 'inactive' | 'discontinued';
|
|
availability: 'available' | 'out_of_stock' | 'coming_soon' | 'discontinued';
|
|
stockQuantity: number;
|
|
sku: string;
|
|
tags?: string[];
|
|
metadata?: Record<string, any>;
|
|
createdBy: number;
|
|
updatedBy?: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
creator?: {
|
|
id: number;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
company?: string;
|
|
};
|
|
updater?: {
|
|
id: number;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
};
|
|
vendor?: {
|
|
id: number;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
company?: string;
|
|
};
|
|
isAdminCreated?: boolean;
|
|
source?: 'admin' | 'vendor';
|
|
purchaseUrl?: string;
|
|
}
|
|
|
|
export interface TrainingCategory {
|
|
id: number;
|
|
name: string;
|
|
description?: string;
|
|
icon?: string;
|
|
color?: string;
|
|
sortOrder: number;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface TrainingVideo {
|
|
id: number;
|
|
moduleId: number;
|
|
title: string;
|
|
description?: string;
|
|
youtubeUrl?: string;
|
|
duration?: string;
|
|
thumbnail?: string;
|
|
sortOrder: number;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface TrainingMaterial {
|
|
id: number;
|
|
moduleId: number;
|
|
title: string;
|
|
description?: string;
|
|
type: 'PDF' | 'PPT' | 'DOC' | 'VIDEO';
|
|
downloadUrl?: string;
|
|
fileSize?: string;
|
|
sortOrder: number;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface TrainingModule {
|
|
id: number;
|
|
title: string;
|
|
description?: string;
|
|
duration?: string;
|
|
level: 'Beginner' | 'Intermediate' | 'Advanced';
|
|
categoryId?: number;
|
|
thumbnailUrl?: string;
|
|
isActive: boolean;
|
|
sortOrder: number;
|
|
createdBy?: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
category?: TrainingCategory;
|
|
videos?: TrainingVideo[];
|
|
materials?: TrainingMaterial[];
|
|
userProgress?: {
|
|
status: 'not_started' | 'in_progress' | 'completed';
|
|
progressPercentage: number;
|
|
timeSpent: number;
|
|
completedAt?: string;
|
|
};
|
|
}
|
|
|
|
export interface TrainingProgress {
|
|
moduleId: number;
|
|
videoId?: number;
|
|
materialId?: number;
|
|
status: 'not_started' | 'in_progress' | 'completed';
|
|
progressPercentage: number;
|
|
timeSpent: number;
|
|
}
|
|
|
|
class ApiService {
|
|
private baseURL: string;
|
|
|
|
constructor() {
|
|
this.baseURL = API_BASE_URL;
|
|
}
|
|
|
|
private async request<T>(
|
|
endpoint: string,
|
|
options: RequestInit = {}
|
|
): Promise<T> {
|
|
const url = `${this.baseURL}${endpoint}`;
|
|
|
|
const config: RequestInit = {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
...options,
|
|
};
|
|
|
|
// Add auth token if available
|
|
const token = localStorage.getItem('accessToken');
|
|
if (token) {
|
|
config.headers = {
|
|
...config.headers,
|
|
Authorization: `Bearer ${token}`,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(url, config);
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
// Handle account status errors
|
|
if (response.status === 403 && data.errorCode) {
|
|
const error = new Error(data.message || 'Account is not active');
|
|
(error as any).errorCode = data.errorCode;
|
|
(error as any).status = data.status;
|
|
throw error;
|
|
}
|
|
|
|
throw new Error(data.message || 'API request failed');
|
|
}
|
|
|
|
return data;
|
|
} catch (error) {
|
|
console.error('API request failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Authentication endpoints
|
|
async login(credentials: LoginRequest): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/login', {
|
|
method: 'POST',
|
|
body: JSON.stringify(credentials),
|
|
});
|
|
}
|
|
|
|
async register(userData: RegisterRequest): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/register', {
|
|
method: 'POST',
|
|
body: JSON.stringify(userData),
|
|
});
|
|
}
|
|
|
|
async verifyEmail(email: string, otp: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/verify-email', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ email, otp }),
|
|
});
|
|
}
|
|
|
|
async resendVerificationEmail(email: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/resend-verification', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ email }),
|
|
});
|
|
}
|
|
|
|
async refreshToken(refreshToken: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/refresh-token', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ refreshToken }),
|
|
});
|
|
}
|
|
|
|
async getCurrentUser(): Promise<{ success: boolean; data: User }> {
|
|
return this.request<{ success: boolean; data: User }>('/auth/me');
|
|
}
|
|
|
|
async logout(sessionId: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/logout', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ sessionId }),
|
|
});
|
|
}
|
|
|
|
async forgotPassword(email: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/forgot-password', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ email }),
|
|
});
|
|
}
|
|
|
|
async resetPassword(token: string, password: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/reset-password', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ token, password }),
|
|
});
|
|
}
|
|
|
|
// Two-factor authentication
|
|
async setupTwoFactor(): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/setup-2fa', {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
async enableTwoFactor(code: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/enable-2fa', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ code }),
|
|
});
|
|
}
|
|
|
|
async disableTwoFactor(code: string): Promise<AuthResponse> {
|
|
return this.request<AuthResponse>('/auth/disable-2fa', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ code }),
|
|
});
|
|
}
|
|
|
|
// Profile management
|
|
async updateProfile(profileData: Partial<User>): Promise<{ success: boolean; data: User }> {
|
|
return this.request<{ success: boolean; data: User }>('/auth/profile', {
|
|
method: 'PUT',
|
|
body: JSON.stringify(profileData),
|
|
});
|
|
}
|
|
|
|
async changePassword(currentPassword: string, newPassword: string): Promise<{ success: boolean; message: string }> {
|
|
return this.request<{ success: boolean; message: string }>('/auth/change-password', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ currentPassword, newPassword }),
|
|
});
|
|
}
|
|
|
|
// Vendor operations
|
|
async getAvailableVendorCompanies(): Promise<{ success: boolean; data: Array<{ id: number; company: string; firstName: string; lastName: string; email: string }> }> {
|
|
return this.request<{ success: boolean; data: Array<{ id: number; company: string; firstName: string; lastName: string; email: string }> }>('/public/vendors/available-companies');
|
|
}
|
|
|
|
// Reseller operations
|
|
async getResellerUserTypes(): Promise<{ success: boolean; data: Array<{ value: string; label: string; description: string; permissions: string[] }> }> {
|
|
return this.request<{ success: boolean; data: Array<{ value: string; label: string; description: string; permissions: string[] }> }>('/public/reseller/user-types');
|
|
}
|
|
|
|
async getPendingResellerRequests(): Promise<{ success: boolean; data: User[] }> {
|
|
return this.request<{ success: boolean; data: User[] }>('/vendors/pending-resellers');
|
|
}
|
|
|
|
async getVendorResellers(): Promise<{ success: boolean; data: User[] }> {
|
|
return this.request<{ success: boolean; data: User[] }>('/vendors/resellers');
|
|
}
|
|
|
|
async createReseller(resellerData: {
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
phone: string;
|
|
company: string;
|
|
userType: 'reseller_admin' | 'reseller_sales' | 'reseller_support' | 'read_only';
|
|
region: string;
|
|
businessType: string;
|
|
address?: string;
|
|
}): Promise<{ success: boolean; message: string; data: any }> {
|
|
return this.request<{ success: boolean; message: string; data: any }>('/vendors/resellers', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(resellerData),
|
|
});
|
|
}
|
|
|
|
async getVendorDashboardStats(): Promise<{ success: boolean; data: any }> {
|
|
return this.request<{ success: boolean; data: any }>('/vendors/dashboard/stats');
|
|
}
|
|
|
|
async getVendorProducts(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
category?: string;
|
|
status?: string;
|
|
search?: string;
|
|
}): Promise<{ success: boolean; data: { products: Product[]; pagination: any } }> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
}
|
|
return this.request<{ success: boolean; data: { products: Product[]; pagination: any } }>(`/vendors/products?${queryParams}`);
|
|
}
|
|
|
|
async getVendorCommissions(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
status?: string;
|
|
dateRange?: string;
|
|
}): Promise<{ success: boolean; data: { commissions: any[]; pagination: any } }> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
}
|
|
return this.request<{ success: boolean; data: { commissions: any[]; pagination: any } }>(`/vendors/commissions?${queryParams}`);
|
|
}
|
|
|
|
async approveResellerRequest(userId: number): Promise<{ success: boolean; message: string }> {
|
|
return this.request<{ success: boolean; message: string }>(`/vendors/resellers/${userId}/approve`, {
|
|
method: 'POST'
|
|
});
|
|
}
|
|
|
|
async rejectResellerRequest(userId: number, reason: string): Promise<{ success: boolean; message: string }> {
|
|
return this.request<{ success: boolean; message: string }>(`/vendors/resellers/${userId}/reject`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ reason })
|
|
});
|
|
}
|
|
|
|
// Product management
|
|
async getAllProducts(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
category?: string;
|
|
status?: string;
|
|
search?: string;
|
|
vendor?: string;
|
|
sortBy?: string;
|
|
sortOrder?: string;
|
|
}): Promise<{ success: boolean; data: { products: Product[]; pagination: any } }> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
}
|
|
return this.request<{ success: boolean; data: { products: Product[]; pagination: any } }>(`/products?${queryParams}`);
|
|
}
|
|
|
|
async getProductById(id: number): Promise<{ success: boolean; data: Product }> {
|
|
return this.request<{ success: boolean; data: Product }>(`/products/${id}`);
|
|
}
|
|
|
|
async createProduct(productData: Partial<Product>): Promise<{ success: boolean; data: Product }> {
|
|
return this.request<{ success: boolean; data: Product }>('/products', {
|
|
method: 'POST',
|
|
body: JSON.stringify(productData),
|
|
});
|
|
}
|
|
|
|
async updateProduct(id: number, productData: Partial<Product>): Promise<{ success: boolean; data: Product }> {
|
|
return this.request<{ success: boolean; data: Product }>(`/products/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(productData),
|
|
});
|
|
}
|
|
|
|
async deleteProduct(id: number): Promise<{ success: boolean; message: string }> {
|
|
return this.request<{ success: boolean; message: string }>(`/products/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
async getProductCategories(): Promise<{ success: boolean; data: string[] }> {
|
|
return this.request<{ success: boolean; data: string[] }>('/products/categories');
|
|
}
|
|
|
|
async getProductStats(): Promise<{ success: boolean; data: any }> {
|
|
return this.request<{ success: boolean; data: any }>('/products/stats');
|
|
}
|
|
|
|
async getActiveVendors(): Promise<{ success: boolean; data: Array<{ id: number; firstName: string; lastName: string; company?: string }> }> {
|
|
return this.request<{ success: boolean; data: Array<{ id: number; firstName: string; lastName: string; company?: string }> }>('/products/vendors');
|
|
}
|
|
|
|
async getVendorById(id: number): Promise<{ success: boolean; data: { id: number; firstName: string; lastName: string; email: string; company?: string } }> {
|
|
return this.request<{ success: boolean; data: { id: number; firstName: string; lastName: string; email: string; company?: string } }>(`/vendors/${id}`);
|
|
}
|
|
|
|
// Receipt management
|
|
async uploadReceipt(data: FormData): Promise<{ success: boolean; data: any; message: string }> {
|
|
return this.request<{ success: boolean; data: any; message: string }>('/receipts/upload', {
|
|
method: 'POST',
|
|
body: data,
|
|
});
|
|
}
|
|
|
|
async getResellerReceipts(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
status?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
}): Promise<{ success: boolean; data: any[]; pagination?: any }> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
}
|
|
return this.request<{ success: boolean; data: any[]; pagination?: any }>(`/receipts/reseller?${queryParams}`);
|
|
}
|
|
|
|
async getVendorReceipts(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
status?: string;
|
|
resellerId?: number;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
}): Promise<{ success: boolean; data: any[]; pagination?: any }> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
}
|
|
return this.request<{ success: boolean; data: any[]; pagination?: any }>(`/receipts/vendor?${queryParams}`);
|
|
}
|
|
|
|
async getReceiptById(id: number): Promise<{ success: boolean; data: any }> {
|
|
return this.request<{ success: boolean; data: any }>(`/receipts/reseller/${id}`);
|
|
}
|
|
|
|
async getVendorReceiptById(id: number): Promise<{ success: boolean; data: any }> {
|
|
return this.request<{ success: boolean; data: any }>(`/receipts/vendor/${id}`);
|
|
}
|
|
|
|
async updateReceiptStatus(id: number, statusUpdate: { status: string; rejectionReason?: string }): Promise<{ success: boolean; data: any; message: string }> {
|
|
return this.request<{ success: boolean; data: any; message: string }>(`/receipts/vendor/${id}/status`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(statusUpdate),
|
|
});
|
|
}
|
|
|
|
async downloadReceipt(id: number): Promise<Blob> {
|
|
const response = await fetch(`${this.baseURL}/receipts/${id}/download`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('accessToken')}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.blob();
|
|
}
|
|
|
|
async deleteReceipt(id: number): Promise<{ success: boolean; message: string }> {
|
|
return this.request<{ success: boolean; message: string }>(`/receipts/reseller/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Get vendor products with current stock quantities
|
|
async getVendorProductsWithStock(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
category?: string;
|
|
status?: string;
|
|
search?: string;
|
|
}): Promise<{ success: boolean; data: { products: any[]; pagination: any } }> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
}
|
|
return this.request<{ success: boolean; data: { products: any[]; pagination: any } }>(`/receipts/vendor/products/stock?${queryParams}`);
|
|
}
|
|
|
|
// Get products shared by vendor for reseller with current stock
|
|
async getResellerVendorProducts(params: {
|
|
vendorId: number;
|
|
page?: number;
|
|
limit?: number;
|
|
category?: string;
|
|
status?: string;
|
|
search?: string;
|
|
}): Promise<{ success: boolean; data: { products: any[]; pagination: any } }> {
|
|
const queryParams = new URLSearchParams();
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined) queryParams.append(key, value.toString());
|
|
});
|
|
return this.request<{ success: boolean; data: { products: any[]; pagination: any } }>(`/receipts/reseller/vendor/products?${queryParams}`);
|
|
}
|
|
|
|
// Manually update product stock quantity
|
|
async updateProductStock(productId: number, stockQuantity: number): Promise<{ success: boolean; data: any; message: string }> {
|
|
return this.request<{ success: boolean; data: any; message: string }>(`/receipts/vendor/products/${productId}/stock`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ stockQuantity }),
|
|
});
|
|
}
|
|
}
|
|
|
|
export const apiService = new ApiService();
|
|
export default apiService;
|