feat: expand price automation contract
This commit is contained in:
@@ -329,6 +329,7 @@
|
||||
--green: #34d399;
|
||||
--red: #f87171;
|
||||
--hotel: #3b82f6;
|
||||
--flight: #38bdf8;
|
||||
--golf: #22c55e;
|
||||
--nightlife: #a855f7;
|
||||
--excursion: #06b6d4;
|
||||
@@ -835,6 +836,7 @@
|
||||
width: 0%;
|
||||
}
|
||||
.vote-bar-fill.hotel { background: var(--hotel); }
|
||||
.vote-bar-fill.flight { background: var(--flight); }
|
||||
.vote-bar-fill.golf { background: var(--golf); }
|
||||
.vote-bar-fill.nightlife { background: var(--nightlife); }
|
||||
.vote-bar-fill.excursion { background: var(--excursion); }
|
||||
@@ -1430,6 +1432,7 @@
|
||||
<div class="btn-row">
|
||||
<select id="addCategory">
|
||||
<option value="hotel">🏨 Hotel</option>
|
||||
<option value="flight">✈️ Flight</option>
|
||||
<option value="golf">⛳ Golf</option>
|
||||
<option value="nightlife">🎧 Nightlife</option>
|
||||
<option value="excursion">🚤 Excursion</option>
|
||||
@@ -1677,6 +1680,26 @@
|
||||
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
function formatBookingType(bookingType, priceBasis) {
|
||||
const typeLabels = {
|
||||
package: 'Package',
|
||||
standalone: 'Standalone',
|
||||
calculated: 'Calculated',
|
||||
};
|
||||
const basisLabels = {
|
||||
perTraveler: 'per traveler',
|
||||
perNight: 'per night',
|
||||
perPerson: 'per person',
|
||||
perGroup: 'per group',
|
||||
totalPackage: 'total package',
|
||||
perRound: 'per round',
|
||||
perTable: 'per table',
|
||||
};
|
||||
const typeLabel = typeLabels[bookingType] || '';
|
||||
const basisLabel = basisLabels[priceBasis] || priceBasis || '';
|
||||
return [typeLabel, basisLabel].filter(Boolean).join(' · ');
|
||||
}
|
||||
|
||||
function normalizeSourceKey(value) {
|
||||
return String(value || '')
|
||||
.trim()
|
||||
@@ -1699,6 +1722,8 @@
|
||||
sourceKey: defaultKey,
|
||||
sourceLabel: defaultLabel,
|
||||
sourceUrl: opt.automationInsights?.sourceUrl || null,
|
||||
bookingType: opt.automationInsights?.bookingType || null,
|
||||
priceBasis: opt.automationInsights?.priceBasis || null,
|
||||
pointCount: Array.isArray(opt.priceHistory) ? opt.priceHistory.length : 0,
|
||||
latestCheckedAt: opt.latestPricePoint?.checkedAt || null,
|
||||
latestPrice: opt.latestPricePoint?.price ?? null,
|
||||
@@ -1838,6 +1863,7 @@
|
||||
const source = sources[0] || null;
|
||||
const sourceLabel = source?.sourceLabel || opt.automationInsights?.source || 'Unknown source';
|
||||
const meta = source ? [
|
||||
formatBookingType(source.bookingType, source.priceBasis),
|
||||
source.latestDisplayPrice || (typeof source.latestPrice === 'number' ? formatCurrency(source.latestPrice, source.currency || 'USD') : ''),
|
||||
source.pointCount ? `${source.pointCount} point${source.pointCount === 1 ? '' : 's'}` : '',
|
||||
].filter(Boolean).join(' · ') : '';
|
||||
@@ -1852,6 +1878,7 @@
|
||||
${sources.map(source => {
|
||||
const labelParts = [
|
||||
source.sourceLabel || 'Unknown source',
|
||||
formatBookingType(source.bookingType, source.priceBasis),
|
||||
source.latestDisplayPrice || (typeof source.latestPrice === 'number' ? formatCurrency(source.latestPrice, source.currency || 'USD') : ''),
|
||||
source.pointCount ? `${source.pointCount} pt${source.pointCount === 1 ? '' : 's'}` : '',
|
||||
].filter(Boolean);
|
||||
@@ -1870,6 +1897,8 @@
|
||||
const insights = selectedPoint ? {
|
||||
source: selectedMeta?.sourceLabel || selectedPoint.source || 'Automation feed',
|
||||
sourceUrl: selectedMeta?.sourceUrl || selectedPoint.sourceUrl || null,
|
||||
bookingType: selectedMeta?.bookingType || selectedPoint.bookingType || null,
|
||||
priceBasis: selectedMeta?.priceBasis || selectedPoint.priceBasis || null,
|
||||
availability: selectedPoint.availability || null,
|
||||
decisionNote: selectedPoint.decisionNote || null,
|
||||
displayPrice: selectedPoint.displayPrice || null,
|
||||
@@ -1878,6 +1907,10 @@
|
||||
const currentPrice = typeof selectedPoint?.price === 'number' ? formatCurrency(selectedPoint.price, insights.currency || 'USD') : '';
|
||||
const priceLabel = selectedPoint?.displayPrice || insights.displayPrice || currentPrice || 'Not yet tracked';
|
||||
const sourceLabel = selectedMeta?.sourceLabel || insights.source || 'Automation feed';
|
||||
const bookingLabel = formatBookingType(
|
||||
selectedMeta?.bookingType || insights.bookingType,
|
||||
selectedMeta?.priceBasis || insights.priceBasis,
|
||||
);
|
||||
const statusLabel = selectedPoint?.availability || selectedPoint?.decisionNote || insights.availability || insights.decisionNote || 'Matched from live search';
|
||||
const overviewItems = normalizeTextList(opt.details);
|
||||
const autoHighlights = normalizeTextList(selectedPoint?.highlights || opt.automationInsights?.highlights);
|
||||
@@ -1904,6 +1937,10 @@
|
||||
${renderSourceSelect(opt)}
|
||||
${availableSources.length > 1 && sourceMetaLine ? `<div class="option-source-sub">${escapeHtml(sourceLabel)}${sourceMetaLine ? ` · ${escapeHtml(sourceMetaLine)}` : ''}</div>` : ''}
|
||||
</div>
|
||||
<div class="option-fact">
|
||||
<span class="option-fact-label">Booking type</span>
|
||||
<div class="option-fact-value">${escapeHtml(bookingLabel || 'Not classified')}</div>
|
||||
</div>
|
||||
<div class="option-fact">
|
||||
<span class="option-fact-label">Status</span>
|
||||
<div class="option-fact-value">${escapeHtml(statusLabel)}</div>
|
||||
@@ -2232,15 +2269,17 @@
|
||||
const winner = rank === 1 ? 'winner' : '';
|
||||
const barColor = cat.id === 'hotel'
|
||||
? 'var(--hotel)'
|
||||
: cat.id === 'golf'
|
||||
? 'var(--golf)'
|
||||
: cat.id === 'nightlife'
|
||||
? 'var(--nightlife)'
|
||||
: cat.id === 'excursion'
|
||||
? 'var(--excursion)'
|
||||
: cat.id === 'budget'
|
||||
? 'var(--budget)'
|
||||
: 'var(--itinerary)';
|
||||
: cat.id === 'flight'
|
||||
? 'var(--flight)'
|
||||
: cat.id === 'golf'
|
||||
? 'var(--golf)'
|
||||
: cat.id === 'nightlife'
|
||||
? 'var(--nightlife)'
|
||||
: cat.id === 'excursion'
|
||||
? 'var(--excursion)'
|
||||
: cat.id === 'budget'
|
||||
? 'var(--budget)'
|
||||
: 'var(--itinerary)';
|
||||
return `
|
||||
<div class="results-row">
|
||||
<div class="results-rank ${medalClass}">${medal}</div>
|
||||
|
||||
Reference in New Issue
Block a user