💄 trip: display selected item position on map, 💄 trip: grouped table mode, 💄 trip: fix price display, 🐛 trip: fix item edit on day change

This commit is contained in:
itskovacs 2025-08-07 18:47:02 +02:00
parent 1217052d34
commit d0d30a134f

View File

@ -39,7 +39,7 @@ import {
import { YesNoModalComponent } from "../../modals/yes-no-modal/yes-no-modal.component"; import { YesNoModalComponent } from "../../modals/yes-no-modal/yes-no-modal.component";
import { UtilsService } from "../../services/utils.service"; import { UtilsService } from "../../services/utils.service";
import { TripCreateModalComponent } from "../../modals/trip-create-modal/trip-create-modal.component"; import { TripCreateModalComponent } from "../../modals/trip-create-modal/trip-create-modal.component";
import { AsyncPipe } from "@angular/common"; import { AsyncPipe, DecimalPipe } from "@angular/common";
import { MenuItem } from "primeng/api"; import { MenuItem } from "primeng/api";
import { MenuModule } from "primeng/menu"; import { MenuModule } from "primeng/menu";
import { LinkifyPipe } from "../../shared/linkify.pipe"; import { LinkifyPipe } from "../../shared/linkify.pipe";
@ -60,6 +60,7 @@ import { Settings } from "../../types/settings";
FloatLabelModule, FloatLabelModule,
TableModule, TableModule,
ButtonModule, ButtonModule,
DecimalPipe,
], ],
templateUrl: "./trip.component.html", templateUrl: "./trip.component.html",
styleUrls: ["./trip.component.scss"], styleUrls: ["./trip.component.scss"],
@ -71,6 +72,7 @@ export class TripComponent implements AfterViewInit {
places: Place[] = []; places: Place[] = [];
flattenedTripItems: FlattenedTripItem[] = []; flattenedTripItems: FlattenedTripItem[] = [];
selectedItem?: TripItem & { status?: TripStatus }; selectedItem?: TripItem & { status?: TripStatus };
tableExpandableMode = false;
isMapFullscreen = false; isMapFullscreen = false;
totalPrice = 0; totalPrice = 0;
@ -80,7 +82,8 @@ export class TripComponent implements AfterViewInit {
map?: L.Map; map?: L.Map;
markerClusterGroup?: L.MarkerClusterGroup; markerClusterGroup?: L.MarkerClusterGroup;
hoveredElement?: HTMLElement; tripMapTemporaryMarker?: L.Marker;
tripMapHoveredElement?: HTMLElement;
tripMapAntLayer?: L.FeatureGroup; tripMapAntLayer?: L.FeatureGroup;
tripMapAntLayerDayID?: number; tripMapAntLayerDayID?: number;
@ -338,9 +341,16 @@ export class TripComponent implements AfterViewInit {
} }
resetPlaceHighlightMarker() { resetPlaceHighlightMarker() {
if (!this.hoveredElement) return; if (this.tripMapHoveredElement) {
this.hoveredElement.classList.remove("listHover"); this.tripMapHoveredElement.classList.remove("listHover");
this.hoveredElement = undefined; this.tripMapHoveredElement = undefined;
}
if (this.tripMapTemporaryMarker) {
this.map?.removeLayer(this.tripMapTemporaryMarker);
this.tripMapTemporaryMarker = undefined;
}
this.resetMapBounds();
} }
placeHighlightMarker(lat: number, lng: number) { placeHighlightMarker(lat: number, lng: number) {
@ -353,12 +363,23 @@ export class TripComponent implements AfterViewInit {
} }
}); });
if (!marker) return; if (!marker) {
// TripItem without place, but latlng
const item = {
text: this.selectedItem?.text || "",
lat: lat,
lng: lng,
};
this.tripMapTemporaryMarker = tripDayMarker(item).addTo(this.map!);
this.map?.fitBounds([[lat, lng]], { padding: [30, 30] });
return;
}
const markerElement = marker.getElement() as HTMLElement; // search for Marker. If 'null', is inside Cluster const markerElement = marker.getElement() as HTMLElement; // search for Marker. If 'null', is inside Cluster
if (markerElement) { if (markerElement) {
// marker, not clustered // marker, not clustered
markerElement.classList.add("listHover"); markerElement.classList.add("listHover");
this.hoveredElement = markerElement; this.tripMapHoveredElement = markerElement;
} else { } else {
// marker is clustered // marker is clustered
const parentCluster = (this.markerClusterGroup as any).getVisibleParent( const parentCluster = (this.markerClusterGroup as any).getVisibleParent(
@ -368,18 +389,22 @@ export class TripComponent implements AfterViewInit {
const clusterEl = parentCluster.getElement(); const clusterEl = parentCluster.getElement();
if (clusterEl) { if (clusterEl) {
clusterEl.classList.add("listHover"); clusterEl.classList.add("listHover");
this.hoveredElement = clusterEl; this.tripMapHoveredElement = clusterEl;
} }
} }
} }
} }
resetDayHighlight() {
this.map?.removeLayer(this.tripMapAntLayer!);
this.tripMapAntLayerDayID = undefined;
this.tripMapAntLayer = undefined;
this.resetMapBounds();
}
toggleTripDaysHighlight() { toggleTripDaysHighlight() {
if (this.tripMapAntLayerDayID == -1) { if (this.tripMapAntLayerDayID == -1) {
this.map?.removeLayer(this.tripMapAntLayer!); this.resetDayHighlight();
this.tripMapAntLayerDayID = undefined;
this.tripMapAntLayer = undefined;
this.resetMapBounds();
return; return;
} }
if (!this.trip) return; if (!this.trip) return;
@ -497,10 +522,7 @@ export class TripComponent implements AfterViewInit {
toggleTripDayHighlightPathDay(day_id: number) { 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.resetDayHighlight();
this.tripMapAntLayerDayID = undefined;
this.tripMapAntLayer = undefined;
this.resetMapBounds();
return; return;
} }
@ -1024,44 +1046,39 @@ export class TripComponent implements AfterViewInit {
updateItemFromTrip(old: TripItem, updated: TripItem): void { updateItemFromTrip(old: TripItem, updated: TripItem): void {
if (!this.trip) return; if (!this.trip) return;
if (old.day_id != updated.day_id) { if (old.day_id !== updated.day_id) {
const prevDayIdx = this.trip.days.findIndex((d) => d.id == old.day_id); const oldDay = this.trip.days.find((d) => d.id === old.day_id);
if (prevDayIdx === -1) { if (oldDay) {
const prevDay = this.trip.days[prevDayIdx]; oldDay.items = oldDay.items.filter((i) => i.id !== old.id);
const prevItemIdx = prevDay.items.findIndex((i) => i.id == updated.id);
if (prevItemIdx != -1) prevDay.items.splice(prevItemIdx, 1);
this.dayStatsCache.delete(old.day_id); this.dayStatsCache.delete(old.day_id);
} }
} }
const dayIdx = this.trip.days.findIndex((d) => d.id == updated.day_id); const newDay = this.trip.days.find((d) => d.id === updated.day_id);
if (dayIdx != -1) { if (newDay) {
const day = this.trip.days[dayIdx]; const itemIdx = newDay.items.findIndex((i) => i.id === updated.id);
const itemIdx = day.items.findIndex((i) => i.id === updated.id);
if (itemIdx !== -1) { if (itemIdx !== -1) {
day.items[itemIdx] = updated; newDay.items[itemIdx] = updated;
} else {
newDay.items.push(updated);
} }
this.dayStatsCache.delete(updated.day_id);
} }
this.flattenTripDayItems(); this.flattenTripDayItems();
this.computePlacesUsedInTable();
const updatedPrice = (updated.price || 0) - (old.price || 0);
this.updateTotalPrice(updatedPrice);
if (this.tripMapAntLayerDayID) this.resetDayHighlight();
if (updated.place?.id || old.place?.id) this.setPlacesAndMarkers();
if (this.selectedItem && this.selectedItem.id === old.id) if (this.selectedItem && this.selectedItem.id === old.id) {
this.selectedItem = { this.selectedItem = {
...updated, ...updated,
status: updated.status status: updated.status
? this.statusToTripStatus(updated.status as string) ? this.statusToTripStatus(updated.status as string)
: undefined, : undefined,
}; };
this.dayStatsCache.delete(updated.day_id); }
this.computePlacesUsedInTable();
const updatedPrice = (updated.price || 0) - (old.price || 0);
this.updateTotalPrice(updatedPrice);
if (this.tripMapAntLayerDayID == updated.day_id)
this.toggleTripDayHighlightPathDay(updated.day_id);
if (updated.place?.id || old.place?.id) this.setPlacesAndMarkers();
} }
removeItemFromTrip(item: TripItem): void { removeItemFromTrip(item: TripItem): void {