Skip to content

Commit

Permalink
chore: add on hold status (#415)
Browse files Browse the repository at this point in the history
* feat: follow an issue (#414)

* patch: issue deletion

* feat: update client

* feat: follow an issue

* feat: notifications when following

* feat: see who is subscribed to this issue

* patch: on hold

* patch: migratiom

* patch: fix notififaction
  • Loading branch information
potts99 authored Nov 16, 2024
1 parent 60c3137 commit 7f910d1
Show file tree
Hide file tree
Showing 13 changed files with 562 additions and 60 deletions.
237 changes: 232 additions & 5 deletions apps/api/src/controllers/ticket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import { sendTicketCreate } from "../lib/nodemailer/ticket/create";
import { sendTicketStatus } from "../lib/nodemailer/ticket/status";
import { assignedNotification } from "../lib/notifications/issue/assigned";
import { commentNotification } from "../lib/notifications/issue/comment";
import { priorityNotification } from "../lib/notifications/issue/priority";
import {
activeStatusNotification,
statusUpdateNotification,
} from "../lib/notifications/issue/status";
import { sendWebhookNotification } from "../lib/notifications/webhook";
import { requirePermission } from "../lib/roles";
import { checkSession } from "../lib/session";
Expand Down Expand Up @@ -43,6 +48,8 @@ export function ticketRoutes(fastify: FastifyInstance) {
createdBy,
}: any = request.body;

const user = await checkSession(request);

const ticket: any = await prisma.ticket.create({
data: {
name,
Expand Down Expand Up @@ -89,7 +96,109 @@ export function ticketRoutes(fastify: FastifyInstance) {

await sendAssignedEmail(assgined!.email);

await assignedNotification(engineer.id, ticket);
await assignedNotification(engineer, ticket, user);
}

const webhook = await prisma.webhooks.findMany({
where: {
type: "ticket_created",
},
});

for (let i = 0; i < webhook.length; i++) {
if (webhook[i].active === true) {
const message = {
event: "ticket_created",
id: ticket.id,
title: ticket.title,
priority: ticket.priority,
email: ticket.email,
name: ticket.name,
type: ticket.type,
createdBy: ticket.createdBy,
assignedTo: ticket.assignedTo,
client: ticket.client,
};

await sendWebhookNotification(webhook[i], message);
}
}

const hog = track();

hog.capture({
event: "ticket_created",
distinctId: ticket.id,
});

reply.status(200).send({
message: "Ticket created correctly",
success: true,
id: ticket.id,
});
}
);

fastify.post(
"/api/v1/ticket/public/create",
async (request: FastifyRequest, reply: FastifyReply) => {
const {
name,
company,
detail,
title,
priority,
email,
engineer,
type,
createdBy,
}: any = request.body;

const ticket: any = await prisma.ticket.create({
data: {
name,
title,
detail: JSON.stringify(detail),
priority: priority ? priority : "low",
email,
type: type ? type.toLowerCase() : "support",
createdBy: createdBy
? {
id: createdBy.id,
name: createdBy.name,
role: createdBy.role,
email: createdBy.email,
}
: undefined,
client:
company !== undefined
? {
connect: { id: company.id || company },
}
: undefined,
fromImap: false,
assignedTo:
engineer && engineer.name !== "Unassigned"
? {
connect: { id: engineer.id },
}
: undefined,
isComplete: Boolean(false),
},
});

if (!email && !validateEmail(email)) {
await sendTicketCreate(ticket);
}

if (engineer && engineer.name !== "Unassigned") {
const assgined = await prisma.user.findUnique({
where: {
id: ticket.userId,
},
});

await sendAssignedEmail(assgined!.email);
}

const webhook = await prisma.webhooks.findMany({
Expand Down Expand Up @@ -193,7 +302,9 @@ export function ticketRoutes(fastify: FastifyInstance) {

await sendAssignedEmail(assgined!.email);

await assignedNotification(engineer.id, ticket);
const user = await checkSession(request);

await assignedNotification(engineer, ticket, user);
}

const webhook = await prisma.webhooks.findMany({
Expand Down Expand Up @@ -483,6 +594,12 @@ export function ticketRoutes(fastify: FastifyInstance) {
const { id, note, detail, title, priority, status, client }: any =
request.body;

const user = await checkSession(request);

const issue = await prisma.ticket.findUnique({
where: { id: id },
});

await prisma.ticket.update({
where: { id: id },
data: {
Expand All @@ -494,6 +611,14 @@ export function ticketRoutes(fastify: FastifyInstance) {
},
});

if (priority && issue!.priority !== priority) {
await priorityNotification(issue, user, issue!.priority, priority);
}

if (status && issue!.status !== status) {
await statusUpdateNotification(issue, user, status);
}

reply.send({
success: true,
});
Expand All @@ -509,6 +634,8 @@ export function ticketRoutes(fastify: FastifyInstance) {
async (request: FastifyRequest, reply: FastifyReply) => {
const { user, id }: any = request.body;

const assigner = await checkSession(request);

if (user) {
const assigned = await prisma.user.update({
where: { id: user },
Expand All @@ -523,7 +650,12 @@ export function ticketRoutes(fastify: FastifyInstance) {

const { email } = assigned;

const ticket = await prisma.ticket.findUnique({
where: { id: id },
});

await sendAssignedEmail(email);
await assignedNotification(assigned, ticket, assigner);
} else {
await prisma.ticket.update({
where: { id: id },
Expand Down Expand Up @@ -647,7 +779,7 @@ export function ticketRoutes(fastify: FastifyInstance) {
sendComment(text, title, ticket!.id, email!);
}

await commentNotification(user!.id, ticket, user!.name);
await commentNotification(ticket, user);

const hog = track();

Expand Down Expand Up @@ -691,13 +823,19 @@ export function ticketRoutes(fastify: FastifyInstance) {
async (request: FastifyRequest, reply: FastifyReply) => {
const { status, id }: any = request.body;

const user = await checkSession(request);

const ticket: any = await prisma.ticket.update({
where: { id: id },
data: {
isComplete: status,
},
});

await activeStatusNotification(ticket, user, status);

await sendTicketStatus(ticket);

const webhook = await prisma.webhooks.findMany({
where: {
type: "ticket_status_changed",
Expand Down Expand Up @@ -738,8 +876,6 @@ export function ticketRoutes(fastify: FastifyInstance) {
}
}

sendTicketStatus(ticket);

reply.send({
success: true,
});
Expand Down Expand Up @@ -975,4 +1111,95 @@ export function ticketRoutes(fastify: FastifyInstance) {
});
}
);

// Subscribe to a ticket
fastify.get(
"/api/v1/ticket/subscribe/:id",
{
preHandler: requirePermission(["issue::read"]),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { id }: any = request.params;

const user = await checkSession(request);

if (id) {
const ticket = await prisma.ticket.findUnique({
where: { id: id },
});

const following = ticket?.following as string[];

if (following.includes(user!.id)) {
reply.send({
success: false,
message: "You are already following this issue",
});
}

if (ticket) {
await prisma.ticket.update({
where: { id: id },
data: {
following: [...following, user!.id],
},
});
} else {
reply.status(400).send({
success: false,
message: "No ticket ID provided",
});
}

reply.send({
success: true,
});
}
}
);

// Unsubscribe from a ticket
fastify.get(
"/api/v1/ticket/unsubscribe/:id",
{
preHandler: requirePermission(["issue::read"]),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { id }: any = request.params;
const user = await checkSession(request);

if (id) {
const ticket = await prisma.ticket.findUnique({
where: { id: id },
});

const following = ticket?.following as string[];

if (!following.includes(user!.id)) {
return reply.send({
success: false,
message: "You are not following this issue",
});
}

if (ticket) {
await prisma.ticket.update({
where: { id: id },
data: {
following: following.filter((userId) => userId !== user!.id),
},
});
} else {
return reply.status(400).send({
success: false,
message: "No ticket ID provided",
});
}

reply.send({
success: true,
});
}
}
);
}
40 changes: 31 additions & 9 deletions apps/api/src/lib/notifications/issue/assigned.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
import { prisma } from "../../../prisma";

/**
* Creates assignment notifications for all ticket followers.
*
* @param {object} ticket - The ticket object
* @param {object} assignee - The user object being assigned
* @param {object} assigner - The user object doing the assigning
* @returns {Promise<void>}
*/
export async function assignedNotification(
userId: string,
ticket: any
assignee: any,
ticket: any,
assigner: any
) {
try {
return await prisma.notifications.create({
data: {
text: `Assigned Ticket #${ticket.Number}`,
userId,
ticketId: ticket.id,
},
const text = `Ticket #${ticket.Number} was assigned to ${assignee.name} by ${assigner.name}`;

// Get all followers of the ticket, ensuring the creator is not already a follower
const followers = [
...(ticket.following || []),
...(ticket.following?.includes(ticket.createdBy.id)
? []
: [ticket.createdBy.id]),
];

// Create notifications for all followers (except the assigner)
await prisma.notifications.createMany({
data: followers
.filter((userId: string) => userId !== assigner.id)
.map((userId: string) => ({
text,
userId,
ticketId: ticket.id,
})),
});
} catch (error) {
console.error("Error creating notification:", error);
console.error("Error creating assignment notifications:", error);
}
}
Loading

0 comments on commit 7f910d1

Please sign in to comment.