Trip: Archive review

This commit is contained in:
itskovacs 2025-10-09 21:37:18 +02:00
parent 3cc466e518
commit 41f275fae3
7 changed files with 140 additions and 28 deletions

View File

@ -1,5 +1,5 @@
<section class="mt-4" [class.prettyprint]="isPrinting">
<div class="p-4 print:p-0 flex items-center justify-between">
<div class="p-4 print:p-0 flex flex-wrap 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">
@ -16,6 +16,20 @@
</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">
<div class="text-center font-semibold">This Trip is archived, you cannot modify it.</div>
<div class="mt-2 flex items-center justify-between">
@if (trip?.archival_review) {
<p-button text icon="pi pi-comment" [label]="isArchivalReviewDisplayed ? 'Hide review' : 'View review'"
(click)="isArchivalReviewDisplayed = !isArchivalReviewDisplayed" />
}
<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>
@ -29,18 +43,13 @@
}
</div>
</div>
</section>
@if (trip?.archived) {
<div class="mx-auto p-4 my-4 w-fit max-w-[400px] text-center text-orange-800 rounded-lg bg-orange-50"
[class.prettyprint]="isPrinting">
<div class="flex items-center justify-between">
<div class="font-semibold">Archived</div>
<p-button text icon="pi pi-box" label="Restore" (click)="toggleArchiveTrip()" [size]="'small'" />
</div>
This Trip is archived, you cannot modify it.
</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">
<div [ngClass]="isExpanded ? 'lg:col-span-3' : 'lg:col-span-2'" class="p-4 shadow self-start rounded-md max-w-screen">

View File

@ -63,6 +63,7 @@ import { TripInviteMemberModalComponent } from "../../modals/trip-invite-member-
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",
@ -99,6 +100,7 @@ export class TripComponent implements AfterViewInit {
selectedItem?: TripItem & { status?: TripStatus };
tableExpandableMode = false;
isPrinting = false;
isArchivalReviewDisplayed = false;
isMapFullscreen = false;
isMapFullscreenDays = false;
@ -185,7 +187,7 @@ export class TripComponent implements AfterViewInit {
label: "Archive",
icon: "pi pi-box",
command: () => {
this.toggleArchiveTrip();
this.openArchiveTripModal();
},
},
{
@ -942,30 +944,59 @@ export class TripComponent implements AfterViewInit {
});
}
toggleArchiveTrip() {
const currentArchiveStatus = this.trip?.archived;
openUnarchiveTripModal() {
const modal = this.dialogService.open(YesNoModalComponent, {
header: "Confirm Action",
header: "Restore Trip",
modal: true,
closable: true,
dismissableMask: true,
breakpoints: {
"640px": "90vw",
},
data: `${currentArchiveStatus ? "Restore" : "Archive"} ${this.trip?.name} ?${currentArchiveStatus ? "" : " This will make everything read-only."}`,
data: `Restore ${this.trip?.name} ?`,
})!;
modal.onClose.pipe(take(1)).subscribe({
next: (bool) => {
if (bool)
this.apiService
.putTrip({ archived: !currentArchiveStatus }, this.trip?.id!)
.pipe(take(1))
.subscribe({
next: () => {
this.trip!.archived = !currentArchiveStatus;
},
});
if (!bool) return;
this.apiService
.putTrip({ archived: false }, this.trip?.id!)
.pipe(take(1))
.subscribe({
next: (trip) => (this.trip = trip),
});
},
});
}
openArchiveTripModal() {
if (!this.trip) return;
const currentArchiveStatus = this.trip?.archived;
const modal = this.dialogService.open(TripArchiveModalComponent, {
header: `Archive ${this.trip.name}`,
modal: true,
closable: true,
dismissableMask: true,
width: "30vw",
breakpoints: {
"1024px": "60vw",
"640px": "90vw",
},
data: this.trip,
})!;
modal.onClose.pipe(take(1)).subscribe({
next: (review: string) => {
if (review === undefined) return;
this.apiService
.putTrip(
{ archived: !currentArchiveStatus, archival_review: review },
this.trip?.id!,
)
.pipe(take(1))
.subscribe({
next: (trip) => (this.trip = trip),
});
},
});
}
@ -1853,7 +1884,7 @@ export class TripComponent implements AfterViewInit {
next: (notes: string) => {
if (notes === undefined) return;
this.apiService
.putTrip({ notes: notes ?? "" }, this.trip!.id)
.putTrip({ notes: notes }, this.trip!.id)
.pipe(take(1))
.subscribe({
next: (trip) => (this.trip = trip),

View File

@ -0,0 +1,17 @@
<section>
<div class="p-4 mb-4 text-sm text-blue-800 rounded-md bg-blue-50 dark:bg-gray-800 dark:text-blue-400">
<span class="font-semibold">You're about to archive your Trip!</span> This will make the Trip read-only.<br>
You can review your trip and add general feedback. You can also skip this part and delete everything.
</div>
<div>
<p-floatlabel variant="in">
<textarea pTextarea id="review" [formControl]="review" rows="12" fluid></textarea>
<label for="review">Review Notes</label>
</p-floatlabel>
</div>
<div class="mt-6 text-center">
<p-button (click)="closeDialog()" label="Archive Trip" icon="pi pi-box" severity="secondary" />
</div>
</section>

View File

@ -0,0 +1,54 @@
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,
],
standalone: true,
templateUrl: "./trip-archive-modal.component.html",
styleUrl: "./trip-archive-modal.component.scss",
})
export class TripArchiveModalComponent {
review = new FormControl("");
constructor(
private ref: DynamicDialogRef,
private config: DynamicDialogConfig,
) {
this.computeReviewPlaceholder(this.config.data);
}
computeReviewPlaceholder(trip: Trip) {
if (trip.archival_review) {
this.review.setValue(trip.archival_review);
return;
}
if (!trip.days.length) return;
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`),
);
});
placeholder += "\nAnything else?";
this.review.setValue(placeholder);
}
closeDialog() {
// Normalize data for API POST
this.ref.close(this.review.value);
}
}

View File

@ -18,7 +18,7 @@ import { TextareaModule } from "primeng/textarea";
styleUrl: "./trip-notes-modal.component.scss",
})
export class TripNotesModalComponent {
notes = new FormControl('');
notes = new FormControl("");
isEditing: boolean = false;
constructor(

View File

@ -21,6 +21,7 @@ export interface Trip {
collaborators: TripMember[];
currency: string;
notes?: string;
archival_review?: string;
// POST / PUT
places: Place[];