100 lines
2.8 KiB
JavaScript
100 lines
2.8 KiB
JavaScript
// Lightweight client that talks to your PHP proxy via fetch.
|
||
// Returns Airport/Flight objects with helpful methods.
|
||
|
||
export class Airport {
|
||
constructor(dto) {
|
||
Object.assign(this, dto);
|
||
}
|
||
get display() {
|
||
const code = this.iata || this.icao || '';
|
||
return `${code} – ${this.name}${this.city ? ' (' + this.city + ')' : ''}`;
|
||
}
|
||
}
|
||
|
||
export class Flight {
|
||
constructor(dto) {
|
||
Object.assign(this, dto);
|
||
}
|
||
summary() {
|
||
const s = [];
|
||
if (this.departureAirport?.iata) s.push(this.departureAirport.iata);
|
||
if (this.arrivalAirport?.iata) s.push(this.arrivalAirport.iata);
|
||
const route = s.length ? s.join(' → ') : 'Flight';
|
||
const co2 = this.emissions?.co2_total ?? this.co2 ?? null;
|
||
return co2 != null ? `${route}: ${co2} kg CO₂e` : route;
|
||
}
|
||
}
|
||
|
||
export class Calco2latoClient {
|
||
/**
|
||
* @param {string} proxyUrl e.g. "/api-proxy.php"
|
||
* @param {object} [options]
|
||
* @param {number} [options.timeoutMs=10000]
|
||
*/
|
||
constructor(proxyUrl, { timeoutMs = 10000 } = {}) {
|
||
this.proxyUrl = proxyUrl;
|
||
this.timeoutMs = timeoutMs;
|
||
}
|
||
|
||
async _fetchJSON(payloadOrQuery) {
|
||
const { method = 'GET', query = null, body = null } = payloadOrQuery;
|
||
const url = new URL(this.proxyUrl, window.location.origin);
|
||
if (query) {
|
||
for (const [k, v] of Object.entries(query)) {
|
||
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
|
||
}
|
||
}
|
||
|
||
const ctrl = new AbortController();
|
||
const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
||
|
||
try {
|
||
const res = await fetch(url.toString(), {
|
||
method,
|
||
headers: body ? { 'Content-Type': 'application/json' } : undefined,
|
||
body: body ? JSON.stringify(body) : undefined,
|
||
credentials: 'include',
|
||
signal: ctrl.signal,
|
||
});
|
||
const data = await res.json().catch(() => ({}));
|
||
if (!res.ok) {
|
||
throw new Error(data?.error || `HTTP ${res.status}`);
|
||
}
|
||
return data;
|
||
} finally {
|
||
clearTimeout(t);
|
||
}
|
||
}
|
||
|
||
// ---------- Airports ----------
|
||
|
||
/**
|
||
* @param {string} q
|
||
* @param {number} [limit=20]
|
||
* @param {number} [offset=1]
|
||
* @returns {Promise<Airport[]>}
|
||
*/
|
||
async searchAirports(q, limit = 20, offset = 1) {
|
||
const data = await this._fetchJSON({
|
||
method: 'GET',
|
||
query: { endpoint: 'airports.search', q, limit, offset }
|
||
});
|
||
const items = Array.isArray(data?.results) ? data.results : (Array.isArray(data) ? data : []);
|
||
return items.map(a => new Airport(a));
|
||
}
|
||
|
||
// ---------- Flights ----------
|
||
|
||
/**
|
||
* @param {object} params e.g. { origin: "FRA", destination: "LHR", date: "2025-09-20", pax: 1, cabin: "economy" }
|
||
* @returns {Promise<Flight>}
|
||
*/
|
||
async estimateFlight(params) {
|
||
const data = await this._fetchJSON({
|
||
method: 'POST',
|
||
body: { endpoint: 'flights.estimate', params }
|
||
});
|
||
return new Flight(data);
|
||
}
|
||
}
|