✨ Trip: attachments, archived QoL, empty lists placeholders
This commit is contained in:
parent
0a2d0d5c6f
commit
85495f55e8
@ -1,5 +1,5 @@
|
||||
<section class="mt-4" [class.prettyprint]="isPrinting">
|
||||
<div class="p-4 print:p-0 flex flex-wrap items-center justify-between">
|
||||
<div class="p-4 print:p-0 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<p-button text icon="pi pi-chevron-left" class="print:hidden" (click)="back()" severity="secondary" />
|
||||
<div class="flex flex-col max-w-[55vw] md:max-w-full">
|
||||
@ -10,12 +10,24 @@
|
||||
trip?.days?.length }} {{ trip?.days!.length > 1 ? 'days' :
|
||||
'day'}}</span>
|
||||
<span
|
||||
class="bg-gray-100 text-gray-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded min-w-fit dark:bg-gray-400">{{
|
||||
class="bg-gray-100 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded min-w-fit dark:bg-gray-400">{{
|
||||
(totalPrice | number:'1.0-2') || '-' }} {{ trip?.currency }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden print:flex flex-col items-center">
|
||||
<img src="favicon.png" class="size-20">
|
||||
<div class="flex gap-2 items-center text-xs text-gray-500"><i class="pi pi-github"></i>itskovacs/trip</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 print:hidden">
|
||||
<div class="flex">
|
||||
<p-button (click)="openMenuTripActionsItems($event)" severity="secondary" text icon="pi pi-ellipsis-h" />
|
||||
<p-menu #menuTripActions [model]="menuTripActionsItems" [popup]="true" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (trip?.archived) {
|
||||
<div class="mx-auto p-4 mt-4 md:mt-0 w-full md:w-fit text-orange-800 rounded-md bg-orange-50"
|
||||
[class.prettyprint]="isPrinting">
|
||||
@ -28,27 +40,12 @@
|
||||
<p-button text icon="pi pi-box" label="Restore" severity="success" (click)="openUnarchiveTripModal()" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="hidden print:flex flex-col items-center">
|
||||
<img src="favicon.png" class="size-20">
|
||||
<div class="flex gap-2 items-center text-xs text-gray-500"><i class="pi pi-github"></i>itskovacs/trip</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 print:hidden">
|
||||
@if (!trip?.archived) {
|
||||
<div class="flex">
|
||||
<p-button (click)="menuTripActions.toggle($event)" severity="secondary" text icon="pi pi-ellipsis-h" />
|
||||
<p-menu #menuTripActions [model]="menuTripActionsItems" [popup]="true" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isArchivalReviewDisplayed) {
|
||||
<div
|
||||
class="m-4 whitespace-pre-line text-gray-800 dark:text-gray-200 max-h-[600px] overflow-y-auto p-4 rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800">
|
||||
{{ trip?.archival_review }}</div>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
|
||||
<section class="p-4 print:px-1 grid lg:grid-cols-3 gap-4 print:block" [class.prettyprint]="isPrinting">
|
||||
@ -320,8 +317,8 @@
|
||||
<div class="relative rounded-md shadow p-4">
|
||||
<p class="font-bold mb-1">Place</p>
|
||||
<div class="text-sm text-gray-500 truncate">{{ selectedItem.place.name }}</div>
|
||||
<div class="absolute top-2 right-2"><p-button severity="help" text icon="pi pi-pencil"
|
||||
(click)="editPlace(selectedItem.place)" /></div>
|
||||
<div class="absolute top-2 right-2"><p-button severity="help" pTooltip="Edit place details" text
|
||||
icon="pi pi-pencil" (click)="editPlace(selectedItem.place)" /></div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -395,8 +392,8 @@
|
||||
|
||||
<div
|
||||
class="bg-white rounded py-2 absolute top-1/2 -translate-y-1/2 left-0 hidden group-hover:block slide-x dark:bg-surface-900">
|
||||
<p-button [icon]="collapsedTripPlaces ? 'pi pi-chevron-down' : 'pi pi-chevron-up'" text
|
||||
(click)="collapsedTripPlaces = !collapsedTripPlaces" />
|
||||
<p-button [icon]="isCollapsedTripPlaces ? 'pi pi-chevron-down' : 'pi pi-chevron-up'" text
|
||||
(click)="isCollapsedTripPlaces = !isCollapsedTripPlaces" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -414,7 +411,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!collapsedTripPlaces) {
|
||||
@if (!isCollapsedTripPlaces) {
|
||||
<div [class.max-h-64!]="!isExpanded" class="max-h-[340px] overflow-y-auto">
|
||||
@defer {
|
||||
@for (p of places; track p.id) {
|
||||
@ -475,15 +472,15 @@
|
||||
|
||||
<div
|
||||
class="bg-white rounded py-2 absolute top-1/2 -translate-y-1/2 left-0 hidden group-hover:block slide-x dark:bg-surface-900">
|
||||
<p-button [icon]="collapsedTripDays ? 'pi pi-chevron-down' : 'pi pi-chevron-up'" text
|
||||
(click)="collapsedTripDays = !collapsedTripDays" />
|
||||
<p-button [icon]="isCollapsedTripDays ? 'pi pi-chevron-down' : 'pi pi-chevron-up'" text
|
||||
(click)="isCollapsedTripDays = !isCollapsedTripDays" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-button icon="pi pi-plus" [disabled]="trip?.archived" (click)="addDay()" text />
|
||||
</div>
|
||||
|
||||
@if (!collapsedTripDays) {
|
||||
@if (!isCollapsedTripDays) {
|
||||
<div [class.max-h-64!]="!isExpanded" class="max-h-[340px] overflow-y-auto">
|
||||
@defer {
|
||||
@for (d of trip?.days; track d.id) {
|
||||
@ -568,9 +565,9 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<p-dialog header="Share" [draggable]="false" [dismissableMask]="true" [modal]="true" [(visible)]="shareDialogVisible"
|
||||
<p-dialog header="Share" [draggable]="false" [dismissableMask]="true" [modal]="true" [(visible)]="isShareDialogVisible"
|
||||
[style]="{ width: '25rem' }">
|
||||
@if (shareDialogVisible) {
|
||||
@if (isShareDialogVisible) {
|
||||
<ng-container>
|
||||
@if (trip?.shared) {
|
||||
<div class="flex items-center flex-col gap-2">
|
||||
@ -596,10 +593,10 @@
|
||||
|
||||
<p-menu #menuTripPacking [model]="menuTripPackingItems" attachTo="body" [popup]="true" />
|
||||
<p-dialog header="Packing list" [draggable]="false" [dismissableMask]="true" [modal]="true"
|
||||
[(visible)]="packingDialogVisible" styleClass="w-[95%] md:w-[70%] lg:w-[50%]">
|
||||
[(visible)]="isPackingDialogVisible" styleClass="w-[95%] md:w-[70%] lg:w-[50%]">
|
||||
<section class="md:max-w-3/4 md:mx-auto max-h-[80%] md:max-h-[600px]">
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<p-button (click)="addPackingItem()" icon="pi pi-plus" label="Add item" text />
|
||||
<p-button (click)="addPackingItem()" [disabled]="trip?.archived" icon="pi pi-plus" label="Add item" text />
|
||||
<p-button (click)="menuTripPacking.toggle($event)" icon="pi pi-ellipsis-h" text />
|
||||
</div>
|
||||
|
||||
@ -621,39 +618,52 @@
|
||||
</label>
|
||||
<div
|
||||
class="md:opacity-0 absolute right-0 top-1/2 -translate-y-1/2 md:group-hover:opacity-100 bg-white md:bg-gray-100 rounded">
|
||||
<p-button size="small" text icon="pi pi-trash" (click)="deletePackingItem(item)" severity="danger" />
|
||||
<p-button size="small" [disabled]="trip?.archived" text icon="pi pi-trash" (click)="deletePackingItem(item)"
|
||||
severity="danger" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} @empty {
|
||||
<div class="flex flex-col items-center justify-center mt-4 text-center select-none">
|
||||
<i style="font-size: 3rem" class="pi pi-inbox mb-2 text-gray-300 dark:text-gray-700"></i>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">No item</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</p-dialog>
|
||||
|
||||
<p-dialog header="Checklist" [draggable]="false" [dismissableMask]="true" [modal]="true"
|
||||
[(visible)]="checklistDialogVisible" styleClass="w-[95%] md:w-[50%] lg:w-[30%]">
|
||||
[(visible)]="isChecklistDialogVisible" styleClass="w-[95%] md:w-[50%] lg:w-[30%]">
|
||||
<section class="p-4 max-w-full max-h-[80%] md:max-h-[600px]">
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<p-button (click)="addChecklistItem()" icon="pi pi-plus" label="Add item" text />
|
||||
<p-button (click)="addChecklistItem()" [disabled]="trip?.archived" icon="pi pi-plus" label="Add item" text />
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 xl:grid-cols-3 gap-2 mt-2 pb-4">
|
||||
@for (item of checklistItems; track item.id) {
|
||||
<div class="relative group flex items-center gap-3 rounded-md p-2 hover:bg-gray-100 dark:hover:bg-white/5">
|
||||
<div
|
||||
class="relative group flex items-center gap-3 rounded-md py-1 min-w-0 hover:bg-gray-100 dark:hover:bg-white/5">
|
||||
<label [for]="item.id" [pTooltip]="item.text" [class.line-through]="item.checked"
|
||||
class="flex items-center gap-2 w-full cursor-pointer">
|
||||
<p-checkbox (onChange)="onCheckChecklistItem($event, item.id)" [binary]="true" [inputId]="item.id.toString()"
|
||||
[(ngModel)]="item.checked" />
|
||||
<div class="pr-6 md:pr-0 truncate select-none flex-1">
|
||||
<p-checkbox (onChange)="onCheckChecklistItem($event, item.id)" [binary]="true"
|
||||
[disabled]="!!this.trip?.archived" [inputId]="item.id.toString()" [(ngModel)]="item.checked" />
|
||||
<div class="truncate select-none">
|
||||
<span>{{ item.text }}</span>
|
||||
</div>
|
||||
</label>
|
||||
<div
|
||||
class="md:opacity-0 absolute right-0 top-1/2 -translate-y-1/2 md:group-hover:opacity-100 bg-white md:bg-gray-100 rounded">
|
||||
<p-button size="small" text icon="pi pi-trash" (click)="deleteChecklistItem(item)" severity="danger" />
|
||||
class="md:opacity-0 absolute right-0 top-1/2 -translate-y-1/2 md:group-hover:opacity-100 bg-white md:bg-transparent rounded">
|
||||
<p-button size="small" text icon="pi pi-trash" [disabled]="trip?.archived" (click)="deleteChecklistItem(item)"
|
||||
severity="danger" />
|
||||
</div>
|
||||
</div>
|
||||
} @empty {
|
||||
<div class="col-span-full flex flex-col items-center justify-center mt-4 text-center select-none">
|
||||
<i style="font-size: 3rem" class="pi pi-inbox mb-2 text-gray-300 dark:text-gray-700"></i>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">No item</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -685,16 +695,21 @@
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
} @empty {
|
||||
<div class="col-span-full flex flex-col items-center justify-center mt-4 text-center select-none">
|
||||
<i style="font-size: 3rem" class="pi pi-inbox mb-2 text-gray-300 dark:text-gray-700"></i>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">No item</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</p-dialog>
|
||||
|
||||
<p-dialog header="Members" [draggable]="false" [dismissableMask]="true" [modal]="true"
|
||||
[(visible)]="membersDialogVisible" styleClass="w-[95%] md:w-[40%] lg:w-[30%]">
|
||||
[(visible)]="isMembersDialogVisible" styleClass="w-[95%] md:w-[40%] lg:w-[30%]">
|
||||
<section class="p-4 max-w-full max-h-[80%] md:max-h-[600px]">
|
||||
<div class="flex justify-center">
|
||||
<p-button (click)="addMember()" icon="pi pi-plus" label="Member" text />
|
||||
<p-button (click)="addMember()" [disabled]="trip?.archived" icon="pi pi-plus" label="Member" text />
|
||||
</div>
|
||||
|
||||
<div class="divide-y divide-gray-100 mt-4 pb-4">
|
||||
@ -738,7 +753,8 @@
|
||||
m.balance || '-' }} {{ trip?.currency }}</span>
|
||||
|
||||
@if (m.invited_at) {
|
||||
<p-button text (click)="deleteMember(m.user)" icon="pi pi-trash" severity="danger" />
|
||||
<p-button text (click)="deleteMember(m.user)" [disabled]="trip?.archived" icon="pi pi-trash"
|
||||
severity="danger" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@ -747,6 +763,52 @@
|
||||
</section>
|
||||
</p-dialog>
|
||||
|
||||
<p-dialog header="Attachments" [draggable]="false" [dismissableMask]="true" [modal]="true"
|
||||
[(visible)]="isAttachmentsDialogVisible" styleClass="w-[95%] md:w-[70%] lg:w-[50%]">
|
||||
<section class="md:max-w-3/4 md:mx-auto max-h-[80%] md:max-h-[600px]">
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<p-button (click)="fileUpload.click()" [disabled]="trip?.archived" icon="pi pi-file-import" label="Add document"
|
||||
text />
|
||||
<input type="file" style="display: none;" (change)="onFileUploadInputChange($event)" #fileUpload>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 xl:grid-cols-3 gap-2 mt-2 pb-4">
|
||||
@for (attachment of trip?.attachments; track attachment.id) {
|
||||
<div (click)="downloadAttachment(attachment)"
|
||||
class="group relative cursor-pointer flex items-center gap-3 rounded-lg border border-gray-200 bg-white hover:bg-gray-100 dark:hover:bg-white/5 p-3 transition-all dark:border-gray-800 dark:bg-gray-950">
|
||||
<div
|
||||
class="group relative flex h-10 w-10 shrink-0 cursor-pointer items-center justify-center rounded-md bg-red-100 transition-colors dark:bg-red-900">
|
||||
<i class="pi pi-file text-red-600 transition-opacity group-hover:opacity-0 dark:text-red-400"></i>
|
||||
<i
|
||||
class="pi pi-arrow-down absolute -translate-y-4 text-red-600 opacity-0 transition-none group-hover:translate-y-0 group-hover:opacity-100 group-hover:transition-all group-hover:duration-300 dark:text-red-400"></i>
|
||||
</div>
|
||||
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate font-mono text-md text-gray-900 dark:text-gray-100">{{ attachment.filename }}</div>
|
||||
<div class="flex items-center gap-2 mt-1 text-xs font-mono text-gray-500 dark:text-gray-400">
|
||||
<span>{{ attachment.file_size | fileSize }}</span>
|
||||
<span
|
||||
class="bg-gray-100 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded min-w-fit dark:bg-gray-400">{{
|
||||
attachment.uploaded_by }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="md:opacity-0 absolute right-2 top-1/2 -translate-y-1/2 translate-x-2 group-hover:translate-x-0 md:group-hover:opacity-100 group-hover:transition-all group-hover:duration-300 bg-white md:bg-transparent rounded">
|
||||
<p-button text icon="pi pi-trash" [disabled]="trip?.archived"
|
||||
(click)="deleteAttachment(attachment.id); $event.stopPropagation()" severity="danger" />
|
||||
</div>
|
||||
</div>
|
||||
} @empty {
|
||||
<div class="col-span-full flex flex-col items-center justify-center mt-4 text-center select-none">
|
||||
<i style="font-size: 3rem" class="pi pi-inbox mb-2 text-gray-300 dark:text-gray-700"></i>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">No attachments</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</p-dialog>
|
||||
|
||||
@if (isPrinting) {
|
||||
<section class="hidden print:block">
|
||||
<div class="flex items-center justify-center">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component } from '@angular/core';
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
@ -17,6 +17,7 @@ import {
|
||||
PackingItem,
|
||||
ChecklistItem,
|
||||
TripMember,
|
||||
TripAttachment,
|
||||
} from '../../types/trip';
|
||||
import { Place } from '../../types/poi';
|
||||
import { createMap, placeToMarker, createClusterGroup, tripDayMarker, gpxToPolyline } from '../../shared/map';
|
||||
@ -32,7 +33,7 @@ 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 { Menu, 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';
|
||||
@ -49,6 +50,7 @@ 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';
|
||||
import { FileSizePipe } from '../../shared/filesize.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-trip',
|
||||
@ -72,11 +74,13 @@ import { TripArchiveModalComponent } from '../../modals/trip-archive-modal/trip-
|
||||
MultiSelectModule,
|
||||
CheckboxModule,
|
||||
orderByPipe,
|
||||
FileSizePipe,
|
||||
],
|
||||
templateUrl: './trip.component.html',
|
||||
styleUrls: ['./trip.component.scss'],
|
||||
})
|
||||
export class TripComponent implements AfterViewInit {
|
||||
@ViewChild('menuTripActions') menuTripActions!: Menu;
|
||||
tripSharedURL$?: Observable<string>;
|
||||
statuses: TripStatus[] = [];
|
||||
trip?: Trip;
|
||||
@ -87,21 +91,22 @@ export class TripComponent implements AfterViewInit {
|
||||
isPrinting = false;
|
||||
isArchivalReviewDisplayed = false;
|
||||
|
||||
totalPrice = 0;
|
||||
isMapFullscreen = false;
|
||||
isMapFullscreenDays = false;
|
||||
totalPrice = 0;
|
||||
collapsedTripDays = false;
|
||||
collapsedTripPlaces = false;
|
||||
shareDialogVisible = false;
|
||||
packingDialogVisible = false;
|
||||
isCollapsedTripDays = false;
|
||||
isCollapsedTripPlaces = false;
|
||||
isShareDialogVisible = false;
|
||||
isPackingDialogVisible = false;
|
||||
isMembersDialogVisible = false;
|
||||
isAttachmentsDialogVisible = false;
|
||||
isChecklistDialogVisible = false;
|
||||
isExpanded = false;
|
||||
isFilteringMode = false;
|
||||
packingList: PackingItem[] = [];
|
||||
dispPackingList: Record<string, PackingItem[]> = {};
|
||||
checklistDialogVisible = false;
|
||||
checklistItems: ChecklistItem[] = [];
|
||||
dispchecklist: ChecklistItem[] = [];
|
||||
membersDialogVisible = false;
|
||||
tripMembers: TripMember[] = [];
|
||||
|
||||
map?: L.Map;
|
||||
@ -112,86 +117,7 @@ export class TripComponent implements AfterViewInit {
|
||||
tripMapAntLayer?: L.FeatureGroup;
|
||||
tripMapAntLayerDayID?: number;
|
||||
|
||||
readonly menuTripActionsItems: MenuItem[] = [
|
||||
{
|
||||
label: 'Lists',
|
||||
items: [
|
||||
{
|
||||
label: 'Checklist',
|
||||
icon: 'pi pi-list-check',
|
||||
command: () => {
|
||||
this.openChecklist();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Packing list',
|
||||
icon: 'pi pi-briefcase',
|
||||
command: () => {
|
||||
this.openPackingList();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Collaboration',
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
icon: 'pi pi-users',
|
||||
command: () => {
|
||||
this.openMembersDialog();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Share',
|
||||
icon: 'pi pi-share-alt',
|
||||
command: () => {
|
||||
this.shareDialogVisible = true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Trip',
|
||||
items: [
|
||||
{
|
||||
label: 'Pretty Print',
|
||||
icon: 'pi pi-print',
|
||||
command: () => {
|
||||
this.togglePrint();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Notes',
|
||||
icon: 'pi pi-info-circle',
|
||||
command: () => {
|
||||
this.openTripNotesModal();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Archive',
|
||||
icon: 'pi pi-box',
|
||||
command: () => {
|
||||
this.openArchiveTripModal();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
icon: 'pi pi-pencil',
|
||||
command: () => {
|
||||
this.editTrip();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
icon: 'pi pi-trash',
|
||||
command: () => {
|
||||
this.deleteTrip();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
menuTripActionsItems: MenuItem[] = [];
|
||||
readonly menuTripTableActionsItems: MenuItem[] = [
|
||||
{
|
||||
label: 'Actions',
|
||||
@ -913,6 +839,12 @@ export class TripComponent implements AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
toggleArchiveTrip() {
|
||||
if (!this.trip) return;
|
||||
if (this.trip.archived) this.openUnarchiveTripModal();
|
||||
else this.openArchiveTripModal();
|
||||
}
|
||||
|
||||
openArchiveTripModal() {
|
||||
if (!this.trip) return;
|
||||
const currentArchiveStatus = this.trip?.archived;
|
||||
@ -1455,7 +1387,7 @@ export class TripComponent implements AfterViewInit {
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.trip!.shared = false;
|
||||
this.shareDialogVisible = false;
|
||||
this.isShareDialogVisible = false;
|
||||
},
|
||||
});
|
||||
},
|
||||
@ -1481,7 +1413,7 @@ export class TripComponent implements AfterViewInit {
|
||||
label: `Quick Paste (${this.utilsService.packingListToCopy.length})`,
|
||||
icon: 'pi pi-copy',
|
||||
command: () => this.pastePackingList(),
|
||||
disabled: !this.utilsService.packingListToCopy.length,
|
||||
disabled: this.trip?.archived || !this.utilsService.packingListToCopy.length,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -1501,7 +1433,7 @@ export class TripComponent implements AfterViewInit {
|
||||
this.computeDispPackingList();
|
||||
},
|
||||
});
|
||||
this.packingDialogVisible = true;
|
||||
this.isPackingDialogVisible = true;
|
||||
}
|
||||
|
||||
addPackingItem() {
|
||||
@ -1677,7 +1609,7 @@ export class TripComponent implements AfterViewInit {
|
||||
this.computeDispChecklistList();
|
||||
},
|
||||
});
|
||||
this.checklistDialogVisible = true;
|
||||
this.isChecklistDialogVisible = true;
|
||||
}
|
||||
|
||||
addChecklistItem() {
|
||||
@ -1784,7 +1716,7 @@ export class TripComponent implements AfterViewInit {
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.membersDialogVisible = true;
|
||||
this.isMembersDialogVisible = true;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@ -1874,7 +1806,7 @@ export class TripComponent implements AfterViewInit {
|
||||
'1024px': '70vw',
|
||||
'640px': '90vw',
|
||||
},
|
||||
data: this.trip?.notes,
|
||||
data: this.trip,
|
||||
})!;
|
||||
|
||||
modal.onClose.pipe(take(1)).subscribe({
|
||||
@ -1889,4 +1821,152 @@ export class TripComponent implements AfterViewInit {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
openMenuTripActionsItems(event: any) {
|
||||
const lists = {
|
||||
label: 'Lists',
|
||||
items: [
|
||||
{
|
||||
label: 'Attachments',
|
||||
icon: 'pi pi-paperclip',
|
||||
command: () => {
|
||||
this.openAttachmentsModal();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Checklist',
|
||||
icon: 'pi pi-list-check',
|
||||
command: () => {
|
||||
this.openChecklist();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Packing list',
|
||||
icon: 'pi pi-briefcase',
|
||||
command: () => {
|
||||
this.openPackingList();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const collaboration = {
|
||||
label: 'Collaboration',
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
icon: 'pi pi-users',
|
||||
command: () => {
|
||||
this.openMembersDialog();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Share',
|
||||
icon: 'pi pi-share-alt',
|
||||
command: () => {
|
||||
this.isShareDialogVisible = true;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const actions = {
|
||||
label: 'Trip',
|
||||
items: [
|
||||
{
|
||||
label: 'Pretty Print',
|
||||
icon: 'pi pi-print',
|
||||
command: () => {
|
||||
this.togglePrint();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Notes',
|
||||
icon: 'pi pi-info-circle',
|
||||
command: () => {
|
||||
this.openTripNotesModal();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: this.trip?.archived ? 'Unarchive' : 'Archive',
|
||||
icon: 'pi pi-box',
|
||||
command: () => {
|
||||
this.toggleArchiveTrip();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
icon: 'pi pi-pencil',
|
||||
disabled: this.trip?.archived,
|
||||
command: () => {
|
||||
this.editTrip();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
icon: 'pi pi-trash',
|
||||
disabled: this.trip?.archived,
|
||||
command: () => {
|
||||
this.deleteTrip();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
this.menuTripActionsItems = [lists, collaboration, actions];
|
||||
this.menuTripActions.toggle(event);
|
||||
}
|
||||
|
||||
openAttachmentsModal() {
|
||||
if (!this.trip) return;
|
||||
this.isAttachmentsDialogVisible = true;
|
||||
}
|
||||
|
||||
onFileUploadInputChange(event: Event) {
|
||||
if (!this.trip) return;
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (!input.files?.length) return;
|
||||
|
||||
const formdata = new FormData();
|
||||
formdata.append('file', input.files[0]);
|
||||
|
||||
this.apiService
|
||||
.postTripAttachment(this.trip?.id, formdata)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (attachment) => (this.trip!.attachments = [...this.trip!.attachments!, attachment]),
|
||||
});
|
||||
}
|
||||
|
||||
downloadAttachment(attachment: TripAttachment) {
|
||||
if (!this.trip) return;
|
||||
this.apiService
|
||||
.downloadTripAttachment(this.trip.id, attachment.id)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
const blob = new Blob([data], { type: 'application/pdf' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.download = attachment.filename;
|
||||
anchor.href = url;
|
||||
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
|
||||
document.body.removeChild(anchor);
|
||||
window.URL.revokeObjectURL(url);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
deleteAttachment(attachmentId: number) {
|
||||
if (!this.trip) return;
|
||||
this.apiService
|
||||
.deleteTripAttachment(this.trip.id, attachmentId)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.trip!.attachments = this.trip?.attachments?.filter((att) => att.id != attachmentId);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user