Calco2lato client commit
This commit is contained in:
117
public/js/calco2lato.js
Normal file
117
public/js/calco2lato.js
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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.origin?.iata) s.push(this.origin.iata);
|
||||
if (this.destination?.iata) s.push(this.destination.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 Calco2Client {
|
||||
/**
|
||||
* @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=0]
|
||||
* @returns {Promise<Airport[]>}
|
||||
*/
|
||||
async searchAirports(q, limit = 20, offset = 0) {
|
||||
const data = await this._fetchJSON({
|
||||
method: 'GET',
|
||||
query: { endpoint: 'airports.search', q, limit, offset }
|
||||
});
|
||||
const items = Array.isArray(data?.items) ? data.items : (Array.isArray(data) ? data : []);
|
||||
return items.map(a => new Airport(a));
|
||||
}
|
||||
|
||||
/** @returns {Promise<Airport>} */
|
||||
async getAirport(code) {
|
||||
const data = await this._fetchJSON({
|
||||
method: 'GET',
|
||||
query: { endpoint: 'airports.get', code }
|
||||
});
|
||||
return new Airport(data);
|
||||
}
|
||||
|
||||
// ---------- 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);
|
||||
}
|
||||
|
||||
/** @returns {Promise<Flight>} */
|
||||
async getFlight(id) {
|
||||
const data = await this._fetchJSON({
|
||||
method: 'GET',
|
||||
query: { endpoint: 'flights.get', id }
|
||||
});
|
||||
return new Flight(data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user