/** * API Client for making authenticated requests to the server * * This class provides a wrapper around the Fetch API to handle * authentication and common request patterns. * * The client uses JWT tokens for authentication, which are automatically * included in requests via the authFetch function provided by the auth service. */ class ApiClient { /** * Constructor * * Initialises the API client and sets up the authenticated fetch function. * Relies on the auth service being available in the global scope. */ constructor() { // Ensure auth service is available if (!window.auth) { console.error('Auth service not available'); } // Create authenticated fetch function if not already available this.authFetch = window.authFetch || window.auth?.createAuthFetch() || fetch; } /** * Makes a GET request to the API * * This method handles GET requests with query parameters. * It automatically converts the params object to a query string * and handles error responses. * * @param {string} endpoint - The API endpoint * @param {Object} params - Query parameters * @returns {Promise} The response data */ async get(endpoint, params = {}) { // Build query string const queryString = Object.keys(params).length > 0 ? '?' + new URLSearchParams(params).toString() : ''; try { const response = await this.authFetch(`${endpoint}${queryString}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error(`GET request to ${endpoint} failed:`, error); throw error; } } /** * Makes a POST request to the API * * This method handles POST requests with either JSON data or FormData. * It automatically sets the appropriate headers and handles error responses. * * @param {string} endpoint - The API endpoint * @param {Object|FormData} data - The data to send * @returns {Promise} The response data */ async post(endpoint, data = {}) { try { // Prepare request options const options = { method: 'POST' }; // Handle FormData or JSON if (data instanceof FormData) { options.body = data; } else { options.headers = { 'Content-Type': 'application/json' }; options.body = JSON.stringify(data); } const response = await this.authFetch(endpoint, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error(`POST request to ${endpoint} failed:`, error); throw error; } } /** * Makes a facility-related API request * * This is a helper method that simplifies making requests to the facility controller. * It automatically creates a FormData object with the action and data parameters. * * @param {string} action - The action to perform * @param {Object} data - The data to send * @returns {Promise} The response data */ async facility(action, data = {}) { // Create FormData const formData = new FormData(); formData.append('action', action); // Add all data to FormData Object.entries(data).forEach(([key, value]) => { formData.append(key, value); }); return this.post('/facilitycontroller.php', formData); } /** * Creates a new facility * * This method sends a request to create a new facility with the provided data. * * @param {Object} facilityData - The facility data * @returns {Promise} The response data */ async createFacility(facilityData) { return this.facility('create', facilityData); } /** * Updates a facility * * This method sends a request to update an existing facility with the provided data. * * @param {Object} facilityData - The facility data * @returns {Promise} The response data */ async updateFacility(facilityData) { return this.facility('update', facilityData); } /** * Deletes a facility * * This method sends a request to delete a facility with the specified ID. * * @param {number|string} id - The facility ID * @returns {Promise} The response data */ async deleteFacility(id) { return this.facility('delete', { id }); } /** * Gets a facility by ID * * This method retrieves a single facility with the specified ID. * * @param {number|string} id - The facility ID * @returns {Promise} The response data */ async getFacility(id) { return this.facility('read', { id }); } /** * Gets statuses for a facility * * This method retrieves all status updates for a facility with the specified ID. * * @param {number|string} facilityId - The facility ID * @returns {Promise} The response data */ async getFacilityStatuses(facilityId) { return this.facility('getStatuses', { facilityId }); } /** * Adds a status to a facility * * This method adds a new status update to a facility. * * @param {number|string} idStatus - The facility ID * @param {string} updateStatus - The status comment * @returns {Promise} The response data */ async addFacilityStatus(idStatus, updateStatus) { return this.facility('status', { idStatus, updateStatus }); } /** * Updates a facility status * * This method updates an existing status for a facility. * * @param {number|string} statusId - The status ID * @param {string} editStatus - The updated status comment * @param {number|string} facilityId - The facility ID * @returns {Promise} The response data */ async updateFacilityStatus(statusId, editStatus, facilityId) { return this.facility('editStatus', { statusId, editStatus, facilityId }); } /** * Deletes a facility status * * This method deletes a status update from a facility. * * @param {number|string} statusId - The status ID * @param {number|string} facilityId - The facility ID * @returns {Promise} The response data */ async deleteFacilityStatus(statusId, facilityId) { return this.facility('deleteStatus', { statusId, facilityId }); } /** * Authenticates a user * * This method sends a login request with the provided credentials. * It uses a direct fetch call rather than authFetch since the user * isn't authenticated yet. * * @param {string} username - The username * @param {string} password - The password * @param {string} captchaInput - The CAPTCHA input (optional) * @returns {Promise} The response data */ async login(username, password, captchaInput = null) { const formData = new FormData(); formData.append('action', 'login'); formData.append('username', username); formData.append('password', password); if (captchaInput) { formData.append('captchaInput', captchaInput); } const response = await fetch('/logincontroller.php', { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }); return await response.json(); } /** * Refreshes the access token * * This method sends a request to refresh an expired JWT token * using the provided refresh token. * * @param {string} refreshToken - The refresh token * @returns {Promise} The response data */ async refreshToken(refreshToken) { const formData = new FormData(); formData.append('action', 'refresh'); formData.append('refreshToken', refreshToken); const response = await fetch('/logincontroller.php', { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }); return await response.json(); } /** * Logs out the current user * * This method sends a logout request to invalidate the session. * Note that client-side token removal is handled separately. * * @returns {Promise} The response data */ async logout() { const formData = new FormData(); formData.append('action', 'logout'); const response = await fetch('/logincontroller.php', { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }); return await response.json(); } } // Initialize API client const api = new ApiClient(); // Export API client window.api = api;