✨ Trip route, 💄 trip manage places modal collapsed selected by default, 💄 trip days sorting, 💄 Handle place details overflows
This commit is contained in:
parent
c32a04c991
commit
4cdc11af69
@ -1 +1 @@
|
|||||||
__version__ = "1.3.1"
|
__version__ = "1.4.0"
|
||||||
|
|||||||
@ -53,6 +53,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2 print:hidden">
|
<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 />
|
<p-button icon="pi pi-print" (click)="printTable()" text />
|
||||||
<div class="border-l border-solid border-gray-700 h-4"></div>
|
<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 />
|
<p-button icon="pi pi-ellipsis-v" [disabled]="trip?.archived" (click)="addItems()" text />
|
||||||
@ -82,7 +84,7 @@
|
|||||||
@if (rowgroup) {
|
@if (rowgroup) {
|
||||||
<td [attr.rowspan]="rowspan" class="font-normal! max-w-20 truncate cursor-pointer"
|
<td [attr.rowspan]="rowspan" class="font-normal! max-w-20 truncate cursor-pointer"
|
||||||
[class.text-blue-500]="tripMapAntLayerDayID == tripitem.day_id"
|
[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>
|
<div class="truncate">{{tripitem.td_label }}</div>
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
@ -368,9 +370,7 @@
|
|||||||
@defer {
|
@defer {
|
||||||
@for (item of getReviewData; track item.id) {
|
@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="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">
|
<div class="line-clamp-1">{{ item.text }}</div>
|
||||||
{{ item.text }}
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 flex-none">
|
<div class="flex items-center gap-2 flex-none">
|
||||||
@if (item.status) {
|
@if (item.status) {
|
||||||
<span [style.background]="item.status.color+'1A'" [style.color]="item.status.color"
|
<span [style.background]="item.status.color+'1A'" [style.color]="item.status.color"
|
||||||
|
|||||||
@ -171,6 +171,7 @@ export class TripComponent implements AfterViewInit {
|
|||||||
this.apiService.getTrip(+id).subscribe({
|
this.apiService.getTrip(+id).subscribe({
|
||||||
next: (trip) => {
|
next: (trip) => {
|
||||||
this.trip = trip;
|
this.trip = trip;
|
||||||
|
this.sortTripDays();
|
||||||
this.flattenedTripItems = this.flattenTripDayItems(trip.days);
|
this.flattenedTripItems = this.flattenTripDayItems(trip.days);
|
||||||
|
|
||||||
this.updateTotalPrice();
|
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 } {
|
getDayStats(day: TripDay): { price: number; places: number } {
|
||||||
if (this.dayStatsCache.has(day.id)) {
|
if (this.dayStatsCache.has(day.id)) {
|
||||||
return this.dayStatsCache.get(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
|
// Click on the currently displayed day: remove
|
||||||
if (this.tripMapAntLayerDayID == day_id) {
|
if (this.tripMapAntLayerDayID == day_id) {
|
||||||
this.map.removeLayer(this.tripMapAntLayer);
|
this.map.removeLayer(this.tripMapAntLayer);
|
||||||
@ -484,6 +537,7 @@ export class TripComponent implements AfterViewInit {
|
|||||||
this.apiService.postTripDay(day, this.trip?.id!).subscribe({
|
this.apiService.postTripDay(day, this.trip?.id!).subscribe({
|
||||||
next: (day) => {
|
next: (day) => {
|
||||||
this.trip?.days.push(day);
|
this.trip?.days.push(day);
|
||||||
|
this.sortTripDays();
|
||||||
this.flattenedTripItems.push(...this.flattenTripDayItems([day]));
|
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);
|
let index = this.trip?.days.findIndex((d) => d.id == day.id);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
this.trip?.days.splice(index as number, 1, day);
|
this.trip?.days.splice(index as number, 1, day);
|
||||||
|
this.sortTripDays();
|
||||||
this.flattenedTripItems = this.flattenTripDayItems(
|
this.flattenedTripItems = this.flattenTripDayItems(
|
||||||
this.trip?.days!,
|
this.trip?.days!,
|
||||||
);
|
);
|
||||||
@ -693,7 +748,7 @@ export class TripComponent implements AfterViewInit {
|
|||||||
this.updateTotalPrice(updatedPrice);
|
this.updateTotalPrice(updatedPrice);
|
||||||
|
|
||||||
if (this.tripMapAntLayerDayID == item.day_id)
|
if (this.tripMapAntLayerDayID == item.day_id)
|
||||||
this.toggleTripDayHighlightPath(item.day_id);
|
this.toggleTripDayHighlightPathDay(item.day_id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,13 +7,20 @@
|
|||||||
</p-floatlabel>
|
</p-floatlabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8 flex justify-between items-center gap-4">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex flex-col">
|
||||||
<h1 class="font-semibold tracking-tight text-xl">Selected</h1>
|
<div class="flex items-center gap-2">
|
||||||
<span class="bg-blue-100 text-blue-800 text-sm me-2 px-2.5 py-0.5 rounded"> {{ selectedPlaces.length }}</span>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xs text-gray-500">Here are your selected place{{ selectedPlaces.length > 1 ? 's' : '' }}</span>
|
|
||||||
|
<p-button [icon]="showSelectedPlaces ? 'pi pi-chevron-up' : 'pi pi-chevron-down'" text
|
||||||
|
(click)="showSelectedPlaces = !showSelectedPlaces" />
|
||||||
</div>
|
</div>
|
||||||
|
@if (showSelectedPlaces) {
|
||||||
@for (p of selectedPlaces; track p.id) {
|
@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"
|
<div class="mt-4 flex items-center gap-4 hover:bg-gray-50 rounded-xl cursor-pointer py-2 px-4"
|
||||||
(click)="togglePlace(p)">
|
(click)="togglePlace(p)">
|
||||||
@ -45,10 +52,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<h1 class="font-semibold tracking-tight text-xl">List</h1>
|
<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 {
|
@defer {
|
||||||
|
|
||||||
@for (p of displayedPlaces; track p.id) {
|
@for (p of displayedPlaces; track p.id) {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export class TripPlaceSelectModalComponent {
|
|||||||
searchInput = new FormControl("");
|
searchInput = new FormControl("");
|
||||||
|
|
||||||
selectedPlaces: Place[] = [];
|
selectedPlaces: Place[] = [];
|
||||||
|
showSelectedPlaces: boolean = false;
|
||||||
selectedPlacesID: number[] = [];
|
selectedPlacesID: number[] = [];
|
||||||
|
|
||||||
places: Place[] = [];
|
places: Place[] = [];
|
||||||
|
|||||||
@ -3,12 +3,12 @@
|
|||||||
<div class="place-box-dialog-content">
|
<div class="place-box-dialog-content">
|
||||||
<div class="flex justify-between items-center mb-3">
|
<div class="flex justify-between items-center mb-3">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<img [src]="selectedPlace.image || '/assets/unknown.png'"
|
<img [src]="selectedPlace.image" class="object-cover rounded-full size-16">
|
||||||
style="height: 64px; width: 64px; object-fit: cover; border-radius: 100%;">
|
<div class="flex md:flex-col">
|
||||||
<div class="hidden md:flex md:flex-col">
|
<h1 class="text-gray-800 font-bold mb-0 line-clamp-1">{{ selectedPlace.name }}
|
||||||
<h1 class="text-gray-800 font-bold mb-0 truncate">{{ selectedPlace.name }}</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="flex mt-2 gap-1">
|
<div class="hidden md:flex mt-2 gap-1">
|
||||||
<span
|
<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="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>
|
class="pi pi-box text-xs"></i>{{ selectedPlace.category.name }}</span>
|
||||||
@ -34,33 +34,32 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</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="flex items-center justify-end gap-2">
|
||||||
<div class="hidden md:flex">
|
<div class="hidden md:flex">
|
||||||
@if(selectedPlace.gpx) {<p-button (click)="displayGPX()" text icon="pi pi-compass" />}
|
@if(selectedPlace.gpx) {<p-button (click)="displayGPX()" text icon="pi pi-compass" />}
|
||||||
<p-button (click)="visitPlace()" styleClass="text-green-500!" text
|
<p-button (click)="visitPlace()" styleClass="text-green-500!" text
|
||||||
[icon]="selectedPlace.visited ? 'pi pi-check-circle': 'pi pi-check'" />
|
[icon]="selectedPlace.visited ? 'pi pi-check-circle': 'pi pi-check'" />
|
||||||
<p-button (click)="favoritePlace()" styleClass="text-yellow-500!" text
|
<p-button (click)="favoritePlace()" styleClass="text-yellow-500!" text
|
||||||
[icon]="selectedPlace.favorite ? 'pi pi-star-fill' : 'pi pi-star'" />
|
[icon]="selectedPlace.favorite ? 'pi pi-star-fill' : 'pi pi-star'" />
|
||||||
<p-button (click)="deletePlace()" severity="danger" text icon="pi pi-trash" />
|
<p-button (click)="deletePlace()" severity="danger" text icon="pi pi-trash" />
|
||||||
<p-button styleClass="text-blue-500!" (click)="editPlace()" text icon="pi pi-pencil" />
|
<p-button styleClass="text-blue-500!" (click)="editPlace()" text icon="pi pi-pencil" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex md:hidden">
|
||||||
|
<p-button (click)="menu.toggle($event)" severity="secondary" text icon="pi pi-ellipsis-h" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-l border-solid border-gray-700 h-4"></div>
|
||||||
|
<p-button (click)="close()" text icon="pi pi-times" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex md:hidden">
|
|
||||||
<p-button (click)="menu.toggle($event)" severity="secondary" text icon="pi pi-ellipsis-h" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="border-l border-solid border-gray-700 h-4"></div>
|
|
||||||
<p-button (click)="close()" text icon="pi pi-times" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 grid grid-cols-2 md:grid-cols-4">
|
<div class="mt-4 grid grid-cols-2 md:grid-cols-4">
|
||||||
<div class="col-span-2 flex flex-col mb-4">
|
<div class="col-span-2 flex flex-col mb-4">
|
||||||
<span class="text-gray-500">Place</span>
|
<span class="text-gray-500">Place</span>
|
||||||
<span>{{ selectedPlace.place }}</span>
|
<div class="line-clamp-2">{{ selectedPlace.place }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col mb-4">
|
<div class="flex flex-col mb-4">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user