315 lines
7.8 KiB
TypeScript
315 lines
7.8 KiB
TypeScript
import mongoose from "mongoose";
|
|
import { AuthenticatedUser } from "../auth";
|
|
import { orgModel } from "../organization/organization.schema";
|
|
import {
|
|
getFilterObject,
|
|
getSortObject,
|
|
getTaggedOrgsFilter,
|
|
getTaggedUsersFilter,
|
|
PageQueryParams,
|
|
} from "../pagination";
|
|
import { generateId } from "../utils/id";
|
|
import {
|
|
CreateNotificationInput,
|
|
notificationFields,
|
|
notificationModel,
|
|
UpdateNotificationInput,
|
|
} from "./notification.schema";
|
|
import { getUser } from "../user/user.service";
|
|
import { createNote } from "../note/note.service";
|
|
import { createAlert } from "../alert/alert.service";
|
|
import { arrayDiff } from "../utils/diff";
|
|
|
|
export async function createNotification(
|
|
input: CreateNotificationInput,
|
|
tenantId: string,
|
|
) {
|
|
const notification = await notificationModel.create({
|
|
...input,
|
|
pid: generateId(),
|
|
tenantId: tenantId,
|
|
createdAt: new Date(),
|
|
});
|
|
|
|
if (input.assignedToOrg) {
|
|
}
|
|
|
|
return await notificationModel
|
|
.findOne({ pid: notification.pid })
|
|
.populate({ path: "assignedTo", select: "pid name avatar" });
|
|
}
|
|
|
|
export async function getNotification(
|
|
notifId: string,
|
|
user: AuthenticatedUser,
|
|
) {
|
|
return await notificationModel
|
|
.findOne({
|
|
tenantId: user.tenantId,
|
|
pid: notifId,
|
|
})
|
|
.populate({ path: "assignedTo", select: "pid name avatar" });
|
|
}
|
|
|
|
export async function updateNotification(
|
|
notifId: string,
|
|
input: UpdateNotificationInput,
|
|
user: AuthenticatedUser,
|
|
) {
|
|
if (input.assignedToOrg && input.assignedTo) {
|
|
delete input.assignedTo;
|
|
}
|
|
|
|
const oldNotification = await notificationModel.findOne(
|
|
{ pid: notifId },
|
|
{ assignedTo: 1, assignedToOrg: 1 },
|
|
);
|
|
|
|
const updateNotificationResult = await notificationModel
|
|
.findOneAndUpdate(
|
|
{ $and: [{ tenantId: user.tenantId }, { pid: notifId }] },
|
|
{
|
|
...input,
|
|
updatedAt: new Date(),
|
|
},
|
|
{ new: true },
|
|
)
|
|
.populate({ path: "assignedTo", select: "pid name avatar" });
|
|
|
|
if (updateNotificationResult) {
|
|
for (const key in input) {
|
|
if (["status"].includes(key)) {
|
|
let msg = "";
|
|
|
|
if (input[key] === null) {
|
|
msg = `Cleared ${key}`;
|
|
} else {
|
|
msg = `Updated ${key} to '${input[key]}'`;
|
|
}
|
|
|
|
await createNote(
|
|
{
|
|
content: msg,
|
|
},
|
|
notifId,
|
|
"notifications",
|
|
user,
|
|
);
|
|
} else if (key == "assignedTo") {
|
|
const newAssignees = arrayDiff(
|
|
updateNotificationResult.assignedTo.map((item) => item._id),
|
|
oldNotification.assignedTo,
|
|
);
|
|
|
|
if (newAssignees.length > 0) {
|
|
let msg = "Assigned to:\n\n";
|
|
|
|
for (const assignee of newAssignees) {
|
|
const user = await getUser(assignee);
|
|
if (!user) continue;
|
|
|
|
msg += `${user.firstName + " " + user.lastName}\n`;
|
|
|
|
await createAlert(
|
|
user.tenantId,
|
|
`You are assigned to ${updateNotificationResult.permitNumber}`,
|
|
"user",
|
|
assignee,
|
|
updateNotificationResult.pid,
|
|
"permits",
|
|
{
|
|
client: updateNotificationResult.client.toString(),
|
|
county: updateNotificationResult.county.id.toString(),
|
|
},
|
|
);
|
|
}
|
|
|
|
await createNote(
|
|
{
|
|
content: msg,
|
|
},
|
|
notifId,
|
|
"notifications",
|
|
user,
|
|
);
|
|
}
|
|
} else if (key == "assignedToOrg") {
|
|
if (
|
|
oldNotification.assignedToOrg ==
|
|
updateNotificationResult.assignedToOrg
|
|
)
|
|
continue;
|
|
|
|
const orgName =
|
|
input.assignedToOrg == "agent"
|
|
? "Suncoast"
|
|
: updateNotificationResult.clientData.name;
|
|
|
|
await createAlert(
|
|
user.tenantId,
|
|
`${orgName} assigned to ${updateNotificationResult.permitNumber}`,
|
|
"team",
|
|
input.assignedToOrg == "client"
|
|
? updateNotificationResult.client.toString()
|
|
: process.env.SUNCOAST_ID,
|
|
updateNotificationResult.pid,
|
|
"permits",
|
|
{
|
|
client: updateNotificationResult.client.toString(),
|
|
county: updateNotificationResult.county.id.toString(),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return updateNotificationResult;
|
|
}
|
|
|
|
export async function listNotifications(
|
|
params: PageQueryParams,
|
|
user: AuthenticatedUser,
|
|
) {
|
|
const page = params.page || 1;
|
|
const pageSize = params.pageSize || 10;
|
|
const sortObj = getSortObject(params, notificationFields);
|
|
let filterObj = getFilterObject(params) || [];
|
|
|
|
filterObj.forEach((item) => {
|
|
if (item.changes) {
|
|
if (item.changes.$eq) {
|
|
item[`changes.${item.changes.$eq}`] = { $exists: true };
|
|
} else if (item.changes.$in) {
|
|
item.changes.$in.forEach((value) => {
|
|
item[`changes.${value}`] = { $exists: true };
|
|
});
|
|
}
|
|
|
|
delete item["changes"];
|
|
}
|
|
});
|
|
|
|
if (user.role == "client") {
|
|
filterObj.push({
|
|
client: {
|
|
$in: user.orgId.map((item) => new mongoose.Types.ObjectId(item)),
|
|
},
|
|
});
|
|
}
|
|
|
|
if (user.counties && user.counties.length > 0) {
|
|
filterObj.push({
|
|
"county.id": {
|
|
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
|
|
},
|
|
});
|
|
}
|
|
|
|
let { taggedUsersFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
|
|
filterObj,
|
|
sortObj,
|
|
);
|
|
|
|
let { taggedOrgsFilter, taggedOrgsFilterIndex } = getTaggedOrgsFilter(
|
|
filterObj,
|
|
sortObj,
|
|
);
|
|
|
|
if (taggedUserFilterIndex != -1) filterObj.splice(taggedUserFilterIndex, 1);
|
|
if (taggedOrgsFilterIndex != -1) filterObj.splice(taggedOrgsFilterIndex, 1);
|
|
|
|
const pipeline: any = [
|
|
{
|
|
$match: { $and: [{ tenantId: user.tenantId }, ...filterObj] },
|
|
},
|
|
];
|
|
|
|
if (params.searchToken && params.searchToken != "") {
|
|
const regex = new RegExp(params.searchToken, "i");
|
|
pipeline.push({
|
|
$match: {
|
|
$or: [{ permitNumber: { $regex: regex } }, { link: { $regex: regex } }],
|
|
},
|
|
});
|
|
}
|
|
|
|
pipeline.push(
|
|
...[
|
|
...taggedUsersFilter,
|
|
...taggedOrgsFilter,
|
|
{
|
|
$lookup: {
|
|
from: "users",
|
|
localField: "assignedTo",
|
|
foreignField: "_id",
|
|
as: "assignedTo",
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 1,
|
|
pid: 1,
|
|
permitId: 1,
|
|
tenantId: 1,
|
|
permitNumber: 1,
|
|
link: 1,
|
|
status: 1,
|
|
permitType: 1,
|
|
accelaStatus: 1,
|
|
changes: 1,
|
|
county: 1,
|
|
client: 1,
|
|
clientData: 1,
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
taggedUsers: 1,
|
|
taggedOrgs: 1,
|
|
assignedToOrg: 1,
|
|
assignedTo: {
|
|
$map: {
|
|
input: "$assignedTo",
|
|
as: "user",
|
|
in: {
|
|
_id: "$$user._id",
|
|
pid: "$$user.pid",
|
|
name: "$$user.name",
|
|
avatar: "$$user.avatar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$facet: {
|
|
metadata: [{ $count: "count" }],
|
|
data: [
|
|
{ $sort: sortObj },
|
|
{ $skip: (page - 1) * pageSize },
|
|
{ $limit: pageSize },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
);
|
|
|
|
const notifications = await notificationModel.aggregate(pipeline);
|
|
|
|
if (notifications[0].data.length === 0)
|
|
return { notifications: [], metadata: { count: 0, page, pageSize } };
|
|
|
|
return {
|
|
notifications: notifications[0].data,
|
|
metadata: {
|
|
count: notifications[0].metadata[0].count,
|
|
page,
|
|
pageSize,
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function deleteNotification(notifId: string, tenantId: string) {
|
|
return await notificationModel.deleteOne({
|
|
$and: [{ tenantId: tenantId }, { pid: notifId }],
|
|
});
|
|
}
|