Skip to content

Commit 7f910d1

Browse files
authored
chore: add on hold status (#415)
* 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
1 parent 60c3137 commit 7f910d1

File tree

13 files changed

+562
-60
lines changed

13 files changed

+562
-60
lines changed

apps/api/src/controllers/ticket.ts

Lines changed: 232 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import { sendTicketCreate } from "../lib/nodemailer/ticket/create";
1111
import { sendTicketStatus } from "../lib/nodemailer/ticket/status";
1212
import { assignedNotification } from "../lib/notifications/issue/assigned";
1313
import { commentNotification } from "../lib/notifications/issue/comment";
14+
import { priorityNotification } from "../lib/notifications/issue/priority";
15+
import {
16+
activeStatusNotification,
17+
statusUpdateNotification,
18+
} from "../lib/notifications/issue/status";
1419
import { sendWebhookNotification } from "../lib/notifications/webhook";
1520
import { requirePermission } from "../lib/roles";
1621
import { checkSession } from "../lib/session";
@@ -43,6 +48,8 @@ export function ticketRoutes(fastify: FastifyInstance) {
4348
createdBy,
4449
}: any = request.body;
4550

51+
const user = await checkSession(request);
52+
4653
const ticket: any = await prisma.ticket.create({
4754
data: {
4855
name,
@@ -89,7 +96,109 @@ export function ticketRoutes(fastify: FastifyInstance) {
8996

9097
await sendAssignedEmail(assgined!.email);
9198

92-
await assignedNotification(engineer.id, ticket);
99+
await assignedNotification(engineer, ticket, user);
100+
}
101+
102+
const webhook = await prisma.webhooks.findMany({
103+
where: {
104+
type: "ticket_created",
105+
},
106+
});
107+
108+
for (let i = 0; i < webhook.length; i++) {
109+
if (webhook[i].active === true) {
110+
const message = {
111+
event: "ticket_created",
112+
id: ticket.id,
113+
title: ticket.title,
114+
priority: ticket.priority,
115+
email: ticket.email,
116+
name: ticket.name,
117+
type: ticket.type,
118+
createdBy: ticket.createdBy,
119+
assignedTo: ticket.assignedTo,
120+
client: ticket.client,
121+
};
122+
123+
await sendWebhookNotification(webhook[i], message);
124+
}
125+
}
126+
127+
const hog = track();
128+
129+
hog.capture({
130+
event: "ticket_created",
131+
distinctId: ticket.id,
132+
});
133+
134+
reply.status(200).send({
135+
message: "Ticket created correctly",
136+
success: true,
137+
id: ticket.id,
138+
});
139+
}
140+
);
141+
142+
fastify.post(
143+
"/api/v1/ticket/public/create",
144+
async (request: FastifyRequest, reply: FastifyReply) => {
145+
const {
146+
name,
147+
company,
148+
detail,
149+
title,
150+
priority,
151+
email,
152+
engineer,
153+
type,
154+
createdBy,
155+
}: any = request.body;
156+
157+
const ticket: any = await prisma.ticket.create({
158+
data: {
159+
name,
160+
title,
161+
detail: JSON.stringify(detail),
162+
priority: priority ? priority : "low",
163+
email,
164+
type: type ? type.toLowerCase() : "support",
165+
createdBy: createdBy
166+
? {
167+
id: createdBy.id,
168+
name: createdBy.name,
169+
role: createdBy.role,
170+
email: createdBy.email,
171+
}
172+
: undefined,
173+
client:
174+
company !== undefined
175+
? {
176+
connect: { id: company.id || company },
177+
}
178+
: undefined,
179+
fromImap: false,
180+
assignedTo:
181+
engineer && engineer.name !== "Unassigned"
182+
? {
183+
connect: { id: engineer.id },
184+
}
185+
: undefined,
186+
isComplete: Boolean(false),
187+
},
188+
});
189+
190+
if (!email && !validateEmail(email)) {
191+
await sendTicketCreate(ticket);
192+
}
193+
194+
if (engineer && engineer.name !== "Unassigned") {
195+
const assgined = await prisma.user.findUnique({
196+
where: {
197+
id: ticket.userId,
198+
},
199+
});
200+
201+
await sendAssignedEmail(assgined!.email);
93202
}
94203

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

194303
await sendAssignedEmail(assgined!.email);
195304

196-
await assignedNotification(engineer.id, ticket);
305+
const user = await checkSession(request);
306+
307+
await assignedNotification(engineer, ticket, user);
197308
}
198309

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

597+
const user = await checkSession(request);
598+
599+
const issue = await prisma.ticket.findUnique({
600+
where: { id: id },
601+
});
602+
486603
await prisma.ticket.update({
487604
where: { id: id },
488605
data: {
@@ -494,6 +611,14 @@ export function ticketRoutes(fastify: FastifyInstance) {
494611
},
495612
});
496613

614+
if (priority && issue!.priority !== priority) {
615+
await priorityNotification(issue, user, issue!.priority, priority);
616+
}
617+
618+
if (status && issue!.status !== status) {
619+
await statusUpdateNotification(issue, user, status);
620+
}
621+
497622
reply.send({
498623
success: true,
499624
});
@@ -509,6 +634,8 @@ export function ticketRoutes(fastify: FastifyInstance) {
509634
async (request: FastifyRequest, reply: FastifyReply) => {
510635
const { user, id }: any = request.body;
511636

637+
const assigner = await checkSession(request);
638+
512639
if (user) {
513640
const assigned = await prisma.user.update({
514641
where: { id: user },
@@ -523,7 +650,12 @@ export function ticketRoutes(fastify: FastifyInstance) {
523650

524651
const { email } = assigned;
525652

653+
const ticket = await prisma.ticket.findUnique({
654+
where: { id: id },
655+
});
656+
526657
await sendAssignedEmail(email);
658+
await assignedNotification(assigned, ticket, assigner);
527659
} else {
528660
await prisma.ticket.update({
529661
where: { id: id },
@@ -647,7 +779,7 @@ export function ticketRoutes(fastify: FastifyInstance) {
647779
sendComment(text, title, ticket!.id, email!);
648780
}
649781

650-
await commentNotification(user!.id, ticket, user!.name);
782+
await commentNotification(ticket, user);
651783

652784
const hog = track();
653785

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

826+
const user = await checkSession(request);
827+
694828
const ticket: any = await prisma.ticket.update({
695829
where: { id: id },
696830
data: {
697831
isComplete: status,
698832
},
699833
});
700834

835+
await activeStatusNotification(ticket, user, status);
836+
837+
await sendTicketStatus(ticket);
838+
701839
const webhook = await prisma.webhooks.findMany({
702840
where: {
703841
type: "ticket_status_changed",
@@ -738,8 +876,6 @@ export function ticketRoutes(fastify: FastifyInstance) {
738876
}
739877
}
740878

741-
sendTicketStatus(ticket);
742-
743879
reply.send({
744880
success: true,
745881
});
@@ -975,4 +1111,95 @@ export function ticketRoutes(fastify: FastifyInstance) {
9751111
});
9761112
}
9771113
);
1114+
1115+
// Subscribe to a ticket
1116+
fastify.get(
1117+
"/api/v1/ticket/subscribe/:id",
1118+
{
1119+
preHandler: requirePermission(["issue::read"]),
1120+
},
1121+
async (request: FastifyRequest, reply: FastifyReply) => {
1122+
const { id }: any = request.params;
1123+
1124+
const user = await checkSession(request);
1125+
1126+
if (id) {
1127+
const ticket = await prisma.ticket.findUnique({
1128+
where: { id: id },
1129+
});
1130+
1131+
const following = ticket?.following as string[];
1132+
1133+
if (following.includes(user!.id)) {
1134+
reply.send({
1135+
success: false,
1136+
message: "You are already following this issue",
1137+
});
1138+
}
1139+
1140+
if (ticket) {
1141+
await prisma.ticket.update({
1142+
where: { id: id },
1143+
data: {
1144+
following: [...following, user!.id],
1145+
},
1146+
});
1147+
} else {
1148+
reply.status(400).send({
1149+
success: false,
1150+
message: "No ticket ID provided",
1151+
});
1152+
}
1153+
1154+
reply.send({
1155+
success: true,
1156+
});
1157+
}
1158+
}
1159+
);
1160+
1161+
// Unsubscribe from a ticket
1162+
fastify.get(
1163+
"/api/v1/ticket/unsubscribe/:id",
1164+
{
1165+
preHandler: requirePermission(["issue::read"]),
1166+
},
1167+
async (request: FastifyRequest, reply: FastifyReply) => {
1168+
const { id }: any = request.params;
1169+
const user = await checkSession(request);
1170+
1171+
if (id) {
1172+
const ticket = await prisma.ticket.findUnique({
1173+
where: { id: id },
1174+
});
1175+
1176+
const following = ticket?.following as string[];
1177+
1178+
if (!following.includes(user!.id)) {
1179+
return reply.send({
1180+
success: false,
1181+
message: "You are not following this issue",
1182+
});
1183+
}
1184+
1185+
if (ticket) {
1186+
await prisma.ticket.update({
1187+
where: { id: id },
1188+
data: {
1189+
following: following.filter((userId) => userId !== user!.id),
1190+
},
1191+
});
1192+
} else {
1193+
return reply.status(400).send({
1194+
success: false,
1195+
message: "No ticket ID provided",
1196+
});
1197+
}
1198+
1199+
reply.send({
1200+
success: true,
1201+
});
1202+
}
1203+
}
1204+
);
9781205
}
Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,40 @@
11
import { prisma } from "../../../prisma";
22

3+
/**
4+
* Creates assignment notifications for all ticket followers.
5+
*
6+
* @param {object} ticket - The ticket object
7+
* @param {object} assignee - The user object being assigned
8+
* @param {object} assigner - The user object doing the assigning
9+
* @returns {Promise<void>}
10+
*/
311
export async function assignedNotification(
4-
userId: string,
5-
ticket: any
12+
assignee: any,
13+
ticket: any,
14+
assigner: any
615
) {
716
try {
8-
return await prisma.notifications.create({
9-
data: {
10-
text: `Assigned Ticket #${ticket.Number}`,
11-
userId,
12-
ticketId: ticket.id,
13-
},
17+
const text = `Ticket #${ticket.Number} was assigned to ${assignee.name} by ${assigner.name}`;
18+
19+
// Get all followers of the ticket, ensuring the creator is not already a follower
20+
const followers = [
21+
...(ticket.following || []),
22+
...(ticket.following?.includes(ticket.createdBy.id)
23+
? []
24+
: [ticket.createdBy.id]),
25+
];
26+
27+
// Create notifications for all followers (except the assigner)
28+
await prisma.notifications.createMany({
29+
data: followers
30+
.filter((userId: string) => userId !== assigner.id)
31+
.map((userId: string) => ({
32+
text,
33+
userId,
34+
ticketId: ticket.id,
35+
})),
1436
});
1537
} catch (error) {
16-
console.error("Error creating notification:", error);
38+
console.error("Error creating assignment notifications:", error);
1739
}
1840
}

0 commit comments

Comments
 (0)