Skip to content

Commit 44f5dd9

Browse files
authored
Take over (#117)
Implement ability for staff to take over tickets
1 parent f79680c commit 44f5dd9

File tree

3 files changed

+75
-16
lines changed

3 files changed

+75
-16
lines changed

src/components/queue/TicketCard.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from 'react';
22
import Router from 'next/router';
3-
import { Box, Button, useColorModeValue, Text, Divider, Tag, Flex, Spinner } from '@chakra-ui/react';
3+
import { Box, Button, useColorModeValue, Text, Divider, Tag, Flex, Spinner, useToast } from '@chakra-ui/react';
44
import { TicketStatus, UserRole } from '@prisma/client';
55
import { TicketWithNames } from '../../server/trpc/router/ticket';
66
import { trpc } from '../../utils/trpc';
@@ -24,6 +24,7 @@ const TicketCard = (props: TicketCardProps) => {
2424
const { ticket, userRole, userId, idx } = props;
2525

2626
const context = trpc.useContext();
27+
const toast = useToast();
2728
const [areButtonsLoading, setAreButtonsLoading] = useState(false);
2829
const [areButtonsDisabled, setAreButtonsDisabled] = useState(false);
2930

@@ -77,8 +78,19 @@ const TicketCard = (props: TicketCardProps) => {
7778

7879
const handleHelpTicket = async () => {
7980
onClickWrapper(async () => {
80-
await assignTicketsMutation.mutateAsync({ ticketIds: [ticket.id] });
81-
Router.push(getTicketUrl(ticket.id));
81+
const res = await assignTicketsMutation.mutateAsync({ ticketIds: [ticket.id] });
82+
if (res.length > 0) {
83+
Router.push(getTicketUrl(ticket.id));
84+
} else {
85+
toast({
86+
title: 'Ticket already assigned',
87+
description: 'This ticket is already assigned to a staff member.',
88+
status: 'error',
89+
duration: 5000,
90+
isClosable: true,
91+
position: 'top-right',
92+
});
93+
}
8294
})();
8395
};
8496

@@ -132,13 +144,11 @@ const TicketCard = (props: TicketCardProps) => {
132144
ticketId: ticket.id,
133145
isPriority: !isPriority,
134146
});
135-
await requeueTicketsMutation.mutateAsync({
136-
ticketIds: [ticket.id]
147+
await requeueTicketsMutation.mutateAsync({
148+
ticketIds: [ticket.id],
137149
});
138-
}
139-
await onClickWrapper(() =>
140-
prioritizeAndRequeue(),
141-
)();
150+
};
151+
await onClickWrapper(() => prioritizeAndRequeue())();
142152
};
143153

144154
const handleCloseTicket = async () => {

src/components/ticket-page/TicketButtons.tsx

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const TicketButtons = (props: TicketCardProps) => {
3636
const markAsAbsentMutation = trpc.ticket.markAsAbsent.useMutation();
3737
const markAsPriorityMutation = trpc.ticket.markAsPriority.useMutation();
3838
const toggleIsPublicMutation = trpc.ticket.toggleIsPublic.useMutation();
39+
const takeOverTicketMutation = trpc.ticket.takeOverTicket.useMutation();
3940
const isPending = ticket.status === TicketStatus.PENDING;
4041
const isOpen = ticket.status === TicketStatus.OPEN;
4142
const isClosed = ticket.status === TicketStatus.CLOSED;
@@ -78,6 +79,12 @@ const TicketButtons = (props: TicketCardProps) => {
7879
})();
7980
};
8081

82+
const handleTakeOverTicket = async () => {
83+
onClickWrapper(async () => {
84+
await takeOverTicketMutation.mutateAsync({ ticketId: ticket.id });
85+
})();
86+
};
87+
8188
const handleReopenTicket = async () => {
8289
onClickWrapper(async () => {
8390
await reopenTicketsMutation.mutateAsync({ ticketIds: [ticket.id] });
@@ -132,13 +139,11 @@ const TicketButtons = (props: TicketCardProps) => {
132139
ticketId: ticket.id,
133140
isPriority: !isPriority,
134141
});
135-
await requeueTicketsMutation.mutateAsync({
136-
ticketIds: [ticket.id]
142+
await requeueTicketsMutation.mutateAsync({
143+
ticketIds: [ticket.id],
137144
});
138-
}
139-
await onClickWrapper(() =>
140-
prioritizeAndRequeue(),
141-
)();
145+
};
146+
await onClickWrapper(() => prioritizeAndRequeue())();
142147
};
143148

144149
const handleToggleIsPublic = async () => {
@@ -175,6 +180,18 @@ const TicketButtons = (props: TicketCardProps) => {
175180
>
176181
Help
177182
</Button>
183+
<Button
184+
title={areButtonsDisabled ? BUTTONS_DISABLED_WAIT_MSG : ''}
185+
disabled={areButtonsDisabled}
186+
isLoading={areButtonsLoading}
187+
m={4}
188+
mt={[1, 1, 1, 4]}
189+
onClick={handleTakeOverTicket}
190+
hidden={(!isStaff && !isIntern) || !isAssigned || ticket.helpedByUserId === userId}
191+
colorScheme='twitter'
192+
>
193+
Take Over
194+
</Button>
178195
<Button
179196
title={areButtonsDisabled ? BUTTONS_DISABLED_WAIT_MSG : ''}
180197
disabled={areButtonsDisabled}

src/server/trpc/router/ticket.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,42 @@ export const ticketRouter = router({
255255
await channel.publish('tickets-assigned', undefined);
256256

257257
// Uses ticket inner page channel
258-
for (const id of input.ticketIds) {
258+
for (const id of ticketsToAssign) {
259259
const channel = ably.channels.get(`ticket-${id}`);
260260
await channel.publish('ticket-assigned', undefined);
261261
}
262+
263+
return ticketsToAssign;
264+
}),
265+
266+
// Assigns a ticket to the current user
267+
takeOverTicket: protectedNotStudentProcedure
268+
.input(
269+
z.object({
270+
ticketId: z.number(),
271+
}),
272+
)
273+
.mutation(async ({ input, ctx }) => {
274+
await ctx.prisma.ticket.update({
275+
where: { id: input.ticketId },
276+
data: {
277+
status: TicketStatus.ASSIGNED,
278+
helpedAt: new Date(),
279+
helpedBy: {
280+
connect: {
281+
id: ctx.session?.user?.id,
282+
},
283+
},
284+
},
285+
});
286+
287+
const ably = new Ably.Rest(process.env.ABLY_SERVER_API_KEY!);
288+
const channel = ably.channels.get('tickets');
289+
channel.publish('tickets-assigned', undefined);
290+
291+
// Uses ticket inner page channel
292+
const ticketChannel = ably.channels.get(`ticket-${input.ticketId}`);
293+
ticketChannel.publish('ticket-assigned', undefined);
262294
}),
263295

264296
resolveTickets: protectedNotStudentProcedure

0 commit comments

Comments
 (0)