Skip to content

Commit

Permalink
feat(Support): auto-close inactive tickets
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeaturner committed May 31, 2024
1 parent 8fd3005 commit b02a94e
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 12 deletions.
35 changes: 35 additions & 0 deletions client/src/components/support/TicketAutoCloseWarning.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Button, Icon } from "semantic-ui-react";
import { SupportTicket } from "../../types";
import { format as formatDate } from "date-fns";

interface TicketAutoCloseWarningProps {
ticket: SupportTicket;
onDisableAutoClose: () => void;
}

const TicketAutoCloseWarning: React.FC<TicketAutoCloseWarningProps> = ({
ticket,
onDisableAutoClose,
}) => {
return (
<div className="flex flex-col border rounded-md p-4 shadow-md bg-white mb-4">
<div className="flex flex-row items-center mb-2">
<Icon name="warning sign" color="yellow" size="big" />
<p className="text-lg ml-2">
<strong>Notice: </strong>
Ticket will be automatically closed on{" "}
{formatDate(
new Date(ticket.autoCloseDate?.toString() ?? ""),
"MM/dd/yyyy hh:mm aa"
)}{" "}
if no new comments are added.
</p>
</div>
<Button color="blue" onClick={onDisableAutoClose}>
Disable Auto Close
</Button>
</div>
);
};

export default TicketAutoCloseWarning;
19 changes: 19 additions & 0 deletions client/src/screens/conductor/support/Ticket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ConfirmDeleteTicketModal from "../../../components/support/ConfirmDeleteT
import api from "../../../api";
import { capitalizeFirstLetter } from "../../../components/util/HelperFunctions";
import { ca, hi } from "date-fns/locale";
import TicketAutoCloseWarning from "../../../components/support/TicketAutoCloseWarning";
const AssignTicketModal = lazy(
() => import("../../../components/support/AssignTicketModal")
);
Expand Down Expand Up @@ -86,6 +87,13 @@ const SupportTicketView = () => {
},
});

const disableAutoCloseMutation = useMutation({
mutationFn: () => updateTicket({ autoCloseSilenced: true }),
onSuccess: () => {
queryClient.invalidateQueries(["ticket", id]);
},
});

const deleteTicketMutation = useMutation({
mutationFn: () => deleteTicket(),
onSuccess: () => {
Expand Down Expand Up @@ -130,9 +138,11 @@ const SupportTicketView = () => {
async function updateTicket({
status,
priority,
autoCloseSilenced,
}: {
status?: "open" | "in_progress" | "closed";
priority?: "high" | "medium" | "low";
autoCloseSilenced?: boolean;
}) {
try {
if (typeof id !== "string" || !id) {
Expand All @@ -144,6 +154,7 @@ const SupportTicketView = () => {
...ticket,
...(status && { status }),
...(priority && { priority: priority.toLowerCase() }),
...(autoCloseSilenced && { autoCloseSilenced }),
});

if (res.data.err) {
Expand Down Expand Up @@ -293,6 +304,14 @@ const SupportTicketView = () => {
</div>
<div className="flex flex-col xl:flex-row-reverse w-full mt-4">
<div className="flex flex-col xl:basis-2/5 xl:pl-4">
{ticket?.autoCloseTriggered && (
<TicketAutoCloseWarning
ticket={ticket}
onDisableAutoClose={() =>
disableAutoCloseMutation.mutateAsync()
}
/>
)}
<TicketDetails ticket={ticket} />
<div className="mt-4">
<TicketFeed ticket={ticket} />
Expand Down
7 changes: 7 additions & 0 deletions client/src/screens/conductor/support/closed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ const SupportDashboard = () => {
<Table.Cell>
{capitalizeFirstLetter(ticket.priority)}
</Table.Cell>
<Table.Cell>
{ticket.timeClosed &&
format(
parseISO(ticket.timeClosed),
"MM/dd/yyyy hh:mm aa"
)}
</Table.Cell>
<Table.Cell>
<Button
color="blue"
Expand Down
3 changes: 3 additions & 0 deletions client/src/types/support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export type SupportTicket = {
timeOpened: string;
timeClosed?: string;
feed: SupportTicketFeedEntry[];
autoCloseTriggered?: boolean;
autoCloseDate?: string;
autoCloseSilenced?: boolean;
};

export type SupportTicketMessage = {
Expand Down
10 changes: 10 additions & 0 deletions server/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1833,4 +1833,14 @@ router.route('/support/ticket').post(
supportAPI.createTicket
)

router.route('/support/init-autoclose').post(
middleware.checkEventBridgeAPIKey,
supportAPI.findTicketsToInitAutoClose
)

router.route('/support/run-autoclose').post(
middleware.checkEventBridgeAPIKey,
supportAPI.autoCloseTickets
)

export default router;
5 changes: 0 additions & 5 deletions server/api/kb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,11 +423,6 @@ async function searchKB(req: z.infer<typeof SearchKBValidator>, res: Response) {
},
]).limit(10);

// Leave this here for debugging purposes
console.log('[SYSTEM] Search query: ', query)
console.log('[SYSTEM] Search results: ')
console.log(pages)

return res.send({
err: false,
pages,
Expand Down
27 changes: 27 additions & 0 deletions server/api/mail.js
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,32 @@ const sendNewInternalTicketMessageAssignedStaffNotification = (recipientAddresse
});
};

/**
* Sends a warning that a ticket will be closed in 3 days if no response is received.
* @param {string[]} recipientAddresses - the email addresses to send the notification to
* @param {string} ticketID - the ticket's uuid
* @param {string} subject - the ticket's subject
* @param {string} params - any URL parameters to append to the ticket URL
*/
const sendSupportTicketAutoCloseWarning = (recipientAddresses, ticketID, subject, params) => {
return mailgun.messages.create(process.env.MAILGUN_DOMAIN, {
from: 'LibreTexts Support <[email protected]>',
to: recipientAddresses,
subject: `Ticket Will Automatically Close Soon (ID #${ticketID.slice(-7)})`,
html: `
<p>Hi,</p>
<p>We're writing to let you know that a support ticket you have subscribed to will automatically close for inactivity in three days from this notification: "${subject}"</p>
<br />
<p>If you would like to keep the ticket open, please respond to the ticket at <a href="https://commons.libretexts.org/support/ticket/${ticketID}${params ? `?${params}` : ''}" target="_blank" rel="noopener noreferrer">https://commons.libretexts.org/support/ticket/${ticketID}${params ? `?${params}` : ''}</a>.</p>
<br />
<p>Otherwise, no action is required.
<p>Sincerely,</p>
<p>The LibreTexts team</p>
${autoGenNoticeHTML}
`,
});
};

const sendZIPFileReadyNotification = (url, recipientAddress) => {
return mailgun.messages.create(process.env.MAILGUN_DOMAIN, {
from: 'LibreTexts Support <[email protected]>',
Expand Down Expand Up @@ -980,5 +1006,6 @@ export default {
sendNewTicketMessageAssignedStaffNotification,
sendNewInternalTicketMessageAssignedStaffNotification,
sendSupportTicketAssignedNotification,
sendSupportTicketAutoCloseWarning,
sendZIPFileReadyNotification
}
Loading

0 comments on commit b02a94e

Please sign in to comment.