// ─── Savuki Drilling — API Layer ───────────────────────────────────────── // All server communication lives here. Every function returns parsed data // or throws an Error with a human-readable message. const API_BASE = '/api'; /** Read token from localStorage. */ export const getToken = () => localStorage.getItem('sd_token') || ''; /** Read stored user object. */ export const getUser = () => { try { return JSON.parse(localStorage.getItem('sd_user') || 'null'); } catch { return null; } }; /** Persist auth data after login. */ export const saveAuth = (token, user) => { localStorage.setItem('sd_token', token); localStorage.setItem('sd_user', JSON.stringify(user)); }; /** Clear all auth data. */ export const clearAuth = () => { localStorage.removeItem('sd_token'); localStorage.removeItem('sd_user'); }; // ── Core fetch helpers ──────────────────────────────────────────────────── async function _request(method, path, params = {}) { const token = getToken(); let url = API_BASE + path; let opts = { method }; if (method === 'GET') { const qs = new URLSearchParams({ ...params, token }).toString(); url += '?' + qs; } else { const body = new URLSearchParams({ ...params, token }); opts.body = body; opts.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; } const res = await fetch(url, opts); const json = await res.json().catch(() => ({ success: false, message: 'Invalid server response.' })); if (res.status === 401 || json.message?.toLowerCase().includes('invalid token') || json.message?.toLowerCase().includes('expired')) { localStorage.removeItem('sd_token'); localStorage.removeItem('sd_user'); window.location.href = '/'; return; } if (!json.success) { const err = new Error(json.message || 'Request failed.'); err.status = res.status; throw err; } return json.data; } const get = (path, params) => _request('GET', path, params); const post = (path, params) => _request('POST', path, params); // Generic export for modules that use apiFetch directly export const apiFetch = (path, params = {}) => get('/' + path.replace(/^\//, ''), params); export const apiPost = (path, params = {}) => post('/' + path.replace(/^\//, ''), params); // ── Auth endpoints ──────────────────────────────────────────────────────── export async function login(username, password) { const res = await fetch(API_BASE + '/auth/login.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ username, password }), }); const json = await res.json(); if (!json.success) throw new Error(json.message || 'Login failed.'); return json.data; // { token, user, expires_at } } export async function logout() { try { await post('/auth/logout.php'); } catch { } clearAuth(); } // ── Dashboard ───────────────────────────────────────────────────────────── export const getDashboardStats = () => get('/dashboard/stats.php'); // ── Leads ───────────────────────────────────────────────────────────────── export const getLeads = (params = {}) => get('/leads/list.php', params); export const getLead = (record_id) => get('/leads/get.php', { record_id }); export const createLead = (data) => post('/leads/create.php', data); export const updateLead = (data) => post('/leads/update.php', data); export const assignLead = (data) => post('/leads/assign.php', data); // ── Jobcards ────────────────────────────────────────────────────────────── export const getJobcards = (params = {}) => get('/jobcards/list.php', params); export const getNextJcNo = () => get('/jobcards/next-no.php'); export const getJobcard = (id) => get('/jobcards/get.php', { id }); export const updateJobcard = (data) => post('/jobcards/update.php', data); export const addTimeline = (data) => post('/jobcards/timeline-add.php', data); export const updateTimeline = (data) => post('/jobcards/timeline-update.php', data); export const deleteTimeline = (record_id) => post('/jobcards/timeline-delete.php', { record_id }); // ── Stock ───────────────────────────────────────────────────────────────── export const getStock = (params = {}) => get('/stock/list.php', params); export const assignStock = (data) => post('/stock/assign.php', data); export const returnStock = (data) => post('/stock/return.php', data); // ── Assets ──────────────────────────────────────────────────────────────── export const getAssets = () => get('/assets/list.php'); export const updateAsset = (data) => post('/assets/update.php', data); // ── Teams ───────────────────────────────────────────────────────────────── export const getTeams = () => get('/teams/list.php'); // ── Users ───────────────────────────────────────────────────────────────── export const getUsers = () => get('/users/list.php');