diff --git a/apps/edr-freight-api/src/modules/bookings/booking-transition.service.ts b/apps/edr-freight-api/src/modules/bookings/booking-transition.service.ts index 41e8fc78b..acbd857cd 100644 --- a/apps/edr-freight-api/src/modules/bookings/booking-transition.service.ts +++ b/apps/edr-freight-api/src/modules/bookings/booking-transition.service.ts @@ -627,6 +627,9 @@ export class BookingTransitionService { file: { id: string; name: string; url: string } | null; reviewStatus: "PENDING" | "APPROVED" | "QUERIED" | null; note: string | null; + uploadedAt: string | null; + reviewedAt: string | null; + reviewedByName: string | null; }>; allApproved: boolean; phase?: string | null; @@ -652,6 +655,9 @@ export class BookingTransitionService { const reviewByKey = new Map( reviews.map((r) => [`${r.settingCode}:${r.fileKey}`, r]), ); + const reviewerNames = await this.bookingsRepository.resolveStaffNames( + reviews.map((r) => r.reviewedByStaffId), + ); const documents: Awaited< ReturnType @@ -680,6 +686,11 @@ export class BookingTransitionService { file: file ? { id: file.id, name: file.name, url: file.url } : null, reviewStatus: review?.status ?? null, note: review?.note ?? null, + uploadedAt: file?.createdAt ? file.createdAt.toISOString() : null, + reviewedAt: review?.reviewedAt ? review.reviewedAt.toISOString() : null, + reviewedByName: review?.reviewedByStaffId + ? (reviewerNames.get(review.reviewedByStaffId) ?? null) + : null, }); } }; @@ -700,6 +711,11 @@ export class BookingTransitionService { file: { id: f.id, name: f.name, url: f.url }, reviewStatus: review?.status ?? null, note: review?.note ?? null, + uploadedAt: f.createdAt ? f.createdAt.toISOString() : null, + reviewedAt: review?.reviewedAt ? review.reviewedAt.toISOString() : null, + reviewedByName: review?.reviewedByStaffId + ? (reviewerNames.get(review.reviewedByStaffId) ?? null) + : null, }); } diff --git a/apps/edr-freight-api/src/modules/bookings/bookings.repository.ts b/apps/edr-freight-api/src/modules/bookings/bookings.repository.ts index 7b042e0c7..b3ae07861 100644 --- a/apps/edr-freight-api/src/modules/bookings/bookings.repository.ts +++ b/apps/edr-freight-api/src/modules/bookings/bookings.repository.ts @@ -13,6 +13,7 @@ import { } from 'typeorm'; import { computeFacets, FacetBucket } from '../../common/utils/facets.util'; +import { resolveIamUserNames } from '../../common/utils/iam-user-name.util'; import { wagonsPerUnitForSize } from '../rule-engine/container-type.util'; import { ContainerType } from '../rule-engine/entities/container-type.entity'; import { Contract } from '../contracts/entities/contract.entity'; @@ -660,6 +661,13 @@ export class BookingsRepository extends BaseRepository { await repo.save(repo.create({ ...input, status: 'PENDING' })); } + /** Display names for reviewer staff ids — one query for the whole set. */ + async resolveStaffNames( + staffIds: (string | null | undefined)[], + ): Promise> { + return resolveIamUserNames(this.dataSource, staffIds); + } + /** GL marks a document APPROVED or QUERIED (with an optional note). */ async setDocumentReviewStatus( bookingId: string, diff --git a/apps/edr-freight-api/src/modules/contracts/booking-clearance.service.ts b/apps/edr-freight-api/src/modules/contracts/booking-clearance.service.ts index 4b53f4dee..5ce4183ab 100644 --- a/apps/edr-freight-api/src/modules/contracts/booking-clearance.service.ts +++ b/apps/edr-freight-api/src/modules/contracts/booking-clearance.service.ts @@ -52,6 +52,9 @@ export interface BookingClearanceView { file: { id: string; name: string; url: string } | null; reviewStatus: 'PENDING' | 'APPROVED' | 'QUERIED' | null; note: string | null; + uploadedAt: string | null; + reviewedAt: string | null; + reviewedByName: string | null; }>; allApproved: boolean; phase?: string | null; @@ -185,6 +188,9 @@ export class BookingClearanceService { const fileByCode = new Map(files.map((f) => [f.code, f])); const reviews = await this.bookingsRepository.findDocumentReviews(bookingId); const reviewByKey = new Map(reviews.map((r) => [`${r.settingCode}:${r.fileKey}`, r])); + const reviewerNames = await this.bookingsRepository.resolveStaffNames( + reviews.map((r) => r.reviewedByStaffId), + ); const documents: BookingClearanceView['documents'] = []; @@ -208,6 +214,11 @@ export class BookingClearanceService { file: file ? { id: file.id, name: file.name, url: file.url } : null, reviewStatus: review?.status ?? null, note: review?.note ?? null, + uploadedAt: file?.createdAt ? file.createdAt.toISOString() : null, + reviewedAt: review?.reviewedAt ? review.reviewedAt.toISOString() : null, + reviewedByName: review?.reviewedByStaffId + ? (reviewerNames.get(review.reviewedByStaffId) ?? null) + : null, }); } }; @@ -227,6 +238,11 @@ export class BookingClearanceService { file: { id: f.id, name: f.name, url: f.url }, reviewStatus: review?.status ?? null, note: review?.note ?? null, + uploadedAt: f.createdAt ? f.createdAt.toISOString() : null, + reviewedAt: review?.reviewedAt ? review.reviewedAt.toISOString() : null, + reviewedByName: review?.reviewedByStaffId + ? (reviewerNames.get(review.reviewedByStaffId) ?? null) + : null, }); } diff --git a/packages/types/src/freight/index.ts b/packages/types/src/freight/index.ts index 3ed0ec903..d236eb03b 100644 --- a/packages/types/src/freight/index.ts +++ b/packages/types/src/freight/index.ts @@ -813,6 +813,12 @@ export interface ClearanceDocument { file: { id: string; name: string; url: string } | null; reviewStatus: DocumentReviewStatus | null; note: string | null; + /** When the current file version was uploaded — a re-upload (query response) refreshes it. */ + uploadedAt?: string | null; + /** When the latest review decision (approve/query) was recorded. */ + reviewedAt?: string | null; + /** Display name of the staff member who recorded the decision. */ + reviewedByName?: string | null; } /**