mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-26 13:54:53 +00:00
SP-1757, SP-1758, SP-1809, SP-1810: Feat/implement booking (#469)
* fix: commission device API * task: add create booking API * add get All api for dashboard & mobile * add Find APIs for bookings * implement sending email updates on update bookable space * move email interfaces to separate files
This commit is contained in:
@ -1,12 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ErrorMessageService } from 'src/error-message/error-message.service';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { CommonService } from './common.service';
|
||||
import config from './config';
|
||||
import { DatabaseModule } from './database/database.module';
|
||||
import { HelperModule } from './helper/helper.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import config from './config';
|
||||
import { EmailService } from './util/email.service';
|
||||
import { ErrorMessageService } from 'src/error-message/error-message.service';
|
||||
import { TuyaService } from './integrations/tuya/services/tuya.service';
|
||||
import { SceneDeviceRepository } from './modules/scene-device/repositories';
|
||||
import { SpaceRepository } from './modules/space';
|
||||
@ -15,6 +14,7 @@ import {
|
||||
SubspaceModelRepository,
|
||||
} from './modules/space-model';
|
||||
import { SubspaceRepository } from './modules/space/repositories/subspace.repository';
|
||||
import { EmailService } from './util/email/email.service';
|
||||
@Module({
|
||||
providers: [
|
||||
CommonService,
|
||||
|
||||
@ -10,6 +10,8 @@ export default registerAs(
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_SENDER: process.env.SMTP_SENDER,
|
||||
SMTP_PASSWORD: process.env.SMTP_PASSWORD,
|
||||
BATCH_EMAIL_API_URL: process.env.BATCH_EMAIL_API_URL,
|
||||
SEND_EMAIL_API_URL: process.env.SEND_EMAIL_API_URL,
|
||||
MAILTRAP_API_TOKEN: process.env.MAILTRAP_API_TOKEN,
|
||||
MAILTRAP_INVITATION_TEMPLATE_UUID:
|
||||
process.env.MAILTRAP_INVITATION_TEMPLATE_UUID,
|
||||
@ -21,5 +23,9 @@ export default registerAs(
|
||||
process.env.MAILTRAP_EDIT_USER_TEMPLATE_UUID,
|
||||
MAILTRAP_SEND_OTP_TEMPLATE_UUID:
|
||||
process.env.MAILTRAP_SEND_OTP_TEMPLATE_UUID,
|
||||
MAILTRAP_SEND_BOOKING_AVAILABILITY_UPDATE_TEMPLATE_UUID:
|
||||
process.env.MAILTRAP_SEND_BOOKING_AVAILABILITY_UPDATE_TEMPLATE_UUID,
|
||||
MAILTRAP_SEND_BOOKING_TIMING_UPDATE_TEMPLATE_UUID:
|
||||
process.env.MAILTRAP_SEND_BOOKING_TIMING_UPDATE_TEMPLATE_UUID,
|
||||
}),
|
||||
);
|
||||
|
||||
@ -91,6 +91,25 @@ export class ControllerRoute {
|
||||
'This endpoint allows you to update existing bookable spaces by providing the required details.';
|
||||
};
|
||||
};
|
||||
static BOOKING = class {
|
||||
public static readonly ROUTE = 'bookings';
|
||||
static ACTIONS = class {
|
||||
public static readonly ADD_BOOKING_SUMMARY = 'Add new booking';
|
||||
|
||||
public static readonly ADD_BOOKING_DESCRIPTION =
|
||||
'This endpoint allows you to add new booking by providing the required details.';
|
||||
|
||||
public static readonly GET_ALL_BOOKINGS_SUMMARY = 'Get all bookings';
|
||||
|
||||
public static readonly GET_ALL_BOOKINGS_DESCRIPTION =
|
||||
'This endpoint retrieves all bookings.';
|
||||
|
||||
public static readonly GET_MY_BOOKINGS_SUMMARY = 'Get my bookings';
|
||||
|
||||
public static readonly GET_MY_BOOKINGS_DESCRIPTION =
|
||||
'This endpoint retrieves all bookings for the authenticated user.';
|
||||
};
|
||||
};
|
||||
static COMMUNITY = class {
|
||||
public static readonly ROUTE = '/projects/:projectUuid/communities';
|
||||
static ACTIONS = class {
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
export const SEND_EMAIL_API_URL_PROD = 'https://send.api.mailtrap.io/api/send/';
|
||||
export const SEND_EMAIL_API_URL_DEV =
|
||||
'https://sandbox.api.mailtrap.io/api/send/2634012';
|
||||
@ -14,6 +14,8 @@ import { createLogger } from 'winston';
|
||||
import { winstonLoggerOptions } from '../logger/services/winston.logger';
|
||||
import { AqiSpaceDailyPollutantStatsEntity } from '../modules/aqi/entities';
|
||||
import { AutomationEntity } from '../modules/automation/entities';
|
||||
import { BookableSpaceEntity } from '../modules/booking/entities/bookable-space.entity';
|
||||
import { BookingEntity } from '../modules/booking/entities/booking.entity';
|
||||
import { ClientEntity } from '../modules/client/entities';
|
||||
import { CommunityEntity } from '../modules/community/entities';
|
||||
import { DeviceStatusLogEntity } from '../modules/device-status-log/entities';
|
||||
@ -58,7 +60,6 @@ import {
|
||||
UserSpaceEntity,
|
||||
} from '../modules/user/entities';
|
||||
import { VisitorPasswordEntity } from '../modules/visitor-password/entities';
|
||||
import { BookableSpaceEntity } from '../modules/booking/entities';
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRootAsync({
|
||||
@ -119,6 +120,7 @@ import { BookableSpaceEntity } from '../modules/booking/entities';
|
||||
AqiSpaceDailyPollutantStatsEntity,
|
||||
SpaceDailyOccupancyDurationEntity,
|
||||
BookableSpaceEntity,
|
||||
BookingEntity,
|
||||
],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))),
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { BookableSpaceEntity } from './entities/bookable-space.entity';
|
||||
import { BookingEntity } from './entities/booking.entity';
|
||||
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [TypeOrmModule.forFeature([BookableSpaceEntity])],
|
||||
imports: [TypeOrmModule.forFeature([BookableSpaceEntity, BookingEntity])],
|
||||
})
|
||||
export class BookableRepositoryModule {}
|
||||
export class BookingRepositoryModule {}
|
||||
|
||||
44
libs/common/src/modules/booking/entities/booking.entity.ts
Normal file
44
libs/common/src/modules/booking/entities/booking.entity.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { SpaceEntity } from '../../space/entities/space.entity';
|
||||
import { UserEntity } from '../../user/entities';
|
||||
|
||||
@Entity('booking')
|
||||
export class BookingEntity extends AbstractEntity {
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
default: () => 'gen_random_uuid()',
|
||||
nullable: false,
|
||||
})
|
||||
public uuid: string;
|
||||
|
||||
@ManyToOne(() => SpaceEntity, (space) => space.bookableConfig)
|
||||
space: SpaceEntity;
|
||||
|
||||
@ManyToOne(() => UserEntity, (user) => user.bookings)
|
||||
user: UserEntity;
|
||||
|
||||
@Column({ type: Date, nullable: false })
|
||||
date: Date;
|
||||
|
||||
@Column({ type: 'time' })
|
||||
startTime: string;
|
||||
|
||||
@Column({ type: 'time' })
|
||||
endTime: string;
|
||||
|
||||
@Column({ type: 'int', default: null })
|
||||
cost?: number;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from './bookable-space.entity';
|
||||
@ -0,0 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BookableSpaceEntity } from '../entities/bookable-space.entity';
|
||||
|
||||
@Injectable()
|
||||
export class BookableSpaceEntityRepository extends Repository<BookableSpaceEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(BookableSpaceEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BookableSpaceEntity } from '../entities/bookable-space.entity';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { BookingEntity } from '../entities/booking.entity';
|
||||
|
||||
@Injectable()
|
||||
export class BookableSpaceEntityRepository extends Repository<BookableSpaceEntity> {
|
||||
export class BookingEntityRepository extends Repository<BookingEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(BookableSpaceEntity, dataSource.createEntityManager());
|
||||
super(BookingEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from './booking.repository';
|
||||
@ -8,7 +8,7 @@ import {
|
||||
} from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities';
|
||||
import { BookableSpaceEntity } from '../../booking/entities';
|
||||
import { BookableSpaceEntity } from '../../booking/entities/bookable-space.entity';
|
||||
import { CommunityEntity } from '../../community/entities';
|
||||
import { DeviceEntity } from '../../device/entities';
|
||||
import { InviteUserSpaceEntity } from '../../Invite-user/entities';
|
||||
@ -20,6 +20,7 @@ import { UserSpaceEntity } from '../../user/entities';
|
||||
import { SpaceDto } from '../dtos';
|
||||
import { SpaceProductAllocationEntity } from './space-product-allocation.entity';
|
||||
import { SubspaceEntity } from './subspace/subspace.entity';
|
||||
import { BookingEntity } from '../../booking/entities/booking.entity';
|
||||
|
||||
@Entity({ name: 'space' })
|
||||
export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
||||
@ -132,6 +133,9 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
||||
@OneToOne(() => BookableSpaceEntity, (bookable) => bookable.space)
|
||||
bookableConfig: BookableSpaceEntity;
|
||||
|
||||
@OneToMany(() => BookingEntity, (booking) => booking.space)
|
||||
bookings: BookingEntity[];
|
||||
|
||||
constructor(partial: Partial<SpaceEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
|
||||
@ -29,6 +29,7 @@ import {
|
||||
UserOtpDto,
|
||||
UserSpaceDto,
|
||||
} from '../dtos';
|
||||
import { BookingEntity } from '../../booking/entities/booking.entity';
|
||||
|
||||
@Entity({ name: 'user' })
|
||||
export class UserEntity extends AbstractEntity<UserDto> {
|
||||
@ -121,6 +122,9 @@ export class UserEntity extends AbstractEntity<UserDto> {
|
||||
)
|
||||
deviceUserNotification: DeviceNotificationEntity[];
|
||||
|
||||
@OneToMany(() => BookingEntity, (booking) => booking.user)
|
||||
bookings: BookingEntity[];
|
||||
|
||||
@ManyToOne(() => RegionEntity, (region) => region.users, { nullable: true })
|
||||
region: RegionEntity;
|
||||
@ManyToOne(() => TimeZoneEntity, (timezone) => timezone.users, {
|
||||
|
||||
8
libs/common/src/util/email/batch-email.interface.ts
Normal file
8
libs/common/src/util/email/batch-email.interface.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface BatchEmailData {
|
||||
base: { from: { email: string }; template_uuid: string };
|
||||
requests: Array<{
|
||||
to: { email: string }[];
|
||||
template_variables: Record<string, any>;
|
||||
}>;
|
||||
isBatch: true;
|
||||
}
|
||||
@ -1,15 +1,17 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import axios from 'axios';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import {
|
||||
SEND_EMAIL_API_URL_DEV,
|
||||
SEND_EMAIL_API_URL_PROD,
|
||||
} from '../constants/mail-trap';
|
||||
import nodemailer from 'nodemailer';
|
||||
import Mail from 'nodemailer/lib/mailer';
|
||||
import { BatchEmailData } from './batch-email.interface';
|
||||
import { SingleEmailData } from './single-email.interface';
|
||||
|
||||
@Injectable()
|
||||
export class EmailService {
|
||||
private smtpConfig: any;
|
||||
private API_TOKEN: string;
|
||||
private SEND_EMAIL_API_URL: string;
|
||||
private BATCH_EMAIL_API_URL: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.smtpConfig = {
|
||||
@ -22,6 +24,15 @@ export class EmailService {
|
||||
pass: this.configService.get<string>('email-config.SMTP_PASSWORD'),
|
||||
},
|
||||
};
|
||||
this.API_TOKEN = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_API_TOKEN',
|
||||
);
|
||||
this.SEND_EMAIL_API_URL = this.configService.get<string>(
|
||||
'email-config.SEND_EMAIL_API_URL',
|
||||
);
|
||||
this.BATCH_EMAIL_API_URL = this.configService.get<string>(
|
||||
'email-config.BATCH_EMAIL_API_URL',
|
||||
);
|
||||
}
|
||||
|
||||
async sendEmail(
|
||||
@ -31,7 +42,7 @@ export class EmailService {
|
||||
): Promise<void> {
|
||||
const transporter = nodemailer.createTransport(this.smtpConfig);
|
||||
|
||||
const mailOptions = {
|
||||
const mailOptions: Mail.Options = {
|
||||
from: this.smtpConfig.sender,
|
||||
to: email,
|
||||
subject,
|
||||
@ -44,13 +55,6 @@ export class EmailService {
|
||||
email: string,
|
||||
emailInvitationData: any,
|
||||
): Promise<void> {
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const API_TOKEN = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_API_TOKEN',
|
||||
);
|
||||
const API_URL = isProduction
|
||||
? SEND_EMAIL_API_URL_PROD
|
||||
: SEND_EMAIL_API_URL_DEV;
|
||||
const TEMPLATE_UUID = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_INVITATION_TEMPLATE_UUID',
|
||||
);
|
||||
@ -68,21 +72,12 @@ export class EmailService {
|
||||
template_variables: emailInvitationData,
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(API_URL, emailData, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${API_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.response?.data?.message ||
|
||||
'Error sending email using Mailtrap template',
|
||||
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
return this.sendEmailWithTemplateV2({
|
||||
...emailData,
|
||||
isBatch: false,
|
||||
});
|
||||
}
|
||||
|
||||
async sendEmailWithTemplate({
|
||||
email,
|
||||
name,
|
||||
@ -94,14 +89,6 @@ export class EmailService {
|
||||
isEnable: boolean;
|
||||
isDelete: boolean;
|
||||
}): Promise<void> {
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const API_TOKEN = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_API_TOKEN',
|
||||
);
|
||||
const API_URL = isProduction
|
||||
? SEND_EMAIL_API_URL_PROD
|
||||
: SEND_EMAIL_API_URL_DEV;
|
||||
|
||||
// Determine the template UUID based on the arguments
|
||||
const templateUuid = isDelete
|
||||
? this.configService.get<string>(
|
||||
@ -128,32 +115,16 @@ export class EmailService {
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(API_URL, emailData, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${API_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.response?.data?.message ||
|
||||
'Error sending email using Mailtrap template',
|
||||
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
return this.sendEmailWithTemplateV2({
|
||||
...emailData,
|
||||
isBatch: false,
|
||||
});
|
||||
}
|
||||
|
||||
async sendEditUserEmailWithTemplate(
|
||||
email: string,
|
||||
emailEditData: any,
|
||||
): Promise<void> {
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const API_TOKEN = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_API_TOKEN',
|
||||
);
|
||||
const API_URL = isProduction
|
||||
? SEND_EMAIL_API_URL_PROD
|
||||
: SEND_EMAIL_API_URL_DEV;
|
||||
const TEMPLATE_UUID = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_EDIT_USER_TEMPLATE_UUID',
|
||||
);
|
||||
@ -171,32 +142,15 @@ export class EmailService {
|
||||
template_variables: emailEditData,
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(API_URL, emailData, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${API_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.response?.data?.message ||
|
||||
'Error sending email using Mailtrap template',
|
||||
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
return this.sendEmailWithTemplateV2({
|
||||
...emailData,
|
||||
isBatch: false,
|
||||
});
|
||||
}
|
||||
async sendOtpEmailWithTemplate(
|
||||
email: string,
|
||||
emailEditData: any,
|
||||
): Promise<void> {
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const API_TOKEN = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_API_TOKEN',
|
||||
);
|
||||
const API_URL = isProduction
|
||||
? SEND_EMAIL_API_URL_PROD
|
||||
: SEND_EMAIL_API_URL_DEV;
|
||||
const TEMPLATE_UUID = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_SEND_OTP_TEMPLATE_UUID',
|
||||
);
|
||||
@ -214,20 +168,84 @@ export class EmailService {
|
||||
template_variables: emailEditData,
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(API_URL, emailData, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${API_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
return this.sendEmailWithTemplateV2({
|
||||
...emailData,
|
||||
isBatch: false,
|
||||
});
|
||||
}
|
||||
|
||||
async sendUpdateBookingTimingEmailWithTemplate(
|
||||
emails: {
|
||||
email: string;
|
||||
name: string;
|
||||
bookings: {
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}[];
|
||||
}[],
|
||||
emailVariables: {
|
||||
space_name: string;
|
||||
days: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
const TEMPLATE_UUID = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_SEND_BOOKING_TIMING_UPDATE_TEMPLATE_UUID',
|
||||
);
|
||||
|
||||
const emailData = {
|
||||
base: {
|
||||
from: {
|
||||
email: this.smtpConfig.sender,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.response?.data?.message ||
|
||||
'Error sending email using Mailtrap template',
|
||||
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
template_uuid: TEMPLATE_UUID,
|
||||
},
|
||||
requests: emails.map(({ email, name, bookings }) => ({
|
||||
to: [{ email }],
|
||||
template_variables: { ...emailVariables, name, bookings },
|
||||
})),
|
||||
};
|
||||
|
||||
return this.sendEmailWithTemplateV2({
|
||||
...emailData,
|
||||
isBatch: true,
|
||||
});
|
||||
}
|
||||
|
||||
async sendUpdateBookingAvailabilityEmailWithTemplate(
|
||||
emails: { email: string; name: string }[],
|
||||
emailVariables: {
|
||||
space_name: string;
|
||||
availability: string;
|
||||
isAvailable: boolean;
|
||||
},
|
||||
): Promise<void> {
|
||||
const TEMPLATE_UUID = this.configService.get<string>(
|
||||
'email-config.MAILTRAP_SEND_BOOKING_AVAILABILITY_UPDATE_TEMPLATE_UUID',
|
||||
);
|
||||
|
||||
const emailData = {
|
||||
base: {
|
||||
from: {
|
||||
email: this.smtpConfig.sender,
|
||||
},
|
||||
template_uuid: TEMPLATE_UUID,
|
||||
},
|
||||
requests: emails.map(({ email, name }) => ({
|
||||
to: [{ email }],
|
||||
template_variables: {
|
||||
...emailVariables,
|
||||
name,
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
||||
return this.sendEmailWithTemplateV2({
|
||||
...emailData,
|
||||
isBatch: true,
|
||||
});
|
||||
}
|
||||
generateUserChangesEmailBody(
|
||||
addedSpaceNames: string[],
|
||||
@ -264,4 +282,30 @@ export class EmailService {
|
||||
nameChanged,
|
||||
};
|
||||
}
|
||||
|
||||
private async sendEmailWithTemplateV2({
|
||||
isBatch,
|
||||
...emailData
|
||||
}: BatchEmailData | SingleEmailData): Promise<void> {
|
||||
try {
|
||||
await axios.post(
|
||||
isBatch ? this.BATCH_EMAIL_API_URL : this.SEND_EMAIL_API_URL,
|
||||
{
|
||||
...emailData,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.API_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.response?.data?.message ||
|
||||
'Error sending email using Mailtrap template',
|
||||
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
libs/common/src/util/email/single-email.interface.ts
Normal file
7
libs/common/src/util/email/single-email.interface.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface SingleEmailData {
|
||||
from: { email: string };
|
||||
to: { email: string }[];
|
||||
template_uuid: string;
|
||||
template_variables?: Record<string, any>;
|
||||
isBatch: false;
|
||||
}
|
||||
7
libs/common/src/util/time-to-12-hours-convetion.ts
Normal file
7
libs/common/src/util/time-to-12-hours-convetion.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { format, parse } from 'date-fns';
|
||||
|
||||
export function to12HourFormat(timeString: string): string {
|
||||
timeString = timeString.padEnd(8, ':00');
|
||||
const parsedTime = parse(timeString, 'HH:mm:ss', new Date());
|
||||
return format(parsedTime, 'hh:mm a');
|
||||
}
|
||||
Reference in New Issue
Block a user