361 lines
11 KiB
TypeScript
361 lines
11 KiB
TypeScript
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api';
|
|
|
|
class ApiService {
|
|
private baseURL: string;
|
|
|
|
constructor(baseURL: string) {
|
|
this.baseURL = baseURL;
|
|
}
|
|
|
|
private getToken(): string | null {
|
|
return localStorage.getItem('token');
|
|
}
|
|
|
|
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
const token = this.getToken();
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
};
|
|
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
|
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
...options,
|
|
headers,
|
|
});
|
|
|
|
if (response.status === 401) {
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('user');
|
|
window.location.href = '/';
|
|
throw new Error('Unauthorized');
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ error: 'Request failed' }));
|
|
throw new Error(error.error || 'Request failed');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Auth
|
|
async login(username: string, password: string) {
|
|
return this.request<{ token: string; user: any }>('/auth/login', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
}
|
|
|
|
async getMe() {
|
|
return this.request<any>('/auth/me');
|
|
}
|
|
|
|
async changePassword(currentPassword: string, newPassword: string) {
|
|
return this.request<{ message: string }>('/auth/change-password', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ currentPassword, newPassword }),
|
|
});
|
|
}
|
|
|
|
// Users
|
|
async getUsers(params?: { role?: string; departmentId?: number }) {
|
|
const query = new URLSearchParams(params as any).toString();
|
|
return this.request<any[]>(`/users${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getUser(id: number) {
|
|
return this.request<any>(`/users/${id}`);
|
|
}
|
|
|
|
async createUser(data: any) {
|
|
return this.request<any>('/users', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateUser(id: number, data: any) {
|
|
return this.request<any>(`/users/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteUser(id: number) {
|
|
return this.request<{ message: string }>(`/users/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Departments
|
|
async getDepartments() {
|
|
return this.request<any[]>('/departments');
|
|
}
|
|
|
|
async getDepartment(id: number) {
|
|
return this.request<any>(`/departments/${id}`);
|
|
}
|
|
|
|
async getSubDepartments(departmentId: number) {
|
|
return this.request<any[]>(`/departments/${departmentId}/sub-departments`);
|
|
}
|
|
|
|
async createDepartment(name: string) {
|
|
return this.request<any>('/departments', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ name }),
|
|
});
|
|
}
|
|
|
|
// Work Allocations
|
|
async getWorkAllocations(params?: { employeeId?: number; status?: string; departmentId?: number }) {
|
|
const query = new URLSearchParams(params as any).toString();
|
|
return this.request<any[]>(`/work-allocations${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getWorkAllocation(id: number) {
|
|
return this.request<any>(`/work-allocations/${id}`);
|
|
}
|
|
|
|
async createWorkAllocation(data: any) {
|
|
return this.request<any>('/work-allocations', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateWorkAllocationStatus(id: number, status: string, completionDate?: string) {
|
|
return this.request<any>(`/work-allocations/${id}/status`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ status, completionDate }),
|
|
});
|
|
}
|
|
|
|
async deleteWorkAllocation(id: number) {
|
|
return this.request<{ message: string }>(`/work-allocations/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Attendance
|
|
async getAttendance(params?: { employeeId?: number; startDate?: string; endDate?: string; status?: string }) {
|
|
const query = new URLSearchParams(params as any).toString();
|
|
return this.request<any[]>(`/attendance${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async checkIn(employeeId: number, workDate: string) {
|
|
return this.request<any>('/attendance/check-in', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ employeeId, workDate }),
|
|
});
|
|
}
|
|
|
|
async checkOut(employeeId: number, workDate: string) {
|
|
return this.request<any>('/attendance/check-out', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ employeeId, workDate }),
|
|
});
|
|
}
|
|
|
|
async getAttendanceSummary(params?: { startDate?: string; endDate?: string; departmentId?: number }) {
|
|
const query = new URLSearchParams(params as any).toString();
|
|
return this.request<any[]>(`/attendance/summary/stats${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async updateAttendanceStatus(id: number, status: string, remark?: string) {
|
|
return this.request<any>(`/attendance/${id}/status`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ status, remark }),
|
|
});
|
|
}
|
|
|
|
async markAbsent(employeeId: number, workDate: string, remark?: string) {
|
|
return this.request<any>('/attendance/mark-absent', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ employeeId, workDate, remark }),
|
|
});
|
|
}
|
|
|
|
// Employee Swaps
|
|
async getEmployeeSwaps(params?: { status?: string; employeeId?: number; startDate?: string; endDate?: string }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<any[]>(`/employee-swaps${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getEmployeeSwap(id: number) {
|
|
return this.request<any>(`/employee-swaps/${id}`);
|
|
}
|
|
|
|
async createEmployeeSwap(data: {
|
|
employeeId: number;
|
|
targetDepartmentId: number;
|
|
targetContractorId?: number;
|
|
swapReason: string;
|
|
reasonDetails?: string;
|
|
workCompletionPercentage?: number;
|
|
swapDate: string;
|
|
}) {
|
|
return this.request<any>('/employee-swaps', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async completeEmployeeSwap(id: number) {
|
|
return this.request<any>(`/employee-swaps/${id}/complete`, {
|
|
method: 'PUT',
|
|
});
|
|
}
|
|
|
|
async cancelEmployeeSwap(id: number) {
|
|
return this.request<any>(`/employee-swaps/${id}/cancel`, {
|
|
method: 'PUT',
|
|
});
|
|
}
|
|
|
|
// Contractor Rates
|
|
async getContractorRates(params?: { contractorId?: number; subDepartmentId?: number }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<any[]>(`/contractor-rates${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getCurrentRate(contractorId: number, subDepartmentId?: number) {
|
|
const query = subDepartmentId ? `?subDepartmentId=${subDepartmentId}` : '';
|
|
return this.request<any>(`/contractor-rates/contractor/${contractorId}/current${query}`);
|
|
}
|
|
|
|
async setContractorRate(data: {
|
|
contractorId: number;
|
|
subDepartmentId?: number;
|
|
activity?: string;
|
|
rate: number;
|
|
effectiveDate: string
|
|
}) {
|
|
return this.request<any>('/contractor-rates', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateContractorRate(id: number, data: { rate?: number; activity?: string; effectiveDate?: string }) {
|
|
return this.request<any>(`/contractor-rates/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteContractorRate(id: number) {
|
|
return this.request<{ message: string }>(`/contractor-rates/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Reports
|
|
async getCompletedAllocationsReport(params?: {
|
|
startDate?: string;
|
|
endDate?: string;
|
|
departmentId?: number;
|
|
contractorId?: number;
|
|
employeeId?: number;
|
|
}) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<{
|
|
allocations: any[];
|
|
summary: { totalAllocations: number; totalAmount: string; totalUnits: string }
|
|
}>(`/reports/completed-allocations${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getReportSummary(params?: { startDate?: string; endDate?: string }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<{
|
|
byContractor: any[];
|
|
bySubDepartment: any[];
|
|
byActivity: any[];
|
|
}>(`/reports/summary${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
// Standard Rates
|
|
async getStandardRates(params?: { departmentId?: number; subDepartmentId?: number; activity?: string }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<any[]>(`/standard-rates${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getAllRates(params?: { departmentId?: number; startDate?: string; endDate?: string }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<{
|
|
allRates: any[];
|
|
summary: { totalContractorRates: number; totalStandardRates: number; totalRates: number };
|
|
}>(`/standard-rates/all-rates${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async compareRates(params?: { contractorId?: number; subDepartmentId?: number }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<{
|
|
standardRates: any[];
|
|
contractorRates: any[];
|
|
comparisons: any[];
|
|
}>(`/standard-rates/compare${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async createStandardRate(data: {
|
|
subDepartmentId?: number;
|
|
activity?: string;
|
|
rate: number;
|
|
effectiveDate: string
|
|
}) {
|
|
return this.request<any>('/standard-rates', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateStandardRate(id: number, data: { rate?: number; activity?: string; effectiveDate?: string }) {
|
|
return this.request<any>(`/standard-rates/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteStandardRate(id: number) {
|
|
return this.request<{ message: string }>(`/standard-rates/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Activities
|
|
async getActivities(params?: { departmentId?: number; subDepartmentId?: number }) {
|
|
const query = params ? new URLSearchParams(params as any).toString() : '';
|
|
return this.request<any[]>(`/activities${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
async getActivity(id: number) {
|
|
return this.request<any>(`/activities/${id}`);
|
|
}
|
|
|
|
async createActivity(data: { sub_department_id: number; name: string; unit_of_measurement?: string }) {
|
|
return this.request<any>('/activities', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateActivity(id: number, data: { name?: string; unit_of_measurement?: string }) {
|
|
return this.request<any>(`/activities/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteActivity(id: number) {
|
|
return this.request<{ message: string }>(`/activities/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
}
|
|
|
|
export const api = new ApiService(API_BASE_URL);
|