mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2026-05-18 11:47:04 -04:00
feat: enhance LodgingCard and TransportationCard components with expandable details and improved layout
This commit is contained in:
@@ -133,61 +133,110 @@ def search_osm(query):
|
||||
# -----------------
|
||||
|
||||
def extractIsoCode(user, data):
|
||||
"""
|
||||
Extract the ISO code from the response data.
|
||||
Returns a dictionary containing the region name, country name, and ISO code if found.
|
||||
"""
|
||||
iso_code = None
|
||||
town_city_or_county = None
|
||||
display_name = None
|
||||
country_code = None
|
||||
city = None
|
||||
visited_city = None
|
||||
location_name = None
|
||||
"""
|
||||
Extract the ISO code from the response data.
|
||||
Returns a dictionary containing the region name, country name, and ISO code if found.
|
||||
"""
|
||||
iso_code = None
|
||||
display_name = None
|
||||
country_code = None
|
||||
city = None
|
||||
visited_city = None
|
||||
location_name = None
|
||||
|
||||
# town = None
|
||||
# city = None
|
||||
# county = None
|
||||
if 'name' in data.keys():
|
||||
location_name = data['name']
|
||||
|
||||
if 'name' in data.keys():
|
||||
location_name = data['name']
|
||||
|
||||
if 'address' in data.keys():
|
||||
keys = data['address'].keys()
|
||||
for key in keys:
|
||||
if key.find("ISO") != -1:
|
||||
iso_code = data['address'][key]
|
||||
address = data.get('address', {}) or {}
|
||||
|
||||
if not iso_code:
|
||||
return {"error": "No region found"}
|
||||
|
||||
# Prefer the most specific ISO 3166-2 code available before falling back to country-level.
|
||||
preferred_iso_keys = [
|
||||
"ISO3166-2-lvl6",
|
||||
"ISO3166-2-lvl5",
|
||||
"ISO3166-2-lvl4",
|
||||
"ISO3166-2-lvl3",
|
||||
"ISO3166-2-lvl2",
|
||||
"ISO3166-2-lvl1",
|
||||
"ISO3166-2",
|
||||
]
|
||||
for key in preferred_iso_keys:
|
||||
if key in address and address[key]:
|
||||
iso_code = address[key]
|
||||
break
|
||||
|
||||
# If no region-level code, fall back to country code only as a last resort.
|
||||
if not iso_code and "ISO3166-1" in address:
|
||||
iso_code = address.get("ISO3166-1")
|
||||
|
||||
# Capture country code early for matching by name if needed.
|
||||
country_code = address.get("ISO3166-1")
|
||||
state_name = address.get("state")
|
||||
|
||||
region = None
|
||||
if iso_code and len(str(iso_code)) > 2:
|
||||
region = Region.objects.filter(id=iso_code).first()
|
||||
visited_region = VisitedRegion.objects.filter(region=region, user=user).first()
|
||||
|
||||
region_visited = False
|
||||
city_visited = False
|
||||
country_code = iso_code[:2]
|
||||
|
||||
if region:
|
||||
if 'city' in keys:
|
||||
city = City.objects.filter(name__contains=data['address']['city'], region=region).first()
|
||||
if 'county' in keys and not city:
|
||||
city = City.objects.filter(name__contains=data['address']['county'], region=region).first()
|
||||
if 'town' in keys and not city:
|
||||
city = City.objects.filter(name__contains=data['address']['town'], region=region).first()
|
||||
|
||||
if city:
|
||||
display_name = f"{city.name}, {region.name}, {country_code}"
|
||||
visited_city = VisitedCity.objects.filter(city=city, user=user).first()
|
||||
|
||||
if visited_region:
|
||||
region_visited = True
|
||||
if visited_city:
|
||||
city_visited = True
|
||||
# Fallback: attempt to resolve region by name and country code when no ISO match.
|
||||
if not region and state_name:
|
||||
region_queryset = Region.objects.filter(name__iexact=state_name)
|
||||
if country_code:
|
||||
region_queryset = region_queryset.filter(country__country_code=country_code)
|
||||
region = region_queryset.first()
|
||||
if region:
|
||||
return {"region_id": iso_code, "region": region.name, "country": region.country.name, "country_id": region.country.country_code, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited, 'location_name': location_name}
|
||||
iso_code = region.id
|
||||
if not country_code:
|
||||
country_code = region.country.country_code
|
||||
|
||||
if not region:
|
||||
return {"error": "No region found"}
|
||||
|
||||
visited_region = VisitedRegion.objects.filter(region=region, user=user).first()
|
||||
region_visited = bool(visited_region)
|
||||
city_visited = False
|
||||
|
||||
# ordered preference for best-effort locality matching
|
||||
locality_keys = [
|
||||
'suburb',
|
||||
'city',
|
||||
'town',
|
||||
'village',
|
||||
'hamlet',
|
||||
'neighbourhood',
|
||||
'neighborhood', # alternate spelling
|
||||
'locality',
|
||||
'county',
|
||||
'municipality',
|
||||
]
|
||||
|
||||
def match_locality(key_name):
|
||||
value = address.get(key_name)
|
||||
if not value:
|
||||
return None
|
||||
return City.objects.filter(name__icontains=value, region=region).first()
|
||||
|
||||
for key_name in locality_keys:
|
||||
city = match_locality(key_name)
|
||||
if city:
|
||||
break
|
||||
|
||||
if city:
|
||||
display_name = f"{city.name}, {region.name}, {country_code or region.country.country_code}"
|
||||
visited_city = VisitedCity.objects.filter(city=city, user=user).first()
|
||||
city_visited = bool(visited_city)
|
||||
|
||||
return {
|
||||
"region_id": iso_code,
|
||||
"region": region.name,
|
||||
"country": region.country.name,
|
||||
"country_id": region.country.country_code,
|
||||
"region_visited": region_visited,
|
||||
"display_name": display_name,
|
||||
"city": city.name if city else None,
|
||||
"city_id": city.id if city else None,
|
||||
"city_visited": city_visited,
|
||||
'location_name': location_name,
|
||||
}
|
||||
|
||||
def is_host_resolvable(hostname: str) -> bool:
|
||||
try:
|
||||
socket.gethostbyname(hostname)
|
||||
@@ -266,12 +315,22 @@ def _parse_google_address_components(components):
|
||||
state_code = short_name
|
||||
if "administrative_area_level_2" in types:
|
||||
parsed["county"] = long_name
|
||||
if "administrative_area_level_3" in types:
|
||||
parsed["municipality"] = long_name
|
||||
if "locality" in types:
|
||||
parsed["city"] = long_name
|
||||
if "postal_town" in types:
|
||||
parsed.setdefault("city", long_name)
|
||||
if "sublocality" in types:
|
||||
parsed["town"] = long_name
|
||||
if "neighborhood" in types:
|
||||
parsed["neighbourhood"] = long_name
|
||||
if "route" in types:
|
||||
parsed["road"] = long_name
|
||||
if "street_address" in types:
|
||||
parsed["address"] = long_name
|
||||
|
||||
# Build composite ISO 3166-2 code like US-ME
|
||||
# Build composite ISO 3166-2 code like US-ME (matches Region.id in DB)
|
||||
if country_code and state_code:
|
||||
parsed["ISO3166-2-lvl1"] = f"{country_code}-{state_code}"
|
||||
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
return stars;
|
||||
}
|
||||
|
||||
const hasTimePortion = (date: string | null) => !!date && !isAllDay(date);
|
||||
|
||||
const localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? 'UTC';
|
||||
const getTimezoneLabel = (zone?: string | null) => zone ?? localTimeZone;
|
||||
const getTimezoneTip = (zone?: string | null) => {
|
||||
@@ -56,6 +54,14 @@
|
||||
if (!zone) return false;
|
||||
return getTimezoneLabel(zone) !== localTimeZone;
|
||||
};
|
||||
const hasTimePortion = (date: string | null) => !!date && !isAllDay(date);
|
||||
const isTimedStay = (date: string | null) => hasTimePortion(date);
|
||||
|
||||
let showMoreDetails = false;
|
||||
$: hasExpandableDetails = Boolean(
|
||||
lodging.check_out && (isTimedStay(lodging.check_out) || isTimedStay(lodging.check_in))
|
||||
);
|
||||
$: if (!hasExpandableDetails) showMoreDetails = false;
|
||||
|
||||
export let lodging: Lodging;
|
||||
export let user: User | null = null;
|
||||
@@ -233,12 +239,12 @@
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Timed dates with mini cards -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<!-- Check-in Card -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-1.5">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<!-- Timed dates with tidy mini cards and toggle -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- Check-in Card (always shown) -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-2 flex flex-col gap-2">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<span class="text-xs text-base-content/60">Check-in</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{#if isAllDay(lodging.check_in)}
|
||||
@@ -248,44 +254,67 @@
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if hasTimePortion(lodging.check_in) && shouldShowTzBadge(lodging.timezone)}
|
||||
</div>
|
||||
|
||||
{#if hasTimePortion(lodging.check_in) && shouldShowTzBadge(lodging.timezone)}
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<div class="tooltip" data-tip={getTimezoneTip(lodging.timezone) ?? undefined}>
|
||||
<span class="badge badge-primary badge-sm">
|
||||
<span class="badge badge-ghost badge-sm">
|
||||
{getTimezoneLabel(lodging.timezone)}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Check-out Card -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-1.5">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-xs text-base-content/60">Check-out</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{#if isAllDay(lodging.check_out)}
|
||||
{formatAllDayDate(lodging.check_out)}
|
||||
{:else}
|
||||
{formatDateInTimezone(lodging.check_out, lodging.timezone)}
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if hasTimePortion(lodging.check_out) && shouldShowTzBadge(lodging.timezone)}
|
||||
<div class="tooltip" data-tip={getTimezoneTip(lodging.timezone) ?? undefined}>
|
||||
<span class="badge badge-primary badge-sm">
|
||||
{getTimezoneLabel(lodging.timezone)}
|
||||
{#if hasExpandableDetails}
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
class="btn btn-neutral-200 btn-xs"
|
||||
aria-expanded={showMoreDetails}
|
||||
on:click={() => (showMoreDetails = !showMoreDetails)}
|
||||
type="button"
|
||||
>
|
||||
{showMoreDetails
|
||||
? ($t('common.show_less') ?? 'Hide details')
|
||||
: ($t('common.show_more') ?? 'Show more')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showMoreDetails && hasExpandableDetails}
|
||||
<!-- Check-out Card (expandable) -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-2 flex flex-col gap-2">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<span class="text-xs text-base-content/60">Check-out</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{#if isAllDay(lodging.check_out)}
|
||||
{formatAllDayDate(lodging.check_out)}
|
||||
{:else}
|
||||
{formatDateInTimezone(lodging.check_out, lodging.timezone)}
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if hasTimePortion(lodging.check_out) && shouldShowTzBadge(lodging.timezone)}
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<div class="tooltip" data-tip={getTimezoneTip(lodging.timezone) ?? undefined}>
|
||||
<span class="badge badge-ghost badge-sm">
|
||||
{getTimezoneLabel(lodging.timezone)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if lodging.check_in}
|
||||
<!-- Check-in only -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-1.5">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="bg-base-200 rounded-lg px-3 py-2 flex flex-col gap-2">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-xs text-base-content/60">Check-in</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
@@ -296,19 +325,22 @@
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if hasTimePortion(lodging.check_in) && shouldShowTzBadge(lodging.timezone)}
|
||||
</div>
|
||||
|
||||
{#if hasTimePortion(lodging.check_in) && shouldShowTzBadge(lodging.timezone)}
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<div class="tooltip" data-tip={getTimezoneTip(lodging.timezone) ?? undefined}>
|
||||
<span class="badge badge-primary badge-sm">
|
||||
<span class="badge badge-ghost badge-sm">
|
||||
{getTimezoneLabel(lodging.timezone)}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if lodging.check_out}
|
||||
<!-- Check-out only -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-1.5">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="bg-base-200 rounded-lg px-3 py-2 flex flex-col gap-2">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-xs text-base-content/60">Check-out</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
@@ -319,14 +351,17 @@
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if hasTimePortion(lodging.check_out) && shouldShowTzBadge(lodging.timezone)}
|
||||
</div>
|
||||
|
||||
{#if hasTimePortion(lodging.check_out) && shouldShowTzBadge(lodging.timezone)}
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<div class="tooltip" data-tip={getTimezoneTip(lodging.timezone) ?? undefined}>
|
||||
<span class="badge badge-primary badge-sm">
|
||||
<span class="badge badge-ghost badge-sm">
|
||||
{getTimezoneLabel(lodging.timezone)}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -52,11 +52,6 @@
|
||||
}: ${localTimeZone}.`;
|
||||
};
|
||||
|
||||
const shouldShowTzBadge = (zone?: string | null) => {
|
||||
if (!zone) return false;
|
||||
return getTimezoneLabel(zone) !== localTimeZone;
|
||||
};
|
||||
|
||||
export let transportation: Transportation;
|
||||
export let user: User | null = null;
|
||||
export let collection: Collection | null = null;
|
||||
@@ -79,6 +74,18 @@
|
||||
let travelDurationLabel: string | null = null;
|
||||
$: travelDurationLabel = formatTravelDuration(transportation?.travel_duration_minutes ?? null);
|
||||
|
||||
let showMoreDetails = false;
|
||||
|
||||
$: hasCodePair = Boolean(transportation?.start_code && transportation?.end_code);
|
||||
$: routeFromLabel = hasCodePair
|
||||
? transportation.start_code
|
||||
: (transportation.from_location ?? transportation.start_code ?? null);
|
||||
$: routeToLabel = hasCodePair
|
||||
? transportation.end_code
|
||||
: (transportation.to_location ?? transportation.end_code ?? null);
|
||||
$: hasExpandableDetails = Boolean(transportation?.end_date || travelDurationLabel);
|
||||
$: if (!hasExpandableDetails) showMoreDetails = false;
|
||||
|
||||
$: routeGeojson =
|
||||
transportation?.attachments?.find((attachment) => attachment?.geojson)?.geojson ?? null;
|
||||
|
||||
@@ -242,30 +249,26 @@
|
||||
</div>
|
||||
|
||||
<!-- Route & Flight Info -->
|
||||
{#if (transportation.start_code && transportation.end_code) || transportation.from_location || transportation.to_location}
|
||||
{#if routeFromLabel || routeToLabel}
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
{#if transportation.start_code && transportation.end_code}
|
||||
<span class="text-base font-semibold text-base-content">{transportation.start_code}</span>
|
||||
{#if routeFromLabel}
|
||||
<span class="text-base font-semibold text-base-content truncate max-w-[10rem]"
|
||||
>{routeFromLabel}</span
|
||||
>
|
||||
{/if}
|
||||
{#if routeFromLabel && routeToLabel}
|
||||
<span class="text-primary text-lg">→</span>
|
||||
<span class="text-base font-semibold text-base-content">{transportation.end_code}</span>
|
||||
{#if transportation.type === 'plane' && transportation.flight_number}
|
||||
<div class="divider divider-horizontal mx-1"></div>
|
||||
<span class="badge badge-primary badge-sm font-medium"
|
||||
>{transportation.flight_number}</span
|
||||
>
|
||||
{/if}
|
||||
{:else if transportation.from_location && transportation.to_location}
|
||||
<span class="truncate max-w-[10rem] text-sm text-base-content/80"
|
||||
>{transportation.from_location}</span
|
||||
{/if}
|
||||
{#if routeToLabel}
|
||||
<span class="text-base font-semibold text-base-content truncate max-w-[10rem]"
|
||||
>{routeToLabel}</span
|
||||
>
|
||||
<span class="text-primary">→</span>
|
||||
<span class="truncate max-w-[10rem] text-sm text-base-content/80"
|
||||
>{transportation.to_location}</span
|
||||
{/if}
|
||||
{#if hasCodePair && transportation.type === 'plane' && transportation.flight_number}
|
||||
<div class="divider divider-horizontal mx-1"></div>
|
||||
<span class="badge badge-primary badge-sm font-medium"
|
||||
>{transportation.flight_number}</span
|
||||
>
|
||||
{:else if transportation.from_location}
|
||||
<span class="truncate text-sm text-base-content/80">{transportation.from_location}</span>
|
||||
{:else}
|
||||
<span class="truncate text-sm text-base-content/80">{transportation.to_location}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -287,61 +290,89 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Timed events with mini cards -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<!-- Departure Card -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-1.5">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-xs text-base-content/60">Departure</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{formatDateInTimezone(transportation.date, transportation.start_timezone)}
|
||||
</span>
|
||||
</div>
|
||||
{#if shouldShowTzBadge(transportation.start_timezone)}
|
||||
<div
|
||||
class="tooltip"
|
||||
data-tip={getTimezoneTip(transportation.start_timezone) ?? undefined}
|
||||
>
|
||||
<span class="badge badge-primary badge-sm">
|
||||
{getTimezoneLabel(transportation.start_timezone)}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Compact departure card with tidy layout -->
|
||||
<div class="bg-base-200 rounded-lg px-3 py-2 flex flex-col gap-2">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<span class="text-xs text-base-content/60">Departure</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{formatDateInTimezone(transportation.date, transportation.start_timezone)}
|
||||
</span>
|
||||
</div>
|
||||
{#if hasCodePair}
|
||||
<span class="badge badge-outline badge-sm font-medium whitespace-nowrap">
|
||||
{transportation.start_code} → {transportation.end_code}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Arrival Card -->
|
||||
{#if transportation.end_date}
|
||||
<div class="bg-base-200 rounded-lg px-3 py-1.5">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-xs text-base-content/60">Arrival</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{formatDateInTimezone(
|
||||
transportation.end_date,
|
||||
transportation.end_timezone ?? transportation.start_timezone
|
||||
)}
|
||||
</span>
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<div
|
||||
class="tooltip"
|
||||
data-tip={getTimezoneTip(transportation.start_timezone) ?? undefined}
|
||||
>
|
||||
<span class="badge badge-ghost badge-sm">
|
||||
{getTimezoneLabel(transportation.start_timezone)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if hasExpandableDetails}
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
class="btn btn-neutral-200 btn-xs"
|
||||
aria-expanded={showMoreDetails}
|
||||
on:click={() => (showMoreDetails = !showMoreDetails)}
|
||||
type="button"
|
||||
>
|
||||
{showMoreDetails
|
||||
? ($t('common.show_less') ?? 'Hide details')
|
||||
: ($t('common.show_more') ?? 'Show more')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showMoreDetails && hasExpandableDetails}
|
||||
<div class="flex flex-col gap-1">
|
||||
{#if transportation.end_date}
|
||||
<div class="bg-base-200 rounded-lg px-3 py-2 flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<span class="text-xs text-base-content/60">Arrival</span>
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{formatDateInTimezone(
|
||||
transportation.end_date,
|
||||
transportation.end_timezone ?? transportation.start_timezone
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{#if shouldShowTzBadge(transportation.end_timezone ?? transportation.start_timezone)}
|
||||
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<div
|
||||
class="tooltip"
|
||||
data-tip={getTimezoneTip(
|
||||
transportation.end_timezone ?? transportation.start_timezone
|
||||
) ?? undefined}
|
||||
>
|
||||
<span class="badge badge-primary badge-sm">
|
||||
<span class="badge badge-ghost badge-sm">
|
||||
{getTimezoneLabel(
|
||||
transportation.end_timezone ?? transportation.start_timezone
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if travelDurationLabel}
|
||||
<div class="flex items-center gap-2 text-xs text-base-content/70">
|
||||
<span class="badge badge-ghost badge-xs">⏱️ {travelDurationLabel}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export let appVersion = 'v0.12.0-pre-dev-010226-7';
|
||||
export let appVersion = 'v0.12.0-pre-dev-010326';
|
||||
export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.11.0';
|
||||
export let appTitle = 'AdventureLog';
|
||||
export let copyrightYear = '2023-2026';
|
||||
|
||||
@@ -1077,5 +1077,9 @@
|
||||
"remove_from_itinerary": "Remove from Day",
|
||||
"item_remove_success": "Item removed from itinerary",
|
||||
"item_remove_error": "Error removing item from itinerary"
|
||||
},
|
||||
"common": {
|
||||
"show_less": "Hide details",
|
||||
"show_more": "Show more"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user