Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 44 additions & 57 deletions src/notifications/notifications.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,63 +35,50 @@ export class NotificationsService {
{ user: transaction.seller, role: 'Seller' },
];

for (const party of parties) {
const { user, role } = party;
const preferences = user.preferences;

const title = `Transaction ${transaction.status}`;
const message = `Your transaction for property "${transaction.property.title}" has been updated to ${transaction.status}.`;

// 1. In-App Notification
const canInApp = await this.userPreferencesService.shouldDeliverNotification(
user.id,
'TRANSACTION_UPDATE',
'inApp',
);
if (canInApp) {
await this.sendNotification(user.id, title, message, 'TRANSACTION_UPDATE', {
transactionId: transaction.id,
status: transaction.status,
});
}

// 2. Email Notification with template
const canEmail = await this.userPreferencesService.shouldDeliverNotification(
user.id,
'TRANSACTION_UPDATE',
'email',
);
if (canEmail) {
await this.emailService.sendTransactionStatusEmail(user.email, transaction.status, {
transactionId: transaction.id,
propertyTitle: transaction.property.title,
propertyAddress: `${transaction.property.address}, ${transaction.property.city}, ${transaction.property.state} ${transaction.property.zipCode}`,
buyerName: transaction.buyer.firstName
? `${transaction.buyer.firstName} ${transaction.buyer.lastName || ''}`
: transaction.buyer.email,
sellerName: transaction.seller.firstName
? `${transaction.seller.firstName} ${transaction.seller.lastName || ''}`
: transaction.seller.email,
amount: `$${Number(transaction.amount || 0).toLocaleString()}`,
completionDate:
transaction.status === 'COMPLETED' ? new Date().toLocaleDateString() : undefined,
blockchainTxHash: transaction.blockchainTxHash || undefined,
cancellationReason: transaction.cancellationReason || undefined,
cancelledDate:
transaction.status === 'CANCELLED' ? new Date().toLocaleDateString() : undefined,
});
}

// 3. SMS Notification
const canSms = await this.userPreferencesService.shouldDeliverNotification(
user.id,
'TRANSACTION_UPDATE',
'sms',
);
if (canSms && user.phone) {
await this.smsService.sendSms(user.phone, message);
}
}
await Promise.all(
parties.map(async ({ user, role }) => {
const title = `Transaction ${transaction.status}`;
const message = `Your transaction for property "${transaction.property.title}" has been updated to ${transaction.status}.`;

const [canInApp, canEmail, canSms] = await Promise.all([
this.userPreferencesService.shouldDeliverNotification(user.id, 'TRANSACTION_UPDATE', 'inApp'),
this.userPreferencesService.shouldDeliverNotification(user.id, 'TRANSACTION_UPDATE', 'email'),
this.userPreferencesService.shouldDeliverNotification(user.id, 'TRANSACTION_UPDATE', 'sms'),
]);

await Promise.all([
canInApp
? this.sendNotification(user.id, title, message, 'TRANSACTION_UPDATE', {
transactionId: transaction.id,
status: transaction.status,
})
: Promise.resolve(),
canEmail
? this.emailService.sendTransactionStatusEmail(user.email, transaction.status, {
transactionId: transaction.id,
propertyTitle: transaction.property.title,
propertyAddress: `${transaction.property.address}, ${transaction.property.city}, ${transaction.property.state} ${transaction.property.zipCode}`,
buyerName: transaction.buyer.firstName
? `${transaction.buyer.firstName} ${transaction.buyer.lastName || ''}`
: transaction.buyer.email,
sellerName: transaction.seller.firstName
? `${transaction.seller.firstName} ${transaction.seller.lastName || ''}`
: transaction.seller.email,
amount: `$${Number(transaction.amount || 0).toLocaleString()}`,
completionDate:
transaction.status === 'COMPLETED' ? new Date().toLocaleDateString() : undefined,
blockchainTxHash: transaction.blockchainTxHash || undefined,
cancellationReason: transaction.cancellationReason || undefined,
cancelledDate:
transaction.status === 'CANCELLED' ? new Date().toLocaleDateString() : undefined,
})
: Promise.resolve(),
canSms && user.phone
? this.smsService.sendSms(user.phone, message)
: Promise.resolve(),
]);
}),
);
}

async sendNotification(
Expand Down
70 changes: 36 additions & 34 deletions src/properties/property-expiry.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,24 @@ export class PropertyExpiryService {
},
});

for (const property of properties) {
const title = `Property Listing Expiring Soon`;
const message = `Your property "${property.title}" is scheduled to expire in 7 days. Consider renewing it to keep it active.`;

// Send notification to property owner
await this.notificationsService.sendNotification(
property.ownerId,
title,
message,
'PROPERTY_EXPIRY_WARNING',
{
propertyId: property.id,
propertyTitle: property.title,
expiryDate: property.expiryDate,
},
);
}
await Promise.all(
properties.map((property) => {
const title = `Property Listing Expiring Soon`;
const message = `Your property "${property.title}" is scheduled to expire in 7 days. Consider renewing it to keep it active.`;

return this.notificationsService.sendNotification(
property.ownerId,
title,
message,
'PROPERTY_EXPIRY_WARNING',
{
propertyId: property.id,
propertyTitle: property.title,
expiryDate: property.expiryDate,
},
);
}),
);
}

/**
Expand Down Expand Up @@ -120,23 +121,24 @@ export class PropertyExpiryService {
},
});

for (const property of properties) {
const title = `Property Listing Expired`;
const message = `Your property "${property.title}" has expired due to reaching its expiry date. You can renew it to make it active again.`;

// Send notification to property owner
await this.notificationsService.sendNotification(
property.ownerId,
title,
message,
'PROPERTY_EXPIRED',
{
propertyId: property.id,
propertyTitle: property.title,
expiryDate: property.expiryDate,
},
);
}
await Promise.all(
properties.map((property) => {
const title = `Property Listing Expired`;
const message = `Your property "${property.title}" has expired due to reaching its expiry date. You can renew it to make it active again.`;

return this.notificationsService.sendNotification(
property.ownerId,
title,
message,
'PROPERTY_EXPIRED',
{
propertyId: property.id,
propertyTitle: property.title,
expiryDate: property.expiryDate,
},
);
}),
);
}

/**
Expand Down
16 changes: 13 additions & 3 deletions src/transactions/transaction-documents.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ export class TransactionDocumentsService {
const tx = await this.ensureTransactionExists(transactionId);
this.assertAccess(tx, userId, userRole);

const doc = await this.findOne(transactionId, documentId, userId, userRole);
const doc = await this.prisma.document.findFirst({
where: { id: documentId, transactionId },
include: { versions: { orderBy: { versionNumber: 'asc' } } },
});
if (!doc) throw new NotFoundException('Document not found for this transaction');

const nextVersion = (doc.versions?.length ?? 0) + 1;

Expand Down Expand Up @@ -111,7 +115,10 @@ export class TransactionDocumentsService {
const tx = await this.ensureTransactionExists(transactionId);
this.assertAccess(tx, userId, userRole);

await this.findOne(transactionId, documentId, userId, userRole);
const doc = await this.prisma.document.findFirst({
where: { id: documentId, transactionId },
});
if (!doc) throw new NotFoundException('Document not found for this transaction');
return this.prisma.documentVersion.findMany({
where: { documentId },
orderBy: { versionNumber: 'asc' },
Expand All @@ -126,7 +133,10 @@ export class TransactionDocumentsService {
const tx = await this.ensureTransactionExists(transactionId);
this.assertAccess(tx, userId, userRole);

await this.findOne(transactionId, documentId, userId, userRole);
const doc = await this.prisma.document.findFirst({
where: { id: documentId, transactionId },
});
if (!doc) throw new NotFoundException('Document not found for this transaction');
return this.prisma.document.delete({ where: { id: documentId } });
}

Expand Down