integratedhub/services/notificationService.ts
2026-02-14 02:20:19 +00:00

276 lines
6.8 KiB
TypeScript

// services/notificationService.ts
// Handle notifications for lease approval workflow
import { eq } from "drizzle-orm";
import { db } from "../db/db.ts";
import { users } from "../db/schema.ts";
export interface NotificationPreferences {
email_messages?: boolean;
email_reminders?: boolean;
email_payments?: boolean;
email_lease_approvals?: boolean;
}
export interface LeaseApprovalNotificationData {
lease_id: string;
property_address?: string;
tenant_name?: string;
landlord_name?: string;
monthly_rent?: number;
}
export interface NotificationResult {
success: boolean;
sent_via: string[];
skipped: string[];
error?: string;
}
function parseNotificationPreferences(
preferencesJson: string | null,
): NotificationPreferences {
if (!preferencesJson) {
return {
email_messages: true,
email_reminders: true,
email_payments: true,
email_lease_approvals: true,
};
}
try {
const parsed = JSON.parse(preferencesJson);
return {
email_messages: parsed.email_messages ?? true,
email_reminders: parsed.email_reminders ?? true,
email_payments: parsed.email_payments ?? true,
email_lease_approvals: parsed.email_lease_approvals ?? true,
};
} catch {
return {
email_messages: true,
email_reminders: true,
email_payments: true,
email_lease_approvals: true,
};
}
}
async function getUserNotificationPreferences(
userId: string,
): Promise<NotificationPreferences> {
const result = await db.select()
.from(users)
.where(eq(users.id, userId));
if (result.length === 0) {
return {
email_messages: true,
email_reminders: true,
email_payments: true,
email_lease_approvals: true,
};
}
return parseNotificationPreferences(
result[0].notification_preferences_json as string | null,
);
}
async function getUserEmail(userId: string): Promise<string | null> {
const result = await db.select()
.from(users)
.where(eq(users.id, userId));
return result[0]?.email || null;
}
function buildEmailSubject(
type: "approval_request" | "approved" | "rejected",
): string {
switch (type) {
case "approval_request":
return "New Lease Approval Required";
case "approved":
return "Lease Approved - Congratulations!";
case "rejected":
return "Lease Request Update";
default:
return "Lease Notification";
}
}
function buildEmailBody(
type: "approval_request" | "approved" | "rejected",
data: LeaseApprovalNotificationData,
): string {
const { property_address, tenant_name, landlord_name, monthly_rent } = data;
switch (type) {
case "approval_request":
return `
Dear ${landlord_name || "Landlord"},
A new lease approval request has been submitted for your property.
Property: ${property_address || "N/A"}
Tenant: ${tenant_name || "N/A"}
Monthly Rent: ${monthly_rent ? `$${monthly_rent.toLocaleString()}` : "N/A"}
Please review this request in your landlord dashboard and either approve or reject the lease.
You can view and manage pending approvals by visiting your dashboard.
Best regards,
The Property Management Team
`.trim();
case "approved":
return `
Dear ${tenant_name || "Tenant"},
Great news! Your lease request has been approved.
Property: ${property_address || "N/A"}
Landlord: ${landlord_name || "N/A"}
Your lease is now active. You can access your tenant portal to view lease details, set up payment methods, and manage your rental.
Congratulations on your new lease!
Best regards,
The Property Management Team
`.trim();
case "rejected":
return `
Dear ${tenant_name || "Tenant"},
We regret to inform you that your lease request has been declined.
Property: ${property_address || "N/A"}
Landlord: ${landlord_name || "N/A"}
If you have questions about this decision or would like to discuss it further, please contact your landlord directly.
You may request a new invite from your landlord if you're still interested in this property.
Best regards,
The Property Management Team
`.trim();
default:
return "You have received a notification regarding your lease request.";
}
}
function sendEmail(
to: string,
subject: string,
body: string,
): boolean {
try {
console.log(`[Notification] Sending email to: ${to}`);
console.log(`[Notification] Subject: ${subject}`);
return true;
} catch (error) {
console.error(`[Notification] Failed to send email to ${to}:`, error);
return false;
}
}
export async function sendLeaseApprovalRequest(
landlordId: string,
data: LeaseApprovalNotificationData,
): Promise<NotificationResult> {
const preferences = await getUserNotificationPreferences(landlordId);
const email = await getUserEmail(landlordId);
const sent_via: string[] = [];
const skipped: string[] = [];
if (preferences.email_lease_approvals && email) {
const subject = buildEmailSubject("approval_request");
const body = buildEmailBody("approval_request", data);
const success = await sendEmail(email, subject, body);
if (success) {
sent_via.push("email");
} else {
skipped.push("email (failed)");
}
} else {
skipped.push("email (disabled or no email)");
}
return {
success: sent_via.length > 0,
sent_via,
skipped,
};
}
export async function sendLeaseApproved(
tenantId: string,
data: LeaseApprovalNotificationData,
): Promise<NotificationResult> {
const preferences = await getUserNotificationPreferences(tenantId);
const email = await getUserEmail(tenantId);
const sent_via: string[] = [];
const skipped: string[] = [];
if (preferences.email_lease_approvals && email) {
const subject = buildEmailSubject("approved");
const body = buildEmailBody("approved", data);
const success = await sendEmail(email, subject, body);
if (success) {
sent_via.push("email");
} else {
skipped.push("email (failed)");
}
} else {
skipped.push("email (disabled or no email)");
}
return {
success: sent_via.length > 0,
sent_via,
skipped,
};
}
export async function sendLeaseRejected(
tenantId: string,
data: LeaseApprovalNotificationData,
): Promise<NotificationResult> {
const preferences = await getUserNotificationPreferences(tenantId);
const email = await getUserEmail(tenantId);
const sent_via: string[] = [];
const skipped: string[] = [];
if (preferences.email_lease_approvals && email) {
const subject = buildEmailSubject("rejected");
const body = buildEmailBody("rejected", data);
const success = await sendEmail(email, subject, body);
if (success) {
sent_via.push("email");
} else {
skipped.push("email (failed)");
}
} else {
skipped.push("email (disabled or no email)");
}
return {
success: sent_via.length > 0,
sent_via,
skipped,
};
}