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:
Marshal
2026-07-06 21:03:08 +00:00
parent 8bd00ea78a
commit 05b13e84a8
38 changed files with 1450 additions and 95 deletions

View File

@@ -22,6 +22,7 @@ import { FilesService } from '../files/files.service';
import { SignaturesService } from '../signatures/signatures.service';
import { OtpService } from '../otp/otp.service';
import { ContractPricingService } from './contract-pricing.service';
import { ContractNotifierService } from './contract-notifier.service';
import { ClearanceMilestoneService } from './clearance-milestone.service';
import { ContractsRepository } from './contracts.repository';
import { ContractsService } from './contracts.service';
@@ -66,6 +67,7 @@ export class ContractTransitionService {
private readonly pdfService: ContractPdfService,
private readonly minioService: MinioService,
private readonly otpService: OtpService,
private readonly notifier: ContractNotifierService,
) {}
/** Customer submits the contract for approval → SUBMITTED; freeze unit rates. */
@@ -79,7 +81,9 @@ export class ContractTransitionService {
await this.contractsRepository.update(contractId, {
status: 'SUBMITTED',
} as never);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.submittedToStaff(updated);
return updated;
}
/** Confirm a price change before submit (mirrors booking confirm-submit). */
@@ -93,7 +97,9 @@ export class ContractTransitionService {
await this.contractsRepository.update(contractId, {
status: 'SUBMITTED',
} as never);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.submittedToStaff(updated);
return updated;
}
/**
@@ -130,7 +136,9 @@ export class ContractTransitionService {
contractValidFrom: validFrom,
contractValidUntil: validUntil,
} as never);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.accepted(updated);
return updated;
}
/**
@@ -218,7 +226,9 @@ export class ContractTransitionService {
await this.contractsRepository.update(contractId, {
status: 'CHANGES_REQUESTED',
} as never);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.changesRequested(updated, note);
return updated;
}
async reject(contractId: string, reason: string, actorId: string): Promise<Contract> {
@@ -235,7 +245,9 @@ export class ContractTransitionService {
await this.contractsRepository.update(contractId, {
status: 'REJECTED',
} as never);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.rejected(updated, reason);
return updated;
}
/** Approve one approval step in sequence; → APPROVED when all complete. */
@@ -297,7 +309,11 @@ export class ContractTransitionService {
if (Object.keys(updates).length > 0) {
await this.contractsRepository.update(contractId, updates as never);
}
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
if (allDone) {
this.notifier.approved(updated);
}
return updated;
}
/**
@@ -535,7 +551,9 @@ export class ContractTransitionService {
customerSignedAt: new Date(),
} as never);
await this.regenerateContractPdf(contractId, contract.reference);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.customerSignedToStaff(updated);
return updated;
}
return this.counterSign(contractId, dto, options);
@@ -605,7 +623,9 @@ export class ContractTransitionService {
await this.contractsRepository.update(contractId, updates as never);
await this.regenerateContractPdf(contractId, contract.reference);
return this.contractsService.findById(contractId);
const updated = await this.contractsService.findById(contractId);
this.notifier.signedActive(updated);
return updated;
}
/** Customer requests renewal → RENEWAL_DRAFT linked via renewalOfId. */