File

src/notifications/notification.service.ts

Index

Methods

Constructor

constructor()

Methods

Async sendAlertNotification
sendAlertNotification(email: string, alertTitle: string, alertDescription: string, severity: string)

Send alert escalation notification

Parameters :
Name Type Optional
email string No
alertTitle string No
alertDescription string No
severity string No
Returns : Promise<void>
Async sendDeliveryConfirmation
sendDeliveryConfirmation(clientEmail: string, orderNumber: string, recipientName: string)

Send delivery confirmation to client

Parameters :
Name Type Optional
clientEmail string No
orderNumber string No
recipientName string No
Returns : Promise<void>
Async sendEmail
sendEmail(to: string, subject: string, html: string)

Send an email notification

Parameters :
Name Type Optional
to string No
subject string No
html string No
Returns : Promise<boolean>
Async sendEtaUpdate
sendEtaUpdate(clientEmail: string, orderNumber: string, newEta: Date, reason?: string)

Send ETA update notification to client contact

Parameters :
Name Type Optional
clientEmail string No
orderNumber string No
newEta Date No
reason string Yes
Returns : Promise<void>
Async sendSms
sendSms(to: string, message: string)

Send SMS via Twilio (or log if not configured)

Parameters :
Name Type Optional
to string No
message string No
Returns : Promise<boolean>
import { Injectable, Logger } from '@nestjs/common';
import * as nodemailer from 'nodemailer';

@Injectable()
export class NotificationService {
  private readonly logger = new Logger(NotificationService.name);
  private emailTransport: nodemailer.Transporter | null = null;

  constructor() {
    // Set up email transport if SMTP configured
    if (process.env.SMTP_HOST) {
      this.emailTransport = nodemailer.createTransport({
        host: process.env.SMTP_HOST,
        port: parseInt(process.env.SMTP_PORT || '587'),
        secure: process.env.SMTP_SECURE === 'true',
        auth: {
          user: process.env.SMTP_USER,
          pass: process.env.SMTP_PASS,
        },
      });
      this.logger.log('Email transport configured');
    } else {
      this.logger.warn('SMTP not configured — emails will be logged only');
    }
  }

  /**
   * Send an email notification
   */
  async sendEmail(
    to: string,
    subject: string,
    html: string,
  ): Promise<boolean> {
    if (!this.emailTransport) {
      this.logger.log(`[EMAIL LOG] To: ${to} | Subject: ${subject}`);
      return true; // Log-only mode
    }

    try {
      await this.emailTransport.sendMail({
        from:
          process.env.SMTP_FROM || 'FleetCommand <noreply@fleetcommand.io>',
        to,
        subject,
        html,
      });
      this.logger.log(`Email sent to ${to}: ${subject}`);
      return true;
    } catch (err) {
      this.logger.error(`Failed to send email to ${to}: ${err}`);
      return false;
    }
  }

  /**
   * Send SMS via Twilio (or log if not configured)
   */
  async sendSms(to: string, message: string): Promise<boolean> {
    if (!process.env.TWILIO_ACCOUNT_SID) {
      this.logger.log(`[SMS LOG] To: ${to} | Message: ${message}`);
      return true;
    }

    try {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      const twilio = require('twilio');
      const client = twilio(
        process.env.TWILIO_ACCOUNT_SID,
        process.env.TWILIO_AUTH_TOKEN,
      );
      await client.messages.create({
        body: message,
        from: process.env.TWILIO_PHONE_NUMBER,
        to,
      });
      this.logger.log(`SMS sent to ${to}`);
      return true;
    } catch (err) {
      this.logger.error(`Failed to send SMS to ${to}: ${err}`);
      return false;
    }
  }

  /**
   * Send ETA update notification to client contact
   */
  async sendEtaUpdate(
    clientEmail: string,
    orderNumber: string,
    newEta: Date,
    reason?: string,
  ): Promise<void> {
    const etaStr = newEta.toLocaleString('en-US', {
      dateStyle: 'medium',
      timeStyle: 'short',
    });
    const subject = `FleetCommand — ETA Update for ${orderNumber}`;
    const html = `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <div style="background: linear-gradient(135deg, #6366f1, #3b82f6); padding: 20px 30px; border-radius: 12px 12px 0 0;">
          <h2 style="color: white; margin: 0;">FleetCommand</h2>
        </div>
        <div style="background: #f8fafc; padding: 30px; border: 1px solid #e2e8f0; border-radius: 0 0 12px 12px;">
          <h3 style="color: #1e293b; margin-top: 0;">ETA Update for Order ${orderNumber}</h3>
          <p style="color: #64748b;">The estimated time of arrival has been updated:</p>
          <div style="background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; margin: 16px 0;">
            <p style="margin: 0; font-size: 24px; font-weight: bold; color: #1e293b;">${etaStr}</p>
          </div>
          ${reason ? `<p style="color: #64748b;">Reason: ${reason}</p>` : ''}
          <p style="color: #94a3b8; font-size: 12px; margin-top: 20px;">This is an automated notification from FleetCommand TMS.</p>
        </div>
      </div>
    `;
    await this.sendEmail(clientEmail, subject, html);
  }

  /**
   * Send alert escalation notification
   */
  async sendAlertNotification(
    email: string,
    alertTitle: string,
    alertDescription: string,
    severity: string,
  ): Promise<void> {
    const severityColor =
      severity === 'CRITICAL'
        ? '#ef4444'
        : severity === 'WARNING'
          ? '#f59e0b'
          : '#3b82f6';
    const subject = `[${severity}] FleetCommand Alert: ${alertTitle}`;
    const html = `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <div style="background: ${severityColor}; padding: 20px 30px; border-radius: 12px 12px 0 0;">
          <h2 style="color: white; margin: 0;">${severity} Alert</h2>
        </div>
        <div style="background: #f8fafc; padding: 30px; border: 1px solid #e2e8f0; border-radius: 0 0 12px 12px;">
          <h3 style="color: #1e293b; margin-top: 0;">${alertTitle}</h3>
          <p style="color: #64748b;">${alertDescription}</p>
          <a href="#" style="display: inline-block; background: #6366f1; color: white; padding: 10px 20px; border-radius: 8px; text-decoration: none; margin-top: 16px;">View in FleetCommand</a>
        </div>
      </div>
    `;
    await this.sendEmail(email, subject, html);
  }

  /**
   * Send delivery confirmation to client
   */
  async sendDeliveryConfirmation(
    clientEmail: string,
    orderNumber: string,
    recipientName: string,
  ): Promise<void> {
    const subject = `FleetCommand — Delivery Confirmed: ${orderNumber}`;
    const html = `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <div style="background: linear-gradient(135deg, #10b981, #14b8a6); padding: 20px 30px; border-radius: 12px 12px 0 0;">
          <h2 style="color: white; margin: 0;">Delivery Confirmed</h2>
        </div>
        <div style="background: #f8fafc; padding: 30px; border: 1px solid #e2e8f0; border-radius: 0 0 12px 12px;">
          <h3 style="color: #1e293b; margin-top: 0;">Order ${orderNumber} has been delivered</h3>
          <p style="color: #64748b;">Received by: <strong>${recipientName}</strong></p>
          <p style="color: #64748b;">Delivered at: ${new Date().toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' })}</p>
          <p style="color: #94a3b8; font-size: 12px; margin-top: 20px;">Proof of delivery is available in your FleetCommand dashboard.</p>
        </div>
      </div>
    `;
    await this.sendEmail(clientEmail, subject, html);
  }
}

results matching ""

    No results matching ""