diff --git a/src/src/app/components/shared-trip/shared-trip.component.html b/src/src/app/components/shared-trip/shared-trip.component.html index 0c57016..bce8ef9 100644 --- a/src/src/app/components/shared-trip/shared-trip.component.html +++ b/src/src/app/components/shared-trip/shared-trip.component.html @@ -72,7 +72,8 @@ text /> - + +
@@ -239,6 +240,7 @@ +
{{ tripitem.td_date | date: 'd MMM, y' }}
{{ tripitem.td_label }}
} @@ -262,7 +264,7 @@
- +
{{ tripitem.place.name }}
@@ -559,6 +561,9 @@ {{ d.label }}
+  {{ (d.dt | date: 'd MMM, y' ) || '-' }} {{ getDayStats(d).price || '-' }} @if (getDayStats(d).price) { @@ -678,22 +683,25 @@ class="flex items-center gap-2 w-full cursor-pointer">
- @if (item.qt) { - {{ item.qt }} - } + @if (item.qt) {{{ item.qt }}} {{ item.text }}
} + } @empty { +
+ +

No item

+
} + [(visible)]="checklistDialogVisible" styleClass="w-[95%] md:w-[80%]">
@for (item of checklistItems; track item.id) { @@ -792,7 +800,7 @@
- +
{{ item.place.name }}
diff --git a/src/src/app/components/shared-trip/shared-trip.component.ts b/src/src/app/components/shared-trip/shared-trip.component.ts index 24416e4..dd71cf6 100644 --- a/src/src/app/components/shared-trip/shared-trip.component.ts +++ b/src/src/app/components/shared-trip/shared-trip.component.ts @@ -25,6 +25,8 @@ import { InputTextModule } from 'primeng/inputtext'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { calculateDistanceBetween } from '../../shared/haversine'; import { orderByPipe } from '../../shared/order-by.pipe'; +import { generateTripICSFile } from '../trip/ics'; +import { generateTripCSVFile } from '../trip/csv'; @Component({ selector: 'app-shared-trip', @@ -168,6 +170,30 @@ export class SharedTripComponent implements AfterViewInit { ], }, ]; + readonly menuTripExportItems: MenuItem[] = [ + { + label: 'Actions', + items: [ + { + label: 'Calendar (.ics)', + icon: 'pi pi-calendar', + command: () => generateTripICSFile(this.flattenedTripItems, this.trip?.name, this.utilsService), + }, + { + label: 'CSV', + icon: 'pi pi-file', + command: () => generateTripCSVFile(this.flattenedTripItems, this.trip?.name), + }, + { + label: 'Pretty Print', + icon: 'pi pi-print', + command: () => { + this.togglePrint(); + }, + }, + ], + }, + ]; readonly tripTableColumns: string[] = [ 'day', 'time', @@ -299,52 +325,62 @@ export class SharedTripComponent implements AfterViewInit { } flattenTripDayItems(searchValue?: string) { + const searchLower = (searchValue || '').toLowerCase(); let prevLat: number, prevLng: number; - this.flattenedTripItems = this.trip!.days.flatMap((day) => - [...day.items] - .filter((item) => - searchValue - ? item.text.toLowerCase().includes(searchValue) || - item.place?.name.toLowerCase().includes(searchValue) || - item.comment?.toLowerCase().includes(searchValue) - : true, - ) - .sort((a, b) => (a.time < b.time ? -1 : a.time > b.time ? 1 : 0)) - .map((item) => { - const lat = item.lat ?? (item.place ? item.place.lat : undefined); - const lng = item.lng ?? (item.place ? item.place.lng : undefined); + this.flattenedTripItems = this.trip!.days.flatMap((day) => day.items.map((item) => ({ item, day }))) + .filter( + ({ item }) => + !searchLower || + item.text.toLowerCase().includes(searchLower) || + item.place?.name.toLowerCase().includes(searchLower) || + item.comment?.toLowerCase().includes(searchLower), + ) + .sort((a, b) => { + const dateA = a.day.dt; + const dateB = b.day.dt; + if (dateA && dateB) return dateA.localeCompare(dateB) || (a.item.time || '').localeCompare(b.item.time || ''); + if (!dateA && !dateB) { + return ( + (a.day.label || '').localeCompare(b.day.label || '') || (a.item.time || '').localeCompare(b.item.time || '') + ); + } + return dateA ? -1 : 1; + }) + .map(({ item, day }) => { + const lat = item.lat ?? item.place?.lat; + const lng = item.lng ?? item.place?.lng; - let distance: number | undefined; - if (lat && lng) { - if (prevLat && prevLng) { - const d = calculateDistanceBetween(prevLat, prevLng, lat, lng); - distance = +(Math.round(d * 1000) / 1000).toFixed(2); - } - prevLat = lat; - prevLng = lng; + let distance: number | undefined; + if (lat && lng) { + if (prevLat && prevLng) { + const d = calculateDistanceBetween(prevLat, prevLng, lat, lng); + distance = +(Math.round(d * 1000) / 1000).toFixed(2); } + prevLat = lat; + prevLng = lng; + } - return { - td_id: day.id, - td_label: day.label, - id: item.id, - time: item.time, - text: item.text, - status: this.statusToTripStatus(item.status as string), - comment: item.comment, - price: item.price || undefined, - day_id: item.day_id, - place: item.place, - image: item.image, - image_id: item.image_id, - gpx: item.gpx, - lat, - lng, - distance, - paid_by: item.paid_by, - }; - }), - ); + return { + td_id: day.id, + td_label: day.label, + td_date: day.dt, + id: item.id, + time: item.time, + text: item.text, + status: this.statusToTripStatus(item.status as string), + comment: item.comment, + price: item.price || undefined, + day_id: item.day_id, + place: item.place, + image: item.image, + image_id: item.image_id, + gpx: item.gpx, + lat, + lng, + distance, + paid_by: item.paid_by, + }; + }); } computePlacesUsedInTable() { @@ -779,11 +815,11 @@ export class SharedTripComponent implements AfterViewInit { } openChecklist() { - if (!this.trip) return; + if (!this.token) return; if (!this.checklistItems.length) this.apiService - .getChecklist(this.trip.id) + .getSharedTripChecklist(this.token) .pipe(take(1)) .subscribe({ next: (items) => {