This commit is contained in:
alex wiesner
2026-03-13 20:15:22 +00:00
parent e68c95b2dd
commit c4d39f2812
33 changed files with 2383 additions and 162 deletions

View File

@@ -65,7 +65,10 @@
type DayTemperature = {
available: boolean;
temperature_low_c: number | null;
temperature_high_c: number | null;
temperature_c: number | null;
is_estimate: boolean;
};
$: days = groupItemsByDay(collection);
@@ -653,7 +656,12 @@
if (!result?.date) continue;
nextMap[result.date] = {
available: !!result.available,
temperature_c: typeof result.temperature_c === 'number' ? result.temperature_c : null
temperature_low_c:
typeof result.temperature_low_c === 'number' ? result.temperature_low_c : null,
temperature_high_c:
typeof result.temperature_high_c === 'number' ? result.temperature_high_c : null,
temperature_c: typeof result.temperature_c === 'number' ? result.temperature_c : null,
is_estimate: result.is_estimate === true
};
}
@@ -993,12 +1001,22 @@
function formatDayTemperature(day: DayGroup, temps: Record<string, DayTemperature>): string {
const temperature = temps[day.date];
if (!temperature?.available || temperature.temperature_c === null) {
if (
!temperature?.available ||
temperature.temperature_low_c === null ||
temperature.temperature_high_c === null
) {
return getI18nText('itinerary.temperature_unavailable', 'Temperature unavailable');
}
const rounded = Math.round(temperature.temperature_c);
return `${rounded}°C`;
const low = Math.round(temperature.temperature_low_c);
const high = Math.round(temperature.temperature_high_c);
const rangeText = `${low}°–${high}°C`;
if (temperature.is_estimate) {
return `${rangeText} ${getI18nText('itinerary.temperature_estimated_marker', 'est.')}`;
}
return rangeText;
}
type HardAnchorTiming = {
@@ -2624,6 +2642,18 @@
displayDate={suggestionModalDisplayDate}
on:close={() => (isSuggestionModalOpen = false)}
on:addItem={(e) => {
if (e.detail.type === 'location' && e.detail.location?.id) {
const createdLocation = e.detail.location;
const existingLocations = collection.locations || [];
collection = {
...collection,
locations: [
createdLocation,
...existingLocations.filter((loc) => loc.id !== createdLocation.id)
]
};
}
addItineraryItemForObject(
e.detail.type,
e.detail.itemId,
@@ -2917,8 +2947,6 @@
<div class="flex-1 min-w-0 space-y-1">
<!-- Main date title + optional day name -->
<div class="flex items-baseline gap-2 flex-wrap">
<h3 class="text-lg md:text-xl font-bold">{day.displayDate}</h3>
<!-- Day name - inline with date -->
{#if canModify}
{#if day.dayMetadata?.name}

View File

@@ -18,6 +18,8 @@
location?: string;
rating?: number | string | null;
price_level?: string | null;
latitude?: number | string | null;
longitude?: number | string | null;
};
const dispatch = createEventDispatcher();
@@ -46,6 +48,7 @@
];
const supportedApiCategories = ['restaurant', 'activity', 'event', 'lodging'];
const LOCATION_MODEL_TEXT_MAX_LENGTH = 200;
const activityTypes = ['outdoor', 'cultural', 'entertainment', 'other'];
const durations = ['few hours', 'half-day', 'full-day'];
@@ -131,6 +134,10 @@
return value.trim();
}
function truncateToModelSafeLength(value: string): string {
return value.slice(0, LOCATION_MODEL_TEXT_MAX_LENGTH);
}
function normalizeRating(value: unknown): number | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value;
@@ -146,6 +153,19 @@
return null;
}
function normalizeCoordinate(value: unknown): number | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value;
}
if (typeof value === 'string') {
const parsed = Number(value.trim());
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function normalizeSuggestionItem(value: unknown): SuggestionItem | null {
const item = asRecord(value);
if (!item) return null;
@@ -169,6 +189,8 @@
normalizeText(item.priceLevel) ||
normalizeText(item.price);
const rating = normalizeRating(item.rating ?? item.score);
const latitude = normalizeCoordinate(item.latitude ?? item.lat);
const longitude = normalizeCoordinate(item.longitude ?? item.lon ?? item.lng);
const finalName = name || location;
if (!finalName) return null;
@@ -180,30 +202,39 @@
category: category || undefined,
location: location || undefined,
rating,
price_level: priceLevel || null
price_level: priceLevel || null,
latitude,
longitude
};
}
function buildLocationPayload(suggestion: SuggestionItem) {
const name =
const resolvedName =
normalizeText(suggestion.name) || normalizeText(suggestion.location) || 'Suggestion';
const locationText =
const name = truncateToModelSafeLength(resolvedName);
const resolvedLocation =
normalizeText(suggestion.location) ||
getCollectionLocation() ||
normalizeText(suggestion.name);
normalizeText(suggestion.name) ||
name;
const locationText = truncateToModelSafeLength(resolvedLocation || name);
const description =
normalizeText(suggestion.description) ||
normalizeText(suggestion.why_fits) ||
(suggestion.category ? `${suggestion.category} suggestion` : '');
const rating = normalizeRating(suggestion.rating);
const latitude = normalizeCoordinate(suggestion.latitude);
const longitude = normalizeCoordinate(suggestion.longitude);
return {
name,
description,
location: locationText || name,
rating,
latitude,
longitude,
collections: [collection.id],
is_public: false
is_public: Boolean(collection?.is_public)
};
}
@@ -293,7 +324,8 @@
dispatch('addItem', {
type: 'location',
itemId: location.id,
updateDate: false
updateDate: false,
location
});
} catch (_err) {
error = $t('suggestions.error');

View File

@@ -1193,7 +1193,9 @@
"drag_to_reorder": "Drag to reorder",
"add_to_day": "Add to day",
"optimize": "Optimize",
"add_place": "+ Add place"
"add_place": "+ Add place",
"temperature_unavailable": "Temperature unavailable",
"temperature_estimated_marker": "est."
},
"common": {
"show_less": "Hide details",