/** * — Canvas-embeddable vehicle card shape. * * Shows: cover photo placeholder, title, type icon, year/make/model, * owner name + trust badge, sleeps, mileage policy, pickup location, endorsement count. */ const ECONOMY_COLORS: Record = { gift: { bg: 'rgba(52,211,153,0.12)', fg: '#34d399', label: 'Gift', icon: '\u{1F49A}' }, exchange: { bg: 'rgba(96,165,250,0.12)', fg: '#60a5fa', label: 'Exchange', icon: '\u{1F91D}' }, sliding_scale: { bg: 'rgba(245,158,11,0.12)', fg: '#f59e0b', label: 'Sliding Scale', icon: '\u{2696}' }, suggested: { bg: 'rgba(167,139,250,0.12)', fg: '#a78bfa', label: 'Suggested', icon: '\u{1F4AD}' }, fixed: { bg: 'rgba(148,163,184,0.12)', fg: '#94a3b8', label: 'Fixed', icon: '\u{1F3F7}' }, }; const TYPE_ICONS: Record = { motorhome: '\u{1F690}', camper_van: '\u{1F68C}', travel_trailer: '\u{1F3D5}', truck_camper: '\u{1F6FB}', skoolie: '\u{1F68E}', other: '\u{1F3E0}', }; const MILEAGE_LABELS: Record = { unlimited: '\u{267E} Unlimited', per_mile: '\u{1F4CF} Per Mile', included_miles: '\u{2705} Included Miles', }; class FolkVehicleCard extends HTMLElement { static observedAttributes = ['vehicle-id', 'space']; #shadow: ShadowRoot; #data: any = null; #endorsementCount = 0; #isAvailable = true; constructor() { super(); this.#shadow = this.attachShadow({ mode: 'open' }); } connectedCallback() { this.#fetchAndRender(); } attributeChangedCallback() { this.#fetchAndRender(); } set vehicleData(data: { vehicle: any; endorsementCount?: number; isAvailable?: boolean }) { this.#data = data.vehicle; this.#endorsementCount = data.endorsementCount ?? 0; this.#isAvailable = data.isAvailable ?? true; this.#render(); } async #fetchAndRender() { const space = this.getAttribute('space') || 'demo'; const vehicleId = this.getAttribute('vehicle-id'); if (!vehicleId) return; try { const res = await fetch(`/${space}/rvnb/api/vehicles/${vehicleId}`); if (res.ok) { this.#data = await res.json(); this.#render(); } } catch { /* offline */ } } #render() { if (!this.#data) { this.#shadow.innerHTML = `
Loading vehicle...
`; return; } const d = this.#data; const eco = ECONOMY_COLORS[d.economy] || ECONOMY_COLORS.suggested; const typeIcon = TYPE_ICONS[d.type] || '\u{1F690}'; const typeLabel = (d.type || 'camper_van').replace(/_/g, ' '); const specsLine = [d.year, d.make, d.model].filter(Boolean).join(' '); const mileageLabel = MILEAGE_LABELS[d.mileage_policy] || '\u{267E} Unlimited'; this.#shadow.innerHTML = `
${d.cover_photo ? `${d.title}` : typeIcon}
${this.#esc(d.title)}
${specsLine ? `
${this.#esc(specsLine)}
` : ''}
${this.#esc(d.owner_name)} ${d.instant_accept ? '\u{26A1} Auto-accept' : ''}
${eco.icon} ${eco.label} ${typeIcon} ${typeLabel} \u{1F6CF} Sleeps ${d.sleeps} ${mileageLabel}
\u{1F4CD} ${this.#esc(d.pickup_location_name)}
`; } #esc(s: string): string { const el = document.createElement('span'); el.textContent = s || ''; return el.innerHTML; } } if (!customElements.get('folk-vehicle-card')) { customElements.define('folk-vehicle-card', FolkVehicleCard); } export { FolkVehicleCard };