import './style.css'; import { fetchMarketplaceOffers, fetchMarketplaceStats, submitMarketplaceBid, MARKETPLACE_CONFIG, } from './lib/api'; import type { MarketplaceOffer, MarketplaceStats } from './lib/api'; const app = document.querySelector('#app'); if (!app) { throw new Error('Unable to mount marketplace app'); } app.innerHTML = `

Total Offers

-- Listings currently visible

Open Capacity

-- GPU / compute units available

Average Price

-- Credits per unit per hour

Active Bids

-- Open bids awaiting match

Available Offers

Fetching marketplace offers…

Submit a Bid

`; const selectors = { totalOffers: document.querySelector('#stat-total-offers'), openCapacity: document.querySelector('#stat-open-capacity'), averagePrice: document.querySelector('#stat-average-price'), activeBids: document.querySelector('#stat-active-bids'), offersWrapper: document.querySelector('#offers-table-wrapper'), bidForm: document.querySelector('#bid-form'), toast: document.querySelector('#toast'), }; function formatNumber(value: number, options: Intl.NumberFormatOptions = {}): string { return new Intl.NumberFormat(undefined, options).format(value); } function renderStats(stats: MarketplaceStats): void { selectors.totalOffers!.textContent = formatNumber(stats.totalOffers); selectors.openCapacity!.textContent = `${formatNumber(stats.openCapacity)} units`; selectors.averagePrice!.textContent = `${formatNumber(stats.averagePrice, { minimumFractionDigits: 2, maximumFractionDigits: 2, })} credits`; selectors.activeBids!.textContent = formatNumber(stats.activeBids); } function statusClass(status: string): string { switch (status.toLowerCase()) { case 'open': return 'status-pill status-open'; case 'reserved': return 'status-pill status-reserved'; default: return 'status-pill'; } } function renderOffers(offers: MarketplaceOffer[]): void { if (!selectors.offersWrapper) { return; } if (offers.length === 0) { selectors.offersWrapper.innerHTML = '

No offers available right now. Check back soon or submit a bid.

'; return; } const rows = offers .map( (offer) => ` ${offer.id} ${offer.provider} ${formatNumber(offer.capacity)} units ${formatNumber(offer.price, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${offer.sla} ${offer.status} `, ) .join(''); selectors.offersWrapper.innerHTML = `
${rows}
ID Provider Capacity Price SLA Status
`; } function showToast(message: string, duration = 2500): void { if (!selectors.toast) return; selectors.toast.textContent = message; selectors.toast.classList.add('visible'); window.setTimeout(() => { selectors.toast?.classList.remove('visible'); }, duration); } async function loadDashboard(): Promise { try { const [stats, offers] = await Promise.all([ fetchMarketplaceStats(), fetchMarketplaceOffers(), ]); renderStats(stats); renderOffers(offers); } catch (error) { console.error(error); if (selectors.offersWrapper) { selectors.offersWrapper.innerHTML = '

Failed to load offers. Please retry shortly.

'; } showToast('Failed to load marketplace data.'); } } selectors.bidForm?.addEventListener('submit', async (event) => { event.preventDefault(); const formData = new FormData(selectors.bidForm!); const provider = formData.get('provider')?.toString().trim(); const capacity = Number(formData.get('capacity')); const price = Number(formData.get('price')); const notes = formData.get('notes')?.toString().trim(); if (!provider || Number.isNaN(capacity) || Number.isNaN(price)) { showToast('Please complete the required fields.'); return; } try { selectors.bidForm!.querySelector('button')!.setAttribute('disabled', 'disabled'); await submitMarketplaceBid({ provider, capacity, price, notes }); selectors.bidForm!.reset(); showToast('Bid submitted successfully!'); } catch (error) { console.error(error); showToast('Unable to submit bid. Please try again.'); } finally { selectors.bidForm!.querySelector('button')!.removeAttribute('disabled'); } }); loadDashboard();