Skip to content

Commit 664ba7b

Browse files
authored
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
1 parent 71ae71b commit 664ba7b

File tree

18 files changed

+970
-207
lines changed

18 files changed

+970
-207
lines changed

apps/api/src/controllers/ticket.ts

Lines changed: 262 additions & 6 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";
@@ -25,7 +30,6 @@ const validateEmail = (email: string) => {
2530
};
2631

2732
export function ticketRoutes(fastify: FastifyInstance) {
28-
// Create a new ticket - public endpoint, no preHandler needed
2933
fastify.post(
3034
"/api/v1/ticket/create",
3135
{
@@ -44,6 +48,8 @@ export function ticketRoutes(fastify: FastifyInstance) {
4448
createdBy,
4549
}: any = request.body;
4650

51+
const user = await checkSession(request);
52+
4753
const ticket: any = await prisma.ticket.create({
4854
data: {
4955
name,
@@ -90,7 +96,109 @@ export function ticketRoutes(fastify: FastifyInstance) {
9096

9197
await sendAssignedEmail(assgined!.email);
9298

93-
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);
94202
}
95203

96204
const webhook = await prisma.webhooks.findMany({
@@ -377,7 +485,14 @@ export function ticketRoutes(fastify: FastifyInstance) {
377485
preHandler: requirePermission(["issue::update"]),
378486
},
379487
async (request: FastifyRequest, reply: FastifyReply) => {
380-
const { id, note, detail, title, priority, status }: any = request.body;
488+
const { id, note, detail, title, priority, status, client }: any =
489+
request.body;
490+
491+
const user = await checkSession(request);
492+
493+
const issue = await prisma.ticket.findUnique({
494+
where: { id: id },
495+
});
381496

382497
await prisma.ticket.update({
383498
where: { id: id },
@@ -390,6 +505,14 @@ export function ticketRoutes(fastify: FastifyInstance) {
390505
},
391506
});
392507

508+
if (priority && issue!.priority !== priority) {
509+
await priorityNotification(issue, user, issue!.priority, priority);
510+
}
511+
512+
if (status && issue!.status !== status) {
513+
await statusUpdateNotification(issue, user, status);
514+
}
515+
393516
reply.send({
394517
success: true,
395518
});
@@ -405,6 +528,8 @@ export function ticketRoutes(fastify: FastifyInstance) {
405528
async (request: FastifyRequest, reply: FastifyReply) => {
406529
const { user, id }: any = request.body;
407530

531+
const assigner = await checkSession(request);
532+
408533
if (user) {
409534
const assigned = await prisma.user.update({
410535
where: { id: user },
@@ -419,7 +544,12 @@ export function ticketRoutes(fastify: FastifyInstance) {
419544

420545
const { email } = assigned;
421546

547+
const ticket = await prisma.ticket.findUnique({
548+
where: { id: id },
549+
});
550+
422551
await sendAssignedEmail(email);
552+
await assignedNotification(assigned, ticket, assigner);
423553
} else {
424554
await prisma.ticket.update({
425555
where: { id: id },
@@ -435,6 +565,37 @@ export function ticketRoutes(fastify: FastifyInstance) {
435565
}
436566
);
437567

568+
// Transfer an Issue to another client
569+
fastify.post(
570+
"/api/v1/ticket/transfer/client",
571+
{
572+
preHandler: requirePermission(["issue::transfer"]),
573+
},
574+
async (request: FastifyRequest, reply: FastifyReply) => {
575+
const { client, id }: any = request.body;
576+
577+
if (client) {
578+
await prisma.ticket.update({
579+
where: { id: id },
580+
data: {
581+
clientId: client,
582+
},
583+
});
584+
} else {
585+
await prisma.ticket.update({
586+
where: { id: id },
587+
data: {
588+
clientId: null,
589+
},
590+
});
591+
}
592+
593+
reply.send({
594+
success: true,
595+
});
596+
}
597+
);
598+
438599
// Link a ticket to another ticket
439600

440601
// fastify.post(
@@ -512,7 +673,7 @@ export function ticketRoutes(fastify: FastifyInstance) {
512673
sendComment(text, title, ticket!.id, email!);
513674
}
514675

515-
await commentNotification(user!.id, ticket, user!.name);
676+
await commentNotification(ticket, user);
516677

517678
const hog = track();
518679

@@ -556,13 +717,19 @@ export function ticketRoutes(fastify: FastifyInstance) {
556717
async (request: FastifyRequest, reply: FastifyReply) => {
557718
const { status, id }: any = request.body;
558719

720+
const user = await checkSession(request);
721+
559722
const ticket: any = await prisma.ticket.update({
560723
where: { id: id },
561724
data: {
562725
isComplete: status,
563726
},
564727
});
565728

729+
await activeStatusNotification(ticket, user, status);
730+
731+
await sendTicketStatus(ticket);
732+
566733
const webhook = await prisma.webhooks.findMany({
567734
where: {
568735
type: "ticket_status_changed",
@@ -603,8 +770,6 @@ export function ticketRoutes(fastify: FastifyInstance) {
603770
}
604771
}
605772

606-
sendTicketStatus(ticket);
607-
608773
reply.send({
609774
success: true,
610775
});
@@ -840,4 +1005,95 @@ export function ticketRoutes(fastify: FastifyInstance) {
8401005
});
8411006
}
8421007
);
1008+
1009+
// Subscribe to a ticket
1010+
fastify.get(
1011+
"/api/v1/ticket/subscribe/:id",
1012+
{
1013+
preHandler: requirePermission(["issue::read"]),
1014+
},
1015+
async (request: FastifyRequest, reply: FastifyReply) => {
1016+
const { id }: any = request.params;
1017+
1018+
const user = await checkSession(request);
1019+
1020+
if (id) {
1021+
const ticket = await prisma.ticket.findUnique({
1022+
where: { id: id },
1023+
});
1024+
1025+
const following = ticket?.following as string[];
1026+
1027+
if (following.includes(user!.id)) {
1028+
reply.send({
1029+
success: false,
1030+
message: "You are already following this issue",
1031+
});
1032+
}
1033+
1034+
if (ticket) {
1035+
await prisma.ticket.update({
1036+
where: { id: id },
1037+
data: {
1038+
following: [...following, user!.id],
1039+
},
1040+
});
1041+
} else {
1042+
reply.status(400).send({
1043+
success: false,
1044+
message: "No ticket ID provided",
1045+
});
1046+
}
1047+
1048+
reply.send({
1049+
success: true,
1050+
});
1051+
}
1052+
}
1053+
);
1054+
1055+
// Unsubscribe from a ticket
1056+
fastify.get(
1057+
"/api/v1/ticket/unsubscribe/:id",
1058+
{
1059+
preHandler: requirePermission(["issue::read"]),
1060+
},
1061+
async (request: FastifyRequest, reply: FastifyReply) => {
1062+
const { id }: any = request.params;
1063+
const user = await checkSession(request);
1064+
1065+
if (id) {
1066+
const ticket = await prisma.ticket.findUnique({
1067+
where: { id: id },
1068+
});
1069+
1070+
const following = ticket?.following as string[];
1071+
1072+
if (!following.includes(user!.id)) {
1073+
return reply.send({
1074+
success: false,
1075+
message: "You are not following this issue",
1076+
});
1077+
}
1078+
1079+
if (ticket) {
1080+
await prisma.ticket.update({
1081+
where: { id: id },
1082+
data: {
1083+
following: following.filter((userId) => userId !== user!.id),
1084+
},
1085+
});
1086+
} else {
1087+
return reply.status(400).send({
1088+
success: false,
1089+
message: "No ticket ID provided",
1090+
});
1091+
}
1092+
1093+
reply.send({
1094+
success: true,
1095+
});
1096+
}
1097+
}
1098+
);
8431099
}

0 commit comments

Comments
 (0)