import { createAlert } from "../alert/alert.service"; import { AuthenticatedUser } from "../auth"; import { createNote } from "../note/note.service"; import { getFilterObject, getSortObject, PageQueryParams } from "../pagination"; import { generateId } from "../utils/id"; import { taskPipeline } from "../utils/pipeline"; import { CreateTaskInput, taskFields, taskModel, UpdateTaskInput, UploadTaskInput, } from "./task.schema"; export async function createTask( input: CreateTaskInput, user: AuthenticatedUser ) { if (!input.stage) { input.stage = { pipeline: taskPipeline, currentStage: 0, }; } const task = await taskModel.create({ ...input, tenantId: user.tenantId, pid: generateId(), createdAt: new Date(), createdBy: user.userId ?? null, }); if (input.note) { await createNote( { content: input.note, }, task.pid, "tasks", user ); } return await taskModel .findOne({ pid: task.pid }) .populate({ path: "createdBy", select: "pid name avatar" }) .populate({ path: "assignedTo", select: "pid name avatar" }); } export async function updateTask( taskId: string, input: UpdateTaskInput, user: AuthenticatedUser ) { const updatedTask = await taskModel .findOneAndUpdate({ tenantId: user.tenantId, pid: taskId }, input, { new: true, }) .populate({ path: "createdBy", select: "pid name avatar" }) .populate({ path: "assignedTo", select: "pid name avatar" }); if (updatedTask && input.assignedTo) { await createAlert( user.tenantId, `You are assigned to ${updatedTask.title}`, "user", input.assignedTo, updatedTask.pid, "tasks" ); } return updatedTask; } export async function getTask(taskId: string, tenantId: string) { return await taskModel .findOne({ tenantId: tenantId, pid: taskId }) .populate({ path: "createdBy", select: "pid name avatar" }) .populate({ path: "assignedTo", select: "pid name avatar" }); } export async function listTasks( params: PageQueryParams, user: AuthenticatedUser ) { const page = params.page || 1; const pageSize = params.pageSize || 10; const sortObj = getSortObject(params, taskFields); let filterObj = getFilterObject(params) || []; const taggedUserFilterIndex = filterObj.findIndex((item) => Object.keys(item).includes("taggedUsers") ); let taggedFilter = []; if (taggedUserFilterIndex != -1) { const filterItem = filterObj[taggedUserFilterIndex].taggedUsers; let filterValues = null; if (filterItem["$eq"]) filterValues = [filterItem["$eq"]]; else filterValues = filterItem["$in"]; filterValues = filterValues.map((item) => item.toString()); taggedFilter = [ { $addFields: { taggedUsers: { $filter: { input: "$taggedUsers", as: "user", cond: { $in: ["$$user.userId", filterValues] }, }, }, }, }, { $match: { "taggedUsers.0": { $exists: true } } }, { $sort: { "taggedUsers.taggedAt": sortObj.taggedUsers ?? -1 } }, ]; filterObj.splice(taggedUserFilterIndex, 1); } const taskList = await taskModel.aggregate([ { $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] }, }, ...taggedFilter, { $lookup: { from: "users", localField: "createdBy", foreignField: "_id", as: "createdBy", }, }, { $lookup: { from: "users", localField: "assignedTo", foreignField: "_id", as: "assignedTo", }, }, { $project: { _id: 1, pid: 1, title: 1, dueDate: 1, labels: 1, priority: 1, documents: 1, stage: 1, createdAt: 1, taggedUsers: 1, createdBy: { $let: { vars: { createdBy: { $arrayElemAt: ["$createdBy", 0] } }, in: { _id: "$$createdBy._id", pid: "$$createdBy.pid", name: "$$createdBy.name", }, }, }, assignedTo: { $let: { vars: { assignedTo: { $arrayElemAt: ["$assignedTo", 0] } }, in: { _id: "$$assignedTo._id", pid: "$$assignedTo.pid", name: "$$assignedTo.name", }, }, }, }, }, { $facet: { metadata: [{ $count: "count" }], data: [ { $sort: sortObj }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ], }, }, ]); if (taskList[0].data.length === 0) return { tasks: [], metadata: { count: 0, page, pageSize } }; return { tasks: taskList[0]?.data, metadata: { count: taskList[0].metadata[0].count, page, pageSize, }, }; } export async function deleteTask(taskId: string, tenantId: string) { return await taskModel.deleteOne({ pid: taskId, tenantId: tenantId }); } export async function searchTasks(params: PageQueryParams, tenantId: string) { const page = params.page || 1; const pageSize = params.pageSize || 10; const sortObj = getSortObject(params, taskFields); const filterObj = getFilterObject(params) || []; const regex = new RegExp(params.searchToken, "i"); const taskList = await taskModel.aggregate([ { $match: { $and: [{ tenantId: tenantId }, ...filterObj] }, }, { $match: { $or: [{ title: { $regex: regex } }], }, }, { $lookup: { from: "users", localField: "createdBy", foreignField: "_id", as: "createdBy", }, }, { $lookup: { from: "users", localField: "assignedTo", foreignField: "_id", as: "assignedTo", }, }, { $project: { _id: 1, pid: 1, title: 1, dueDate: 1, labels: 1, priority: 1, documents: 1, stage: 1, createdAt: 1, taggedUsers: 1, createdBy: { $let: { vars: { createdBy: { $arrayElemAt: ["$createdBy", 0] } }, in: { _id: "$$createdBy._id", pid: "$$createdBy.pid", name: "$$createdBy.name", }, }, }, assignedTo: { $let: { vars: { assignedTo: { $arrayElemAt: ["$assignedTo", 0] } }, in: { _id: "$$assignedTo._id", pid: "$$assignedTo.pid", name: "$$assignedTo.name", }, }, }, }, }, { $facet: { metadata: [{ $count: "count" }], data: [ { $sort: sortObj }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ], }, }, ]); if (taskList[0].data.length === 0) return { tasks: [], metadata: { count: 0, page, pageSize } }; return { tasks: taskList[0]?.data, metadata: { count: taskList[0].metadata[0].count, page, pageSize, }, }; } export async function newUpload( id: string, newUpload: UploadTaskInput, user: AuthenticatedUser ) { return await taskModel.findOneAndUpdate( { pid: id, tenantId: user.tenantId }, { $push: { documents: { files: newUpload.files, createdAt: new Date(), createdBy: user.userId, }, }, } ); }