Calco2lato client commit

This commit is contained in:
2025-09-18 14:18:56 +02:00
parent e8c9bfb7d1
commit ec1489791e

117
public/js/calco2lato.js Normal file
View 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);
}
}