// ── Job Offer API — Uber-style offer/accept for dispatch jobs ──────────────── // Offers are stored as "Dispatch Offer" docs in ERPNext. // Flow: dispatcher/customer creates offer → matching techs notified → tech accepts → job assigned // ───────────────────────────────────────────────────────────────────────────── import { BASE_URL } from 'src/config/erpnext' import { authFetch } from './auth' async function apiGet (path) { const res = await authFetch(BASE_URL + path) const data = await res.json() if (data.exc) throw new Error(data.exc) return data } async function apiPut (doctype, name, body) { const res = await authFetch( `${BASE_URL}/api/resource/${encodeURIComponent(doctype)}/${encodeURIComponent(name)}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }, ) const data = await res.json() if (data.exc) throw new Error(data.exc) return data } // ── CRUD ───────────────────────────────────────────────────────────────────── export async function fetchOffers (filters = null) { let url = '/api/resource/Dispatch%20Offer?fields=["*"]&limit_page_length=200&order_by=creation+desc' if (filters) url += '&filters=' + encodeURIComponent(JSON.stringify(filters)) const data = await apiGet(url) return data.data || [] } export async function fetchActiveOffers () { return fetchOffers([['status', 'in', ['open', 'pending']]]) } export async function createOffer (payload) { const res = await authFetch( `${BASE_URL}/api/resource/Dispatch%20Offer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }, ) const data = await res.json() if (data.exc) throw new Error(data.exc) return data.data } export async function updateOffer (name, payload) { return apiPut('Dispatch Offer', name, payload) } // ── Offer actions ──────────────────────────────────────────────────────────── export async function acceptOffer (offerName, techId) { return updateOffer(offerName, { status: 'accepted', accepted_by: techId, accepted_at: new Date().toISOString() }) } export async function declineOffer (offerName, techId, reason = '') { // Record decline — we don't close the offer, just track who declined return updateOffer(offerName, { declined_techs: JSON.stringify([ ...JSON.parse((await apiGet(`/api/resource/Dispatch%20Offer/${offerName}`)).data?.declined_techs || '[]'), { techId, reason, at: new Date().toISOString() }, ]), }) } export async function cancelOffer (offerName) { return updateOffer(offerName, { status: 'cancelled' }) } export async function expireOffer (offerName) { return updateOffer(offerName, { status: 'expired' }) }