+ [pTooltip]="tableExpandableMode ? 'Switch table mode' : 'Switch table mode, allow column resizing'" [icon]="
+ tableExpandableMode
+ ? 'pi pi-arrow-up-right-and-arrow-down-left-from-center'
+ : 'pi pi-arrow-down-left-and-arrow-up-right-to-center'
+ " (click)="tableExpandableMode = !tableExpandableMode" text />
- @if (!tableExpandableMode && tripTableSelectedColumns.includes('day')) {Day
- }
- @if (tripTableSelectedColumns.includes('time')) {Time }
- @if (tripTableSelectedColumns.includes('text')) {Text }
- @if (tripTableSelectedColumns.includes('place')) {Place }
- @if (tripTableSelectedColumns.includes('comment')) {Comment }
- @if (tripTableSelectedColumns.includes('LatLng')) {LatLng }
- @if (tripTableSelectedColumns.includes('price')) {Price }
- @if (tripTableSelectedColumns.includes('status')) {Status }
- @if (tripTableSelectedColumns.includes('distance')) {Distance (km) }
+ @if (!tableExpandableMode && tripTableSelectedColumns.includes('day')) {
+ Day
+ }
+ @if (tripTableSelectedColumns.includes('time')) {
+ Time
+ }
+ @if (tripTableSelectedColumns.includes('text')) {
+ Text
+ }
+ @if (tripTableSelectedColumns.includes('place')) {
+ Place
+ }
+ @if (tripTableSelectedColumns.includes('comment')) {
+ Comment
+ }
+ @if (tripTableSelectedColumns.includes('LatLng')) {
+ LatLng
+ }
+ @if (tripTableSelectedColumns.includes('price')) {
+ Price
+ }
+ @if (tripTableSelectedColumns.includes('status')) {
+ Status
+ }
+ @if (tripTableSelectedColumns.includes('distance')) {
+ Distance (km)
+ }
@if (tableExpandableMode) {
@@ -95,9 +114,10 @@
-
-
{{ tripitem.td_label }}
+ [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'">
+
{{
+ tripitem.td_label
+ }}
- @if (tripTableSelectedColumns.includes('time')) {{{ tripitem.time }} }
- @if (tripTableSelectedColumns.includes('text')) {
+ @if (tripTableSelectedColumns.includes('time')) {
+ {{ tripitem.time }}
+ }
+ @if (tripTableSelectedColumns.includes('text')) {
+
- @if (tripitem.status) {
}
+ @if (tripitem.status) {
+
+
+ }
{{ tripitem.text }}
- }
- @if (tripTableSelectedColumns.includes('place')) {
+
+ }
+ @if (tripTableSelectedColumns.includes('place')) {
+
@if (tripitem.place) {
{{
- tripitem.place.name }}
+ class="absolute left-0 top-1/2 -translate-y-1/2 w-9 rounded-full object-cover print:hidden" />
+ {{ tripitem.place.name }}
- } @else {-}
- }
- @if (tripTableSelectedColumns.includes('comment')) {
+ } @else {
+ -
+ }
+
+ }
+ @if (tripTableSelectedColumns.includes('comment')) {
+
@if (tripitem.image) {
{{
- tripitem.comment }}
+ class="absolute left-0 top-1/2 -translate-y-1/2 size-9 rounded-full object-cover" />
+ {{ tripitem.comment }}
} @else {
{{ tripitem.comment || '-' }}
}
- }
- @if (tripTableSelectedColumns.includes('LatLng')) {
+
+ }
+ @if (tripTableSelectedColumns.includes('LatLng')) {
+
- @if (tripitem.lat) { {{ tripitem.lat }}, {{ tripitem.lng }} }
- @else {-}
+ @if (tripitem.lat) {
+ {{ tripitem.lat }}, {{ tripitem.lng }}
+ } @else {
+ -
+ }
- }
- @if (tripTableSelectedColumns.includes('price')) {@if (tripitem.price) {{{
- tripitem.price }} @if (tripitem.price) { {{ trip.currency }} } } }
- @if (tripTableSelectedColumns.includes('status')) {@if (tripitem.status) {{{
- tripitem.status.label }} } }
- @if (tripTableSelectedColumns.includes('distance')) {
+
+ }
+ @if (tripTableSelectedColumns.includes('price')) {
+
+ @if (tripitem.price) {
+ {{ tripitem.price }}
+ @if (tripitem.price) {
+ {{ trip.currency }}
+ }
+
+ }
+
+ }
+ @if (tripTableSelectedColumns.includes('status')) {
+
+ @if (tripitem.status) {
+ {{ tripitem.status.label }}
+ }
+
+ }
+ @if (tripTableSelectedColumns.includes('distance')) {
+
{{ tripitem.distance || '-' }}
- }
+
+ }
- }
- @else {
+ } @else {
@@ -170,18 +220,25 @@
- {{tripitem.td_label }}
+ {{ tripitem.td_label }}
}
- @if (tripTableSelectedColumns.includes('time')) {{{ tripitem.time }} }
- @if (tripTableSelectedColumns.includes('text')) {
+ @if (tripTableSelectedColumns.includes('time')) {
+ {{ tripitem.time }}
+ }
+ @if (tripTableSelectedColumns.includes('text')) {
+
- @if (tripitem.status) {
}
+ @if (tripitem.status) {
+
+
+ }
{{ tripitem.text }}
- }
- @if (tripTableSelectedColumns.includes('place')) {
+
+ }
+ @if (tripTableSelectedColumns.includes('place')) {
+
@if (tripitem.place) {
@@ -190,38 +247,62 @@
{{ tripitem.place.name }}
- } @else {-}
- }
- @if (tripTableSelectedColumns.includes('comment')) {
+ } @else {
+ -
+ }
+
+ }
+ @if (tripTableSelectedColumns.includes('comment')) {
+
@if (tripitem.image) {
{{
- tripitem.comment }}
+ class="absolute left-0 top-1/2 -translate-y-1/2 size-9 rounded-full object-cover print:hidden" />
+ {{ tripitem.comment }}
} @else {
{{ tripitem.comment || '-' }}
}
- }
- @if (tripTableSelectedColumns.includes('LatLng')) {
+
+ }
+ @if (tripTableSelectedColumns.includes('LatLng')) {
+
- @if (tripitem.lat) { {{ tripitem.lat }}, {{ tripitem.lng }} }
- @else {-}
+ @if (tripitem.lat) {
+ {{ tripitem.lat }}, {{ tripitem.lng }}
+ } @else {
+ -
+ }
- }
- @if (tripTableSelectedColumns.includes('price')) {@if (tripitem.price) {{{
- tripitem.price }} @if (tripitem.price) { {{ trip.currency }} } } }
- @if (tripTableSelectedColumns.includes('status')) {@if (tripitem.status) {{{
- tripitem.status.label }} } }
- @if (tripTableSelectedColumns.includes('distance')) {
+
+ }
+ @if (tripTableSelectedColumns.includes('price')) {
+
+ @if (tripitem.price) {
+ {{ tripitem.price }}
+ @if (tripitem.price) {
+ {{ trip.currency }}
+ }
+
+ }
+
+ }
+ @if (tripTableSelectedColumns.includes('status')) {
+
+ @if (tripitem.status) {
+ {{ tripitem.status.label }}
+ }
+
+ }
+ @if (tripTableSelectedColumns.includes('distance')) {
+
{{ tripitem.distance || '-' }}
- }
+
+ }
}
@@ -234,9 +315,7 @@
-
- No Trip
-
+ No Trip
}
} @placeholder (minimum 0.4s) {
@@ -257,8 +336,7 @@
}
-
{{ selectedItem.text }}
-
+
{{ selectedItem.text }}
@if (selectedItem.gpx) {
@@ -302,16 +380,23 @@
Latitude, Longitude
{{
- selectedItem.lat }}, {{ selectedItem.lng }}
+ [cdkCopyToClipboard]="selectedItem.lat + ',' + selectedItem.lng">
+ {{ selectedItem.lat }}, {{ selectedItem.lng }}
+
}
@if (selectedItem.price) {
Price
-
{{ selectedItem.price }} @if (selectedItem.price) { {{ trip.currency }} }
- @if (selectedItem.paid_by) {(by {{ selectedItem.paid_by }}) }
+
+ {{ selectedItem.price }}
+ @if (selectedItem.price) {
+ {{ trip.currency }}
+ }
+ @if (selectedItem.paid_by) {
+ (by {{ selectedItem.paid_by }})
+ }
}
@@ -319,9 +404,8 @@
@if (selectedItem.status) {
Status
-
{{
- selectedItem.status.label }}
+
{{ selectedItem.status.label }}
}
@@ -349,8 +433,7 @@
-
-
+
@if (!selectedItem) {
@@ -370,7 +453,8 @@
@defer {
{{
- places.length }}
+ places.length
+ }}
} @placeholder (minimum 0.4s) {
}
@@ -383,7 +467,7 @@
@for (p of places; track p.id) {
-
+
{{ p.name }}
@@ -404,8 +488,11 @@
{{
- p.price || '-'
- }} @if (p.price) { {{ trip.currency }} }
+ p.price || '-' }}
+ @if (p.price) {
+ {{ trip.currency }}
+ }
+
@if (trip.collaborators.length) {
{{ p.user
@@ -419,7 +506,7 @@
}
} @placeholder (minimum 0.4s) {
- @for (_ of [1,2,3]; track _) {
+ @for (_ of [1, 2, 3]; track _) {
@@ -454,9 +541,14 @@
{{
- getDayStats(d).price || '-' }} @if (getDayStats(d).price) { {{ trip.currency }} }
+ getDayStats(d).price || '-' }}
+ @if (getDayStats(d).price) {
+ {{ trip.currency }}
+ }
+
{{
- getDayStats(d).places }}
+ getDayStats(d).places
+ }}
} @empty {
@@ -474,16 +566,19 @@
-
TRIP is free and always will be. Building it takes a lot of time and effort, and since I'm committed to
- keeping it free, your support simply helps keep the project going. Thank you! ❤️
+
+ TRIP is free and always will be. Building it takes a lot of time and effort, and since I'm committed to
+ keeping it free, your support simply helps keep the project going. Thank you! ❤️
+
-
Buy me
- a coffee
+
Buy me a
+ coffee
}
@@ -505,12 +600,17 @@
[class.prettyprint]="isPrinting">
@if (isMapFullscreenDays) {
- @for(day of trip.days; track day.id) {
-
-
+ @for (day of trip.days; track day.id) {
+
+
{{ day.items.length }}
-
+
@@ -559,7 +659,9 @@
class="flex items-center gap-2 w-full cursor-pointer">
- @if (item.qt) {{{ item.qt }} }
+ @if (item.qt) {
+ {{ item.qt }}
+ }
{{ item.text }}
@@ -593,8 +695,10 @@
- @if (item.status) {
}
+ @if (item.status) {
+
+
+ }
@@ -612,7 +716,7 @@
-
+
itskovacs/trip
@@ -620,13 +724,11 @@
{{
- trip?.days?.length }} {{ trip?.days!.length > 1 ? 'days' :
- 'day'}}
+ class="pi pi-calendar text-xs"> {{ trip?.days?.length }}
+ {{ (trip?.days)!.length > 1 ? 'days' : 'day' }}
{{
- (totalPrice | number:'1.0-2') || '-' }} {{ trip?.currency }}
+ class="pi pi-wallet text-xs"> {{ (totalPrice | number: '1.0-2') || '-' }} {{ trip?.currency }}
@@ -635,8 +737,7 @@
Notes
-
{{ trip?.notes || 'Nothing there.'
- }}
+
{{ trip?.notes || 'Nothing there.' }}
@@ -680,9 +781,7 @@
@if (item.status) {
{{
- statusToTripStatus(item.status)?.label
- }}
+ [style.color]="statusToTripStatus(item.status)?.color">{{ statusToTripStatus(item.status)?.label }}
}
@@ -700,7 +799,7 @@
📍 Places
- @for(place of trip?.places; track place.id) {
+ @for (place of trip?.places; track place.id) {
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 dc33f86..009fcfb 100644
--- a/src/src/app/components/shared-trip/shared-trip.component.ts
+++ b/src/src/app/components/shared-trip/shared-trip.component.ts
@@ -1,47 +1,33 @@
-import { AfterViewInit, Component } from "@angular/core";
-import { ApiService } from "../../services/api.service";
-import { ButtonModule } from "primeng/button";
-import { SkeletonModule } from "primeng/skeleton";
-import * as L from "leaflet";
-import { antPath } from "leaflet-ant-path";
-import { TableModule } from "primeng/table";
-import {
- Trip,
- FlattenedTripItem,
- TripDay,
- TripItem,
- TripStatus,
- PackingItem,
- ChecklistItem,
-} from "../../types/trip";
-import { Place } from "../../types/poi";
-import {
- createMap,
- placeToMarker,
- createClusterGroup,
- tripDayMarker,
- gpxToPolyline,
-} from "../../shared/map";
-import { ActivatedRoute } from "@angular/router";
-import { debounceTime, take, tap } from "rxjs";
-import { UtilsService } from "../../services/utils.service";
-import { CommonModule, DecimalPipe } from "@angular/common";
-import { MenuItem } from "primeng/api";
-import { MenuModule } from "primeng/menu";
-import { LinkifyPipe } from "../../shared/linkify.pipe";
-import { TooltipModule } from "primeng/tooltip";
-import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms";
-import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import { MultiSelectModule } from "primeng/multiselect";
-import { DialogModule } from "primeng/dialog";
-import { CheckboxModule } from "primeng/checkbox";
-import { InputTextModule } from "primeng/inputtext";
-import { ClipboardModule } from "@angular/cdk/clipboard";
-import { calculateDistanceBetween } from "../../shared/haversine";
-import { orderByPipe } from "../../shared/order-by.pipe";
+import { AfterViewInit, Component } from '@angular/core';
+import { ApiService } from '../../services/api.service';
+import { ButtonModule } from 'primeng/button';
+import { SkeletonModule } from 'primeng/skeleton';
+import * as L from 'leaflet';
+import { antPath } from 'leaflet-ant-path';
+import { TableModule } from 'primeng/table';
+import { Trip, FlattenedTripItem, TripDay, TripItem, TripStatus, PackingItem, ChecklistItem } from '../../types/trip';
+import { Place } from '../../types/poi';
+import { createMap, placeToMarker, createClusterGroup, tripDayMarker, gpxToPolyline } from '../../shared/map';
+import { ActivatedRoute } from '@angular/router';
+import { debounceTime, take, tap } from 'rxjs';
+import { UtilsService } from '../../services/utils.service';
+import { CommonModule, DecimalPipe } from '@angular/common';
+import { MenuItem } from 'primeng/api';
+import { MenuModule } from 'primeng/menu';
+import { LinkifyPipe } from '../../shared/linkify.pipe';
+import { TooltipModule } from 'primeng/tooltip';
+import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { MultiSelectModule } from 'primeng/multiselect';
+import { DialogModule } from 'primeng/dialog';
+import { CheckboxModule } from 'primeng/checkbox';
+import { InputTextModule } from 'primeng/inputtext';
+import { ClipboardModule } from '@angular/cdk/clipboard';
+import { calculateDistanceBetween } from '../../shared/haversine';
+import { orderByPipe } from '../../shared/order-by.pipe';
@Component({
- selector: "app-shared-trip",
+ selector: 'app-shared-trip',
standalone: true,
imports: [
CommonModule,
@@ -61,8 +47,8 @@ import { orderByPipe } from "../../shared/order-by.pipe";
ClipboardModule,
orderByPipe,
],
- templateUrl: "./shared-trip.component.html",
- styleUrls: ["./shared-trip.component.scss"],
+ templateUrl: './shared-trip.component.html',
+ styleUrls: ['./shared-trip.component.scss'],
})
export class SharedTripComponent implements AfterViewInit {
token?: string;
@@ -98,18 +84,18 @@ export class SharedTripComponent implements AfterViewInit {
readonly menuTripActionsItems: MenuItem[] = [
{
- label: "Lists",
+ label: 'Lists',
items: [
{
- label: "Checklist",
- icon: "pi pi-check-square",
+ label: 'Checklist',
+ icon: 'pi pi-check-square',
command: () => {
this.openChecklist();
},
},
{
- label: "Packing",
- icon: "pi pi-briefcase",
+ label: 'Packing',
+ icon: 'pi pi-briefcase',
command: () => {
this.openPackingList();
},
@@ -117,11 +103,11 @@ export class SharedTripComponent implements AfterViewInit {
],
},
{
- label: "Trip",
+ label: 'Trip',
items: [
{
- label: "Pretty Print",
- icon: "pi pi-print",
+ label: 'Pretty Print',
+ icon: 'pi pi-print',
command: () => {
this.togglePrint();
},
@@ -131,11 +117,11 @@ export class SharedTripComponent implements AfterViewInit {
];
readonly menuTripTableActionsItems: MenuItem[] = [
{
- label: "Actions",
+ label: 'Actions',
items: [
{
- label: "Pretty Print",
- icon: "pi pi-print",
+ label: 'Pretty Print',
+ icon: 'pi pi-print',
command: () => {
this.togglePrint();
},
@@ -143,18 +129,18 @@ export class SharedTripComponent implements AfterViewInit {
],
},
{
- label: "Table",
+ label: 'Table',
items: [
{
- label: "Filter",
- icon: "pi pi-filter",
+ label: 'Filter',
+ icon: 'pi pi-filter',
command: () => {
this.toggleFiltering();
},
},
{
- label: "Group",
- icon: "pi pi-arrow-down-left-and-arrow-up-right-to-center",
+ label: 'Group',
+ icon: 'pi pi-arrow-down-left-and-arrow-up-right-to-center',
command: () => {
this.tableExpandableMode = !this.tableExpandableMode;
},
@@ -162,18 +148,18 @@ export class SharedTripComponent implements AfterViewInit {
],
},
{
- label: "Directions",
+ label: 'Directions',
items: [
{
- label: "Highlight",
- icon: "pi pi-directions",
+ label: 'Highlight',
+ icon: 'pi pi-directions',
command: () => {
this.toggleTripDaysHighlight();
},
},
{
- label: "GMaps itinerary",
- icon: "pi pi-car",
+ label: 'GMaps itinerary',
+ icon: 'pi pi-car',
command: () => {
this.tripToNavigation();
},
@@ -182,24 +168,18 @@ export class SharedTripComponent implements AfterViewInit {
},
];
readonly tripTableColumns: string[] = [
- "day",
- "time",
- "text",
- "place",
- "comment",
- "LatLng",
- "price",
- "status",
- "distance",
+ 'day',
+ 'time',
+ 'text',
+ 'place',
+ 'comment',
+ 'LatLng',
+ 'price',
+ 'status',
+ 'distance',
];
- tripTableSelectedColumns: string[] = [
- "day",
- "time",
- "text",
- "place",
- "comment",
- ];
- tripTableSearchInput = new FormControl("");
+ tripTableSelectedColumns: string[] = ['day', 'time', 'text', 'place', 'comment'];
+ tripTableSearchInput = new FormControl('');
dayStatsCache = new Map
();
placesUsedInTable = new Set();
@@ -210,14 +190,12 @@ export class SharedTripComponent implements AfterViewInit {
private route: ActivatedRoute,
) {
this.statuses = this.utilsService.statuses;
- this.tripTableSearchInput.valueChanges
- .pipe(debounceTime(300), takeUntilDestroyed())
- .subscribe({
- next: (value) => {
- if (value) this.flattenTripDayItems(value.toLowerCase());
- else this.flattenTripDayItems();
- },
- });
+ this.tripTableSearchInput.valueChanges.pipe(debounceTime(300), takeUntilDestroyed()).subscribe({
+ next: (value) => {
+ if (value) this.flattenTripDayItems(value.toLowerCase());
+ else this.flattenTripDayItems();
+ },
+ });
}
ngAfterViewInit(): void {
@@ -225,7 +203,7 @@ export class SharedTripComponent implements AfterViewInit {
.pipe(
take(1),
tap((params) => {
- const token = params.get("token");
+ const token = params.get('token');
if (token) {
this.token = token;
this.loadTripData(token);
@@ -252,12 +230,10 @@ export class SharedTripComponent implements AfterViewInit {
initMap(): void {
const contentMenuItems = [
{
- text: "Copy coordinates",
+ text: 'Copy coordinates',
callback: (e: any) => {
const latlng = e.latlng;
- navigator.clipboard.writeText(
- `${parseFloat(latlng.lat).toFixed(5)}, ${parseFloat(latlng.lng).toFixed(5)}`,
- );
+ navigator.clipboard.writeText(`${parseFloat(latlng.lat).toFixed(5)}, ${parseFloat(latlng.lng).toFixed(5)}`);
},
},
];
@@ -305,11 +281,7 @@ export class SharedTripComponent implements AfterViewInit {
if (!this.trip?.days) return [];
return this.trip.days
- .flatMap((day) =>
- day.items.filter((item) =>
- ["constraint", "pending"].includes(item.status as string),
- ),
- )
+ .flatMap((day) => day.items.filter((item) => ['constraint', 'pending'].includes(item.status as string)))
.map((item) => ({
...item,
status: this.statusToTripStatus(item.status as string),
@@ -383,9 +355,7 @@ export class SharedTripComponent implements AfterViewInit {
setPlacesAndMarkers() {
this.computePlacesUsedInTable();
- this.places = [...(this.trip?.places ?? [])].sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
- );
+ this.places = [...(this.trip?.places ?? [])].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
this.markerClusterGroup?.clearLayers();
this.places.forEach((p) => {
const marker = this._placeToMarker(p);
@@ -394,12 +364,8 @@ export class SharedTripComponent implements AfterViewInit {
}
_placeToMarker(place: Place): L.Marker {
- const marker = placeToMarker(
- place,
- false,
- !this.placesUsedInTable.has(place.id),
- );
- marker.on("click", () => {
+ const marker = placeToMarker(place, false, !this.placesUsedInTable.has(place.id));
+ marker.on('click', () => {
this.onMapMarkerClick(place.id);
marker.closeTooltip();
});
@@ -409,9 +375,7 @@ export class SharedTripComponent implements AfterViewInit {
resetMapBounds() {
if (!this.places.length) {
this.map?.fitBounds(
- this.flattenedTripItems
- .filter((i) => i.lat != null && i.lng != null)
- .map((i) => [i.lat!, i.lng!]),
+ this.flattenedTripItems.filter((i) => i.lat != null && i.lng != null).map((i) => [i.lat!, i.lng!]),
{ padding: [30, 30] },
);
return;
@@ -425,7 +389,7 @@ export class SharedTripComponent implements AfterViewInit {
toggleMapFullscreen() {
this.isMapFullscreen = !this.isMapFullscreen;
- document.body.classList.toggle("overflow-hidden");
+ document.body.classList.toggle('overflow-hidden');
setTimeout(() => {
this.map?.invalidateSize();
@@ -444,14 +408,12 @@ export class SharedTripComponent implements AfterViewInit {
return;
}
this.totalPrice =
- this.trip?.days
- .flatMap((d) => d.items)
- .reduce((price, item) => price + (item.price ?? 0), 0) ?? 0;
+ this.trip?.days.flatMap((d) => d.items).reduce((price, item) => price + (item.price ?? 0), 0) ?? 0;
}
resetPlaceHighlightMarker() {
if (this.tripMapHoveredElement) {
- this.tripMapHoveredElement.classList.remove("list-hover");
+ this.tripMapHoveredElement.classList.remove('list-hover');
this.tripMapHoveredElement = undefined;
}
@@ -468,8 +430,7 @@ export class SharedTripComponent implements AfterViewInit {
}
placeHighlightMarker(item: any) {
- if (this.tripMapHoveredElement || this.tripMapTemporaryMarker)
- this.resetPlaceHighlightMarker();
+ if (this.tripMapHoveredElement || this.tripMapTemporaryMarker) this.resetPlaceHighlightMarker();
let marker: L.Marker | undefined;
this.markerClusterGroup?.eachLayer((layer: any) => {
@@ -487,10 +448,7 @@ export class SharedTripComponent implements AfterViewInit {
// TripItem without place, but latlng
this.tripMapTemporaryMarker = tripDayMarker(item).addTo(this.map!);
if (this.tripMapGpxLayer) {
- this.map?.fitBounds(
- [[item.lat, item.lng], (this.tripMapGpxLayer as any).getBounds()],
- { padding: [30, 30] },
- );
+ this.map?.fitBounds([[item.lat, item.lng], (this.tripMapGpxLayer as any).getBounds()], { padding: [30, 30] });
} else this.map?.fitBounds([[item.lat, item.lng]], { padding: [60, 60] });
return;
}
@@ -499,18 +457,16 @@ export class SharedTripComponent implements AfterViewInit {
const markerElement = marker.getElement() as HTMLElement; // search for Marker. If 'null', is inside Cluster
if (markerElement) {
// marker, not clustered
- markerElement.classList.add("list-hover");
+ markerElement.classList.add('list-hover');
this.tripMapHoveredElement = markerElement;
targetLatLng = marker.getLatLng();
} else {
// marker is clustered
- const parentCluster = (this.markerClusterGroup as any).getVisibleParent(
- marker,
- );
+ const parentCluster = (this.markerClusterGroup as any).getVisibleParent(marker);
if (parentCluster) {
const clusterEl = parentCluster.getElement();
if (clusterEl) {
- clusterEl.classList.add("list-hover");
+ clusterEl.classList.add('list-hover');
this.tripMapHoveredElement = clusterEl;
}
targetLatLng = parentCluster.getLatLng();
@@ -574,11 +530,7 @@ export class SharedTripComponent implements AfterViewInit {
.filter((n) => n !== undefined);
if (items.length < 2) {
- this.utilsService.toast(
- "info",
- "Info",
- "Not enough values to map an itinerary",
- );
+ this.utilsService.toast('info', 'Info', 'Not enough values to map an itinerary');
return;
}
@@ -590,20 +542,20 @@ export class SharedTripComponent implements AfterViewInit {
const layGroup = L.featureGroup();
const COLORS: string[] = [
- "#e6194b",
- "#3cb44b",
- "#ffe119",
- "#4363d8",
- "#9a6324",
- "#f58231",
- "#911eb4",
- "#46f0f0",
- "#f032e6",
- "#bcf60c",
- "#fabebe",
- "#008080",
- "#e6beff",
- "#808000",
+ '#e6194b',
+ '#3cb44b',
+ '#ffe119',
+ '#4363d8',
+ '#9a6324',
+ '#f58231',
+ '#911eb4',
+ '#46f0f0',
+ '#f032e6',
+ '#bcf60c',
+ '#fabebe',
+ '#008080',
+ '#e6beff',
+ '#808000',
];
let prevPoint: [number, number] | null = null;
@@ -614,7 +566,7 @@ export class SharedTripComponent implements AfterViewInit {
dashArray: [10, 20],
weight: 5,
color: COLORS[idx % COLORS.length],
- pulseColor: "#FFFFFF",
+ pulseColor: '#FFFFFF',
paused: false,
reverse: false,
hardwareAccelerated: true,
@@ -665,9 +617,7 @@ export class SharedTripComponent implements AfterViewInit {
const idx = this.trip?.days.findIndex((d) => d.id === day_id);
if (!this.trip || idx === undefined || idx == -1) return;
- const data = this.trip.days[idx].items.sort((a, b) =>
- a.time < b.time ? -1 : a.time > b.time ? 1 : 0,
- );
+ const data = this.trip.days[idx].items.sort((a, b) => (a.time < b.time ? -1 : a.time > b.time ? 1 : 0));
const items = data
.map((item) => {
if (item.lat && item.lng)
@@ -693,11 +643,7 @@ export class SharedTripComponent implements AfterViewInit {
.filter((n) => n !== undefined);
if (items.length < 2) {
- this.utilsService.toast(
- "info",
- "Info",
- "Not enough values to map an itinerary",
- );
+ this.utilsService.toast('info', 'Info', 'Not enough values to map an itinerary');
return;
}
@@ -712,8 +658,8 @@ export class SharedTripComponent implements AfterViewInit {
delay: 400,
dashArray: [10, 20],
weight: 5,
- color: "#0000FF",
- pulseColor: "#FFFFFF",
+ color: '#0000FF',
+ pulseColor: '#FFFFFF',
paused: false,
reverse: false,
hardwareAccelerated: true,
@@ -751,15 +697,9 @@ export class SharedTripComponent implements AfterViewInit {
}
onMapMarkerClick(place_id: number) {
- const item = this.flattenedTripItems.find(
- (i) => i.place && i.place.id == place_id,
- );
+ const item = this.flattenedTripItems.find((i) => i.place && i.place.id == place_id);
if (!item) {
- this.utilsService.toast(
- "info",
- "Place not used",
- "The place is not used in the table",
- );
+ this.utilsService.toast('info', 'Place not used', 'The place is not used in the table');
return;
}
@@ -773,14 +713,14 @@ export class SharedTripComponent implements AfterViewInit {
// TODO: More services
// const url = `http://maps.apple.com/?daddr=${this.selectedItem.lat},${this.selectedItem.lng}`;
const url = `https://www.google.com/maps/dir/?api=1&destination=${this.selectedItem.lat},${this.selectedItem.lng}`;
- window.open(url, "_blank");
+ window.open(url, '_blank');
}
downloadItemGPX() {
if (!this.selectedItem?.gpx) return;
const dataBlob = new Blob([this.selectedItem.gpx]);
const downloadURL = URL.createObjectURL(dataBlob);
- const link = document.createElement("a");
+ const link = document.createElement('a');
link.href = downloadURL;
link.download = `TRIP_${this.trip?.name}_${this.selectedItem.text}.gpx`;
link.click();
@@ -791,27 +731,23 @@ export class SharedTripComponent implements AfterViewInit {
tripDayToNavigation(day_id: number) {
const idx = this.trip?.days.findIndex((d) => d.id === day_id);
if (!this.trip || idx === undefined || idx == -1) return;
- const data = this.trip.days[idx].items.sort((a, b) =>
- a.time < b.time ? -1 : a.time > b.time ? 1 : 0,
- );
+ const data = this.trip.days[idx].items.sort((a, b) => (a.time < b.time ? -1 : a.time > b.time ? 1 : 0));
const items = data.filter((item) => item.lat && item.lng);
if (!items.length) return;
- const waypoints = items.map((item) => `${item.lat},${item.lng}`).join("/");
+ const waypoints = items.map((item) => `${item.lat},${item.lng}`).join('/');
const url = `https://www.google.com/maps/dir/${waypoints}`;
- window.open(url, "_blank");
+ window.open(url, '_blank');
}
tripToNavigation() {
// TODO: More services
- const items = this.flattenedTripItems.filter(
- (item) => item.lat && item.lng,
- );
+ const items = this.flattenedTripItems.filter((item) => item.lat && item.lng);
if (!items.length) return;
- const waypoints = items.map((item) => `${item.lat},${item.lng}`).join("/");
+ const waypoints = items.map((item) => `${item.lat},${item.lng}`).join('/');
const url = `https://www.google.com/maps/dir/${waypoints}`;
- window.open(url, "_blank");
+ window.open(url, '_blank');
}
openPackingList() {
@@ -832,24 +768,13 @@ export class SharedTripComponent implements AfterViewInit {
computeDispPackingList() {
const sorted: PackingItem[] = [...this.packingList].sort((a, b) =>
- a.packed !== b.packed
- ? a.packed
- ? 1
- : -1
- : a.text < b.text
- ? -1
- : a.text > b.text
- ? 1
- : 0,
+ a.packed !== b.packed ? (a.packed ? 1 : -1) : a.text < b.text ? -1 : a.text > b.text ? 1 : 0,
);
- this.dispPackingList = sorted.reduce>(
- (acc, item) => {
- (acc[item.category] ??= []).push(item);
- return acc;
- },
- {},
- );
+ this.dispPackingList = sorted.reduce>((acc, item) => {
+ (acc[item.category] ??= []).push(item);
+ return acc;
+ }, {});
}
openChecklist() {
diff --git a/src/src/app/components/trip/trip.component.ts b/src/src/app/components/trip/trip.component.ts
index 126feb7..0612f5e 100644
--- a/src/src/app/components/trip/trip.component.ts
+++ b/src/src/app/components/trip/trip.component.ts
@@ -1,13 +1,13 @@
-import { AfterViewInit, Component } from "@angular/core";
-import { ApiService } from "../../services/api.service";
-import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { InputTextModule } from "primeng/inputtext";
-import { SkeletonModule } from "primeng/skeleton";
-import { FloatLabelModule } from "primeng/floatlabel";
-import * as L from "leaflet";
-import { antPath } from "leaflet-ant-path";
-import { TableModule } from "primeng/table";
+import { AfterViewInit, Component } from '@angular/core';
+import { ApiService } from '../../services/api.service';
+import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { InputTextModule } from 'primeng/inputtext';
+import { SkeletonModule } from 'primeng/skeleton';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import * as L from 'leaflet';
+import { antPath } from 'leaflet-ant-path';
+import { TableModule } from 'primeng/table';
import {
Trip,
FlattenedTripItem,
@@ -17,56 +17,41 @@ import {
PackingItem,
ChecklistItem,
TripMember,
-} from "../../types/trip";
-import { Place } from "../../types/poi";
-import {
- createMap,
- placeToMarker,
- createClusterGroup,
- tripDayMarker,
- gpxToPolyline,
-} from "../../shared/map";
-import { ActivatedRoute, Router } from "@angular/router";
-import { DialogService, DynamicDialogRef } from "primeng/dynamicdialog";
-import { TripPlaceSelectModalComponent } from "../../modals/trip-place-select-modal/trip-place-select-modal.component";
-import { TripCreateDayModalComponent } from "../../modals/trip-create-day-modal/trip-create-day-modal.component";
-import { TripCreateDayItemModalComponent } from "../../modals/trip-create-day-item-modal/trip-create-day-item-modal.component";
-import { TripCreateItemsModalComponent } from "../../modals/trip-create-items-modal/trip-create-items-modal.component";
-import {
- combineLatest,
- debounceTime,
- forkJoin,
- Observable,
- of,
- switchMap,
- take,
- tap,
-} from "rxjs";
-import { YesNoModalComponent } from "../../modals/yes-no-modal/yes-no-modal.component";
-import { UtilsService } from "../../services/utils.service";
-import { TripCreateModalComponent } from "../../modals/trip-create-modal/trip-create-modal.component";
-import { AsyncPipe, CommonModule, DecimalPipe } from "@angular/common";
-import { MenuItem } from "primeng/api";
-import { MenuModule } from "primeng/menu";
-import { LinkifyPipe } from "../../shared/linkify.pipe";
-import { PlaceCreateModalComponent } from "../../modals/place-create-modal/place-create-modal.component";
-import { Settings } from "../../types/settings";
-import { DialogModule } from "primeng/dialog";
-import { ClipboardModule } from "@angular/cdk/clipboard";
-import { TooltipModule } from "primeng/tooltip";
-import { MultiSelectModule } from "primeng/multiselect";
-import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import { CheckboxChangeEvent, CheckboxModule } from "primeng/checkbox";
-import { TripCreatePackingModalComponent } from "../../modals/trip-create-packing-modal/trip-create-packing-modal.component";
-import { TripCreateChecklistModalComponent } from "../../modals/trip-create-checklist-modal/trip-create-checklist-modal.component";
-import { TripInviteMemberModalComponent } from "../../modals/trip-invite-member-modal/trip-invite-member-modal.component";
-import { calculateDistanceBetween } from "../../shared/haversine";
-import { orderByPipe } from "../../shared/order-by.pipe";
-import { TripNotesModalComponent } from "../../modals/trip-notes-modal/trip-notes-modal.component";
-import { TripArchiveModalComponent } from "../../modals/trip-archive-modal/trip-archive-modal.component";
+} from '../../types/trip';
+import { Place } from '../../types/poi';
+import { createMap, placeToMarker, createClusterGroup, tripDayMarker, gpxToPolyline } from '../../shared/map';
+import { ActivatedRoute, Router } from '@angular/router';
+import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { TripPlaceSelectModalComponent } from '../../modals/trip-place-select-modal/trip-place-select-modal.component';
+import { TripCreateDayModalComponent } from '../../modals/trip-create-day-modal/trip-create-day-modal.component';
+import { TripCreateDayItemModalComponent } from '../../modals/trip-create-day-item-modal/trip-create-day-item-modal.component';
+import { TripCreateItemsModalComponent } from '../../modals/trip-create-items-modal/trip-create-items-modal.component';
+import { combineLatest, debounceTime, forkJoin, Observable, of, switchMap, take, tap } from 'rxjs';
+import { YesNoModalComponent } from '../../modals/yes-no-modal/yes-no-modal.component';
+import { UtilsService } from '../../services/utils.service';
+import { TripCreateModalComponent } from '../../modals/trip-create-modal/trip-create-modal.component';
+import { AsyncPipe, CommonModule, DecimalPipe } from '@angular/common';
+import { MenuItem } from 'primeng/api';
+import { MenuModule } from 'primeng/menu';
+import { LinkifyPipe } from '../../shared/linkify.pipe';
+import { PlaceCreateModalComponent } from '../../modals/place-create-modal/place-create-modal.component';
+import { Settings } from '../../types/settings';
+import { DialogModule } from 'primeng/dialog';
+import { ClipboardModule } from '@angular/cdk/clipboard';
+import { TooltipModule } from 'primeng/tooltip';
+import { MultiSelectModule } from 'primeng/multiselect';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { CheckboxChangeEvent, CheckboxModule } from 'primeng/checkbox';
+import { TripCreatePackingModalComponent } from '../../modals/trip-create-packing-modal/trip-create-packing-modal.component';
+import { TripCreateChecklistModalComponent } from '../../modals/trip-create-checklist-modal/trip-create-checklist-modal.component';
+import { TripInviteMemberModalComponent } from '../../modals/trip-invite-member-modal/trip-invite-member-modal.component';
+import { calculateDistanceBetween } from '../../shared/haversine';
+import { orderByPipe } from '../../shared/order-by.pipe';
+import { TripNotesModalComponent } from '../../modals/trip-notes-modal/trip-notes-modal.component';
+import { TripArchiveModalComponent } from '../../modals/trip-archive-modal/trip-archive-modal.component';
@Component({
- selector: "app-trip",
+ selector: 'app-trip',
standalone: true,
imports: [
CommonModule,
@@ -88,8 +73,8 @@ import { TripArchiveModalComponent } from "../../modals/trip-archive-modal/trip-
CheckboxModule,
orderByPipe,
],
- templateUrl: "./trip.component.html",
- styleUrls: ["./trip.component.scss"],
+ templateUrl: './trip.component.html',
+ styleUrls: ['./trip.component.scss'],
})
export class TripComponent implements AfterViewInit {
tripSharedURL$?: Observable;
@@ -129,18 +114,18 @@ export class TripComponent implements AfterViewInit {
readonly menuTripActionsItems: MenuItem[] = [
{
- label: "Lists",
+ label: 'Lists',
items: [
{
- label: "Checklist",
- icon: "pi pi-check-square",
+ label: 'Checklist',
+ icon: 'pi pi-check-square',
command: () => {
this.openChecklist();
},
},
{
- label: "Packing",
- icon: "pi pi-briefcase",
+ label: 'Packing',
+ icon: 'pi pi-briefcase',
command: () => {
this.openPackingList();
},
@@ -148,18 +133,18 @@ export class TripComponent implements AfterViewInit {
],
},
{
- label: "Collaboration",
+ label: 'Collaboration',
items: [
{
- label: "Members",
- icon: "pi pi-users",
+ label: 'Members',
+ icon: 'pi pi-users',
command: () => {
this.openMembersDialog();
},
},
{
- label: "Share",
- icon: "pi pi-share-alt",
+ label: 'Share',
+ icon: 'pi pi-share-alt',
command: () => {
this.shareDialogVisible = true;
},
@@ -167,39 +152,39 @@ export class TripComponent implements AfterViewInit {
],
},
{
- label: "Trip",
+ label: 'Trip',
items: [
{
- label: "Pretty Print",
- icon: "pi pi-print",
+ label: 'Pretty Print',
+ icon: 'pi pi-print',
command: () => {
this.togglePrint();
},
},
{
- label: "Notes",
- icon: "pi pi-info-circle",
+ label: 'Notes',
+ icon: 'pi pi-info-circle',
command: () => {
this.openTripNotesModal();
},
},
{
- label: "Archive",
- icon: "pi pi-box",
+ label: 'Archive',
+ icon: 'pi pi-box',
command: () => {
this.openArchiveTripModal();
},
},
{
- label: "Edit",
- icon: "pi pi-pencil",
+ label: 'Edit',
+ icon: 'pi pi-pencil',
command: () => {
this.editTrip();
},
},
{
- label: "Delete",
- icon: "pi pi-trash",
+ label: 'Delete',
+ icon: 'pi pi-trash',
command: () => {
this.deleteTrip();
},
@@ -209,11 +194,11 @@ export class TripComponent implements AfterViewInit {
];
readonly menuTripTableActionsItems: MenuItem[] = [
{
- label: "Actions",
+ label: 'Actions',
items: [
{
- label: "Pretty Print",
- icon: "pi pi-print",
+ label: 'Pretty Print',
+ icon: 'pi pi-print',
command: () => {
this.togglePrint();
},
@@ -221,18 +206,18 @@ export class TripComponent implements AfterViewInit {
],
},
{
- label: "Table",
+ label: 'Table',
items: [
{
- label: "Filter",
- icon: "pi pi-filter",
+ label: 'Filter',
+ icon: 'pi pi-filter',
command: () => {
this.toggleFiltering();
},
},
{
- label: "Group",
- icon: "pi pi-arrow-down-left-and-arrow-up-right-to-center",
+ label: 'Group',
+ icon: 'pi pi-arrow-down-left-and-arrow-up-right-to-center',
command: () => {
this.tableExpandableMode = !this.tableExpandableMode;
},
@@ -240,18 +225,18 @@ export class TripComponent implements AfterViewInit {
],
},
{
- label: "Directions",
+ label: 'Directions',
items: [
{
- label: "Highlight",
- icon: "pi pi-directions",
+ label: 'Highlight',
+ icon: 'pi pi-directions',
command: () => {
this.toggleTripDaysHighlight();
},
},
{
- label: "GMaps itinerary",
- icon: "pi pi-car",
+ label: 'GMaps itinerary',
+ icon: 'pi pi-car',
command: () => {
this.tripToNavigation();
},
@@ -261,28 +246,28 @@ export class TripComponent implements AfterViewInit {
];
readonly menuTripDayActionsItems: MenuItem[] = [
{
- label: "Actions",
+ label: 'Actions',
items: [
{
- label: "Item",
- icon: "pi pi-plus",
- iconClass: "text-blue-500!",
+ label: 'Item',
+ icon: 'pi pi-plus',
+ iconClass: 'text-blue-500!',
command: () => {
this.addItem();
},
},
{
- label: "Edit",
- icon: "pi pi-pencil",
+ label: 'Edit',
+ icon: 'pi pi-pencil',
command: () => {
if (!this.selectedTripDayForMenu) return;
this.editDay(this.selectedTripDayForMenu);
},
},
{
- label: "Delete",
- icon: "pi pi-trash",
- iconClass: "text-red-500!",
+ label: 'Delete',
+ icon: 'pi pi-trash',
+ iconClass: 'text-red-500!',
command: () => {
if (!this.selectedTripDayForMenu) return;
this.deleteDay(this.selectedTripDayForMenu);
@@ -292,24 +277,18 @@ export class TripComponent implements AfterViewInit {
},
];
readonly tripTableColumns: string[] = [
- "day",
- "time",
- "text",
- "place",
- "comment",
- "LatLng",
- "price",
- "status",
- "distance",
+ 'day',
+ 'time',
+ 'text',
+ 'place',
+ 'comment',
+ 'LatLng',
+ 'price',
+ 'status',
+ 'distance',
];
- tripTableSelectedColumns: string[] = [
- "day",
- "time",
- "text",
- "place",
- "comment",
- ];
- tripTableSearchInput = new FormControl("");
+ tripTableSelectedColumns: string[] = ['day', 'time', 'text', 'place', 'comment'];
+ tripTableSearchInput = new FormControl('');
selectedTripDayForMenu?: TripDay;
dayStatsCache = new Map();
@@ -323,14 +302,12 @@ export class TripComponent implements AfterViewInit {
private route: ActivatedRoute,
) {
this.statuses = this.utilsService.statuses;
- this.tripTableSearchInput.valueChanges
- .pipe(debounceTime(300), takeUntilDestroyed())
- .subscribe({
- next: (value) => {
- if (value) this.flattenTripDayItems(value.toLowerCase());
- else this.flattenTripDayItems();
- },
- });
+ this.tripTableSearchInput.valueChanges.pipe(debounceTime(300), takeUntilDestroyed()).subscribe({
+ next: (value) => {
+ if (value) this.flattenTripDayItems(value.toLowerCase());
+ else this.flattenTripDayItems();
+ },
+ });
}
ngAfterViewInit(): void {
@@ -338,7 +315,7 @@ export class TripComponent implements AfterViewInit {
.pipe(
take(1),
tap((params) => {
- const id = params.get("id");
+ const id = params.get('id');
if (id) {
this.loadTripData(+id);
this.tripSharedURL$ = this.apiService.getSharedTripURL(+id);
@@ -370,12 +347,10 @@ export class TripComponent implements AfterViewInit {
initMap(settings: Settings): void {
const contentMenuItems = [
{
- text: "Copy coordinates",
+ text: 'Copy coordinates',
callback: (e: any) => {
const latlng = e.latlng;
- navigator.clipboard.writeText(
- `${parseFloat(latlng.lat).toFixed(5)}, ${parseFloat(latlng.lng).toFixed(5)}`,
- );
+ navigator.clipboard.writeText(`${parseFloat(latlng.lat).toFixed(5)}, ${parseFloat(latlng.lng).toFixed(5)}`);
},
},
];
@@ -388,7 +363,7 @@ export class TripComponent implements AfterViewInit {
}
back() {
- this.router.navigateByUrl("/trips");
+ this.router.navigateByUrl('/trips');
}
togglePrint() {
@@ -423,11 +398,7 @@ export class TripComponent implements AfterViewInit {
if (!this.trip?.days) return [];
return this.trip.days
- .flatMap((day) =>
- day.items.filter((item) =>
- ["constraint", "pending"].includes(item.status as string),
- ),
- )
+ .flatMap((day) => day.items.filter((item) => ['constraint', 'pending'].includes(item.status as string)))
.map((item) => ({
...item,
status: this.statusToTripStatus(item.status as string),
@@ -501,9 +472,7 @@ export class TripComponent implements AfterViewInit {
setPlacesAndMarkers() {
this.computePlacesUsedInTable();
- this.places = [...(this.trip?.places ?? [])].sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
- );
+ this.places = [...(this.trip?.places ?? [])].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
this.markerClusterGroup?.clearLayers();
this.places.forEach((p) => {
const marker = this._placeToMarker(p);
@@ -512,12 +481,8 @@ export class TripComponent implements AfterViewInit {
}
_placeToMarker(place: Place): L.Marker {
- const marker = placeToMarker(
- place,
- false,
- !this.placesUsedInTable.has(place.id),
- );
- marker.on("click", () => {
+ const marker = placeToMarker(place, false, !this.placesUsedInTable.has(place.id));
+ marker.on('click', () => {
this.onMapMarkerClick(place.id);
marker.closeTooltip();
});
@@ -527,9 +492,7 @@ export class TripComponent implements AfterViewInit {
resetMapBounds() {
if (!this.places.length) {
this.map?.fitBounds(
- this.flattenedTripItems
- .filter((i) => i.lat != null && i.lng != null)
- .map((i) => [i.lat!, i.lng!]),
+ this.flattenedTripItems.filter((i) => i.lat != null && i.lng != null).map((i) => [i.lat!, i.lng!]),
{ padding: [30, 30] },
);
return;
@@ -543,7 +506,7 @@ export class TripComponent implements AfterViewInit {
toggleMapFullscreen() {
this.isMapFullscreen = !this.isMapFullscreen;
- document.body.classList.toggle("overflow-hidden");
+ document.body.classList.toggle('overflow-hidden');
setTimeout(() => {
this.map?.invalidateSize();
@@ -562,14 +525,12 @@ export class TripComponent implements AfterViewInit {
return;
}
this.totalPrice =
- this.trip?.days
- .flatMap((d) => d.items)
- .reduce((price, item) => price + (item.price ?? 0), 0) ?? 0;
+ this.trip?.days.flatMap((d) => d.items).reduce((price, item) => price + (item.price ?? 0), 0) ?? 0;
}
resetPlaceHighlightMarker() {
if (this.tripMapHoveredElement) {
- this.tripMapHoveredElement.classList.remove("list-hover");
+ this.tripMapHoveredElement.classList.remove('list-hover');
this.tripMapHoveredElement = undefined;
}
@@ -586,8 +547,7 @@ export class TripComponent implements AfterViewInit {
}
placeHighlightMarker(item: any) {
- if (this.tripMapHoveredElement || this.tripMapTemporaryMarker)
- this.resetPlaceHighlightMarker();
+ if (this.tripMapHoveredElement || this.tripMapTemporaryMarker) this.resetPlaceHighlightMarker();
let marker: L.Marker | undefined;
this.markerClusterGroup?.eachLayer((layer: any) => {
@@ -605,10 +565,7 @@ export class TripComponent implements AfterViewInit {
// TripItem without place, but latlng
this.tripMapTemporaryMarker = tripDayMarker(item).addTo(this.map!);
if (this.tripMapGpxLayer) {
- this.map?.fitBounds(
- [[item.lat, item.lng], (this.tripMapGpxLayer as any).getBounds()],
- { padding: [30, 30] },
- );
+ this.map?.fitBounds([[item.lat, item.lng], (this.tripMapGpxLayer as any).getBounds()], { padding: [30, 30] });
} else this.map?.fitBounds([[item.lat, item.lng]], { padding: [60, 60] });
return;
}
@@ -617,18 +574,16 @@ export class TripComponent implements AfterViewInit {
const markerElement = marker.getElement() as HTMLElement; // search for Marker. If 'null', is inside Cluster
if (markerElement) {
// marker, not clustered
- markerElement.classList.add("list-hover");
+ markerElement.classList.add('list-hover');
this.tripMapHoveredElement = markerElement;
targetLatLng = marker.getLatLng();
} else {
// marker is clustered
- const parentCluster = (this.markerClusterGroup as any).getVisibleParent(
- marker,
- );
+ const parentCluster = (this.markerClusterGroup as any).getVisibleParent(marker);
if (parentCluster) {
const clusterEl = parentCluster.getElement();
if (clusterEl) {
- clusterEl.classList.add("list-hover");
+ clusterEl.classList.add('list-hover');
this.tripMapHoveredElement = clusterEl;
}
targetLatLng = parentCluster.getLatLng();
@@ -692,11 +647,7 @@ export class TripComponent implements AfterViewInit {
.filter((n) => n !== undefined);
if (items.length < 2) {
- this.utilsService.toast(
- "info",
- "Info",
- "Not enough values to map an itinerary",
- );
+ this.utilsService.toast('info', 'Info', 'Not enough values to map an itinerary');
return;
}
@@ -708,20 +659,20 @@ export class TripComponent implements AfterViewInit {
const layGroup = L.featureGroup();
const COLORS: string[] = [
- "#e6194b",
- "#3cb44b",
- "#ffe119",
- "#4363d8",
- "#9a6324",
- "#f58231",
- "#911eb4",
- "#46f0f0",
- "#f032e6",
- "#bcf60c",
- "#fabebe",
- "#008080",
- "#e6beff",
- "#808000",
+ '#e6194b',
+ '#3cb44b',
+ '#ffe119',
+ '#4363d8',
+ '#9a6324',
+ '#f58231',
+ '#911eb4',
+ '#46f0f0',
+ '#f032e6',
+ '#bcf60c',
+ '#fabebe',
+ '#008080',
+ '#e6beff',
+ '#808000',
];
let prevPoint: [number, number] | null = null;
@@ -732,7 +683,7 @@ export class TripComponent implements AfterViewInit {
dashArray: [10, 20],
weight: 5,
color: COLORS[idx % COLORS.length],
- pulseColor: "#FFFFFF",
+ pulseColor: '#FFFFFF',
paused: false,
reverse: false,
hardwareAccelerated: true,
@@ -783,9 +734,7 @@ export class TripComponent implements AfterViewInit {
const idx = this.trip?.days.findIndex((d) => d.id === day_id);
if (!this.trip || idx === undefined || idx == -1) return;
- const data = this.trip.days[idx].items.sort((a, b) =>
- a.time < b.time ? -1 : a.time > b.time ? 1 : 0,
- );
+ const data = this.trip.days[idx].items.sort((a, b) => (a.time < b.time ? -1 : a.time > b.time ? 1 : 0));
const items = data
.map((item) => {
if (item.lat && item.lng)
@@ -811,11 +760,7 @@ export class TripComponent implements AfterViewInit {
.filter((n) => n !== undefined);
if (items.length < 2) {
- this.utilsService.toast(
- "info",
- "Info",
- "Not enough values to map an itinerary",
- );
+ this.utilsService.toast('info', 'Info', 'Not enough values to map an itinerary');
return;
}
@@ -830,8 +775,8 @@ export class TripComponent implements AfterViewInit {
delay: 400,
dashArray: [10, 20],
weight: 5,
- color: "#0000FF",
- pulseColor: "#FFFFFF",
+ color: '#0000FF',
+ pulseColor: '#FFFFFF',
paused: false,
reverse: false,
hardwareAccelerated: true,
@@ -869,15 +814,9 @@ export class TripComponent implements AfterViewInit {
}
onMapMarkerClick(place_id: number) {
- const item = this.flattenedTripItems.find(
- (i) => i.place && i.place.id == place_id,
- );
+ const item = this.flattenedTripItems.find((i) => i.place && i.place.id == place_id);
if (!item) {
- this.utilsService.toast(
- "info",
- "Place not used",
- "The place is not used in the table",
- );
+ this.utilsService.toast('info', 'Place not used', 'The place is not used in the table');
return;
}
@@ -888,12 +827,12 @@ export class TripComponent implements AfterViewInit {
deleteTrip() {
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Delete ${this.trip?.name} ? This will delete everything.`,
})!;
@@ -906,7 +845,7 @@ export class TripComponent implements AfterViewInit {
.pipe(take(1))
.subscribe({
next: () => {
- this.router.navigateByUrl("/trips");
+ this.router.navigateByUrl('/trips');
},
});
},
@@ -914,21 +853,18 @@ export class TripComponent implements AfterViewInit {
}
editTrip() {
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateModalComponent,
- {
- header: "Update Trip",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "50vw",
- breakpoints: {
- "640px": "80vw",
- },
- data: { trip: this.trip },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateModalComponent, {
+ header: 'Update Trip',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '50vw',
+ breakpoints: {
+ '640px': '80vw',
},
- )!;
+ data: { trip: this.trip },
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (new_trip: Trip | null) => {
@@ -946,12 +882,12 @@ export class TripComponent implements AfterViewInit {
openUnarchiveTripModal() {
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Restore Trip",
+ header: 'Restore Trip',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Restore ${this.trip?.name} ?`,
})!;
@@ -977,10 +913,10 @@ export class TripComponent implements AfterViewInit {
modal: true,
closable: true,
dismissableMask: true,
- width: "30vw",
+ width: '30vw',
breakpoints: {
- "1024px": "60vw",
- "640px": "90vw",
+ '1024px': '60vw',
+ '640px': '90vw',
},
data: this.trip,
})!;
@@ -989,10 +925,7 @@ export class TripComponent implements AfterViewInit {
next: (review: string) => {
if (review === undefined) return;
this.apiService
- .putTrip(
- { archived: !currentArchiveStatus, archival_review: review },
- this.trip?.id!,
- )
+ .putTrip({ archived: !currentArchiveStatus, archival_review: review }, this.trip?.id!)
.pipe(take(1))
.subscribe({
next: (trip) => (this.trip = trip),
@@ -1004,21 +937,18 @@ export class TripComponent implements AfterViewInit {
addDay() {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateDayModalComponent,
- {
- header: "Create Day",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "50vw",
- data: { days: this.trip.days },
- breakpoints: {
- "640px": "80vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateDayModalComponent, {
+ header: 'Create Day',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '50vw',
+ data: { days: this.trip.days },
+ breakpoints: {
+ '640px': '80vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (day: TripDay | null) => {
@@ -1042,14 +972,14 @@ export class TripComponent implements AfterViewInit {
// TODO: More services
// const url = `http://maps.apple.com/?daddr=${this.selectedItem.lat},${this.selectedItem.lng}`;
const url = `https://www.google.com/maps/dir/?api=1&destination=${this.selectedItem.lat},${this.selectedItem.lng}`;
- window.open(url, "_blank");
+ window.open(url, '_blank');
}
downloadItemGPX() {
if (!this.selectedItem?.gpx) return;
const dataBlob = new Blob([this.selectedItem.gpx]);
const downloadURL = URL.createObjectURL(dataBlob);
- const link = document.createElement("a");
+ const link = document.createElement('a');
link.href = downloadURL;
link.download = `TRIP_${this.trip?.name}_${this.selectedItem.text}.gpx`;
link.click();
@@ -1060,47 +990,40 @@ export class TripComponent implements AfterViewInit {
tripDayToNavigation(day_id: number) {
const idx = this.trip?.days.findIndex((d) => d.id === day_id);
if (!this.trip || idx === undefined || idx == -1) return;
- const data = this.trip.days[idx].items.sort((a, b) =>
- a.time < b.time ? -1 : a.time > b.time ? 1 : 0,
- );
+ const data = this.trip.days[idx].items.sort((a, b) => (a.time < b.time ? -1 : a.time > b.time ? 1 : 0));
const items = data.filter((item) => item.lat && item.lng);
if (!items.length) return;
- const waypoints = items.map((item) => `${item.lat},${item.lng}`).join("/");
+ const waypoints = items.map((item) => `${item.lat},${item.lng}`).join('/');
const url = `https://www.google.com/maps/dir/${waypoints}`;
- window.open(url, "_blank");
+ window.open(url, '_blank');
}
tripToNavigation() {
// TODO: More services
- const items = this.flattenedTripItems.filter(
- (item) => item.lat && item.lng,
- );
+ const items = this.flattenedTripItems.filter((item) => item.lat && item.lng);
if (!items.length) return;
- const waypoints = items.map((item) => `${item.lat},${item.lng}`).join("/");
+ const waypoints = items.map((item) => `${item.lat},${item.lng}`).join('/');
const url = `https://www.google.com/maps/dir/${waypoints}`;
- window.open(url, "_blank");
+ window.open(url, '_blank');
}
editDay(day: TripDay) {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateDayModalComponent,
- {
- header: "Create Day",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "50vw",
- data: { day: day, days: this.trip.days },
- breakpoints: {
- "640px": "80vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateDayModalComponent, {
+ header: 'Create Day',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '50vw',
+ data: { day: day, days: this.trip.days },
+ breakpoints: {
+ '640px': '80vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (day: TripDay | null) => {
@@ -1126,12 +1049,12 @@ export class TripComponent implements AfterViewInit {
if (!this.trip) return;
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Delete ${day.label} ? This will delete everything for this day.`,
})!;
@@ -1159,21 +1082,18 @@ export class TripComponent implements AfterViewInit {
manageTripPlaces() {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripPlaceSelectModalComponent,
- {
- header: "Select Place(s)",
- modal: true,
- appendTo: "body",
- closable: true,
- width: "50vw",
- data: { places: this.places },
- breakpoints: {
- "960px": "80vw",
- "640px": "90vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripPlaceSelectModalComponent, {
+ header: 'Select Place(s)',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ width: '50vw',
+ data: { places: this.places },
+ breakpoints: {
+ '960px': '80vw',
+ '640px': '90vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (places: Place[] | null) => {
@@ -1196,37 +1116,30 @@ export class TripComponent implements AfterViewInit {
addItem(day_id?: number) {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateDayItemModalComponent,
- {
- header: "Create Item",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "75vw",
- breakpoints: {
- "1260px": "90vw",
- },
- data: {
- places: this.places,
- days: this.trip.days,
- selectedDay: day_id,
- members: this.tripMembers,
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateDayItemModalComponent, {
+ header: 'Create Item',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '75vw',
+ breakpoints: {
+ '1260px': '90vw',
},
- )!;
+ data: {
+ places: this.places,
+ days: this.trip.days,
+ selectedDay: day_id,
+ members: this.tripMembers,
+ },
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (item: (TripItem & { day_id: number[] }) | null) => {
if (!item) return;
const obs$ = item.day_id.map((day_id) =>
- this.apiService.postTripDayItem(
- { ...item, day_id },
- this.trip!.id!,
- day_id,
- ),
+ this.apiService.postTripDayItem({ ...item, day_id }, this.trip!.id!, day_id),
);
forkJoin(obs$)
@@ -1234,9 +1147,7 @@ export class TripComponent implements AfterViewInit {
.subscribe({
next: (items: TripItem[]) => {
items.forEach((item) => {
- const idx = this.trip!.days.findIndex(
- (d) => d.id == item.day_id,
- );
+ const idx = this.trip!.days.findIndex((d) => d.id == item.day_id);
if (idx === -1) return;
const td: TripDay = this.trip!.days[idx];
@@ -1261,29 +1172,26 @@ export class TripComponent implements AfterViewInit {
editItem(item: TripItem) {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateDayItemModalComponent,
- {
- header: "Update Item",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "75vw",
- breakpoints: {
- "1260px": "90vw",
- },
- data: {
- places: this.places,
- days: this.trip?.days,
- item: {
- ...item,
- status: item.status ? (item.status as TripStatus).label : null,
- },
- members: this.tripMembers,
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateDayItemModalComponent, {
+ header: 'Update Item',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '75vw',
+ breakpoints: {
+ '1260px': '90vw',
},
- )!;
+ data: {
+ places: this.places,
+ days: this.trip?.days,
+ item: {
+ ...item,
+ status: item.status ? (item.status as TripStatus).label : null,
+ },
+ members: this.tripMembers,
+ },
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (updated: TripItem | null) => {
@@ -1302,12 +1210,12 @@ export class TripComponent implements AfterViewInit {
deleteItem(item: TripItem) {
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Delete ${item.text.substring(0, 50)} ? This will delete everything for this day.`,
})!;
@@ -1330,29 +1238,24 @@ export class TripComponent implements AfterViewInit {
addItems() {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateItemsModalComponent,
- {
- header: "Create Items",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "75vw",
- breakpoints: {
- "1260px": "90vw",
- },
- data: { days: this.trip.days },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateItemsModalComponent, {
+ header: 'Create Items',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '75vw',
+ breakpoints: {
+ '1260px': '90vw',
},
- )!;
+ data: { days: this.trip.days },
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (items: TripItem[] | null) => {
if (!items?.length) return;
const day_id = items[0].day_id;
- const obs$ = items.map((item) =>
- this.apiService.postTripDayItem(item, this.trip!.id!, item.day_id),
- );
+ const obs$ = items.map((item) => this.apiService.postTripDayItem(item, this.trip!.id!, item.day_id));
forkJoin(obs$)
.pipe(take(1))
@@ -1371,21 +1274,18 @@ export class TripComponent implements AfterViewInit {
}
addPlace() {
- const modal: DynamicDialogRef = this.dialogService.open(
- PlaceCreateModalComponent,
- {
- header: "Create Place",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "55vw",
- breakpoints: {
- "1920px": "70vw",
- "1260px": "90vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(PlaceCreateModalComponent, {
+ header: 'Create Place',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '55vw',
+ breakpoints: {
+ '1920px': '70vw',
+ '1260px': '90vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (place: Place | null) => {
@@ -1395,10 +1295,7 @@ export class TripComponent implements AfterViewInit {
.postPlace(place)
.pipe(
switchMap((createdPlace: Place) =>
- this.apiService.putTrip(
- { place_ids: [createdPlace, ...this.places].map((p) => p.id) },
- this.trip?.id!,
- ),
+ this.apiService.putTrip({ place_ids: [createdPlace, ...this.places].map((p) => p.id) }, this.trip?.id!),
),
take(1),
)
@@ -1414,24 +1311,21 @@ export class TripComponent implements AfterViewInit {
}
editPlace(pEdit: Place) {
- const modal: DynamicDialogRef = this.dialogService.open(
- PlaceCreateModalComponent,
- {
- header: "Edit Place",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "55vw",
- breakpoints: {
- "1920px": "70vw",
- "1260px": "90vw",
- },
- data: {
- place: { ...pEdit, category: pEdit.category.id },
- },
+ const modal: DynamicDialogRef = this.dialogService.open(PlaceCreateModalComponent, {
+ header: 'Edit Place',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '55vw',
+ breakpoints: {
+ '1920px': '70vw',
+ '1260px': '90vw',
},
- )!;
+ data: {
+ place: { ...pEdit, category: pEdit.category.id },
+ },
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (p: Place | null) => {
@@ -1446,9 +1340,7 @@ export class TripComponent implements AfterViewInit {
const idx = places.findIndex((p) => p.id == place.id);
if (idx > -1) places.splice(idx, 1, place);
places.push(place);
- places.sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
- );
+ places.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
if (this.selectedItem?.place) this.selectedItem.place = place;
},
});
@@ -1487,9 +1379,7 @@ export class TripComponent implements AfterViewInit {
if (this.selectedItem && this.selectedItem.id === old.id) {
this.selectedItem = {
...updated,
- status: updated.status
- ? this.statusToTripStatus(updated.status as string)
- : undefined,
+ status: updated.status ? this.statusToTripStatus(updated.status as string) : undefined,
};
}
}
@@ -1538,12 +1428,12 @@ export class TripComponent implements AfterViewInit {
if (!this.trip) return;
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Stop sharing ${this.trip.name} ?`,
})!;
@@ -1583,21 +1473,18 @@ export class TripComponent implements AfterViewInit {
addPackingItem() {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreatePackingModalComponent,
- {
- header: "Create Packing",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "40vw",
- breakpoints: {
- "1260px": "70vw",
- "600px": "90vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreatePackingModalComponent, {
+ header: 'Create Packing',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '40vw',
+ breakpoints: {
+ '1260px': '70vw',
+ '600px': '90vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (item: PackingItem | null) => {
@@ -1632,12 +1519,12 @@ export class TripComponent implements AfterViewInit {
deletePackingItem(item: PackingItem) {
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Delete ${item.text.substring(0, 50)} ?`,
})!;
@@ -1661,24 +1548,13 @@ export class TripComponent implements AfterViewInit {
computeDispPackingList() {
const sorted: PackingItem[] = [...this.packingList].sort((a, b) =>
- a.packed !== b.packed
- ? a.packed
- ? 1
- : -1
- : a.text < b.text
- ? -1
- : a.text > b.text
- ? 1
- : 0,
+ a.packed !== b.packed ? (a.packed ? 1 : -1) : a.text < b.text ? -1 : a.text > b.text ? 1 : 0,
);
- this.dispPackingList = sorted.reduce>(
- (acc, item) => {
- (acc[item.category] ??= []).push(item);
- return acc;
- },
- {},
- );
+ this.dispPackingList = sorted.reduce>((acc, item) => {
+ (acc[item.category] ??= []).push(item);
+ return acc;
+ }, {});
}
openChecklist() {
@@ -1699,21 +1575,18 @@ export class TripComponent implements AfterViewInit {
addChecklistItem() {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateChecklistModalComponent,
- {
- header: "Create item",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "40vw",
- breakpoints: {
- "1260px": "70vw",
- "600px": "90vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateChecklistModalComponent, {
+ header: 'Create item',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '40vw',
+ breakpoints: {
+ '1260px': '70vw',
+ '600px': '90vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (item: ChecklistItem | null) => {
@@ -1746,12 +1619,12 @@ export class TripComponent implements AfterViewInit {
deleteChecklistItem(item: ChecklistItem) {
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Delete ${item.text.substring(0, 50)} ?`,
})!;
@@ -1764,9 +1637,7 @@ export class TripComponent implements AfterViewInit {
.pipe(take(1))
.subscribe({
next: () => {
- const index = this.checklistItems.findIndex(
- (p) => p.id == item.id,
- );
+ const index = this.checklistItems.findIndex((p) => p.id == item.id);
if (index > -1) this.checklistItems.splice(index, 1);
},
});
@@ -1804,21 +1675,18 @@ export class TripComponent implements AfterViewInit {
addMember() {
if (!this.trip) return;
- const modal: DynamicDialogRef = this.dialogService.open(
- TripInviteMemberModalComponent,
- {
- header: "Invite member",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "40vw",
- breakpoints: {
- "1260px": "70vw",
- "600px": "90vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripInviteMemberModalComponent, {
+ header: 'Invite member',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '40vw',
+ breakpoints: {
+ '1260px': '70vw',
+ '600px': '90vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (user: string | null) => {
@@ -1838,12 +1706,12 @@ export class TripComponent implements AfterViewInit {
deleteMember(username: string) {
const modal = this.dialogService.open(YesNoModalComponent, {
- header: "Confirm deletion",
+ header: 'Confirm deletion',
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
- "640px": "90vw",
+ '640px': '90vw',
},
data: `Delete ${username.substring(0, 50)} from Trip ?`,
})!;
@@ -1856,9 +1724,7 @@ export class TripComponent implements AfterViewInit {
.pipe(take(1))
.subscribe({
next: () => {
- const index = this.tripMembers.findIndex(
- (p) => p.user == username,
- );
+ const index = this.tripMembers.findIndex((p) => p.user == username);
if (index > -1) this.tripMembers.splice(index, 1);
},
});
@@ -1868,14 +1734,14 @@ export class TripComponent implements AfterViewInit {
openTripNotesModal() {
const modal = this.dialogService.open(TripNotesModalComponent, {
- header: "Notes",
+ header: 'Notes',
modal: true,
closable: true,
dismissableMask: true,
- width: "40vw",
+ width: '40vw',
breakpoints: {
- "1024px": "70vw",
- "640px": "90vw",
+ '1024px': '70vw',
+ '640px': '90vw',
},
data: this.trip?.notes,
})!;
diff --git a/src/src/app/components/trips/trips.component.ts b/src/src/app/components/trips/trips.component.ts
index 2c2f46b..3989062 100644
--- a/src/src/app/components/trips/trips.component.ts
+++ b/src/src/app/components/trips/trips.component.ts
@@ -1,14 +1,14 @@
-import { Component, OnInit } from "@angular/core";
-import { ApiService } from "../../services/api.service";
-import { ButtonModule } from "primeng/button";
-import { SkeletonModule } from "primeng/skeleton";
-import { TripBase, TripInvitation } from "../../types/trip";
-import { DialogService, DynamicDialogRef } from "primeng/dynamicdialog";
-import { TripCreateModalComponent } from "../../modals/trip-create-modal/trip-create-modal.component";
-import { Router } from "@angular/router";
-import { forkJoin, take } from "rxjs";
-import { DatePipe } from "@angular/common";
-import { DialogModule } from "primeng/dialog";
+import { Component, OnInit } from '@angular/core';
+import { ApiService } from '../../services/api.service';
+import { ButtonModule } from 'primeng/button';
+import { SkeletonModule } from 'primeng/skeleton';
+import { TripBase, TripInvitation } from '../../types/trip';
+import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { TripCreateModalComponent } from '../../modals/trip-create-modal/trip-create-modal.component';
+import { Router } from '@angular/router';
+import { forkJoin, take } from 'rxjs';
+import { DatePipe } from '@angular/common';
+import { DialogModule } from 'primeng/dialog';
interface TripBaseWithDates extends TripBase {
from?: Date;
@@ -16,11 +16,11 @@ interface TripBaseWithDates extends TripBase {
}
@Component({
- selector: "app-trips",
+ selector: 'app-trips',
standalone: true,
imports: [SkeletonModule, ButtonModule, DialogModule, DatePipe],
- templateUrl: "./trips.component.html",
- styleUrls: ["./trips.component.scss"],
+ templateUrl: './trips.component.html',
+ styleUrls: ['./trips.component.scss'],
})
export class TripsComponent implements OnInit {
trips: TripBase[] = [];
@@ -53,7 +53,7 @@ export class TripsComponent implements OnInit {
}
gotoMap() {
- this.router.navigateByUrl("/");
+ this.router.navigateByUrl('/');
}
viewTrip(id: number) {
@@ -61,20 +61,17 @@ export class TripsComponent implements OnInit {
}
addTrip() {
- const modal: DynamicDialogRef = this.dialogService.open(
- TripCreateModalComponent,
- {
- header: "Create Trip",
- modal: true,
- appendTo: "body",
- closable: true,
- dismissableMask: true,
- width: "50vw",
- breakpoints: {
- "960px": "80vw",
- },
+ const modal: DynamicDialogRef = this.dialogService.open(TripCreateModalComponent, {
+ header: 'Create Trip',
+ modal: true,
+ appendTo: 'body',
+ closable: true,
+ dismissableMask: true,
+ width: '50vw',
+ breakpoints: {
+ '960px': '80vw',
},
- )!;
+ })!;
modal.onClose.pipe(take(1)).subscribe({
next: (trip: TripBaseWithDates | null) => {
@@ -85,12 +82,8 @@ export class TripsComponent implements OnInit {
let dayCount = 0;
if (trip.from && trip.to) {
- const obs$ = this.generateDaysLabel(trip.from!, trip.to!).map(
- (label) =>
- this.apiService.postTripDay(
- { id: -1, label: label, items: [] },
- new_trip.id,
- ),
+ const obs$ = this.generateDaysLabel(trip.from!, trip.to!).map((label) =>
+ this.apiService.postTripDay({ id: -1, label: label, items: [] }, new_trip.id),
);
dayCount = obs$.length;
forkJoin(obs$).pipe(take(1)).subscribe();
@@ -116,32 +109,17 @@ export class TripsComponent implements OnInit {
generateDaysLabel(from: Date, to: Date): string[] {
const labels: string[] = [];
- const sameMonth =
- from.getFullYear() === to.getFullYear() &&
- from.getMonth() === to.getMonth();
+ const sameMonth = from.getFullYear() === to.getFullYear() && from.getMonth() === to.getMonth();
- const months = [
- "Jan.",
- "Feb.",
- "Mar.",
- "Apr.",
- "May.",
- "Jun.",
- "Jul.",
- "Aug.",
- "Sep.",
- "Oct.",
- "Nov.",
- "Dec.",
- ];
+ const months = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'];
const current = new Date(from);
while (current <= to) {
- let label = "";
+ let label = '';
if (sameMonth) {
- label = `${current.getDate().toString().padStart(2, "0")} ${months[current.getMonth()]}`;
+ label = `${current.getDate().toString().padStart(2, '0')} ${months[current.getMonth()]}`;
} else {
- label = `${(current.getMonth() + 1).toString().padStart(2, "0")}/${current.getDate().toString().padStart(2, "0")}`;
+ label = `${(current.getMonth() + 1).toString().padStart(2, '0')}/${current.getDate().toString().padStart(2, '0')}`;
}
labels.push(label);
current.setDate(current.getDate() + 1);
diff --git a/src/src/app/decs.d.ts b/src/src/app/decs.d.ts
index 37180fd..62ed978 100644
--- a/src/src/app/decs.d.ts
+++ b/src/src/app/decs.d.ts
@@ -1 +1 @@
-declare module "leaflet-ant-path";
+declare module 'leaflet-ant-path';
diff --git a/src/src/app/modals/batch-create-modal/batch-create-modal.component.ts b/src/src/app/modals/batch-create-modal/batch-create-modal.component.ts
index 71721a7..e723572 100644
--- a/src/src/app/modals/batch-create-modal/batch-create-modal.component.ts
+++ b/src/src/app/modals/batch-create-modal/batch-create-modal.component.ts
@@ -1,19 +1,19 @@
-import { Component } from "@angular/core";
-import { FormControl, ReactiveFormsModule } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { TextareaModule } from "primeng/textarea";
+import { Component } from '@angular/core';
+import { FormControl, ReactiveFormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { TextareaModule } from 'primeng/textarea';
@Component({
- selector: "app-batch-create-modal",
+ selector: 'app-batch-create-modal',
imports: [FloatLabelModule, ButtonModule, ReactiveFormsModule, TextareaModule],
standalone: true,
- templateUrl: "./batch-create-modal.component.html",
- styleUrl: "./batch-create-modal.component.scss",
+ templateUrl: './batch-create-modal.component.html',
+ styleUrl: './batch-create-modal.component.scss',
})
export class BatchCreateModalComponent {
- batchInput = new FormControl("");
+ batchInput = new FormControl('');
constructor(private ref: DynamicDialogRef) {}
diff --git a/src/src/app/modals/category-create-modal/category-create-modal.component.ts b/src/src/app/modals/category-create-modal/category-create-modal.component.ts
index c479335..ce3f7a6 100644
--- a/src/src/app/modals/category-create-modal/category-create-modal.component.ts
+++ b/src/src/app/modals/category-create-modal/category-create-modal.component.ts
@@ -1,31 +1,19 @@
-import { Component } from "@angular/core";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { FocusTrapModule } from "primeng/focustrap";
-import { ColorPickerModule } from "primeng/colorpicker";
-import { Category } from "../../types/poi";
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { FocusTrapModule } from 'primeng/focustrap';
+import { ColorPickerModule } from 'primeng/colorpicker';
+import { Category } from '../../types/poi';
@Component({
- selector: "app-category-create-modal",
- imports: [
- FloatLabelModule,
- InputTextModule,
- ButtonModule,
- ColorPickerModule,
- ReactiveFormsModule,
- FocusTrapModule,
- ],
+ selector: 'app-category-create-modal',
+ imports: [FloatLabelModule, InputTextModule, ButtonModule, ColorPickerModule, ReactiveFormsModule, FocusTrapModule],
standalone: true,
- templateUrl: "./category-create-modal.component.html",
- styleUrl: "./category-create-modal.component.scss",
+ templateUrl: './category-create-modal.component.html',
+ styleUrl: './category-create-modal.component.scss',
})
export class CategoryCreateModalComponent {
categoryForm: FormGroup;
@@ -38,14 +26,11 @@ export class CategoryCreateModalComponent {
) {
this.categoryForm = this.fb.group({
id: -1,
- name: ["", Validators.required],
+ name: ['', Validators.required],
color: [
- "#000000",
+ '#000000',
{
- validators: [
- Validators.required,
- Validators.pattern("\#[abcdefABCDEF0-9]{6}"),
- ],
+ validators: [Validators.required, Validators.pattern('\#[abcdefABCDEF0-9]{6}')],
},
],
image: null,
@@ -58,8 +43,8 @@ export class CategoryCreateModalComponent {
closeDialog() {
// Normalize data for API POST
let ret = this.categoryForm.value;
- if (!ret["name"]) return;
- if (!this.updatedImage) delete ret["image"];
+ if (!ret['name']) return;
+ if (!this.updatedImage) delete ret['image'];
this.ref.close(ret);
}
@@ -70,8 +55,8 @@ export class CategoryCreateModalComponent {
const reader = new FileReader();
reader.onload = (e) => {
- this.categoryForm.get("image")?.setValue(e.target?.result as string);
- this.categoryForm.get("image")?.markAsDirty();
+ this.categoryForm.get('image')?.setValue(e.target?.result as string);
+ this.categoryForm.get('image')?.markAsDirty();
this.updatedImage = true;
};
@@ -80,7 +65,7 @@ export class CategoryCreateModalComponent {
}
clearImage() {
- this.categoryForm.get("image")?.setValue(null);
+ this.categoryForm.get('image')?.setValue(null);
this.updatedImage = false;
}
}
diff --git a/src/src/app/modals/place-create-modal/place-create-modal.component.ts b/src/src/app/modals/place-create-modal/place-create-modal.component.ts
index 69b7e9b..8961c69 100644
--- a/src/src/app/modals/place-create-modal/place-create-modal.component.ts
+++ b/src/src/app/modals/place-create-modal/place-create-modal.component.ts
@@ -1,32 +1,27 @@
-import { Component } from "@angular/core";
-import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { SelectModule } from "primeng/select";
-import { TextareaModule } from "primeng/textarea";
-import { Observable } from "rxjs";
-import { AsyncPipe } from "@angular/common";
-import { InputGroupModule } from "primeng/inputgroup";
-import { InputGroupAddonModule } from "primeng/inputgroupaddon";
-import { ApiService } from "../../services/api.service";
-import { UtilsService } from "../../services/utils.service";
-import { FocusTrapModule } from "primeng/focustrap";
-import { Category, Place } from "../../types/poi";
-import { CheckboxModule } from "primeng/checkbox";
-import { TooltipModule } from "primeng/tooltip";
-import { checkAndParseLatLng, formatLatLng } from "../../shared/latlng-parser";
-import { InputNumberModule } from "primeng/inputnumber";
+import { Component } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { SelectModule } from 'primeng/select';
+import { TextareaModule } from 'primeng/textarea';
+import { Observable } from 'rxjs';
+import { AsyncPipe } from '@angular/common';
+import { InputGroupModule } from 'primeng/inputgroup';
+import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
+import { ApiService } from '../../services/api.service';
+import { UtilsService } from '../../services/utils.service';
+import { FocusTrapModule } from 'primeng/focustrap';
+import { Category, Place } from '../../types/poi';
+import { CheckboxModule } from 'primeng/checkbox';
+import { TooltipModule } from 'primeng/tooltip';
+import { checkAndParseLatLng, formatLatLng } from '../../shared/latlng-parser';
+import { InputNumberModule } from 'primeng/inputnumber';
@Component({
- selector: "app-place-create-modal",
+ selector: 'app-place-create-modal',
imports: [
FloatLabelModule,
InputTextModule,
@@ -43,8 +38,8 @@ import { InputNumberModule } from "primeng/inputnumber";
FocusTrapModule,
],
standalone: true,
- templateUrl: "./place-create-modal.component.html",
- styleUrl: "./place-create-modal.component.scss",
+ templateUrl: './place-create-modal.component.html',
+ styleUrl: './place-create-modal.component.scss',
})
export class PlaceCreateModalComponent {
placeForm: FormGroup;
@@ -66,32 +61,27 @@ export class PlaceCreateModalComponent {
this.placeForm = this.fb.group({
id: -1,
- name: ["", Validators.required],
- place: ["", { validators: Validators.required, updateOn: "blur" }],
+ name: ['', Validators.required],
+ place: ['', { validators: Validators.required, updateOn: 'blur' }],
lat: [
- "",
+ '',
{
- validators: [
- Validators.required,
- Validators.pattern("-?(90(\\.0+)?|[1-8]?\\d(\\.\\d+)?)"),
- ],
- updateOn: "blur",
+ validators: [Validators.required, Validators.pattern('-?(90(\\.0+)?|[1-8]?\\d(\\.\\d+)?)')],
+ updateOn: 'blur',
},
],
lng: [
- "",
+ '',
{
validators: [
Validators.required,
- Validators.pattern(
- "-?(180(\\.0+)?|1[0-7]\\d(\\.\\d+)?|[1-9]?\\d(\\.\\d+)?)",
- ),
+ Validators.pattern('-?(180(\\.0+)?|1[0-7]\\d(\\.\\d+)?|[1-9]?\\d(\\.\\d+)?)'),
],
},
],
category: [null, Validators.required],
description: null,
- duration: [null, Validators.pattern("\\d+")],
+ duration: [null, Validators.pattern('\\d+')],
price: null,
allowdog: false,
visited: false,
@@ -104,12 +94,11 @@ export class PlaceCreateModalComponent {
if (patchValue) this.placeForm.patchValue(patchValue);
this.placeForm
- .get("place")
+ .get('place')
?.valueChanges.pipe(takeUntilDestroyed())
.subscribe({
next: (value: string) => {
- const isGoogleMapsURL =
- /^(https?:\/\/)?(www\.)?google\.[a-z.]+\/maps/.test(value);
+ const isGoogleMapsURL = /^(https?:\/\/)?(www\.)?google\.[a-z.]+\/maps/.test(value);
if (isGoogleMapsURL) {
this.parseGoogleMapsUrl(value);
}
@@ -117,7 +106,7 @@ export class PlaceCreateModalComponent {
});
this.placeForm
- .get("lat")
+ .get('lat')
?.valueChanges.pipe(takeUntilDestroyed())
.subscribe({
next: (value: string) => {
@@ -125,8 +114,8 @@ export class PlaceCreateModalComponent {
if (!result) return;
const [lat, lng] = result;
- const latControl = this.placeForm.get("lat");
- const lngControl = this.placeForm.get("lng");
+ const latControl = this.placeForm.get('lat');
+ const lngControl = this.placeForm.get('lng');
latControl?.setValue(formatLatLng(lat).trim(), { emitEvent: false });
lngControl?.setValue(formatLatLng(lng).trim(), { emitEvent: false });
@@ -140,15 +129,15 @@ export class PlaceCreateModalComponent {
closeDialog() {
// Normalize data for API POST
let ret = this.placeForm.value;
- ret["category_id"] = ret["category"];
- delete ret["category"];
- if (ret["image_id"]) {
- delete ret["image"];
- delete ret["image_id"];
+ ret['category_id'] = ret['category'];
+ delete ret['category'];
+ if (ret['image_id']) {
+ delete ret['image'];
+ delete ret['image_id'];
}
- if (ret["gpx"] == "1") delete ret["gpx"];
- ret["lat"] = +ret["lat"];
- ret["lng"] = +ret["lng"];
+ if (ret['gpx'] == '1') delete ret['gpx'];
+ ret['lat'] = +ret['lat'];
+ ret['lng'] = +ret['lng'];
this.ref.close(ret);
}
@@ -156,13 +145,12 @@ export class PlaceCreateModalComponent {
const [place, latlng] = this.utilsService.parseGoogleMapsUrl(url);
if (!place || !latlng) return;
- const [lat, lng] = latlng.split(",");
- this.placeForm.get("place")?.setValue(place);
- this.placeForm.get("lat")?.setValue(lat);
- this.placeForm.get("lng")?.setValue(lng);
+ const [lat, lng] = latlng.split(',');
+ this.placeForm.get('place')?.setValue(place);
+ this.placeForm.get('lat')?.setValue(lat);
+ this.placeForm.get('lng')?.setValue(lng);
- if (!this.placeForm.get("name")?.value)
- this.placeForm.get("name")?.setValue(place);
+ if (!this.placeForm.get('name')?.value) this.placeForm.get('name')?.setValue(place);
}
onImageSelected(event: Event) {
@@ -172,14 +160,14 @@ export class PlaceCreateModalComponent {
const reader = new FileReader();
reader.onload = (e) => {
- if (this.placeForm.get("image_id")?.value) {
- this.previous_image_id = this.placeForm.get("image_id")?.value;
- this.previous_image = this.placeForm.get("image")?.value;
- this.placeForm.get("image_id")?.setValue(null);
+ if (this.placeForm.get('image_id')?.value) {
+ this.previous_image_id = this.placeForm.get('image_id')?.value;
+ this.previous_image = this.placeForm.get('image')?.value;
+ this.placeForm.get('image_id')?.setValue(null);
}
- this.placeForm.get("image")?.setValue(e.target?.result as string);
- this.placeForm.get("image")?.markAsDirty();
+ this.placeForm.get('image')?.setValue(e.target?.result as string);
+ this.placeForm.get('image')?.markAsDirty();
};
reader.readAsDataURL(file);
@@ -187,12 +175,12 @@ export class PlaceCreateModalComponent {
}
clearImage() {
- this.placeForm.get("image")?.setValue(null);
- this.placeForm.get("image_id")?.setValue(null);
+ this.placeForm.get('image')?.setValue(null);
+ this.placeForm.get('image_id')?.setValue(null);
if (this.previous_image && this.previous_image_id) {
- this.placeForm.get("image_id")?.setValue(this.previous_image_id);
- this.placeForm.get("image")?.setValue(this.previous_image);
+ this.placeForm.get('image_id')?.setValue(this.previous_image_id);
+ this.placeForm.get('image')?.setValue(this.previous_image);
}
}
@@ -203,8 +191,8 @@ export class PlaceCreateModalComponent {
const reader = new FileReader();
reader.onload = (e) => {
- this.placeForm.get("gpx")?.setValue(e.target?.result as string);
- this.placeForm.get("gpx")?.markAsDirty();
+ this.placeForm.get('gpx')?.setValue(e.target?.result as string);
+ this.placeForm.get('gpx')?.markAsDirty();
};
reader.readAsText(file);
@@ -212,7 +200,7 @@ export class PlaceCreateModalComponent {
}
clearGPX() {
- this.placeForm.get("gpx")?.setValue(null);
- this.placeForm.get("gpx")?.markAsDirty();
+ this.placeForm.get('gpx')?.setValue(null);
+ this.placeForm.get('gpx')?.markAsDirty();
}
}
diff --git a/src/src/app/modals/trip-archive-modal/trip-archive-modal.component.ts b/src/src/app/modals/trip-archive-modal/trip-archive-modal.component.ts
index 3f389a1..38c87c6 100644
--- a/src/src/app/modals/trip-archive-modal/trip-archive-modal.component.ts
+++ b/src/src/app/modals/trip-archive-modal/trip-archive-modal.component.ts
@@ -1,25 +1,20 @@
-import { Component } from "@angular/core";
-import { FormControl, ReactiveFormsModule } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { TextareaModule } from "primeng/textarea";
-import { Trip } from "../../types/trip";
+import { Component } from '@angular/core';
+import { FormControl, ReactiveFormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { TextareaModule } from 'primeng/textarea';
+import { Trip } from '../../types/trip';
@Component({
- selector: "app-trip-archive-modal",
- imports: [
- FloatLabelModule,
- TextareaModule,
- ButtonModule,
- ReactiveFormsModule,
- ],
+ selector: 'app-trip-archive-modal',
+ imports: [FloatLabelModule, TextareaModule, ButtonModule, ReactiveFormsModule],
standalone: true,
- templateUrl: "./trip-archive-modal.component.html",
- styleUrl: "./trip-archive-modal.component.scss",
+ templateUrl: './trip-archive-modal.component.html',
+ styleUrl: './trip-archive-modal.component.scss',
})
export class TripArchiveModalComponent {
- review = new FormControl("");
+ review = new FormControl('');
constructor(
private ref: DynamicDialogRef,
@@ -34,16 +29,13 @@ export class TripArchiveModalComponent {
return;
}
if (!trip.days.length) return;
- let placeholder = "General feedback:\n\n";
+ let placeholder = 'General feedback:\n\n';
trip.days.forEach((day, index) => {
placeholder += `\nDay ${index + 1} (${day.label})\n`;
- if (!day.items.length) placeholder += " No activities.\n";
- else
- day.items.forEach(
- (item) => (placeholder += ` - ${item.time} | ${item.text}\n`),
- );
+ if (!day.items.length) placeholder += ' No activities.\n';
+ else day.items.forEach((item) => (placeholder += ` - ${item.time} | ${item.text}\n`));
});
- placeholder += "\nAnything else?";
+ placeholder += '\nAnything else?';
this.review.setValue(placeholder);
}
diff --git a/src/src/app/modals/trip-create-checklist-modal/trip-create-checklist-modal.component.ts b/src/src/app/modals/trip-create-checklist-modal/trip-create-checklist-modal.component.ts
index 787f465..ff80a4a 100644
--- a/src/src/app/modals/trip-create-checklist-modal/trip-create-checklist-modal.component.ts
+++ b/src/src/app/modals/trip-create-checklist-modal/trip-create-checklist-modal.component.ts
@@ -1,28 +1,17 @@
-import { Component } from "@angular/core";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { FocusTrapModule } from "primeng/focustrap";
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { FocusTrapModule } from 'primeng/focustrap';
@Component({
- selector: "app-trip-create-checklist-modal",
- imports: [
- FloatLabelModule,
- InputTextModule,
- ButtonModule,
- ReactiveFormsModule,
- FocusTrapModule,
- ],
+ selector: 'app-trip-create-checklist-modal',
+ imports: [FloatLabelModule, InputTextModule, ButtonModule, ReactiveFormsModule, FocusTrapModule],
standalone: true,
- templateUrl: "./trip-create-checklist-modal.component.html",
- styleUrl: "./trip-create-checklist-modal.component.scss",
+ templateUrl: './trip-create-checklist-modal.component.html',
+ styleUrl: './trip-create-checklist-modal.component.scss',
})
export class TripCreateChecklistModalComponent {
checklistForm: FormGroup;
@@ -33,7 +22,7 @@ export class TripCreateChecklistModalComponent {
) {
this.checklistForm = this.fb.group({
id: -1,
- text: ["", { validators: Validators.required }],
+ text: ['', { validators: Validators.required }],
});
const patchValue = this.config.data?.packing;
diff --git a/src/src/app/modals/trip-create-day-item-modal/trip-create-day-item-modal.component.ts b/src/src/app/modals/trip-create-day-item-modal/trip-create-day-item-modal.component.ts
index 26891bf..018d042 100644
--- a/src/src/app/modals/trip-create-day-item-modal/trip-create-day-item-modal.component.ts
+++ b/src/src/app/modals/trip-create-day-item-modal/trip-create-day-item-modal.component.ts
@@ -1,30 +1,25 @@
-import { Component, ViewChild } from "@angular/core";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { TripDay, TripMember, TripStatus } from "../../types/trip";
-import { Place } from "../../types/poi";
-import { SelectModule } from "primeng/select";
-import { TextareaModule } from "primeng/textarea";
-import { InputMaskModule } from "primeng/inputmask";
-import { UtilsService } from "../../services/utils.service";
-import { checkAndParseLatLng, formatLatLng } from "../../shared/latlng-parser";
-import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import { InputNumberModule } from "primeng/inputnumber";
-import { MultiSelectModule } from "primeng/multiselect";
-import { InputGroupModule } from "primeng/inputgroup";
-import { InputGroupAddonModule } from "primeng/inputgroupaddon";
-import { Popover, PopoverModule } from "primeng/popover";
+import { Component, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { TripDay, TripMember, TripStatus } from '../../types/trip';
+import { Place } from '../../types/poi';
+import { SelectModule } from 'primeng/select';
+import { TextareaModule } from 'primeng/textarea';
+import { InputMaskModule } from 'primeng/inputmask';
+import { UtilsService } from '../../services/utils.service';
+import { checkAndParseLatLng, formatLatLng } from '../../shared/latlng-parser';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { InputNumberModule } from 'primeng/inputnumber';
+import { MultiSelectModule } from 'primeng/multiselect';
+import { InputGroupModule } from 'primeng/inputgroup';
+import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
+import { Popover, PopoverModule } from 'primeng/popover';
@Component({
- selector: "app-trip-create-day-item-modal",
+ selector: 'app-trip-create-day-item-modal',
imports: [
FloatLabelModule,
InputTextModule,
@@ -44,11 +39,11 @@ import { Popover, PopoverModule } from "primeng/popover";
PopoverModule,
],
standalone: true,
- templateUrl: "./trip-create-day-item-modal.component.html",
- styleUrl: "./trip-create-day-item-modal.component.scss",
+ templateUrl: './trip-create-day-item-modal.component.html',
+ styleUrl: './trip-create-day-item-modal.component.scss',
})
export class TripCreateDayItemModalComponent {
- @ViewChild("op") op!: Popover;
+ @ViewChild('op') op!: Popover;
members: TripMember[] = [];
itemForm: FormGroup;
days: TripDay[] = [];
@@ -68,16 +63,13 @@ export class TripCreateDayItemModalComponent {
this.itemForm = this.fb.group({
id: -1,
time: [
- "",
+ '',
{
- validators: [
- Validators.required,
- Validators.pattern(/^([01]\d|2[0-3])(:[0-5]\d)?$/),
- ],
+ validators: [Validators.required, Validators.pattern(/^([01]\d|2[0-3])(:[0-5]\d)?$/)],
},
],
- text: ["", Validators.required],
- comment: "",
+ text: ['', Validators.required],
+ comment: '',
day_id: [null, Validators.required],
place: null,
status: null,
@@ -86,18 +78,16 @@ export class TripCreateDayItemModalComponent {
image_id: null,
gpx: null,
lat: [
- "",
+ '',
{
- validators: Validators.pattern("-?(90(\\.0+)?|[1-8]?\\d(\\.\\d+)?)"),
- updateOn: "blur",
+ validators: Validators.pattern('-?(90(\\.0+)?|[1-8]?\\d(\\.\\d+)?)'),
+ updateOn: 'blur',
},
],
lng: [
- "",
+ '',
{
- validators: Validators.pattern(
- "-?(180(\\.0+)?|1[0-7]\\d(\\.\\d+)?|[1-9]?\\d(\\.\\d+)?)",
- ),
+ validators: Validators.pattern('-?(180(\\.0+)?|1[0-7]\\d(\\.\\d+)?|[1-9]?\\d(\\.\\d+)?)'),
},
],
paid_by: null,
@@ -115,36 +105,34 @@ export class TripCreateDayItemModalComponent {
place: data.item.place?.id ?? null,
});
- if (data.selectedDay)
- this.itemForm.get("day_id")?.setValue([data.selectedDay]);
+ if (data.selectedDay) this.itemForm.get('day_id')?.setValue([data.selectedDay]);
}
this.itemForm
- .get("place")
+ .get('place')
?.valueChanges.pipe(takeUntilDestroyed())
.subscribe({
next: (value?: number) => {
if (!value) {
- this.itemForm.get("lat")?.setValue("");
- this.itemForm.get("lng")?.setValue("");
+ this.itemForm.get('lat')?.setValue('');
+ this.itemForm.get('lng')?.setValue('');
return;
}
const p: Place = this.places.find((p) => p.id === value) as Place;
if (p) {
- this.itemForm.get("lat")?.setValue(p.lat);
- this.itemForm.get("lng")?.setValue(p.lng);
- this.itemForm.get("price")?.setValue(p.price || 0);
- if (!this.itemForm.get("text")?.value)
- this.itemForm.get("text")?.setValue(p.name);
- if (p.description && !this.itemForm.get("comment")?.value)
- this.itemForm.get("comment")?.setValue(p.description);
+ this.itemForm.get('lat')?.setValue(p.lat);
+ this.itemForm.get('lng')?.setValue(p.lng);
+ this.itemForm.get('price')?.setValue(p.price || 0);
+ if (!this.itemForm.get('text')?.value) this.itemForm.get('text')?.setValue(p.name);
+ if (p.description && !this.itemForm.get('comment')?.value)
+ this.itemForm.get('comment')?.setValue(p.description);
}
},
});
this.itemForm
- .get("lat")
+ .get('lat')
?.valueChanges.pipe(takeUntilDestroyed())
.subscribe({
next: (value: string) => {
@@ -152,8 +140,8 @@ export class TripCreateDayItemModalComponent {
if (!result) return;
const [lat, lng] = result;
- const latControl = this.itemForm.get("lat");
- const lngControl = this.itemForm.get("lng");
+ const latControl = this.itemForm.get('lat');
+ const lngControl = this.itemForm.get('lng');
latControl?.setValue(formatLatLng(lat).trim(), { emitEvent: false });
lngControl?.setValue(formatLatLng(lng).trim(), { emitEvent: false });
@@ -167,16 +155,16 @@ export class TripCreateDayItemModalComponent {
closeDialog() {
// Normalize data for API POST
let ret = this.itemForm.value;
- if (!ret["lat"]) {
- ret["lat"] = null;
- ret["lng"] = null;
+ if (!ret['lat']) {
+ ret['lat'] = null;
+ ret['lng'] = null;
}
- if (ret["image_id"]) {
- delete ret["image"];
- delete ret["image_id"];
+ if (ret['image_id']) {
+ delete ret['image'];
+ delete ret['image_id'];
}
- if (ret["gpx"] == "1") delete ret["gpx"];
- if (!ret["place"]) delete ret["place"];
+ if (ret['gpx'] == '1') delete ret['gpx'];
+ if (!ret['place']) delete ret['place'];
this.ref.close(ret);
}
@@ -185,7 +173,7 @@ export class TripCreateDayItemModalComponent {
}
get paidByControl(): any {
- return this.itemForm.get("paid_by");
+ return this.itemForm.get('paid_by');
}
selectPriceMember(member: any) {
@@ -206,14 +194,14 @@ export class TripCreateDayItemModalComponent {
const reader = new FileReader();
reader.onload = (e) => {
- if (this.itemForm.get("image_id")?.value) {
- this.previous_image_id = this.itemForm.get("image_id")?.value;
- this.previous_image = this.itemForm.get("image")?.value;
- this.itemForm.get("image_id")?.setValue(null);
+ if (this.itemForm.get('image_id')?.value) {
+ this.previous_image_id = this.itemForm.get('image_id')?.value;
+ this.previous_image = this.itemForm.get('image')?.value;
+ this.itemForm.get('image_id')?.setValue(null);
}
- this.itemForm.get("image")?.setValue(e.target?.result as string);
- this.itemForm.get("image")?.markAsDirty();
+ this.itemForm.get('image')?.setValue(e.target?.result as string);
+ this.itemForm.get('image')?.markAsDirty();
};
reader.readAsDataURL(file);
@@ -221,13 +209,13 @@ export class TripCreateDayItemModalComponent {
}
clearImage() {
- this.itemForm.get("image")?.setValue(null);
- this.itemForm.get("image_id")?.setValue(null);
+ this.itemForm.get('image')?.setValue(null);
+ this.itemForm.get('image_id')?.setValue(null);
this.itemForm.markAsDirty();
if (this.previous_image && this.previous_image_id) {
- this.itemForm.get("image_id")?.setValue(this.previous_image_id);
- this.itemForm.get("image")?.setValue(this.previous_image);
+ this.itemForm.get('image_id')?.setValue(this.previous_image_id);
+ this.itemForm.get('image')?.setValue(this.previous_image);
}
}
@@ -238,8 +226,8 @@ export class TripCreateDayItemModalComponent {
const reader = new FileReader();
reader.onload = (e) => {
- this.itemForm.get("gpx")?.setValue(e.target?.result as string);
- this.itemForm.get("gpx")?.markAsDirty();
+ this.itemForm.get('gpx')?.setValue(e.target?.result as string);
+ this.itemForm.get('gpx')?.markAsDirty();
};
reader.readAsText(file);
@@ -247,7 +235,7 @@ export class TripCreateDayItemModalComponent {
}
clearGPX() {
- this.itemForm.get("gpx")?.setValue(null);
- this.itemForm.get("gpx")?.markAsDirty();
+ this.itemForm.get('gpx')?.setValue(null);
+ this.itemForm.get('gpx')?.markAsDirty();
}
}
diff --git a/src/src/app/modals/trip-create-day-modal/trip-create-day-modal.component.ts b/src/src/app/modals/trip-create-day-modal/trip-create-day-modal.component.ts
index b865351..56f90f6 100644
--- a/src/src/app/modals/trip-create-day-modal/trip-create-day-modal.component.ts
+++ b/src/src/app/modals/trip-create-day-modal/trip-create-day-modal.component.ts
@@ -1,27 +1,17 @@
-import { Component } from "@angular/core";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { TripDay } from "../../types/trip";
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { TripDay } from '../../types/trip';
@Component({
- selector: "app-trip-create-day-modal",
- imports: [
- FloatLabelModule,
- InputTextModule,
- ButtonModule,
- ReactiveFormsModule,
- ],
+ selector: 'app-trip-create-day-modal',
+ imports: [FloatLabelModule, InputTextModule, ButtonModule, ReactiveFormsModule],
standalone: true,
- templateUrl: "./trip-create-day-modal.component.html",
- styleUrl: "./trip-create-day-modal.component.scss",
+ templateUrl: './trip-create-day-modal.component.html',
+ styleUrl: './trip-create-day-modal.component.scss',
})
export class TripCreateDayModalComponent {
dayForm: FormGroup;
@@ -34,7 +24,7 @@ export class TripCreateDayModalComponent {
) {
this.dayForm = this.fb.group({
id: -1,
- label: ["", Validators.required],
+ label: ['', Validators.required],
});
if (this.config.data) {
@@ -46,7 +36,7 @@ export class TripCreateDayModalComponent {
closeDialog() {
// Normalize data for API POST
let ret = this.dayForm.value;
- if (!ret["label"]) return;
+ if (!ret['label']) return;
this.ref.close(ret);
}
}
diff --git a/src/src/app/modals/trip-create-items-modal/trip-create-items-modal.component.ts b/src/src/app/modals/trip-create-items-modal/trip-create-items-modal.component.ts
index f38a6aa..20984d4 100644
--- a/src/src/app/modals/trip-create-items-modal/trip-create-items-modal.component.ts
+++ b/src/src/app/modals/trip-create-items-modal/trip-create-items-modal.component.ts
@@ -1,31 +1,31 @@
-import { Component } from "@angular/core";
-import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { TextareaModule } from "primeng/textarea";
-import { TripDay, TripItem } from "../../types/trip";
-import { SelectModule } from "primeng/select";
+import { Component } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { TextareaModule } from 'primeng/textarea';
+import { TripDay, TripItem } from '../../types/trip';
+import { SelectModule } from 'primeng/select';
@Component({
- selector: "app-trip-create-items-modal",
+ selector: 'app-trip-create-items-modal',
imports: [FloatLabelModule, ButtonModule, SelectModule, ReactiveFormsModule, TextareaModule],
standalone: true,
- templateUrl: "./trip-create-items-modal.component.html",
- styleUrl: "./trip-create-items-modal.component.scss",
+ templateUrl: './trip-create-items-modal.component.html',
+ styleUrl: './trip-create-items-modal.component.scss',
})
export class TripCreateItemsModalComponent {
itemBatchForm: FormGroup;
- pholder = "eg.\n14h Just an item example\n15:10 Another format for an item\n16h30 Another item here";
+ pholder = 'eg.\n14h Just an item example\n15:10 Another format for an item\n16h30 Another item here';
days: TripDay[] = [];
constructor(
private ref: DynamicDialogRef,
private fb: FormBuilder,
- private config: DynamicDialogConfig
+ private config: DynamicDialogConfig,
) {
this.itemBatchForm = this.fb.group({
- batch: ["", Validators.required],
+ batch: ['', Validators.required],
day_id: [null, Validators.required],
});
@@ -37,15 +37,15 @@ export class TripCreateItemsModalComponent {
closeDialog() {
const ret = this.itemBatchForm.value;
const day_id = ret.day_id;
- const lines: string[] = ret.batch.trim().split("\n");
+ const lines: string[] = ret.batch.trim().split('\n');
const tripItems: Partial[] = [];
lines.forEach((l) => {
const match = l.match(/^(\d{1,2})(?:h|:)?(\d{0,2})?\s+(.+)$/);
if (match) {
- const [_, hoursStr, minutesStr = "", text] = match;
- const hours = hoursStr.padStart(2, "0");
- const minutes = minutesStr.padStart(2, "0") || "00";
+ const [_, hoursStr, minutesStr = '', text] = match;
+ const hours = hoursStr.padStart(2, '0');
+ const minutes = minutesStr.padStart(2, '0') || '00';
const time = `${hours}:${minutes}`;
tripItems.push({ time: time, text: text, day_id: day_id });
}
diff --git a/src/src/app/modals/trip-create-modal/trip-create-modal.component.ts b/src/src/app/modals/trip-create-modal/trip-create-modal.component.ts
index 3b3bf8a..a6a647f 100644
--- a/src/src/app/modals/trip-create-modal/trip-create-modal.component.ts
+++ b/src/src/app/modals/trip-create-modal/trip-create-modal.component.ts
@@ -1,30 +1,18 @@
-import { Component } from "@angular/core";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { FocusTrapModule } from "primeng/focustrap";
-import { DatePickerModule } from "primeng/datepicker";
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { FocusTrapModule } from 'primeng/focustrap';
+import { DatePickerModule } from 'primeng/datepicker';
@Component({
- selector: "app-trip-create-modal",
- imports: [
- FloatLabelModule,
- InputTextModule,
- DatePickerModule,
- ButtonModule,
- ReactiveFormsModule,
- FocusTrapModule,
- ],
+ selector: 'app-trip-create-modal',
+ imports: [FloatLabelModule, InputTextModule, DatePickerModule, ButtonModule, ReactiveFormsModule, FocusTrapModule],
standalone: true,
- templateUrl: "./trip-create-modal.component.html",
- styleUrl: "./trip-create-modal.component.scss",
+ templateUrl: './trip-create-modal.component.html',
+ styleUrl: './trip-create-modal.component.scss',
})
export class TripCreateModalComponent {
tripForm: FormGroup;
@@ -38,8 +26,8 @@ export class TripCreateModalComponent {
) {
this.tripForm = this.fb.group({
id: -1,
- name: ["", Validators.required],
- image: "",
+ name: ['', Validators.required],
+ image: '',
currency: null,
image_id: null,
from: null,
@@ -48,7 +36,7 @@ export class TripCreateModalComponent {
const patchValue = this.config.data?.trip;
if (patchValue) {
- if (!patchValue.image_id) delete patchValue["image"];
+ if (!patchValue.image_id) delete patchValue['image'];
this.tripForm.patchValue(patchValue);
}
}
@@ -56,10 +44,10 @@ export class TripCreateModalComponent {
closeDialog() {
// Normalize data for API POST
let ret = this.tripForm.value;
- if (!ret["name"]) return;
- if (ret["image_id"]) {
- delete ret["image"];
- delete ret["image_id"];
+ if (!ret['name']) return;
+ if (ret['image_id']) {
+ delete ret['image'];
+ delete ret['image_id'];
}
this.ref.close(ret);
}
@@ -71,14 +59,14 @@ export class TripCreateModalComponent {
const reader = new FileReader();
reader.onload = (e) => {
- if (this.tripForm.get("image_id")?.value) {
- this.previous_image_id = this.tripForm.get("image_id")?.value;
- this.previous_image = this.tripForm.get("image")?.value;
- this.tripForm.get("image_id")?.setValue(null);
+ if (this.tripForm.get('image_id')?.value) {
+ this.previous_image_id = this.tripForm.get('image_id')?.value;
+ this.previous_image = this.tripForm.get('image')?.value;
+ this.tripForm.get('image_id')?.setValue(null);
}
- this.tripForm.get("image")?.setValue(e.target?.result as string);
- this.tripForm.get("image")?.markAsDirty();
+ this.tripForm.get('image')?.setValue(e.target?.result as string);
+ this.tripForm.get('image')?.markAsDirty();
};
reader.readAsDataURL(file);
@@ -86,11 +74,11 @@ export class TripCreateModalComponent {
}
clearImage() {
- this.tripForm.get("image")?.setValue(null);
+ this.tripForm.get('image')?.setValue(null);
if (this.previous_image && this.previous_image_id) {
- this.tripForm.get("image_id")?.setValue(this.previous_image_id);
- this.tripForm.get("image")?.setValue(this.previous_image);
+ this.tripForm.get('image_id')?.setValue(this.previous_image_id);
+ this.tripForm.get('image')?.setValue(this.previous_image);
}
}
}
diff --git a/src/src/app/modals/trip-create-packing-modal/trip-create-packing-modal.component.ts b/src/src/app/modals/trip-create-packing-modal/trip-create-packing-modal.component.ts
index a580e80..2b6acd0 100644
--- a/src/src/app/modals/trip-create-packing-modal/trip-create-packing-modal.component.ts
+++ b/src/src/app/modals/trip-create-packing-modal/trip-create-packing-modal.component.ts
@@ -1,20 +1,15 @@
-import { Component } from "@angular/core";
-import {
- FormBuilder,
- FormGroup,
- ReactiveFormsModule,
- Validators,
-} from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { FocusTrapModule } from "primeng/focustrap";
-import { SelectModule } from "primeng/select";
-import { InputNumberModule } from "primeng/inputnumber";
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { FocusTrapModule } from 'primeng/focustrap';
+import { SelectModule } from 'primeng/select';
+import { InputNumberModule } from 'primeng/inputnumber';
@Component({
- selector: "app-trip-create-packing-modal",
+ selector: 'app-trip-create-packing-modal',
imports: [
FloatLabelModule,
InputTextModule,
@@ -25,17 +20,17 @@ import { InputNumberModule } from "primeng/inputnumber";
InputNumberModule,
],
standalone: true,
- templateUrl: "./trip-create-packing-modal.component.html",
- styleUrl: "./trip-create-packing-modal.component.scss",
+ templateUrl: './trip-create-packing-modal.component.html',
+ styleUrl: './trip-create-packing-modal.component.scss',
})
export class TripCreatePackingModalComponent {
packingForm: FormGroup;
readonly packingCategories = [
- { value: "clothes", dispValue: "Clothes" },
- { value: "toiletries", dispValue: "Toiletries" },
- { value: "tech", dispValue: "Tech" },
- { value: "documents", dispValue: "Documents" },
- { value: "other", dispValue: "Other" },
+ { value: 'clothes', dispValue: 'Clothes' },
+ { value: 'toiletries', dispValue: 'Toiletries' },
+ { value: 'tech', dispValue: 'Tech' },
+ { value: 'documents', dispValue: 'Documents' },
+ { value: 'other', dispValue: 'Other' },
];
constructor(
@@ -46,8 +41,8 @@ export class TripCreatePackingModalComponent {
this.packingForm = this.fb.group({
id: -1,
qt: null,
- text: ["", { validators: Validators.required }],
- category: ["", { validators: Validators.required }],
+ text: ['', { validators: Validators.required }],
+ category: ['', { validators: Validators.required }],
});
const patchValue = this.config.data?.packing;
diff --git a/src/src/app/modals/trip-invite-member-modal/trip-invite-member-modal.component.ts b/src/src/app/modals/trip-invite-member-modal/trip-invite-member-modal.component.ts
index 5bd1f0f..f34bcaa 100644
--- a/src/src/app/modals/trip-invite-member-modal/trip-invite-member-modal.component.ts
+++ b/src/src/app/modals/trip-invite-member-modal/trip-invite-member-modal.component.ts
@@ -1,26 +1,20 @@
-import { Component } from "@angular/core";
-import { FormControl, ReactiveFormsModule } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { FocusTrapModule } from "primeng/focustrap";
+import { Component } from '@angular/core';
+import { FormControl, ReactiveFormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { FocusTrapModule } from 'primeng/focustrap';
@Component({
- selector: "app-trip-invite-member-modal",
- imports: [
- FloatLabelModule,
- InputTextModule,
- ButtonModule,
- ReactiveFormsModule,
- FocusTrapModule,
- ],
+ selector: 'app-trip-invite-member-modal',
+ imports: [FloatLabelModule, InputTextModule, ButtonModule, ReactiveFormsModule, FocusTrapModule],
standalone: true,
- templateUrl: "./trip-invite-member-modal.component.html",
- styleUrl: "./trip-invite-member-modal.component.scss",
+ templateUrl: './trip-invite-member-modal.component.html',
+ styleUrl: './trip-invite-member-modal.component.scss',
})
export class TripInviteMemberModalComponent {
- memberForm = new FormControl("");
+ memberForm = new FormControl('');
constructor(private ref: DynamicDialogRef) {}
closeDialog() {
diff --git a/src/src/app/modals/trip-notes-modal/trip-notes-modal.component.ts b/src/src/app/modals/trip-notes-modal/trip-notes-modal.component.ts
index d84eeef..941af82 100644
--- a/src/src/app/modals/trip-notes-modal/trip-notes-modal.component.ts
+++ b/src/src/app/modals/trip-notes-modal/trip-notes-modal.component.ts
@@ -1,24 +1,19 @@
-import { Component } from "@angular/core";
-import { FormControl, ReactiveFormsModule } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { TextareaModule } from "primeng/textarea";
+import { Component } from '@angular/core';
+import { FormControl, ReactiveFormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { TextareaModule } from 'primeng/textarea';
@Component({
- selector: "app-trip-notes-modal",
- imports: [
- FloatLabelModule,
- TextareaModule,
- ButtonModule,
- ReactiveFormsModule,
- ],
+ selector: 'app-trip-notes-modal',
+ imports: [FloatLabelModule, TextareaModule, ButtonModule, ReactiveFormsModule],
standalone: true,
- templateUrl: "./trip-notes-modal.component.html",
- styleUrl: "./trip-notes-modal.component.scss",
+ templateUrl: './trip-notes-modal.component.html',
+ styleUrl: './trip-notes-modal.component.scss',
})
export class TripNotesModalComponent {
- notes = new FormControl("");
+ notes = new FormControl('');
isEditing: boolean = false;
constructor(
diff --git a/src/src/app/modals/trip-place-select-modal/trip-place-select-modal.component.ts b/src/src/app/modals/trip-place-select-modal/trip-place-select-modal.component.ts
index b7c1299..2af65ed 100644
--- a/src/src/app/modals/trip-place-select-modal/trip-place-select-modal.component.ts
+++ b/src/src/app/modals/trip-place-select-modal/trip-place-select-modal.component.ts
@@ -1,28 +1,22 @@
-import { Component } from "@angular/core";
-import { FormControl, ReactiveFormsModule } from "@angular/forms";
-import { ButtonModule } from "primeng/button";
-import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
-import { FloatLabelModule } from "primeng/floatlabel";
-import { InputTextModule } from "primeng/inputtext";
-import { Place } from "../../types/poi";
-import { ApiService } from "../../services/api.service";
-import { SkeletonModule } from "primeng/skeleton";
+import { Component } from '@angular/core';
+import { FormControl, ReactiveFormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { FloatLabelModule } from 'primeng/floatlabel';
+import { InputTextModule } from 'primeng/inputtext';
+import { Place } from '../../types/poi';
+import { ApiService } from '../../services/api.service';
+import { SkeletonModule } from 'primeng/skeleton';
@Component({
- selector: "app-trip-place-select-modal",
- imports: [
- FloatLabelModule,
- InputTextModule,
- ButtonModule,
- ReactiveFormsModule,
- SkeletonModule,
- ],
+ selector: 'app-trip-place-select-modal',
+ imports: [FloatLabelModule, InputTextModule, ButtonModule, ReactiveFormsModule, SkeletonModule],
standalone: true,
- templateUrl: "./trip-place-select-modal.component.html",
- styleUrl: "./trip-place-select-modal.component.scss",
+ templateUrl: './trip-place-select-modal.component.html',
+ styleUrl: './trip-place-select-modal.component.scss',
})
export class TripPlaceSelectModalComponent {
- searchInput = new FormControl("");
+ searchInput = new FormControl('');
selectedPlaces: Place[] = [];
showSelectedPlaces: boolean = false;
@@ -38,9 +32,7 @@ export class TripPlaceSelectModalComponent {
) {
this.apiService.getPlaces().subscribe({
next: (places) => {
- this.places = places.sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
- );
+ this.places = places.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
this.displayedPlaces = places;
},
});
@@ -60,9 +52,7 @@ export class TripPlaceSelectModalComponent {
const v = value.toLowerCase();
this.displayedPlaces = this.places.filter(
- (p) =>
- p.name.toLowerCase().includes(v) ||
- p.description?.toLowerCase().includes(v),
+ (p) => p.name.toLowerCase().includes(v) || p.description?.toLowerCase().includes(v),
);
},
});
@@ -80,9 +70,7 @@ export class TripPlaceSelectModalComponent {
this.selectedPlacesID.push(p.id);
this.selectedPlaces.push(p);
- this.selectedPlaces.sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
- );
+ this.selectedPlaces.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
}
closeDialog() {
diff --git a/src/src/app/services/api.service.ts b/src/src/app/services/api.service.ts
index 3cc948a..36e19a0 100644
--- a/src/src/app/services/api.service.ts
+++ b/src/src/app/services/api.service.ts
@@ -1,9 +1,9 @@
-import { inject, Injectable } from "@angular/core";
-import { HttpClient } from "@angular/common/http";
-import { Category, Place } from "../types/poi";
-import { BehaviorSubject, map, Observable, shareReplay, tap } from "rxjs";
-import { Info } from "../types/info";
-import { ImportResponse, Settings } from "../types/settings";
+import { inject, Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Category, Place } from '../types/poi';
+import { BehaviorSubject, map, Observable, shareReplay, tap } from 'rxjs';
+import { Info } from '../types/info';
+import { ImportResponse, Settings } from '../types/settings';
import {
ChecklistItem,
PackingItem,
@@ -14,37 +14,31 @@ import {
TripInvitation,
TripItem,
TripMember,
-} from "../types/trip";
+} from '../types/trip';
const NO_AUTH_HEADER = {
- no_auth: "1",
+ no_auth: '1',
};
@Injectable({
- providedIn: "root",
+ providedIn: 'root',
})
export class ApiService {
- public readonly apiBaseUrl: string = "/api";
+ public readonly apiBaseUrl: string = '/api';
private categoriesSubject = new BehaviorSubject(null);
- public categories$: Observable =
- this.categoriesSubject.asObservable();
+ public categories$: Observable = this.categoriesSubject.asObservable();
private settingsSubject = new BehaviorSubject(null);
- public settings$: Observable =
- this.settingsSubject.asObservable();
+ public settings$: Observable = this.settingsSubject.asObservable();
private httpClient = inject(HttpClient);
getInfo(): Observable {
- return this.httpClient.get(this.apiBaseUrl + "/info");
+ return this.httpClient.get(this.apiBaseUrl + '/info');
}
_categoriesSubjectNext(categories: Category[]) {
- this.categoriesSubject.next(
- [...categories].sort((a, b) =>
- a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
- ),
- );
+ this.categoriesSubject.next([...categories].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)));
}
getCategories(): Observable {
@@ -58,46 +52,35 @@ export class ApiService {
postCategory(c: Category): Observable {
return this.httpClient
- .post(this.apiBaseUrl + "/categories", c)
- .pipe(
- tap((category) =>
- this._categoriesSubjectNext([
- ...(this.categoriesSubject.value || []),
- category,
- ]),
- ),
- );
+ .post(this.apiBaseUrl + '/categories', c)
+ .pipe(tap((category) => this._categoriesSubjectNext([...(this.categoriesSubject.value || []), category])));
}
putCategory(c_id: number, c: Partial): Observable {
- return this.httpClient
- .put(this.apiBaseUrl + `/categories/${c_id}`, c)
- .pipe(
- tap((category) => {
- const categories = this.categoriesSubject.value || [];
- const idx = categories?.findIndex((c) => c.id == c_id) || -1;
- if (idx > -1) {
- const updated = [...categories];
- updated[idx] = category;
- this._categoriesSubjectNext(updated);
- }
- }),
- );
+ return this.httpClient.put(this.apiBaseUrl + `/categories/${c_id}`, c).pipe(
+ tap((category) => {
+ const categories = this.categoriesSubject.value || [];
+ const idx = categories?.findIndex((c) => c.id == c_id) || -1;
+ if (idx > -1) {
+ const updated = [...categories];
+ updated[idx] = category;
+ this._categoriesSubjectNext(updated);
+ }
+ }),
+ );
}
deleteCategory(category_id: number): Observable<{}> {
- return this.httpClient
- .delete<{}>(this.apiBaseUrl + `/categories/${category_id}`)
- .pipe(
- tap(() => {
- const categories = this.categoriesSubject.value || [];
- const idx = categories?.findIndex((c) => c.id == category_id) || -1;
- if (idx > -1) {
- const updated = categories.filter((_, i) => i != idx);
- this._categoriesSubjectNext(updated);
- }
- }),
- );
+ return this.httpClient.delete<{}>(this.apiBaseUrl + `/categories/${category_id}`).pipe(
+ tap(() => {
+ const categories = this.categoriesSubject.value || [];
+ const idx = categories?.findIndex((c) => c.id == category_id) || -1;
+ if (idx > -1) {
+ const updated = categories.filter((_, i) => i != idx);
+ this._categoriesSubjectNext(updated);
+ }
+ }),
+ );
}
getPlaces(): Observable {
@@ -109,23 +92,15 @@ export class ApiService {
}
postPlaces(places: Partial): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/places/batch`,
- places,
- );
+ return this.httpClient.post(`${this.apiBaseUrl}/places/batch`, places);
}
putPlace(place_id: number, place: Partial): Observable {
- return this.httpClient.put(
- `${this.apiBaseUrl}/places/${place_id}`,
- place,
- );
+ return this.httpClient.put(`${this.apiBaseUrl}/places/${place_id}`, place);
}
deletePlace(place_id: number): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/places/${place_id}`,
- );
+ return this.httpClient.delete(`${this.apiBaseUrl}/places/${place_id}`);
}
getPlaceGPX(place_id: number): Observable {
@@ -141,9 +116,7 @@ export class ApiService {
}
getTripBalance(id: number): Observable<{ [user: string]: number }> {
- return this.httpClient.get<{ [user: string]: number }>(
- `${this.apiBaseUrl}/trips/${id}/balance`,
- );
+ return this.httpClient.get<{ [user: string]: number }>(`${this.apiBaseUrl}/trips/${id}/balance`);
}
postTrip(trip: TripBase): Observable {
@@ -155,79 +128,42 @@ export class ApiService {
}
putTrip(trip: Partial, trip_id: number): Observable {
- return this.httpClient.put(
- `${this.apiBaseUrl}/trips/${trip_id}`,
- trip,
- );
+ return this.httpClient.put(`${this.apiBaseUrl}/trips/${trip_id}`, trip);
}
postTripDay(tripDay: TripDay, trip_id: number): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/days`,
- tripDay,
- );
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/days`, tripDay);
}
putTripDay(tripDay: Partial, trip_id: number): Observable {
- return this.httpClient.put(
- `${this.apiBaseUrl}/trips/${trip_id}/days/${tripDay.id}`,
- tripDay,
- );
+ return this.httpClient.put(`${this.apiBaseUrl}/trips/${trip_id}/days/${tripDay.id}`, tripDay);
}
deleteTripDay(trip_id: number, day_id: number): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}`,
- );
+ return this.httpClient.delete(`${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}`);
}
- postTripDayItem(
- item: TripItem,
- trip_id: number,
- day_id: number,
- ): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}/items`,
- item,
- );
+ postTripDayItem(item: TripItem, trip_id: number, day_id: number): Observable {
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}/items`, item);
}
- putTripDayItem(
- item: Partial,
- trip_id: number,
- day_id: number,
- item_id: number,
- ): Observable {
- return this.httpClient.put(
- `${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}/items/${item_id}`,
- item,
- );
+ putTripDayItem(item: Partial, trip_id: number, day_id: number, item_id: number): Observable {
+ return this.httpClient.put(`${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}/items/${item_id}`, item);
}
- deleteTripDayItem(
- trip_id: number,
- day_id: number,
- item_id: number,
- ): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}/items/${item_id}`,
- );
+ deleteTripDayItem(trip_id: number, day_id: number, item_id: number): Observable {
+ return this.httpClient.delete(`${this.apiBaseUrl}/trips/${trip_id}/days/${day_id}/items/${item_id}`);
}
getSharedTrip(token: string): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/shared/${token}`,
- { headers: NO_AUTH_HEADER },
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/shared/${token}`, { headers: NO_AUTH_HEADER });
}
getSharedTripURL(trip_id: number): Observable {
- return this.httpClient
- .get(`${this.apiBaseUrl}/trips/${trip_id}/share`)
- .pipe(
- map((t) => window.location.origin + t.url),
- shareReplay(),
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/${trip_id}/share`).pipe(
+ map((t) => window.location.origin + t.url),
+ shareReplay(),
+ );
}
createSharedTrip(trip_id: number): Observable {
@@ -237,138 +173,79 @@ export class ApiService {
}
deleteSharedTrip(trip_id: number): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/trips/${trip_id}/share`,
- );
+ return this.httpClient.delete(`${this.apiBaseUrl}/trips/${trip_id}/share`);
}
getPackingList(trip_id: number): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/${trip_id}/packing`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/${trip_id}/packing`);
}
getSharedTripPackingList(token: string): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/shared/${token}/packing`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/shared/${token}/packing`);
}
- postPackingItem(
- trip_id: number,
- p_item: PackingItem,
- ): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/packing`,
- p_item,
- );
+ postPackingItem(trip_id: number, p_item: PackingItem): Observable {
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/packing`, p_item);
}
- putPackingItem(
- trip_id: number,
- p_id: number,
- p_item: Partial,
- ): Observable {
- return this.httpClient.put(
- `${this.apiBaseUrl}/trips/${trip_id}/packing/${p_id}`,
- p_item,
- );
+ putPackingItem(trip_id: number, p_id: number, p_item: Partial): Observable {
+ return this.httpClient.put(`${this.apiBaseUrl}/trips/${trip_id}/packing/${p_id}`, p_item);
}
deletePackingItem(trip_id: number, p_id: number): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/trips/${trip_id}/packing/${p_id}`,
- );
+ return this.httpClient.delete(`${this.apiBaseUrl}/trips/${trip_id}/packing/${p_id}`);
}
getChecklist(trip_id: number): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/${trip_id}/checklist`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/${trip_id}/checklist`);
}
getSharedTripChecklist(token: string): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/shared/${token}/checklist`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/shared/${token}/checklist`);
}
- postChecklistItem(
- trip_id: number,
- item: ChecklistItem,
- ): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/checklist`,
- item,
- );
+ postChecklistItem(trip_id: number, item: ChecklistItem): Observable {
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/checklist`, item);
}
- putChecklistItem(
- trip_id: number,
- id: number,
- item: Partial,
- ): Observable {
- return this.httpClient.put(
- `${this.apiBaseUrl}/trips/${trip_id}/checklist/${id}`,
- item,
- );
+ putChecklistItem(trip_id: number, id: number, item: Partial): Observable {
+ return this.httpClient.put(`${this.apiBaseUrl}/trips/${trip_id}/checklist/${id}`, item);
}
deleteChecklistItem(trip_id: number, id: number): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/trips/${trip_id}/checklist/${id}`,
- );
+ return this.httpClient.delete(`${this.apiBaseUrl}/trips/${trip_id}/checklist/${id}`);
}
getHasTripsInvitations(): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/invitations/pending`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/invitations/pending`);
}
getTripsInvitations(): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/invitations`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/invitations`);
}
getTripMembers(trip_id: number): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/trips/${trip_id}/members`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/trips/${trip_id}/members`);
}
deleteTripMember(trip_id: number, username: string): Observable {
- return this.httpClient.delete(
- `${this.apiBaseUrl}/trips/${trip_id}/members/${username}`,
- );
+ return this.httpClient.delete(`${this.apiBaseUrl}/trips/${trip_id}/members/${username}`);
}
inviteTripMember(trip_id: number, user: string): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/members`,
- { user },
- );
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/members`, { user });
}
acceptTripMemberInvite(trip_id: number): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/members/accept`,
- {},
- );
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/members/accept`, {});
}
declineTripMemberInvite(trip_id: number): Observable {
- return this.httpClient.post(
- `${this.apiBaseUrl}/trips/${trip_id}/members/decline`,
- {},
- );
+ return this.httpClient.post(`${this.apiBaseUrl}/trips/${trip_id}/members/decline`, {});
}
checkVersion(): Observable {
- return this.httpClient.get(
- `${this.apiBaseUrl}/settings/checkversion`,
- );
+ return this.httpClient.get(`${this.apiBaseUrl}/settings/checkversion`);
}
getSettings(): Observable {
@@ -392,7 +269,7 @@ export class ApiService {
}
settingsUserImport(formdata: FormData): Observable {
- const headers = { enctype: "multipart/form-data" };
+ const headers = { enctype: 'multipart/form-data' };
return this.httpClient
.post(`${this.apiBaseUrl}/settings/import`, formdata, {
headers: headers,
diff --git a/src/src/app/services/auth.guard.ts b/src/src/app/services/auth.guard.ts
index d3d7f76..a982235 100644
--- a/src/src/app/services/auth.guard.ts
+++ b/src/src/app/services/auth.guard.ts
@@ -1,8 +1,8 @@
-import { inject } from "@angular/core";
-import { CanActivateFn, Router } from "@angular/router";
-import { UtilsService } from "./utils.service";
-import { AuthService } from "./auth.service";
-import { of, switchMap, take } from "rxjs";
+import { inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import { UtilsService } from './utils.service';
+import { AuthService } from './auth.service';
+import { of, switchMap, take } from 'rxjs';
export const AuthGuard: CanActivateFn = (_, state) => {
const router: Router = inject(Router);
@@ -14,14 +14,9 @@ export const AuthGuard: CanActivateFn = (_, state) => {
take(1),
switchMap((authenticated) => {
if (!authenticated) {
- const redirectURL =
- state.url === "/auth" ? "" : `redirectURL=${state.url}`;
+ const redirectURL = state.url === '/auth' ? '' : `redirectURL=${state.url}`;
const urlTree = router.parseUrl(`auth?${redirectURL}`);
- utilsService.toast(
- "warn",
- "Authentication required",
- "You must be authenticated",
- );
+ utilsService.toast('warn', 'Authentication required', 'You must be authenticated');
return of(urlTree);
}
diff --git a/src/src/app/services/auth.service.ts b/src/src/app/services/auth.service.ts
index d8dcbd1..f6e079b 100644
--- a/src/src/app/services/auth.service.ts
+++ b/src/src/app/services/auth.service.ts
@@ -1,10 +1,10 @@
-import { HttpClient } from "@angular/common/http";
-import { Injectable } from "@angular/core";
-import { Router } from "@angular/router";
-import { Observable, of, ReplaySubject } from "rxjs";
-import { tap } from "rxjs/operators";
-import { ApiService } from "./api.service";
-import { UtilsService } from "./utils.service";
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+import { Observable, of, ReplaySubject } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { ApiService } from './api.service';
+import { UtilsService } from './utils.service';
export interface Token {
refresh_token: string;
@@ -16,11 +16,11 @@ export interface AuthParams {
oidc?: string;
}
-const JWT_TOKEN = "TRIP_AT";
-const REFRESH_TOKEN = "TRIP_RT";
-const JWT_USER = "TRIP_USER";
+const JWT_TOKEN = 'TRIP_AT';
+const REFRESH_TOKEN = 'TRIP_RT';
+const JWT_USER = 'TRIP_USER';
-@Injectable({ providedIn: "root" })
+@Injectable({ providedIn: 'root' })
export class AuthService {
public readonly apiBaseUrl: string;
private refreshInProgressLock$: ReplaySubject | null = null;
@@ -39,7 +39,7 @@ export class AuthService {
}
get loggedUser(): string {
- return localStorage.getItem(JWT_USER) ?? "";
+ return localStorage.getItem(JWT_USER) ?? '';
}
set accessToken(token: string) {
@@ -47,7 +47,7 @@ export class AuthService {
}
get accessToken(): string {
- return localStorage.getItem(JWT_TOKEN) ?? "";
+ return localStorage.getItem(JWT_TOKEN) ?? '';
}
set refreshToken(token: string) {
@@ -55,11 +55,11 @@ export class AuthService {
}
get refreshToken(): string {
- return localStorage.getItem(REFRESH_TOKEN) ?? "";
+ return localStorage.getItem(REFRESH_TOKEN) ?? '';
}
authParams(): Observable {
- return this.httpClient.get(this.apiBaseUrl + "/auth/params");
+ return this.httpClient.get(this.apiBaseUrl + '/auth/params');
}
storeTokens(tokens: Token): void {
@@ -81,7 +81,7 @@ export class AuthService {
this.refreshInProgressLock$ = new ReplaySubject(1);
this.httpClient
- .post(this.apiBaseUrl + "/auth/refresh", {
+ .post(this.apiBaseUrl + '/auth/refresh', {
refresh_token: this.refreshToken,
})
.pipe(
@@ -103,60 +103,47 @@ export class AuthService {
}
login(authForm: { username: string; password: string }): Observable {
- return this.httpClient
- .post(this.apiBaseUrl + "/auth/login", authForm)
- .pipe(
- tap((tokens: Token) => {
- this.loggedUser = authForm.username;
- this.storeTokens(tokens);
- }),
- );
+ return this.httpClient.post(this.apiBaseUrl + '/auth/login', authForm).pipe(
+ tap((tokens: Token) => {
+ this.loggedUser = authForm.username;
+ this.storeTokens(tokens);
+ }),
+ );
}
- register(authForm: {
- username: string;
- password: string;
- }): Observable {
- return this.httpClient
- .post(this.apiBaseUrl + "/auth/register", authForm)
- .pipe(
- tap((tokens: Token) => {
- this.loggedUser = authForm.username;
- this.storeTokens(tokens);
- }),
- );
+ register(authForm: { username: string; password: string }): Observable {
+ return this.httpClient.post(this.apiBaseUrl + '/auth/register', authForm).pipe(
+ tap((tokens: Token) => {
+ this.loggedUser = authForm.username;
+ this.storeTokens(tokens);
+ }),
+ );
}
oidcLogin(code: string, state: string): Observable {
- return this.httpClient
- .post(this.apiBaseUrl + "/auth/oidc/login", { code, state })
- .pipe(
- tap((data: any) => {
- if (data.access_token && data.refresh_token) {
- this.loggedUser = this._getTokenUsername(data.access_token);
- this.storeTokens(data);
- }
- }),
- );
+ return this.httpClient.post(this.apiBaseUrl + '/auth/oidc/login', { code, state }).pipe(
+ tap((data: any) => {
+ if (data.access_token && data.refresh_token) {
+ this.loggedUser = this._getTokenUsername(data.access_token);
+ this.storeTokens(data);
+ }
+ }),
+ );
}
- logout(custom_msg: string = "", is_error = false): void {
- this.loggedUser = "";
+ logout(custom_msg: string = '', is_error = false): void {
+ this.loggedUser = '';
this.removeTokens();
if (custom_msg) {
if (is_error) {
- this.utilsService.toast(
- "error",
- "You must be authenticated",
- custom_msg,
- );
+ this.utilsService.toast('error', 'You must be authenticated', custom_msg);
} else {
- this.utilsService.toast("success", "Success", custom_msg);
+ this.utilsService.toast('success', 'Success', custom_msg);
}
}
- this.router.navigate(["/auth"]);
+ this.router.navigate(['/auth']);
}
private removeTokens(): void {
@@ -167,7 +154,7 @@ export class AuthService {
isTokenExpired(token: string, offsetSeconds?: number): boolean {
// Return if there is no token
- if (!token || token === "") {
+ if (!token || token === '') {
return true;
}
@@ -186,25 +173,19 @@ export class AuthService {
private _b64DecodeUnicode(str: any): string {
return decodeURIComponent(
Array.prototype.map
- .call(
- this._b64decode(str),
- (c: any) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2),
- )
- .join(""),
+ .call(this._b64decode(str), (c: any) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
+ .join(''),
);
}
private _b64decode(str: string): string {
- const chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
- let output = "";
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+ let output = '';
- str = String(str).replace(/=+$/, "");
+ str = String(str).replace(/=+$/, '');
if (str.length % 4 === 1) {
- throw new Error(
- "'atob' failed: The string to be decoded is not correctly encoded.",
- );
+ throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
/* eslint-disable */
@@ -223,21 +204,21 @@ export class AuthService {
}
private _urlBase64Decode(str: string): string {
- let output = str.replace(/-/g, "+").replace(/_/g, "/");
+ let output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {
case 0: {
break;
}
case 2: {
- output += "==";
+ output += '==';
break;
}
case 3: {
- output += "=";
+ output += '=';
break;
}
default: {
- throw Error("Illegal base64url string!");
+ throw Error('Illegal base64url string!');
}
}
return this._b64DecodeUnicode(output);
@@ -247,11 +228,11 @@ export class AuthService {
const decodedToken = this._decodeToken(token);
if (decodedToken === null) {
- return "";
+ return '';
}
- if (!decodedToken.hasOwnProperty("sub")) {
- return "";
+ if (!decodedToken.hasOwnProperty('sub')) {
+ return '';
}
return decodedToken.sub;
@@ -262,7 +243,7 @@ export class AuthService {
return null;
}
- const parts = token.split(".");
+ const parts = token.split('.');
if (parts.length !== 3) {
return null;
@@ -284,7 +265,7 @@ export class AuthService {
return null;
}
- if (!decodedToken.hasOwnProperty("exp")) {
+ if (!decodedToken.hasOwnProperty('exp')) {
return null;
}
diff --git a/src/src/app/services/interceptor.service.ts b/src/src/app/services/interceptor.service.ts
index b4aaa92..a09b34f 100644
--- a/src/src/app/services/interceptor.service.ts
+++ b/src/src/app/services/interceptor.service.ts
@@ -1,63 +1,52 @@
-import {
- HttpErrorResponse,
- HttpEvent,
- HttpHandlerFn,
- HttpRequest,
-} from "@angular/common/http";
-import { inject } from "@angular/core";
-import { catchError, Observable, switchMap, take, throwError } from "rxjs";
-import { AuthService } from "./auth.service";
-import { UtilsService } from "./utils.service";
+import { HttpErrorResponse, HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';
+import { inject } from '@angular/core';
+import { catchError, Observable, switchMap, take, throwError } from 'rxjs';
+import { AuthService } from './auth.service';
+import { UtilsService } from './utils.service';
const ERROR_CONFIG: Record = {
400: {
- title: "Bad Request",
- detail: "Unknown error, check console for details",
+ title: 'Bad Request',
+ detail: 'Unknown error, check console for details',
},
- 403: { title: "Forbidden", detail: "You are not allowed to do this" },
- 409: { title: "Conflict", detail: "Conflict on resource" },
- 413: { title: "Request Entity Too Large", detail: "The resource is too big" },
+ 403: { title: 'Forbidden', detail: 'You are not allowed to do this' },
+ 409: { title: 'Conflict', detail: 'Conflict on resource' },
+ 413: { title: 'Request Entity Too Large', detail: 'The resource is too big' },
422: {
- title: "Unprocessable Entity",
- detail: "The resource you sent was unprocessable",
+ title: 'Unprocessable Entity',
+ detail: 'The resource you sent was unprocessable',
},
502: {
- title: "Bad Gateway",
- detail: "Check your connectivity and ensure the server is up",
+ title: 'Bad Gateway',
+ detail: 'Check your connectivity and ensure the server is up',
},
- 503: { title: "Service Unavailable", detail: "Resource not available" },
+ 503: { title: 'Service Unavailable', detail: 'Resource not available' },
};
-export const Interceptor = (
- req: HttpRequest,
- next: HttpHandlerFn,
-): Observable> => {
+export const Interceptor = (req: HttpRequest, next: HttpHandlerFn): Observable> => {
const authService = inject(AuthService);
const utilsService = inject(UtilsService);
function showAndThrowError(title: string, details: string) {
- utilsService.toast("error", title, details);
+ utilsService.toast('error', title, details);
return throwError(() => details);
}
- if (req.headers.has("no_auth")) {
+ if (req.headers.has('no_auth')) {
// Shared Trip must be anonymous
return next(req);
}
- if (!req.headers.has("enctype") && !req.headers.has("Content-Type")) {
+ if (!req.headers.has('enctype') && !req.headers.has('Content-Type')) {
req = req.clone({
setHeaders: {
- "Content-Type": "application/json",
- "Accept-Language": "en-US;q=0.9,en-US,en;q=0.8",
+ 'Content-Type': 'application/json',
+ 'Accept-Language': 'en-US;q=0.9,en-US,en;q=0.8',
},
});
}
- if (
- authService.accessToken &&
- !authService.isTokenExpired(authService.accessToken)
- ) {
+ if (authService.accessToken && !authService.isTokenExpired(authService.accessToken)) {
if (req.url.startsWith(authService.apiBaseUrl)) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${authService.accessToken}` },
@@ -70,17 +59,14 @@ export const Interceptor = (
const errDetails = ERROR_CONFIG[err.status];
if (errDetails) {
console.error(err);
- return showAndThrowError(
- errDetails.title,
- `${err.error?.detail || err.message || errDetails.detail}`,
- );
+ return showAndThrowError(errDetails.title, `${err.error?.detail || err.message || errDetails.detail}`);
}
if (err.status == 401 && authService.accessToken) {
// Handle 401 on Refresh (RT expired)
- if (req.url.endsWith("/refresh")) {
- authService.logout("Your session has expired", true);
- return throwError(() => "Your session has expired");
+ if (req.url.endsWith('/refresh')) {
+ authService.logout('Your session has expired', true);
+ return throwError(() => 'Your session has expired');
}
// Unauthenticated, AT exists but is expired (authServices.accessToken truethy), we refresh it
@@ -95,18 +81,15 @@ export const Interceptor = (
return next(refreshedReq);
}),
);
- } else if (err.status == 401 && !req.url.endsWith("/refresh")) {
+ } else if (err.status == 401 && !req.url.endsWith('/refresh')) {
// If any API route 401 -> redirect to login. We skip /refresh/ to prevent toast on login errors.
- authService.logout(
- `${err.error?.detail || err.message || "You must be authenticated"}`,
- true,
- );
+ authService.logout(`${err.error?.detail || err.message || 'You must be authenticated'}`, true);
}
console.error(err);
return showAndThrowError(
- "Request Error",
- `${err.error?.detail || err.message || "Unknown error, check console for details"}`,
+ 'Request Error',
+ `${err.error?.detail || err.message || 'Unknown error, check console for details'}`,
);
}),
);
diff --git a/src/src/app/services/utils.service.ts b/src/src/app/services/utils.service.ts
index 01c10a7..634304b 100644
--- a/src/src/app/services/utils.service.ts
+++ b/src/src/app/services/utils.service.ts
@@ -1,47 +1,42 @@
-import { inject, Injectable } from "@angular/core";
-import { MessageService } from "primeng/api";
-import { TripStatus } from "../types/trip";
-import { ApiService } from "./api.service";
-import { map } from "rxjs";
+import { inject, Injectable } from '@angular/core';
+import { MessageService } from 'primeng/api';
+import { TripStatus } from '../types/trip';
+import { ApiService } from './api.service';
+import { map } from 'rxjs';
-type ToastSeverity = "info" | "warn" | "error" | "success";
+type ToastSeverity = 'info' | 'warn' | 'error' | 'success';
@Injectable({
- providedIn: "root",
+ providedIn: 'root',
})
export class UtilsService {
private apiService = inject(ApiService);
- currency$ = this.apiService.settings$.pipe(map((s) => s?.currency ?? "€"));
+ currency$ = this.apiService.settings$.pipe(map((s) => s?.currency ?? '€'));
readonly statuses: TripStatus[] = [
- { label: "pending", color: "#3258A8" },
- { label: "booked", color: "#00A341" },
- { label: "constraint", color: "#FFB900" },
- { label: "optional", color: "#625A84" },
+ { label: 'pending', color: '#3258A8' },
+ { label: 'booked', color: '#00A341' },
+ { label: 'constraint', color: '#FFB900' },
+ { label: 'optional', color: '#625A84' },
];
constructor(private ngMessageService: MessageService) {}
toGithubTRIP() {
- window.open("https://github.com/itskovacs/trip", "_blank");
+ window.open('https://github.com/itskovacs/trip', '_blank');
}
toggleDarkMode() {
- const element = document.querySelector("html");
- element?.classList.toggle("dark");
+ const element = document.querySelector('html');
+ element?.classList.toggle('dark');
}
enableDarkMode() {
- const element = document.querySelector("html");
- element?.classList.toggle("dark", true);
+ const element = document.querySelector('html');
+ element?.classList.toggle('dark', true);
}
- toast(
- severity: ToastSeverity = "info",
- summary = "Info",
- detail = "",
- life = 3000,
- ): void {
+ toast(severity: ToastSeverity = 'info', summary = 'Info', detail = '', life = 3000): void {
this.ngMessageService.add({
severity,
summary,
@@ -57,12 +52,12 @@ export class UtilsService {
const lngMatch = url.match(/!4d([\d\-.]+)/);
if (!placeMatch || !latMatch || !lngMatch) {
- this.toast("error", "Error", "Unrecognized Google Maps URL format");
- console.error("Unrecognized Google Maps URL format");
- return ["", ""];
+ this.toast('error', 'Error', 'Unrecognized Google Maps URL format');
+ console.error('Unrecognized Google Maps URL format');
+ return ['', ''];
}
- const place = decodeURIComponent(placeMatch[1].replace(/\+/g, " ").trim());
+ const place = decodeURIComponent(placeMatch[1].replace(/\+/g, ' ').trim());
const latlng = `${latMatch[1]},${lngMatch[1]}`;
return [place, latlng];
}
diff --git a/src/src/app/shared/haversine.ts b/src/src/app/shared/haversine.ts
index 7bd7d79..5260bb5 100644
--- a/src/src/app/shared/haversine.ts
+++ b/src/src/app/shared/haversine.ts
@@ -1,9 +1,4 @@
-export function calculateDistanceBetween(
- lat1: number,
- lon1: number,
- lat2: number,
- lon2: number,
-) {
+export function calculateDistanceBetween(lat1: number, lon1: number, lat2: number, lon2: number) {
// returns d in meter
const toRad = (deg: number) => (deg * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
diff --git a/src/src/app/shared/latlng-parser.ts b/src/src/app/shared/latlng-parser.ts
index 29f500d..3f474bf 100644
--- a/src/src/app/shared/latlng-parser.ts
+++ b/src/src/app/shared/latlng-parser.ts
@@ -1,19 +1,13 @@
-import OpenLocationCode from "open-location-code-typescript";
+import OpenLocationCode from 'open-location-code-typescript';
const patternDEC = /^\s*(-?\d{1,3}(?:\.\d+)?)\s*,\s*(-?\d{1,3}(?:\.\d+)?)\s*$/;
-const patternDD =
- /^\s*(\d{1,3}(?:\.\d+)?)°?\s*([NS])\s*,\s*(\d{1,3}(?:\.\d+)?)°?\s*([EW])\s*$/i;
+const patternDD = /^\s*(\d{1,3}(?:\.\d+)?)°?\s*([NS])\s*,\s*(\d{1,3}(?:\.\d+)?)°?\s*([EW])\s*$/i;
const patternDMS =
/^\s*(\d{1,3})°\s*(\d{1,2})['′]\s*(\d{1,2}(?:\.\d+)?)["″]?\s*([NS])\s*,\s*(\d{1,3})°\s*(\d{1,2})['′]\s*(\d{1,2}(?:\.\d+)?)["″]?\s*([EW])\s*$/i;
const patternDMM =
/^\s*(\d{1,3})°\s*(\d{1,2}(?:\.\d+)?)['′]?\s*([NS])\s*,\s*(\d{1,3})°\s*(\d{1,2}(?:\.\d+)?)['′]?\s*([EW])\s*$/i;
-function _dmsToDecimal(
- deg: number,
- min: number,
- sec: number,
- dir: string,
-): number {
+function _dmsToDecimal(deg: number, min: number, sec: number, dir: string): number {
const dec = deg + min / 60 + sec / 3600;
return /[SW]/i.test(dir) ? -dec : dec;
}
@@ -24,14 +18,12 @@ function _dmmToDecimal(deg: number, min: number, dir: string): number {
}
export function formatLatLng(num: number): string {
- const decimals = num.toString().split(".")[1]?.length || 0;
+ const decimals = num.toString().split('.')[1]?.length || 0;
return num.toFixed(Math.min(decimals, 5));
}
-export function checkAndParseLatLng(
- value: string | number,
-): [number, number] | undefined {
- if (typeof value !== "string") return undefined;
+export function checkAndParseLatLng(value: string | number): [number, number] | undefined {
+ if (typeof value !== 'string') return undefined;
// Parse PlusCode
if (OpenLocationCode.isValid(value)) {
diff --git a/src/src/app/shared/linkify.pipe.ts b/src/src/app/shared/linkify.pipe.ts
index fee2bfc..ea95395 100644
--- a/src/src/app/shared/linkify.pipe.ts
+++ b/src/src/app/shared/linkify.pipe.ts
@@ -1,7 +1,7 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
+import { Pipe, PipeTransform } from '@angular/core';
+import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
-@Pipe({ name: "linkify", standalone: true })
+@Pipe({ name: 'linkify', standalone: true })
export class LinkifyPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
@@ -10,11 +10,11 @@ export class LinkifyPipe implements PipeTransform {
/[&<>"']/g,
(char) =>
({
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
})[char]!,
);
}
@@ -26,7 +26,7 @@ export class LinkifyPipe implements PipeTransform {
const safeText = this.basicEscape(text);
const html = safeText.replace(urlRegex, (url) => {
- const href = url.startsWith("http") ? url : `https://${url}`;
+ const href = url.startsWith('http') ? url : `https://${url}`;
return `${url} `;
});
diff --git a/src/src/app/shared/map.ts b/src/src/app/shared/map.ts
index e10b693..020cb74 100644
--- a/src/src/app/shared/map.ts
+++ b/src/src/app/shared/map.ts
@@ -1,10 +1,9 @@
-import * as L from "leaflet";
-import "leaflet.markercluster";
-import "leaflet-contextmenu";
-import { Place } from "../types/poi";
+import * as L from 'leaflet';
+import 'leaflet.markercluster';
+import 'leaflet-contextmenu';
+import { Place } from '../types/poi';
-export const DEFAULT_TILE_URL =
- "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png";
+export const DEFAULT_TILE_URL = 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png';
export interface ContextMenuItem {
text: string;
index?: number;
@@ -20,15 +19,12 @@ export interface MarkerOptions extends L.MarkerOptions {
contextmenuItems: ContextMenuItem[];
}
-export function createMap(
- contextMenuItems: ContextMenuItem[] = [],
- tilelayer: string = DEFAULT_TILE_URL,
-): L.Map {
+export function createMap(contextMenuItems: ContextMenuItem[] = [], tilelayer: string = DEFAULT_TILE_URL): L.Map {
const southWest = L.latLng(-89.99, -180);
const northEast = L.latLng(89.99, 180);
const bounds = L.latLngBounds(southWest, northEast);
- const map = L.map("map", {
+ const map = L.map('map', {
maxBoundsViscosity: 1.0,
zoomControl: false,
contextmenu: true,
@@ -61,34 +57,29 @@ export function createClusterGroup(): L.MarkerClusterGroup {
const count = cluster.getChildCount();
return L.divIcon({
html: `${count}
`,
- className: "",
+ className: '',
iconSize: [40, 40],
});
},
});
}
-export function tripDayMarker(item: {
- text: string;
- lat: number;
- lng: number;
- time?: string;
-}): L.Marker {
+export function tripDayMarker(item: { text: string; lat: number; lng: number; time?: string }): L.Marker {
const marker = new L.Marker([item.lat!, item.lng], {
icon: L.divIcon({
- className: "bg-black rounded-full",
+ className: 'bg-black rounded-full',
iconSize: [14, 14],
}),
});
- const touchDevice = "ontouchstart" in window;
+ const touchDevice = 'ontouchstart' in window;
if (!touchDevice) {
marker.bindTooltip(
`${item.time}
${item.text}
`,
{
- direction: "right",
+ direction: 'right',
offset: [10, 0],
- className: "class-tooltip",
+ className: 'class-tooltip',
},
);
}
@@ -104,28 +95,25 @@ export function placeToMarker(
const options: Partial = {
riseOnHover: true,
title: place.name,
- alt: "",
+ alt: '',
};
- const markerImage = isLowNet
- ? place.category.image
- : (place.image ?? place.category.image);
+ const markerImage = isLowNet ? place.category.image : (place.image ?? place.category.image);
- let markerClasses =
- "w-full h-full rounded-full bg-center bg-cover bg-white dark:bg-surface-900";
- if (grayscale) markerClasses += " grayscale";
+ let markerClasses = 'w-full h-full rounded-full bg-center bg-cover bg-white dark:bg-surface-900';
+ if (grayscale) markerClasses += ' grayscale';
const iconHtml = `
- ${gpxInBubble && place.gpx ? '
' : ""}
+ ${gpxInBubble && place.gpx ? '
' : ''}
`;
const icon = L.divIcon({
html: iconHtml.trim(),
iconSize: [56, 56],
- className: "",
+ className: '',
});
const marker = new L.Marker([+place.lat, +place.lng], {
@@ -133,12 +121,12 @@ export function placeToMarker(
icon,
});
- const touchDevice = "ontouchstart" in window;
+ const touchDevice = 'ontouchstart' in window;
if (!touchDevice) {
marker.bindTooltip(placeHoverTooltip(place), {
- direction: "right",
+ direction: 'right',
offset: [28, 0],
- className: "class-tooltip",
+ className: 'class-tooltip',
});
}
return marker;
@@ -146,16 +134,12 @@ export function placeToMarker(
export function gpxToPolyline(gpx: string): L.Polyline {
const parser = new DOMParser();
- const gpxDoc = parser.parseFromString(gpx, "application/xml");
+ const gpxDoc = parser.parseFromString(gpx, 'application/xml');
- const trkpts = Array.from(gpxDoc.querySelectorAll("trkpt"));
+ const trkpts = Array.from(gpxDoc.querySelectorAll('trkpt'));
const latlngs = trkpts.map(
- (pt) =>
- [
- parseFloat(pt.getAttribute("lat")!),
- parseFloat(pt.getAttribute("lon")!),
- ] as [number, number],
+ (pt) => [parseFloat(pt.getAttribute('lat')!), parseFloat(pt.getAttribute('lon')!)] as [number, number],
);
- return L.polyline(latlngs, { color: "blue" });
+ return L.polyline(latlngs, { color: 'blue' });
}
diff --git a/src/src/app/shared/order-by.pipe.ts b/src/src/app/shared/order-by.pipe.ts
index 33e4a90..9546a53 100644
--- a/src/src/app/shared/order-by.pipe.ts
+++ b/src/src/app/shared/order-by.pipe.ts
@@ -1,6 +1,6 @@
-import { Pipe, PipeTransform } from "@angular/core";
+import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
- name: "orderBy",
+ name: 'orderBy',
pure: true,
standalone: true,
})
@@ -9,8 +9,6 @@ export class orderByPipe implements PipeTransform {
if (!items || items.length === 0) {
return items;
}
- return items
- .slice()
- .sort((a, b) => (a[field] < b[field] ? -1 : a[field] > b[field] ? 1 : 0));
+ return items.slice().sort((a, b) => (a[field] < b[field] ? -1 : a[field] > b[field] ? 1 : 0));
}
}
diff --git a/src/src/app/shared/place-box/place-box.component.ts b/src/src/app/shared/place-box/place-box.component.ts
index 6895d59..5211c0b 100644
--- a/src/src/app/shared/place-box/place-box.component.ts
+++ b/src/src/app/shared/place-box/place-box.component.ts
@@ -1,26 +1,19 @@
-import {
- ChangeDetectionStrategy,
- Component,
- EventEmitter,
- Input,
- OnInit,
- Output,
-} from "@angular/core";
-import { ButtonModule } from "primeng/button";
-import { MenuModule } from "primeng/menu";
-import { Place } from "../../types/poi";
-import { MenuItem } from "primeng/api";
-import { UtilsService } from "../../services/utils.service";
-import { Observable } from "rxjs";
-import { AsyncPipe } from "@angular/common";
-import { LinkifyPipe } from "../linkify.pipe";
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { ButtonModule } from 'primeng/button';
+import { MenuModule } from 'primeng/menu';
+import { Place } from '../../types/poi';
+import { MenuItem } from 'primeng/api';
+import { UtilsService } from '../../services/utils.service';
+import { Observable } from 'rxjs';
+import { AsyncPipe } from '@angular/common';
+import { LinkifyPipe } from '../linkify.pipe';
@Component({
- selector: "app-place-box",
+ selector: 'app-place-box',
standalone: true,
imports: [ButtonModule, MenuModule, AsyncPipe, LinkifyPipe],
- templateUrl: "./place-box.component.html",
- styleUrls: ["./place-box.component.scss"],
+ templateUrl: './place-box.component.html',
+ styleUrls: ['./place-box.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlaceBoxComponent implements OnInit {
@@ -43,33 +36,33 @@ export class PlaceBoxComponent implements OnInit {
ngOnInit() {
const items = [
{
- label: "Edit",
- icon: "pi pi-pencil",
- iconClass: "text-blue-500!",
+ label: 'Edit',
+ icon: 'pi pi-pencil',
+ iconClass: 'text-blue-500!',
command: () => {
this.editPlace();
},
},
{
- label: "Favorite",
- icon: "pi pi-star",
- iconClass: "text-yellow-500!",
+ label: 'Favorite',
+ icon: 'pi pi-star',
+ iconClass: 'text-yellow-500!',
command: () => {
this.favoritePlace();
},
},
{
- label: "Mark",
- icon: "pi pi-check",
- iconClass: "text-green-500!",
+ label: 'Mark',
+ icon: 'pi pi-check',
+ iconClass: 'text-green-500!',
command: () => {
this.visitPlace();
},
},
{
- label: "Delete",
- icon: "pi pi-trash",
- iconClass: "text-red-500!",
+ label: 'Delete',
+ icon: 'pi pi-trash',
+ iconClass: 'text-red-500!',
command: () => {
this.deletePlace();
},
@@ -78,9 +71,9 @@ export class PlaceBoxComponent implements OnInit {
if (this.selectedPlace?.gpx) {
items.unshift({
- label: "Display GPX",
- icon: "pi pi-compass",
- iconClass: "text-gray-500!",
+ label: 'Display GPX',
+ icon: 'pi pi-compass',
+ iconClass: 'text-gray-500!',
command: () => {
this.displayGPX();
},
@@ -89,7 +82,7 @@ export class PlaceBoxComponent implements OnInit {
this.menuItems = [
{
- label: "Place",
+ label: 'Place',
items: items,
},
];
diff --git a/src/src/app/shared/place-gpx/place-gpx.component.ts b/src/src/app/shared/place-gpx/place-gpx.component.ts
index 6a49da8..923f696 100644
--- a/src/src/app/shared/place-gpx/place-gpx.component.ts
+++ b/src/src/app/shared/place-gpx/place-gpx.component.ts
@@ -1,19 +1,13 @@
-import {
- ChangeDetectionStrategy,
- Component,
- EventEmitter,
- Input,
- Output,
-} from "@angular/core";
-import { ButtonModule } from "primeng/button";
-import { Place } from "../../types/poi";
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { ButtonModule } from 'primeng/button';
+import { Place } from '../../types/poi';
@Component({
- selector: "app-place-gpx",
+ selector: 'app-place-gpx',
standalone: true,
imports: [ButtonModule],
- templateUrl: "./place-gpx.component.html",
- styleUrls: ["./place-gpx.component.scss"],
+ templateUrl: './place-gpx.component.html',
+ styleUrls: ['./place-gpx.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlaceGPXComponent {
@@ -33,6 +27,6 @@ export class PlaceGPXComponent {
}
downloadTrace() {
- this.downloadEmitter.emit()
+ this.downloadEmitter.emit();
}
}
diff --git a/src/src/app/types/settings.ts b/src/src/app/types/settings.ts
index d082d3f..1bbfcc1 100644
--- a/src/src/app/types/settings.ts
+++ b/src/src/app/types/settings.ts
@@ -1,4 +1,4 @@
-import { Category, Place } from "./poi";
+import { Category, Place } from './poi';
export interface Settings {
username: string;
diff --git a/src/src/app/types/trip.ts b/src/src/app/types/trip.ts
index afaa30f..ef2f536 100644
--- a/src/src/app/types/trip.ts
+++ b/src/src/app/types/trip.ts
@@ -1,4 +1,4 @@
-import { Place } from "./poi";
+import { Place } from './poi';
export interface TripBase {
id: number;
diff --git a/src/src/main.ts b/src/src/main.ts
index 8882c45..17447a5 100644
--- a/src/src/main.ts
+++ b/src/src/main.ts
@@ -2,6 +2,4 @@ import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
-bootstrapApplication(AppComponent, appConfig).catch((err) =>
- console.error(err),
-);
+bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
diff --git a/src/src/mytheme.ts b/src/src/mytheme.ts
index 6179ee9..bffff07 100644
--- a/src/src/mytheme.ts
+++ b/src/src/mytheme.ts
@@ -388,24 +388,20 @@ export const TripThemePreset = definePreset(Aura, {
overlay: {
select: {
borderRadius: '{border.radius.md}',
- shadow:
- '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
+ shadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
},
popover: {
borderRadius: '{border.radius.md}',
padding: '0.75rem',
- shadow:
- '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
+ shadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
},
modal: {
borderRadius: '{border.radius.xl}',
padding: '1.25rem',
- shadow:
- '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)',
+ shadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)',
},
navigation: {
- shadow:
- '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
+ shadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
},
},
colorScheme: {
diff --git a/src/src/styles.scss b/src/src/styles.scss
index 4dd4911..3eceb59 100644
--- a/src/src/styles.scss
+++ b/src/src/styles.scss
@@ -1,9 +1,9 @@
-@use "primeicons/primeicons.css";
+@use 'primeicons/primeicons.css';
@plugin 'tailwindcss-primeui';
@variant dark (&:where(.dark, .dark *));
@layer tailwind {
- @import "tailwindcss";
+ @import 'tailwindcss';
}
* {
@@ -30,17 +30,17 @@
html {
font-size: 15px;
font-family:
- "Inter",
+ 'Inter',
-apple-system,
BlinkMacSystemFont,
- "Segoe UI",
+ 'Segoe UI',
Roboto,
Helvetica,
Arial,
sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
+ 'Apple Color Emoji',
+ 'Segoe UI Emoji',
+ 'Segoe UI Symbol';
line-height: normal;
}
@@ -82,7 +82,7 @@ html {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
- font-family: "Inter", sans-serif;
+ font-family: 'Inter', sans-serif;
font-size: 15px;
}