Trip route, 💄 trip manage places modal collapsed selected by default, 💄 trip days sorting, 💄 Handle place details overflows

This commit is contained in:
itskovacs 2025-07-21 21:24:58 +02:00
parent c32a04c991
commit 4cdc11af69
6 changed files with 100 additions and 37 deletions

View File

@ -1 +1 @@
__version__ = "1.3.1"
__version__ = "1.4.0"

View File

@ -53,6 +53,8 @@
</div>
<div class="flex items-center gap-2 print:hidden">
<p-button icon="pi pi-directions" [severity]="tripMapAntLayerDayID == -1 ? 'help' : 'primary'"
(click)="toggleTripDaysHighlight()" text />
<p-button icon="pi pi-print" (click)="printTable()" text />
<div class="border-l border-solid border-gray-700 h-4"></div>
<p-button icon="pi pi-ellipsis-v" [disabled]="trip?.archived" (click)="addItems()" text />
@ -82,7 +84,7 @@
@if (rowgroup) {
<td [attr.rowspan]="rowspan" class="font-normal! max-w-20 truncate cursor-pointer"
[class.text-blue-500]="tripMapAntLayerDayID == tripitem.day_id"
(click)="toggleTripDayHighlightPath(tripitem.day_id); $event.stopPropagation()">
(click)="toggleTripDayHighlightPathDay(tripitem.day_id); $event.stopPropagation()">
<div class="truncate">{{tripitem.td_label }}</div>
</td>
}
@ -368,9 +370,7 @@
@defer {
@for (item of getReviewData; track item.id) {
<div class="flex items-center gap-4 rounded-md justify-between h-10 px-4 py-2 w-full max-w-full">
<div class="line-clamp-1">
{{ item.text }}
</div>
<div class="line-clamp-1">{{ item.text }}</div>
<div class="flex items-center gap-2 flex-none">
@if (item.status) {
<span [style.background]="item.status.color+'1A'" [style.color]="item.status.color"

View File

@ -171,6 +171,7 @@ export class TripComponent implements AfterViewInit {
this.apiService.getTrip(+id).subscribe({
next: (trip) => {
this.trip = trip;
this.sortTripDays();
this.flattenedTripItems = this.flattenTripDayItems(trip.days);
this.updateTotalPrice();
@ -187,6 +188,10 @@ export class TripComponent implements AfterViewInit {
});
}
sortTripDays() {
this.trip?.days.sort((a, b) => a.label.localeCompare(b.label));
}
getDayStats(day: TripDay): { price: number; places: number } {
if (this.dayStatsCache.has(day.id)) {
return this.dayStatsCache.get(day.id)!;
@ -323,7 +328,55 @@ export class TripComponent implements AfterViewInit {
}
}
toggleTripDayHighlightPath(day_id: number) {
toggleTripDaysHighlight() {
if (this.tripMapAntLayerDayID == -1) {
this.map.removeLayer(this.tripMapAntLayer);
this.tripMapAntLayerDayID = undefined;
this.resetMapBounds();
return;
}
if (!this.trip) return;
const items = this.trip.days.flatMap((day) =>
day.items.sort((a, b) => a.time.localeCompare(b.time)),
);
const coords = items
.map((item) => {
if (item.lat && item.lng) return [item.lat, item.lng];
if (item.place && item.place) return [item.place.lat, item.place.lng];
return undefined;
})
.filter((n): n is number[] => n !== undefined);
this.map.fitBounds(coords, { padding: [30, 30] });
const path = antPath(coords, {
delay: 400,
dashArray: [10, 20],
weight: 5,
color: "#0000FF",
pulseColor: "#FFFFFF",
paused: false,
reverse: false,
hardwareAccelerated: true,
});
if (this.tripMapAntLayer) {
this.map.removeLayer(this.tripMapAntLayer);
this.tripMapAntLayerDayID = undefined;
}
// UX
setTimeout(() => {
this.map.addLayer(path);
}, 200);
this.tripMapAntLayer = path;
this.tripMapAntLayerDayID = -1; //Hardcoded value for global trace
}
toggleTripDayHighlightPathDay(day_id: number) {
// Click on the currently displayed day: remove
if (this.tripMapAntLayerDayID == day_id) {
this.map.removeLayer(this.tripMapAntLayer);
@ -484,6 +537,7 @@ export class TripComponent implements AfterViewInit {
this.apiService.postTripDay(day, this.trip?.id!).subscribe({
next: (day) => {
this.trip?.days.push(day);
this.sortTripDays();
this.flattenedTripItems.push(...this.flattenTripDayItems([day]));
},
});
@ -517,6 +571,7 @@ export class TripComponent implements AfterViewInit {
let index = this.trip?.days.findIndex((d) => d.id == day.id);
if (index != -1) {
this.trip?.days.splice(index as number, 1, day);
this.sortTripDays();
this.flattenedTripItems = this.flattenTripDayItems(
this.trip?.days!,
);
@ -693,7 +748,7 @@ export class TripComponent implements AfterViewInit {
this.updateTotalPrice(updatedPrice);
if (this.tripMapAntLayerDayID == item.day_id)
this.toggleTripDayHighlightPath(item.day_id);
this.toggleTripDayHighlightPathDay(item.day_id);
},
});
},

View File

@ -7,13 +7,20 @@
</p-floatlabel>
</div>
<div class="mt-8">
<div class="mt-8 flex justify-between items-center gap-4">
<div class="flex flex-col">
<div class="flex items-center gap-2">
<h1 class="font-semibold tracking-tight text-xl">Selected</h1>
<span class="bg-blue-100 text-blue-800 text-sm me-2 px-2.5 py-0.5 rounded"> {{ selectedPlaces.length }}</span>
</div>
<span class="text-xs text-gray-500">Here are your selected place{{ selectedPlaces.length > 1 ? 's' : '' }}</span>
<span class="text-xs text-gray-500">Here are your selected place{{ selectedPlaces.length > 1 ? 's' : ''
}}</span>
</div>
<p-button [icon]="showSelectedPlaces ? 'pi pi-chevron-up' : 'pi pi-chevron-down'" text
(click)="showSelectedPlaces = !showSelectedPlaces" />
</div>
@if (showSelectedPlaces) {
@for (p of selectedPlaces; track p.id) {
<div class="mt-4 flex items-center gap-4 hover:bg-gray-50 rounded-xl cursor-pointer py-2 px-4"
(click)="togglePlace(p)">
@ -45,10 +52,11 @@
</div>
</div>
}
}
<div class="mt-8">
<h1 class="font-semibold tracking-tight text-xl">List</h1>
<span class="text-xs text-gray-500">Currently displayed points</span>
<span class="text-xs text-gray-500">Available points</span>
@defer {
@for (p of displayedPlaces; track p.id) {

View File

@ -25,6 +25,7 @@ export class TripPlaceSelectModalComponent {
searchInput = new FormControl("");
selectedPlaces: Place[] = [];
showSelectedPlaces: boolean = false;
selectedPlacesID: number[] = [];
places: Place[] = [];

View File

@ -3,12 +3,12 @@
<div class="place-box-dialog-content">
<div class="flex justify-between items-center mb-3">
<div class="flex items-center gap-4">
<img [src]="selectedPlace.image || '/assets/unknown.png'"
style="height: 64px; width: 64px; object-fit: cover; border-radius: 100%;">
<div class="hidden md:flex md:flex-col">
<h1 class="text-gray-800 font-bold mb-0 truncate">{{ selectedPlace.name }}</h1>
<img [src]="selectedPlace.image" class="object-cover rounded-full size-16">
<div class="flex md:flex-col">
<h1 class="text-gray-800 font-bold mb-0 line-clamp-1">{{ selectedPlace.name }}
</h1>
<div class="flex mt-2 gap-1">
<div class="hidden md:flex mt-2 gap-1">
<span
class="bg-blue-100 text-blue-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-blue-900 dark:text-blue-300 flex gap-2 items-center truncate"><i
class="pi pi-box text-xs"></i>{{ selectedPlace.category.name }}</span>
@ -34,8 +34,6 @@
}
</div>
</div>
<h1 class="block md:hidden text-gray-800 font-bold mb-0 truncate">{{ selectedPlace.name }}</h1>
</div>
<div class="flex items-center justify-end gap-2">
<div class="hidden md:flex">
@ -56,11 +54,12 @@
<p-button (click)="close()" text icon="pi pi-times" />
</div>
</div>
</div>
<div class="mt-4 grid grid-cols-2 md:grid-cols-4">
<div class="col-span-2 flex flex-col mb-4">
<span class="text-gray-500">Place</span>
<span>{{ selectedPlace.place }}</span>
<div class="line-clamp-2">{{ selectedPlace.place }}</div>
</div>
<div class="flex flex-col mb-4">