Skip to content

Commit 0301576

Browse files
committed
patch: issue deletion
1 parent a3ef4ec commit 0301576

File tree

7 files changed

+280
-125
lines changed

7 files changed

+280
-125
lines changed

apps/api/src/controllers/ticket.ts

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const validateEmail = (email: string) => {
2525
};
2626

2727
export function ticketRoutes(fastify: FastifyInstance) {
28-
// Create a new ticket - public endpoint, no preHandler needed
2928
fastify.post(
3029
"/api/v1/ticket/create",
3130
{
@@ -133,6 +132,110 @@ export function ticketRoutes(fastify: FastifyInstance) {
133132
}
134133
);
135134

135+
fastify.post(
136+
"/api/v1/ticket/public/create",
137+
async (request: FastifyRequest, reply: FastifyReply) => {
138+
const {
139+
name,
140+
company,
141+
detail,
142+
title,
143+
priority,
144+
email,
145+
engineer,
146+
type,
147+
createdBy,
148+
}: any = request.body;
149+
150+
const ticket: any = await prisma.ticket.create({
151+
data: {
152+
name,
153+
title,
154+
detail: JSON.stringify(detail),
155+
priority: priority ? priority : "low",
156+
email,
157+
type: type ? type.toLowerCase() : "support",
158+
createdBy: createdBy
159+
? {
160+
id: createdBy.id,
161+
name: createdBy.name,
162+
role: createdBy.role,
163+
email: createdBy.email,
164+
}
165+
: undefined,
166+
client:
167+
company !== undefined
168+
? {
169+
connect: { id: company.id || company },
170+
}
171+
: undefined,
172+
fromImap: false,
173+
assignedTo:
174+
engineer && engineer.name !== "Unassigned"
175+
? {
176+
connect: { id: engineer.id },
177+
}
178+
: undefined,
179+
isComplete: Boolean(false),
180+
},
181+
});
182+
183+
if (!email && !validateEmail(email)) {
184+
await sendTicketCreate(ticket);
185+
}
186+
187+
if (engineer && engineer.name !== "Unassigned") {
188+
const assgined = await prisma.user.findUnique({
189+
where: {
190+
id: ticket.userId,
191+
},
192+
});
193+
194+
await sendAssignedEmail(assgined!.email);
195+
196+
await assignedNotification(engineer.id, ticket);
197+
}
198+
199+
const webhook = await prisma.webhooks.findMany({
200+
where: {
201+
type: "ticket_created",
202+
},
203+
});
204+
205+
for (let i = 0; i < webhook.length; i++) {
206+
if (webhook[i].active === true) {
207+
const message = {
208+
event: "ticket_created",
209+
id: ticket.id,
210+
title: ticket.title,
211+
priority: ticket.priority,
212+
email: ticket.email,
213+
name: ticket.name,
214+
type: ticket.type,
215+
createdBy: ticket.createdBy,
216+
assignedTo: ticket.assignedTo,
217+
client: ticket.client,
218+
};
219+
220+
await sendWebhookNotification(webhook[i], message);
221+
}
222+
}
223+
224+
const hog = track();
225+
226+
hog.capture({
227+
event: "ticket_created",
228+
distinctId: ticket.id,
229+
});
230+
231+
reply.status(200).send({
232+
message: "Ticket created correctly",
233+
success: true,
234+
id: ticket.id,
235+
});
236+
}
237+
);
238+
136239
// Get a ticket by id - requires auth
137240
fastify.get(
138241
"/api/v1/ticket/:id",

apps/api/src/main.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,39 @@ server.register(multer.contentParser);
3636

3737
registerRoutes(server);
3838

39-
server.get("/", {
40-
schema: {
41-
tags: ['health'], // This groups the endpoint under a category
42-
description: 'Health check endpoint',
43-
response: {
44-
200: {
45-
type: 'object',
46-
properties: {
47-
healthy: { type: 'boolean' }
48-
}
49-
}
50-
}
39+
server.get(
40+
"/",
41+
{
42+
schema: {
43+
tags: ["health"], // This groups the endpoint under a category
44+
description: "Health check endpoint",
45+
response: {
46+
200: {
47+
type: "object",
48+
properties: {
49+
healthy: { type: "boolean" },
50+
},
51+
},
52+
},
53+
},
54+
},
55+
async function (request, response) {
56+
response.send({ healthy: true });
5157
}
52-
}, async function (request, response) {
53-
response.send({ healthy: true });
54-
});
58+
);
5559

5660
// JWT authentication hook
5761
server.addHook("preHandler", async function (request: any, reply: any) {
5862
try {
5963
if (request.url === "/api/v1/auth/login" && request.method === "POST") {
6064
return true;
6165
}
66+
if (
67+
request.url === "/api/v1/ticket/public/create" &&
68+
request.method === "POST"
69+
) {
70+
return true;
71+
}
6272
const bearer = request.headers.authorization!.split(" ")[1];
6373
checkToken(bearer);
6474
} catch (err) {

apps/client/components/TicketDetails/index.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,17 +1319,14 @@ export default function Ticket() {
13191319

13201320
<ContextMenuSeparator />
13211321

1322-
<ContextMenuItem
1323-
className="text-red-600"
1324-
onClick={(e) => {
1325-
e.preventDefault();
1326-
if (confirm("Are you sure you want to delete this ticket?")) {
1327-
deleteIssue(data.ticket.id);
1328-
}
1329-
}}
1330-
>
1331-
Delete Ticket
1332-
</ContextMenuItem>
1322+
{user.isAdmin && (
1323+
<ContextMenuItem
1324+
className="text-red-600"
1325+
onClick={(e) => deleteIssue(data.ticket.id)}
1326+
>
1327+
Delete Ticket
1328+
</ContextMenuItem>
1329+
)}
13331330
</ContextMenuContent>
13341331
</ContextMenu>
13351332
)}

apps/client/pages/issues/closed.tsx

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -84,29 +84,38 @@ export default function Tickets() {
8484

8585
const [filterSelected, setFilterSelected] = useState();
8686
const [selectedPriorities, setSelectedPriorities] = useState<string[]>(() => {
87-
const saved = localStorage.getItem('closed_selectedPriorities');
87+
const saved = localStorage.getItem("closed_selectedPriorities");
8888
return saved ? JSON.parse(saved) : [];
8989
});
9090
const [selectedStatuses, setSelectedStatuses] = useState<string[]>(() => {
91-
const saved = localStorage.getItem('closed_selectedStatuses');
91+
const saved = localStorage.getItem("closed_selectedStatuses");
9292
return saved ? JSON.parse(saved) : [];
9393
});
9494
const [selectedAssignees, setSelectedAssignees] = useState<string[]>(() => {
95-
const saved = localStorage.getItem('closed_selectedAssignees');
95+
const saved = localStorage.getItem("closed_selectedAssignees");
9696
return saved ? JSON.parse(saved) : [];
9797
});
9898
const [users, setUsers] = useState<any[]>([]);
9999

100100
useEffect(() => {
101-
localStorage.setItem('closed_selectedPriorities', JSON.stringify(selectedPriorities));
101+
localStorage.setItem(
102+
"closed_selectedPriorities",
103+
JSON.stringify(selectedPriorities)
104+
);
102105
}, [selectedPriorities]);
103106

104107
useEffect(() => {
105-
localStorage.setItem('closed_selectedStatuses', JSON.stringify(selectedStatuses));
108+
localStorage.setItem(
109+
"closed_selectedStatuses",
110+
JSON.stringify(selectedStatuses)
111+
);
106112
}, [selectedStatuses]);
107113

108114
useEffect(() => {
109-
localStorage.setItem('closed_selectedAssignees', JSON.stringify(selectedAssignees));
115+
localStorage.setItem(
116+
"closed_selectedAssignees",
117+
JSON.stringify(selectedAssignees)
118+
);
110119
}, [selectedAssignees]);
111120

112121
const handlePriorityToggle = (priority: string) => {
@@ -715,30 +724,36 @@ export default function Tickets() {
715724
Share Link
716725
</ContextMenuItem>
717726

718-
<ContextMenuSeparator />
719-
720-
<ContextMenuItem
721-
className="text-red-600"
722-
onClick={(e) => {
723-
e.preventDefault();
724-
if (
725-
confirm(
726-
"Are you sure you want to delete this ticket?"
727-
)
728-
) {
729-
fetch(`/api/v1/ticket/delete`, {
730-
method: "POST",
731-
headers: {
732-
Authorization: `Bearer ${token}`,
733-
"Content-Type": "application/json",
734-
},
735-
body: JSON.stringify({ id: ticket.id }),
736-
});
737-
}
738-
}}
739-
>
740-
Delete Ticket
741-
</ContextMenuItem>
727+
{user.isAdmin && (
728+
<>
729+
<ContextMenuSeparator />
730+
731+
<ContextMenuItem
732+
className="text-red-600"
733+
onClick={(e) => {
734+
e.preventDefault();
735+
if (
736+
confirm(
737+
"Are you sure you want to delete this ticket?"
738+
)
739+
) {
740+
fetch(`/api/v1/ticket/delete`, {
741+
method: "POST",
742+
headers: {
743+
Authorization: `Bearer ${token}`,
744+
"Content-Type": "application/json",
745+
},
746+
body: JSON.stringify({ id: ticket.id }),
747+
}).then(() => {
748+
refetch();
749+
});
750+
}
751+
}}
752+
>
753+
Delete Ticket
754+
</ContextMenuItem>
755+
</>
756+
)}
742757
</ContextMenuContent>
743758
</ContextMenu>
744759
);
@@ -749,7 +764,7 @@ export default function Tickets() {
749764
type="button"
750765
className="relative block w-[400px] rounded-lg border-2 border-dashed border-gray-300 p-12 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
751766
onClick={() => {
752-
const event = new KeyboardEvent('keydown', { key: 'c' });
767+
const event = new KeyboardEvent("keydown", { key: "c" });
753768
document.dispatchEvent(event);
754769
}}
755770
>

0 commit comments

Comments
 (0)