mirror of
https://github.com/mblanke/holiday-travel-app.git
synced 2026-03-01 05:20:22 -05:00
71 lines
3.5 KiB
TypeScript
71 lines
3.5 KiB
TypeScript
import type { Deal, TravelPreferences } from "./types";
|
|
|
|
// Destination feature profiles (0-10 ratings for common destinations)
|
|
const DESTINATION_PROFILES: Record<string, TravelPreferences> = {
|
|
// Caribbean
|
|
"CUN": { beach: 10, pool: 9, golf: 6, spa: 8, food: 8, nightlife: 9, shopping: 7, culture: 5, outdoor: 8, family: 9 },
|
|
"PUJ": { beach: 10, pool: 9, golf: 7, spa: 8, food: 7, nightlife: 8, shopping: 6, culture: 4, outdoor: 7, family: 9 },
|
|
"MBJ": { beach: 10, pool: 8, golf: 5, spa: 7, food: 8, nightlife: 7, shopping: 5, culture: 6, outdoor: 9, family: 8 },
|
|
"NAS": { beach: 9, pool: 8, golf: 6, spa: 6, food: 7, nightlife: 8, shopping: 7, culture: 5, outdoor: 8, family: 8 },
|
|
|
|
// Europe
|
|
"LHR": { beach: 2, pool: 5, golf: 6, spa: 7, food: 9, nightlife: 8, shopping: 10, culture: 10, outdoor: 6, family: 7 },
|
|
"CDG": { beach: 1, pool: 6, golf: 5, spa: 8, food: 10, nightlife: 9, shopping: 10, culture: 10, outdoor: 5, family: 7 },
|
|
"FCO": { beach: 3, pool: 6, golf: 4, spa: 7, food: 10, nightlife: 8, shopping: 9, culture: 10, outdoor: 7, family: 8 },
|
|
"BCN": { beach: 8, pool: 7, golf: 5, spa: 7, food: 9, nightlife: 10, shopping: 8, culture: 9, outdoor: 7, family: 7 },
|
|
"AMS": { beach: 1, pool: 5, golf: 4, spa: 6, food: 8, nightlife: 9, shopping: 8, culture: 9, outdoor: 6, family: 7 },
|
|
|
|
// USA
|
|
"LAX": { beach: 8, pool: 7, golf: 7, spa: 8, food: 9, nightlife: 9, shopping: 9, culture: 8, outdoor: 8, family: 7 },
|
|
"LAS": { beach: 0, pool: 9, golf: 8, spa: 10, food: 9, nightlife: 10, shopping: 10, culture: 5, outdoor: 5, family: 6 },
|
|
"MIA": { beach: 9, pool: 8, golf: 7, spa: 8, food: 8, nightlife: 10, shopping: 9, culture: 7, outdoor: 8, family: 7 },
|
|
"MCO": { beach: 5, pool: 10, golf: 8, spa: 7, food: 7, nightlife: 7, shopping: 8, culture: 6, outdoor: 6, family: 10 },
|
|
"HNL": { beach: 10, pool: 8, golf: 9, spa: 8, food: 8, nightlife: 7, shopping: 7, culture: 8, outdoor: 10, family: 9 },
|
|
|
|
// Default fallback
|
|
"DEFAULT": { beach: 5, pool: 5, golf: 5, spa: 5, food: 5, nightlife: 5, shopping: 5, culture: 5, outdoor: 5, family: 5 }
|
|
};
|
|
|
|
function calculatePreferenceMatch(deal: Deal, preferences?: TravelPreferences): number {
|
|
if (!preferences || !deal.destination) return 0;
|
|
|
|
const destProfile = DESTINATION_PROFILES[deal.destination.toUpperCase()] || DESTINATION_PROFILES["DEFAULT"];
|
|
let matchScore = 0;
|
|
let totalWeight = 0;
|
|
|
|
// Calculate how well the destination matches user preferences
|
|
for (const [feature, userRating] of Object.entries(preferences)) {
|
|
if (userRating && userRating > 0) {
|
|
const destRating = destProfile[feature as keyof TravelPreferences] || 5;
|
|
// Higher user rating + higher destination rating = better match
|
|
matchScore += (userRating * destRating);
|
|
totalWeight += (userRating * 10); // max possible for this feature
|
|
}
|
|
}
|
|
|
|
// Normalize to 0-1000 scale
|
|
if (totalWeight > 0) {
|
|
return (matchScore / totalWeight) * 1000;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
export function scoreDeal(d: Deal, preferences?: TravelPreferences): number {
|
|
// Very simple scoring: prefer lower price, direct, reasonable nights
|
|
let score = 0;
|
|
if (typeof d.price === "number") score += Math.max(0, 10000 - d.price);
|
|
if (d.stops === 0) score += 500;
|
|
if (typeof d.nights === "number") {
|
|
const target = 7;
|
|
score += Math.max(0, 300 - Math.abs(d.nights - target) * 40);
|
|
}
|
|
if (d.source.includes("Deals")) score += 150; // curated deal sites
|
|
|
|
// Add preference matching bonus
|
|
const preferenceBonus = calculatePreferenceMatch(d, preferences);
|
|
score += preferenceBonus;
|
|
|
|
return score;
|
|
}
|