Files
turftracker/frontend/src/services/api.js
2025-09-03 14:39:20 -04:00

270 lines
12 KiB
JavaScript

import axios from 'axios';
import toast from 'react-hot-toast';
// Base API configuration
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000/api';
// Create axios instance
export const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Rate limiting disabled - unlimited requests
// let lastRequestTime = 0;
// const MIN_REQUEST_INTERVAL = 0; // No delay between requests
// Rate limiting interceptor disabled for unlimited requests
// apiClient.interceptors.request.use(async (config) => {
// return config;
// });
// Request interceptor to add auth token
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('authToken');
if (process.env.NODE_ENV !== 'production') {
// Minimal visibility during development only
if (token) console.log('Auth token present');
}
if (token && token !== 'undefined' && token !== 'null') {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Response interceptor for error handling
apiClient.interceptors.response.use(
(response) => response,
(error) => {
// Handle specific error codes
if (error.response?.status === 401 || error.response?.status === 403) {
// Unauthorized or malformed token - clear token and redirect to login
console.log('Clearing invalid token due to auth error:', error.response?.status);
localStorage.removeItem('authToken');
// Use React Router navigation instead of hard redirect
if (window.location.pathname !== '/login' && window.location.pathname !== '/register') {
window.location.href = '/login';
}
} else if (error.response?.status >= 500) {
// Server error
toast.error('Server error. Please try again later.');
} else if (error.code === 'ECONNABORTED') {
// Timeout
toast.error('Request timeout. Please check your connection.');
} else if (!error.response) {
// Network error
toast.error('Network error. Please check your connection.');
}
return Promise.reject(error);
}
);
// Auth API endpoints
export const authAPI = {
login: (credentials) => apiClient.post('/auth/login', credentials),
register: (userData) => apiClient.post('/auth/register', userData),
getCurrentUser: () => apiClient.get('/auth/me'),
changePassword: (passwordData) => apiClient.post('/auth/change-password', passwordData),
forgotPassword: (email) => apiClient.post('/auth/forgot-password', { email }),
};
// Users API endpoints
export const usersAPI = {
getProfile: () => apiClient.get('/users/profile'),
updateProfile: (userData) => apiClient.put('/users/profile', userData),
deleteAccount: () => apiClient.delete('/users/account'),
getStats: () => apiClient.get('/users/stats'),
};
// Properties API endpoints
export const propertiesAPI = {
getAll: () => apiClient.get('/properties'),
getById: (id) => apiClient.get(`/properties/${id}`),
create: (propertyData) => apiClient.post('/properties', propertyData),
update: (id, propertyData) => apiClient.put(`/properties/${id}`, propertyData),
delete: (id) => apiClient.delete(`/properties/${id}`),
// Lawn sections
createSection: (propertyId, sectionData) =>
apiClient.post(`/properties/${propertyId}/sections`, sectionData),
updateSection: (propertyId, sectionId, sectionData) =>
apiClient.put(`/properties/${propertyId}/sections/${sectionId}`, sectionData),
deleteSection: (propertyId, sectionId) =>
apiClient.delete(`/properties/${propertyId}/sections/${sectionId}`),
};
// Equipment API endpoints
export const equipmentAPI = {
getAll: (params) => apiClient.get('/equipment', { params }),
getById: (id) => apiClient.get(`/equipment/${id}`),
create: (equipmentData) => apiClient.post('/equipment', equipmentData),
update: (id, equipmentData) => apiClient.put(`/equipment/${id}`, equipmentData),
delete: (id) => apiClient.delete(`/equipment/${id}`),
getCategories: () => apiClient.get('/equipment/categories'),
getTypes: (params) => apiClient.get('/equipment/types', { params }),
getSpreaders: () => apiClient.get('/equipment/spreaders'), // Get user's spreader equipment
getCalculations: (id, params) => apiClient.get(`/equipment/${id}/calculations`, { params }),
};
// Nozzles API endpoints
export const nozzlesAPI = {
getAll: () => apiClient.get('/nozzles'),
getById: (id) => apiClient.get(`/nozzles/${id}`),
create: (nozzleData) => apiClient.post('/nozzles', nozzleData),
update: (id, nozzleData) => apiClient.put(`/nozzles/${id}`, nozzleData),
delete: (id) => apiClient.delete(`/nozzles/${id}`),
getNozzleTypes: () => apiClient.get('/nozzles/types'),
getUserNozzles: () => apiClient.get('/nozzles/user'),
calculateFlowRate: (nozzleId, pressure) => apiClient.get(`/nozzles/${nozzleId}/flow-rate`, { params: { pressure } }),
// Pump assignments
getPumpAssignments: (sprayerId) => apiClient.get(`/nozzles/sprayer/${sprayerId}/pump-assignments`),
assignPump: (sprayerId, pumpId) => apiClient.post(`/nozzles/sprayer/${sprayerId}/pump-assignments`, { pumpId }),
unassignPump: (assignmentId) => apiClient.delete(`/nozzles/pump-assignments/${assignmentId}`),
// Nozzle configurations
getNozzleConfigurations: (sprayerId) => apiClient.get(`/nozzles/sprayer/${sprayerId}/nozzle-configurations`),
addNozzleConfiguration: (sprayerId, configData) => apiClient.post(`/nozzles/sprayer/${sprayerId}/nozzle-configurations`, configData),
removeNozzleConfiguration: (configId) => apiClient.delete(`/nozzles/nozzle-configurations/${configId}`),
// Legacy endpoints for compatibility
getTypes: (params) => apiClient.get('/nozzles/types', { params }),
getAssignments: (equipmentId) => apiClient.get(`/nozzles/equipment/${equipmentId}/assignments`),
assignToEquipment: (equipmentId, assignmentData) => apiClient.post(`/nozzles/equipment/${equipmentId}/assignments`, assignmentData),
removeAssignment: (assignmentId) => apiClient.delete(`/nozzles/assignments/${assignmentId}`),
};
// Products API endpoints
export const productsAPI = {
getAll: (params) => apiClient.get('/products', { params }),
getById: (id) => apiClient.get(`/products/${id}`),
search: (params) => apiClient.get('/products/search', { params }),
getCategories: () => apiClient.get('/products/categories'),
// User products
getUserProducts: () => apiClient.get('/products/user'),
createUserProduct: (productData) => apiClient.post('/products/user', productData),
getUserProduct: (id) => apiClient.get(`/products/user/${id}`),
updateUserProduct: (id, productData) => apiClient.put(`/products/user/${id}`, productData),
deleteUserProduct: (id) => apiClient.delete(`/products/user/${id}`),
archiveUserProduct: (id) => apiClient.put(`/products/user/${id}/archive`),
unarchiveUserProduct: (id) => apiClient.put(`/products/user/${id}/unarchive`),
};
// Applications API endpoints
export const applicationsAPI = {
// Plans
getPlans: (params) => apiClient.get('/applications/plans', { params }),
getPlan: (id) => apiClient.get(`/applications/plans/${id}`),
createPlan: (planData) => apiClient.post('/applications/plans', planData),
updatePlan: (id, planData) => apiClient.put(`/applications/plans/${id}`, planData),
deletePlan: (id) => apiClient.delete(`/applications/plans/${id}`),
updatePlanStatus: (id, status) => apiClient.put(`/applications/plans/${id}/status`, { status }),
// Logs
getLogs: (params) => apiClient.get('/applications/logs', { params }),
createLog: (logData) => apiClient.post('/applications/logs', logData),
// Stats
getStats: (params) => apiClient.get('/applications/stats', { params }),
};
// Spreader Settings API endpoints
export const spreaderSettingsAPI = {
getAll: () => apiClient.get('/spreader-settings'),
getBrands: () => apiClient.get('/spreader-settings/brands'),
getByBrand: (brand) => apiClient.get(`/spreader-settings/${brand}`),
};
// Product Spreader Settings API endpoints
export const productSpreaderSettingsAPI = {
getByProduct: (productId) => apiClient.get(`/product-spreader-settings/product/${productId}`),
getByUserProduct: (userProductId) => apiClient.get(`/product-spreader-settings/user-product/${userProductId}`),
create: (settingData) => apiClient.post('/product-spreader-settings', settingData),
update: (id, settingData) => apiClient.put(`/product-spreader-settings/${id}`, settingData),
delete: (id) => apiClient.delete(`/product-spreader-settings/${id}`),
};
// Weather API endpoints
export const weatherAPI = {
getCurrent: (propertyId) => apiClient.get(`/weather/${propertyId}`),
getForecast: (propertyId) => apiClient.get(`/weather/${propertyId}/forecast`),
getHistory: (propertyId, params) => apiClient.get(`/weather/${propertyId}/history`, { params }),
checkSuitability: (propertyId, params) =>
apiClient.get(`/weather/conditions/suitable/${propertyId}`, { params }),
};
// Admin API endpoints
export const adminAPI = {
getDashboard: () => apiClient.get('/admin/dashboard'),
// Users management
getUsers: (params) => apiClient.get('/admin/users', { params }),
updateUser: (id, userData) => apiClient.put(`/admin/users/${id}`, userData),
updateUserRole: (id, role) => apiClient.put(`/admin/users/${id}/role`, { role }),
deleteUser: (id) => apiClient.delete(`/admin/users/${id}`),
// Products management
getProducts: (params) => apiClient.get('/admin/products', { params }),
getAllUserProducts: (params) => apiClient.get('/admin/products/user', { params }),
createProduct: (productData) => apiClient.post('/admin/products', productData),
updateProduct: (id, productData) => apiClient.put(`/admin/products/${id}`, productData),
deleteProduct: (id) => apiClient.delete(`/admin/products/${id}`),
promoteUserProduct: (id) => apiClient.post(`/admin/products/user/${id}/promote`),
getProductRates: (id) => apiClient.get(`/admin/products/${id}/rates`),
getUserProductSpreaderSettings: (id) => apiClient.get(`/admin/products/user/${id}/spreader-settings`),
addUserProductSpreaderSetting: (id, settingData) => apiClient.post(`/admin/products/user/${id}/spreader-settings`, settingData),
deleteUserProductSpreaderSetting: (id) => apiClient.delete(`/admin/products/user/spreader-settings/${id}`),
// Equipment management
getAllUserEquipment: (params) => apiClient.get('/admin/equipment/user', { params }),
promoteUserEquipment: (id) => apiClient.post(`/admin/equipment/user/${id}/promote`),
// System health
getSystemHealth: () => apiClient.get('/admin/system/health'),
// Settings management
getSettings: () => apiClient.get('/admin/settings'),
updateSettings: (settings) => apiClient.put('/admin/settings', settings),
};
// Mowing API endpoints
export const mowingAPI = {
// Plans
getPlans: () => apiClient.get('/mowing/plans'),
getPlan: (id) => apiClient.get(`/mowing/plans/${id}`),
createPlan: (data) => apiClient.post('/mowing/plans', data),
updatePlanStatus: (id, status) => apiClient.put(`/mowing/plans/${id}/status`, { status }),
// Logs/Sessions
createLog: (data) => apiClient.post('/mowing/sessions', data),
getLogs: (params) => apiClient.get('/mowing/logs', { params }),
getSession: (id) => apiClient.get(`/mowing/sessions/${id}`),
};
// Utility functions
export const handleApiError = (error, defaultMessage = 'An error occurred') => {
if (error.response?.data?.message) {
return error.response.data.message;
}
if (error.message) {
return error.message;
}
return defaultMessage;
};
export const formatApiResponse = (response) => {
return response.data;
};
// Export the configured axios instance for custom requests
export default apiClient;