mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 07:38:10 +00:00
enhance booking and contract notification systems
- Added detailed logging for socket connection events in useBookingWindowSocket. - Introduced new notification types for contract status and schedule updates. - Updated notification visuals to include new icons for contract status. - Enhanced notification href resolution for contract status and schedule updates. - Implemented booking lifecycle notifier service for customer and staff notifications. - Created contract notifier service for managing contract lifecycle notifications. - Added end-to-end tests for booking window socket functionality.
This commit is contained in:
@@ -2,12 +2,17 @@ import { Injectable, Logger, NotFoundException, OnModuleInit } from '@nestjs/com
|
||||
import { Cron } from '@nestjs/schedule';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { TrainScheduleStatus as TrainScheduleStatusEnum } from '@edr/types';
|
||||
import {
|
||||
NotificationAudience,
|
||||
NotificationType,
|
||||
TrainScheduleStatus as TrainScheduleStatusEnum,
|
||||
} from '@edr/types';
|
||||
|
||||
import { Booking } from '../bookings/entities/booking.entity';
|
||||
import { TrainSchedule } from '../train-schedules/entities/train-schedule.entity';
|
||||
import { TrainSchedulesRepository } from '../train-schedules/train-schedules.repository';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { NotificationInboxService } from '../notification-inbox/notification-inbox.service';
|
||||
import { BookingBatchService } from './booking-batch.service';
|
||||
import { BookingWindowGateway } from './booking-window.gateway';
|
||||
import { TrainSchedulingService, effectiveWindowConfig } from './train-scheduling.service';
|
||||
@@ -41,6 +46,7 @@ export class BookingWindowService implements OnModuleInit {
|
||||
private readonly bookingBatchService: BookingBatchService,
|
||||
private readonly trainSchedulingService: TrainSchedulingService,
|
||||
private readonly notifications: NotificationsService,
|
||||
private readonly inbox: NotificationInboxService,
|
||||
private readonly gateway: BookingWindowGateway,
|
||||
) {}
|
||||
|
||||
@@ -380,22 +386,26 @@ export class BookingWindowService implements OnModuleInit {
|
||||
*/
|
||||
private async notifyWindowOpened(schedule: TrainSchedule): Promise<void> {
|
||||
try {
|
||||
const rows: Array<{ phone: string | null; email: string | null }> =
|
||||
await this.dataSource.query(
|
||||
`SELECT DISTINCT
|
||||
COALESCE(co.contact_person_phone, co.phone) AS phone,
|
||||
COALESCE(co.email, co.general_manager_email) AS email
|
||||
FROM freight.contract_routes cr
|
||||
JOIN freight.contracts c
|
||||
ON c.id = cr.contract_id
|
||||
AND c.status IN ('CONTRACT_ACTIVE', 'FULLY_EXECUTED')
|
||||
AND c.deleted_at IS NULL
|
||||
JOIN freight.companies co ON co.id = c.company_id
|
||||
WHERE cr.origin_yard_id = $1
|
||||
AND cr.destination_yard_id = $2
|
||||
AND cr.deleted_at IS NULL`,
|
||||
[schedule.originStationId, schedule.destinationStationId],
|
||||
);
|
||||
const rows: Array<{
|
||||
company_id: string;
|
||||
phone: string | null;
|
||||
email: string | null;
|
||||
}> = await this.dataSource.query(
|
||||
`SELECT DISTINCT
|
||||
c.company_id,
|
||||
COALESCE(co.contact_person_phone, co.phone) AS phone,
|
||||
COALESCE(co.email, co.general_manager_email) AS email
|
||||
FROM freight.contract_routes cr
|
||||
JOIN freight.contracts c
|
||||
ON c.id = cr.contract_id
|
||||
AND c.status IN ('CONTRACT_ACTIVE', 'FULLY_EXECUTED')
|
||||
AND c.deleted_at IS NULL
|
||||
JOIN freight.companies co ON co.id = c.company_id
|
||||
WHERE cr.origin_yard_id = $1
|
||||
AND cr.destination_yard_id = $2
|
||||
AND cr.deleted_at IS NULL`,
|
||||
[schedule.originStationId, schedule.destinationStationId],
|
||||
);
|
||||
if (!rows.length) return;
|
||||
|
||||
const closes = schedule.windowClosesAt
|
||||
@@ -410,6 +420,7 @@ export class BookingWindowService implements OnModuleInit {
|
||||
|
||||
const seenPhone = new Set<string>();
|
||||
const seenEmail = new Set<string>();
|
||||
const seenCompany = new Set<string>();
|
||||
for (const r of rows) {
|
||||
if (r.phone && !seenPhone.has(r.phone)) {
|
||||
seenPhone.add(r.phone);
|
||||
@@ -423,9 +434,23 @@ export class BookingWindowService implements OnModuleInit {
|
||||
.directSend('email', r.email, msg)
|
||||
.catch((e) => this.logger.warn(`Window-open email failed: ${(e as Error).message}`));
|
||||
}
|
||||
// In-app inbox item for every portal user of each eligible company,
|
||||
// deep-linking to the new-booking page.
|
||||
if (r.company_id && !seenCompany.has(r.company_id)) {
|
||||
seenCompany.add(r.company_id);
|
||||
void this.inbox.notify({
|
||||
recipients: { companyId: r.company_id },
|
||||
audience: NotificationAudience.PORTAL,
|
||||
type: NotificationType.SCHEDULE_UPDATE,
|
||||
title: 'Booking window open',
|
||||
body: msg,
|
||||
link: '/bookings/new',
|
||||
data: { trainScheduleId: schedule.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
this.logger.log(
|
||||
`Notified ${seenPhone.size} phone / ${seenEmail.size} email contacts of open window for schedule ${schedule.id}`,
|
||||
`Notified ${seenPhone.size} phone / ${seenEmail.size} email / ${seenCompany.size} companies (in-app) of open window for schedule ${schedule.id}`,
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.warn(
|
||||
|
||||
Reference in New Issue
Block a user