fix: make itinerary lodging cards compact and remove duplicate overnight summary
This commit is contained in:
@@ -101,6 +101,7 @@
|
|||||||
export let readOnly: boolean = false;
|
export let readOnly: boolean = false;
|
||||||
export let itineraryItem: CollectionItineraryItem | null = null;
|
export let itineraryItem: CollectionItineraryItem | null = null;
|
||||||
export let showImage: boolean = true;
|
export let showImage: boolean = true;
|
||||||
|
export let compact: boolean = false;
|
||||||
|
|
||||||
let isWarningModalOpen: boolean = false;
|
let isWarningModalOpen: boolean = false;
|
||||||
|
|
||||||
@@ -154,13 +155,17 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="card w-full max-w-md bg-base-300 shadow hover:shadow-md transition-all duration-200 border border-base-300 group"
|
class="card w-full bg-base-300 shadow hover:shadow-md transition-all duration-200 border border-base-300 group"
|
||||||
aria-label="lodging-card"
|
aria-label="lodging-card"
|
||||||
>
|
>
|
||||||
{#if showImage}
|
{#if showImage}
|
||||||
<!-- Image Section with Overlay -->
|
<!-- Image Section with Overlay -->
|
||||||
<div class="relative overflow-hidden rounded-t-2xl">
|
<div class="relative overflow-hidden rounded-t-2xl">
|
||||||
<CardCarousel images={lodging.images} icon={getLodgingIcon(lodging.type)} name={lodging.name} />
|
<CardCarousel
|
||||||
|
images={lodging.images}
|
||||||
|
icon={getLodgingIcon(lodging.type)}
|
||||||
|
name={lodging.name}
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Privacy Indicator -->
|
<!-- Privacy Indicator -->
|
||||||
<div class="absolute top-2 right-4">
|
<div class="absolute top-2 right-4">
|
||||||
@@ -193,12 +198,22 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="card-body p-4 space-y-3 min-w-0">
|
<div
|
||||||
|
class="card-body min-w-0"
|
||||||
|
class:p-3={compact}
|
||||||
|
class:p-4={!compact}
|
||||||
|
class:space-y-2={compact}
|
||||||
|
class:space-y-3={!compact}
|
||||||
|
>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-start justify-between gap-3">
|
<div class="flex items-start justify-between gap-3">
|
||||||
<a
|
<a
|
||||||
href="/lodging/{lodging.id}"
|
href="/lodging/{lodging.id}"
|
||||||
class="hover:text-primary transition-colors duration-200 line-clamp-2 text-lg font-semibold"
|
class="hover:text-primary transition-colors duration-200 line-clamp-2"
|
||||||
|
class:text-base={compact}
|
||||||
|
class:text-lg={!compact}
|
||||||
|
class:font-medium={compact}
|
||||||
|
class:font-semibold={!compact}
|
||||||
>
|
>
|
||||||
{lodging.name}
|
{lodging.name}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -576,6 +576,10 @@
|
|||||||
return day.items.filter((item) => item.id !== day.boundaryLodgingItem?.id);
|
return day.items.filter((item) => item.id !== day.boundaryLodgingItem?.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldShowOvernightSummary(day: DayGroup): boolean {
|
||||||
|
return day.overnightLodging.length > 0 && !day.boundaryLodgingItem?.resolvedObject;
|
||||||
|
}
|
||||||
|
|
||||||
function reinsertBoundaryLodgingItem(
|
function reinsertBoundaryLodgingItem(
|
||||||
day: DayGroup,
|
day: DayGroup,
|
||||||
timelineItems: ResolvedItineraryItem[]
|
timelineItems: ResolvedItineraryItem[]
|
||||||
@@ -1111,18 +1115,18 @@
|
|||||||
targetPendingDate &&
|
targetPendingDate &&
|
||||||
!addedToItinerary.has(lodgingBeingUpdated.id)
|
!addedToItinerary.has(lodgingBeingUpdated.id)
|
||||||
) {
|
) {
|
||||||
// Normalize check_in to date-only (YYYY-MM-DD) if present
|
// Normalize check_in to date-only (YYYY-MM-DD) if present
|
||||||
const lodgingCheckInDate = lodgingBeingUpdated.check_in
|
const lodgingCheckInDate = lodgingBeingUpdated.check_in
|
||||||
? String(lodgingBeingUpdated.check_in).split('T')[0]
|
? String(lodgingBeingUpdated.check_in).split('T')[0]
|
||||||
: null;
|
: null;
|
||||||
const targetDate = lodgingCheckInDate || targetPendingDate;
|
const targetDate = lodgingCheckInDate || targetPendingDate;
|
||||||
|
|
||||||
addItineraryItemForObject('lodging', lodgingBeingUpdated.id, targetDate);
|
addItineraryItemForObject('lodging', lodgingBeingUpdated.id, targetDate);
|
||||||
// Mark this lodging as added to prevent duplicates
|
// Mark this lodging as added to prevent duplicates
|
||||||
addedToItinerary.add(lodgingBeingUpdated.id);
|
addedToItinerary.add(lodgingBeingUpdated.id);
|
||||||
addedToItinerary = addedToItinerary; // trigger reactivity
|
addedToItinerary = addedToItinerary; // trigger reactivity
|
||||||
pendingAddDate = null;
|
pendingAddDate = null;
|
||||||
pendingLodgingAddDate = null;
|
pendingLodgingAddDate = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2330,11 +2334,12 @@
|
|||||||
lodging={resolvedObj}
|
lodging={resolvedObj}
|
||||||
{user}
|
{user}
|
||||||
{collection}
|
{collection}
|
||||||
itineraryItem={item}
|
itineraryItem={item}
|
||||||
showImage={false}
|
showImage={false}
|
||||||
on:delete={handleItemDelete}
|
compact={true}
|
||||||
on:removeFromItinerary={handleRemoveItineraryItem}
|
on:delete={handleItemDelete}
|
||||||
on:edit={handleEditLodging}
|
on:removeFromItinerary={handleRemoveItineraryItem}
|
||||||
|
on:edit={handleEditLodging}
|
||||||
on:moveToGlobal={(e) => moveItemToGlobal(e.detail.type, e.detail.id)}
|
on:moveToGlobal={(e) => moveItemToGlobal(e.detail.type, e.detail.id)}
|
||||||
/>
|
/>
|
||||||
{:else if objectType === 'note'}
|
{:else if objectType === 'note'}
|
||||||
@@ -2555,6 +2560,7 @@
|
|||||||
{collection}
|
{collection}
|
||||||
itineraryItem={boundaryLodgingItem}
|
itineraryItem={boundaryLodgingItem}
|
||||||
showImage={false}
|
showImage={false}
|
||||||
|
compact={true}
|
||||||
on:delete={handleItemDelete}
|
on:delete={handleItemDelete}
|
||||||
on:removeFromItinerary={handleRemoveItineraryItem}
|
on:removeFromItinerary={handleRemoveItineraryItem}
|
||||||
on:edit={handleEditLodging}
|
on:edit={handleEditLodging}
|
||||||
@@ -2821,11 +2827,12 @@
|
|||||||
lodging={resolvedObj}
|
lodging={resolvedObj}
|
||||||
{user}
|
{user}
|
||||||
{collection}
|
{collection}
|
||||||
itineraryItem={item}
|
itineraryItem={item}
|
||||||
showImage={false}
|
showImage={false}
|
||||||
on:delete={handleItemDelete}
|
compact={true}
|
||||||
on:removeFromItinerary={handleRemoveItineraryItem}
|
on:delete={handleItemDelete}
|
||||||
on:edit={handleEditLodging}
|
on:removeFromItinerary={handleRemoveItineraryItem}
|
||||||
|
on:edit={handleEditLodging}
|
||||||
on:moveToGlobal={(e) =>
|
on:moveToGlobal={(e) =>
|
||||||
moveItemToGlobal(e.detail.type, e.detail.id)}
|
moveItemToGlobal(e.detail.type, e.detail.id)}
|
||||||
on:changeDay={(e) =>
|
on:changeDay={(e) =>
|
||||||
@@ -2887,26 +2894,28 @@
|
|||||||
<LocationMarker class="w-3.5 h-3.5" />
|
<LocationMarker class="w-3.5 h-3.5" />
|
||||||
{locationConnector.durationLabel}
|
{locationConnector.durationLabel}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-base-content/40">•</span>
|
<span class="text-base-content/40">•</span>
|
||||||
{#if directionsUrl}
|
{#if directionsUrl}
|
||||||
<a
|
<a
|
||||||
href={directionsUrl}
|
href={directionsUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="inline-flex items-center gap-1 text-primary/80 font-medium underline underline-offset-2"
|
class="inline-flex items-center gap-1 text-primary/80 font-medium underline underline-offset-2"
|
||||||
>
|
>
|
||||||
<LocationMarker class="w-3.5 h-3.5" />
|
<LocationMarker class="w-3.5 h-3.5" />
|
||||||
{getI18nText('itinerary.directions', 'Directions')}
|
{getI18nText('itinerary.directions', 'Directions')}
|
||||||
</a>
|
</a>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="inline-flex items-center gap-1 text-primary/80 font-medium underline underline-offset-2">
|
<span
|
||||||
<LocationMarker class="w-3.5 h-3.5" />
|
class="inline-flex items-center gap-1 text-primary/80 font-medium underline underline-offset-2"
|
||||||
{getI18nText('itinerary.directions', 'Directions')}
|
>
|
||||||
</span>
|
<LocationMarker class="w-3.5 h-3.5" />
|
||||||
{/if}
|
{getI18nText('itinerary.directions', 'Directions')}
|
||||||
</div>
|
</span>
|
||||||
{:else}
|
{/if}
|
||||||
<div class="flex items-center gap-2 flex-wrap text-base-content">
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2 flex-wrap text-base-content">
|
||||||
<span class="inline-flex items-center gap-1 font-medium">
|
<span class="inline-flex items-center gap-1 font-medium">
|
||||||
{#if locationConnector.mode === 'driving'}
|
{#if locationConnector.mode === 'driving'}
|
||||||
<Car class="w-3.5 h-3.5" />
|
<Car class="w-3.5 h-3.5" />
|
||||||
@@ -2917,26 +2926,28 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="text-base-content/50">•</span>
|
<span class="text-base-content/50">•</span>
|
||||||
<span class="font-medium">{locationConnector.distanceLabel}</span>
|
<span class="font-medium">{locationConnector.distanceLabel}</span>
|
||||||
<span class="text-base-content/50">•</span>
|
<span class="text-base-content/50">•</span>
|
||||||
{#if directionsUrl}
|
{#if directionsUrl}
|
||||||
<a
|
<a
|
||||||
href={directionsUrl}
|
href={directionsUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="inline-flex items-center gap-1 text-primary font-medium underline underline-offset-2"
|
class="inline-flex items-center gap-1 text-primary font-medium underline underline-offset-2"
|
||||||
>
|
>
|
||||||
<LocationMarker class="w-3.5 h-3.5" />
|
<LocationMarker class="w-3.5 h-3.5" />
|
||||||
{getI18nText('itinerary.directions', 'Directions')}
|
{getI18nText('itinerary.directions', 'Directions')}
|
||||||
</a>
|
</a>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="inline-flex items-center gap-1 text-primary font-medium underline underline-offset-2">
|
<span
|
||||||
<LocationMarker class="w-3.5 h-3.5" />
|
class="inline-flex items-center gap-1 text-primary font-medium underline underline-offset-2"
|
||||||
{getI18nText('itinerary.directions', 'Directions')}
|
>
|
||||||
</span>
|
<LocationMarker class="w-3.5 h-3.5" />
|
||||||
{/if}
|
{getI18nText('itinerary.directions', 'Directions')}
|
||||||
</div>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -3011,6 +3022,7 @@
|
|||||||
{collection}
|
{collection}
|
||||||
itineraryItem={boundaryLodgingItem}
|
itineraryItem={boundaryLodgingItem}
|
||||||
showImage={false}
|
showImage={false}
|
||||||
|
compact={true}
|
||||||
on:delete={handleItemDelete}
|
on:delete={handleItemDelete}
|
||||||
on:removeFromItinerary={handleRemoveItineraryItem}
|
on:removeFromItinerary={handleRemoveItineraryItem}
|
||||||
on:edit={handleEditLodging}
|
on:edit={handleEditLodging}
|
||||||
@@ -3075,13 +3087,13 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
pendingAddDate = day.date;
|
pendingAddDate = day.date;
|
||||||
pendingLodgingAddDate = day.date;
|
pendingLodgingAddDate = day.date;
|
||||||
lodgingToEdit = null;
|
lodgingToEdit = null;
|
||||||
lodgingBeingUpdated = null;
|
lodgingBeingUpdated = null;
|
||||||
isLodgingModalOpen = true;
|
isLodgingModalOpen = true;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{$t('adventures.lodging')}
|
{$t('adventures.lodging')}
|
||||||
</button>
|
</button>
|
||||||
@@ -3130,10 +3142,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Overnight Lodging + Dated Trip-wide Indicators (share row to save space) -->
|
<!-- Overnight Lodging + Dated Trip-wide Indicators (share row to save space) -->
|
||||||
{#if day.overnightLodging.length > 0 || day.globalDatedItems.length > 0}
|
{#if shouldShowOvernightSummary(day) || day.globalDatedItems.length > 0}
|
||||||
<div class="mt-4 pt-4 border-t border-base-300 border-dashed">
|
<div class="mt-4 pt-4 border-t border-base-300 border-dashed">
|
||||||
<div class="flex flex-wrap gap-6 items-start">
|
<div class="flex flex-wrap gap-6 items-start">
|
||||||
{#if day.overnightLodging.length > 0}
|
{#if shouldShowOvernightSummary(day)}
|
||||||
<div class="space-y-2 min-w-[240px] flex-1">
|
<div class="space-y-2 min-w-[240px] flex-1">
|
||||||
<div class="flex items-center gap-2 mb-1 opacity-70">
|
<div class="flex items-center gap-2 mb-1 opacity-70">
|
||||||
<Bed class="w-4 h-4" />
|
<Bed class="w-4 h-4" />
|
||||||
@@ -3352,12 +3364,13 @@
|
|||||||
/>
|
/>
|
||||||
{:else if type === 'lodging'}
|
{:else if type === 'lodging'}
|
||||||
<LodgingCard
|
<LodgingCard
|
||||||
lodging={item}
|
lodging={item}
|
||||||
{user}
|
{user}
|
||||||
{collection}
|
{collection}
|
||||||
showImage={false}
|
showImage={false}
|
||||||
on:delete={handleItemDelete}
|
compact={true}
|
||||||
on:edit={handleEditLodging}
|
on:delete={handleItemDelete}
|
||||||
|
on:edit={handleEditLodging}
|
||||||
/>
|
/>
|
||||||
{:else if type === 'note'}
|
{:else if type === 'note'}
|
||||||
<NoteCard
|
<NoteCard
|
||||||
|
|||||||
Reference in New Issue
Block a user