Skip to content

Commit 55d7368

Browse files
committed
feat: issue actions
1 parent bb67cd4 commit 55d7368

File tree

5 files changed

+210
-30
lines changed

5 files changed

+210
-30
lines changed

apps/api/src/controllers/ticket.ts

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -638,16 +638,59 @@ export function ticketRoutes(fastify: FastifyInstance) {
638638
if (token) {
639639
const { hidden, id }: any = request.body;
640640

641-
await prisma.ticket
642-
.update({
643-
where: { id: id },
644-
data: {
645-
hidden: hidden,
646-
},
647-
})
648-
.then(async (ticket) => {
649-
// await sendTicketStatus(ticket);
650-
});
641+
await prisma.ticket.update({
642+
where: { id: id },
643+
data: {
644+
hidden: hidden,
645+
},
646+
});
647+
648+
reply.send({
649+
success: true,
650+
});
651+
}
652+
}
653+
);
654+
655+
// Lock a ticket
656+
fastify.put(
657+
"/api/v1/ticket/status/lock",
658+
659+
async (request: FastifyRequest, reply: FastifyReply) => {
660+
const bearer = request.headers.authorization!.split(" ")[1];
661+
const token = checkToken(bearer);
662+
663+
if (token) {
664+
const { locked, id }: any = request.body;
665+
666+
await prisma.ticket.update({
667+
where: { id: id },
668+
data: {
669+
locked: locked,
670+
},
671+
});
672+
673+
reply.send({
674+
success: true,
675+
});
676+
}
677+
}
678+
);
679+
680+
// Delete a ticket
681+
fastify.post(
682+
"/api/v1/ticket/delete",
683+
684+
async (request: FastifyRequest, reply: FastifyReply) => {
685+
const bearer = request.headers.authorization!.split(" ")[1];
686+
const token = checkToken(bearer);
687+
688+
if (token) {
689+
const { id }: any = request.body;
690+
691+
await prisma.ticket.delete({
692+
where: { id: id },
693+
});
651694

652695
reply.send({
653696
success: true,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Ticket" ADD COLUMN "locked" BOOLEAN NOT NULL DEFAULT false;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- DropForeignKey
2+
ALTER TABLE "Comment" DROP CONSTRAINT "Comment_ticketId_fkey";
3+
4+
-- AddForeignKey
5+
ALTER TABLE "Comment" ADD CONSTRAINT "Comment_ticketId_fkey" FOREIGN KEY ("ticketId") REFERENCES "Ticket"("id") ON DELETE CASCADE ON UPDATE CASCADE;

apps/api/src/prisma/schema.prisma

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ model Ticket {
120120
type TicketType @default(support)
121121
hidden Boolean @default(false)
122122
createdBy Json?
123+
locked Boolean @default(false)
123124
124125
TicketFile TicketFile[]
125126
Comment Comment[]
@@ -164,7 +165,7 @@ model Comment {
164165
userId String?
165166
user User? @relation(fields: [userId], references: [id])
166167
ticketId String
167-
ticket Ticket @relation(fields: [ticketId], references: [id])
168+
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
168169
}
169170

170171
model Client {

apps/client/components/TicketDetails/index.tsx

Lines changed: 148 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,31 @@ import { IconCombo, UserCombo } from "../Combo";
1818
import {
1919
CircleCheck,
2020
CircleDotDashed,
21+
Ellipsis,
22+
Eye,
23+
EyeClosed,
24+
EyeOff,
2125
LifeBuoy,
2226
Loader,
2327
LoaderCircle,
28+
Lock,
2429
SignalHigh,
2530
SignalLow,
2631
SignalMedium,
32+
Trash,
33+
Trash2,
34+
Unlock,
2735
} from "lucide-react";
2836
import { toast } from "@/shadcn/hooks/use-toast";
37+
import {
38+
DropdownMenu,
39+
DropdownMenuContent,
40+
DropdownMenuItem,
41+
DropdownMenuLabel,
42+
DropdownMenuSeparator,
43+
DropdownMenuTrigger,
44+
} from "@/shadcn/ui/dropdown-menu";
45+
import { IconKeyboardHide } from "@tabler/icons-react";
2946

3047
const ticketStatusMap = [
3148
{ id: 1, value: "needs_support", name: "Needs Support", icon: LifeBuoy },
@@ -171,6 +188,46 @@ export default function Ticket() {
171188
.then(() => refetch());
172189
}
173190

191+
async function lock(locked) {
192+
await fetch(`/api/v1/ticket/status/lock`, {
193+
method: "PUT",
194+
headers: {
195+
"Content-Type": "application/json",
196+
Authorization: `Bearer ${token}`,
197+
},
198+
body: JSON.stringify({
199+
locked,
200+
id,
201+
}),
202+
})
203+
.then((res) => res.json())
204+
.then(() => refetch());
205+
}
206+
207+
async function deleteIssue(locked) {
208+
await fetch(`/api/v1/ticket/delete`, {
209+
method: "POST",
210+
headers: {
211+
"Content-Type": "application/json",
212+
Authorization: `Bearer ${token}`,
213+
},
214+
body: JSON.stringify({
215+
id,
216+
}),
217+
})
218+
.then((res) => res.json())
219+
.then((res) => {
220+
if (res.success) {
221+
toast({
222+
variant: "default",
223+
title: "Issue Deleted",
224+
description: "The issue has been deleted",
225+
});
226+
router.push("/issues");
227+
}
228+
});
229+
}
230+
174231
async function addComment() {
175232
await fetch(`/api/v1/ticket/comment`, {
176233
method: "POST",
@@ -410,34 +467,106 @@ export default function Ticket() {
410467
key={data.ticket.id}
411468
/>
412469
</div>
413-
<div className="mt-2 text-xs flex flex-row items-center space-x-1 text-gray-500 dark:text-white">
414-
<div>
415-
{!data.ticket.isComplete ? (
416-
<div className="flex items-center space-x-2">
417-
<span className="inline-flex items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20">
418-
{t("open_issue")}
470+
<div className="mt-2 text-xs flex flex-row justify-between items-center space-x-1 text-gray-500 dark:text-white">
471+
<div className="flex flex-row space-x-1 items-center">
472+
<div>
473+
{!data.ticket.isComplete ? (
474+
<div className="flex items-center space-x-2">
475+
<span className="inline-flex items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20">
476+
{t("open_issue")}
477+
</span>
478+
</div>
479+
) : (
480+
<div className="flex items-center space-x-2">
481+
<span className="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10">
482+
{t("closed_issue")}
483+
</span>
484+
</div>
485+
)}
486+
</div>
487+
<div>
488+
<span className="inline-flex items-center rounded-md bg-orange-50 px-2 py-1 text-xs font-medium text-orange-700 ring-1 ring-inset ring-orange-600/20">
489+
{data.ticket.type}
490+
</span>
491+
</div>
492+
{data.ticket.hidden && (
493+
<div>
494+
<span className="inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-600/20">
495+
Hidden
419496
</span>
420497
</div>
421-
) : (
422-
<div className="flex items-center space-x-2">
423-
<span className="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10">
424-
{t("closed_issue")}
498+
)}
499+
{data.ticket.locked && (
500+
<div>
501+
<span className="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/20">
502+
Locked
425503
</span>
426504
</div>
427505
)}
428506
</div>
429-
<div>
430-
<span className="inline-flex items-center rounded-md bg-orange-50 px-2 py-1 text-xs font-medium text-orange-700 ring-1 ring-inset ring-orange-600/20">
431-
{data.ticket.type}
432-
</span>
433-
</div>
434-
{data.ticket.client && (
435-
<div>
507+
{user.isAdmin && (
508+
<DropdownMenu>
509+
<DropdownMenuTrigger className="inline-flex items-center px-2 py-1 text-xs font-medium text-foreground ring-none outline-none ">
510+
<Ellipsis className="h-4 w-4" />
511+
</DropdownMenuTrigger>
512+
<DropdownMenuContent
513+
align="end"
514+
className="min-w-[160px]"
515+
>
516+
<DropdownMenuLabel>
517+
<span>Issue Actions</span>
518+
</DropdownMenuLabel>
519+
<DropdownMenuSeparator />
520+
{data.ticket.hidden ? (
521+
<DropdownMenuItem
522+
className="flex flex-row space-x-3 items-center"
523+
onClick={() => hide(false)}
524+
>
525+
<Eye className="h-4 w-4" />
526+
<span>Show Issue</span>
527+
</DropdownMenuItem>
528+
) : (
529+
<DropdownMenuItem
530+
className="flex flex-row space-x-3 items-center"
531+
onClick={() => hide(true)}
532+
>
533+
<EyeOff className="h-4 w-4" />
534+
<span>Hide Issue</span>
535+
</DropdownMenuItem>
536+
)}
537+
{data.ticket.locked ? (
538+
<DropdownMenuItem
539+
className="flex flex-row space-x-3 items-center"
540+
onClick={() => lock(false)}
541+
>
542+
<Unlock className="h-4 w-4" />
543+
<span>Unlock Issue</span>
544+
</DropdownMenuItem>
545+
) : (
546+
<DropdownMenuItem
547+
className="flex flex-row space-x-3 items-center"
548+
onClick={() => lock(true)}
549+
>
550+
<Lock className="h-4 w-4" />
551+
<span>Lock Issue</span>
552+
</DropdownMenuItem>
553+
)}
554+
<DropdownMenuSeparator />
555+
<DropdownMenuItem
556+
className="flex flex-row space-x-3 items-center transition-colors duration-200 focus:bg-red-500 focus:text-white"
557+
onClick={() => deleteIssue()}
558+
>
559+
<Trash2 className="h-4 w-4" />
560+
<span className="">Delete Issue</span>
561+
</DropdownMenuItem>
562+
</DropdownMenuContent>
563+
</DropdownMenu>
564+
)}
565+
{/* <div>
436566
<span className="inline-flex items-center rounded-md bg-orange-50 px-2 py-1 text-xs font-medium text-orange-700 ring-1 ring-inset ring-orange-600/20">
437567
{data.ticket.client.name}
438568
</span>
439-
</div>
440-
)}
569+
</div> */}
441570
</div>
442571
</div>
443572
</div>

0 commit comments

Comments
 (0)