2024-03-09 21:01:57 +00:00
|
|
|
import {
|
|
|
|
TicketMessage,
|
|
|
|
useSSESubscription,
|
|
|
|
useTicketMessages,
|
|
|
|
useTicketsFetcher,
|
|
|
|
} from "@frontend/kitui";
|
|
|
|
import FloatingSupportChat from "./FloatingSupportChat";
|
2024-03-06 11:13:28 +00:00
|
|
|
import { useUserStore } from "@root/user";
|
2024-04-25 06:37:48 +00:00
|
|
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
2024-03-06 11:13:28 +00:00
|
|
|
import { sendTicketMessage, shownMessage } from "../../api/ticket";
|
|
|
|
import { useSSETab } from "../../utils/hooks/useSSETab";
|
|
|
|
import {
|
|
|
|
addOrUpdateUnauthMessages,
|
|
|
|
cleanAuthTicketData,
|
2024-04-25 06:37:48 +00:00
|
|
|
cleanUnauthTicketData,
|
|
|
|
setIsMessageSending,
|
2024-03-06 11:13:28 +00:00
|
|
|
setTicketData,
|
2024-04-25 06:37:48 +00:00
|
|
|
setUnauthIsPreventAutoscroll,
|
|
|
|
setUnauthTicketMessageFetchState,
|
|
|
|
useTicketStore,
|
2024-03-06 11:13:28 +00:00
|
|
|
} from "@root/ticket";
|
|
|
|
import { enqueueSnackbar } from "notistack";
|
2024-05-15 12:37:42 +00:00
|
|
|
import { parseAxiosError } from "@utils/parse-error";
|
2024-05-15 11:44:10 +00:00
|
|
|
import { createTicket, sendFile as sendFileRequest } from "@api/ticket";
|
2024-03-06 11:13:28 +00:00
|
|
|
|
2024-03-09 21:01:57 +00:00
|
|
|
type ModalWarningType =
|
|
|
|
| "errorType"
|
|
|
|
| "errorSize"
|
|
|
|
| "picture"
|
|
|
|
| "video"
|
|
|
|
| "audio"
|
|
|
|
| "document"
|
|
|
|
| null;
|
|
|
|
const MAX_FILE_SIZE = 419430400;
|
|
|
|
const ACCEPT_SEND_FILE_TYPES_MAP = [
|
|
|
|
".jpeg",
|
|
|
|
".jpg",
|
|
|
|
".png",
|
|
|
|
".mp4",
|
|
|
|
".doc",
|
|
|
|
".docx",
|
|
|
|
".pdf",
|
|
|
|
".txt",
|
|
|
|
".xlsx",
|
|
|
|
".csv",
|
|
|
|
] as const;
|
|
|
|
|
2024-03-06 11:13:28 +00:00
|
|
|
export default () => {
|
|
|
|
const user = useUserStore((state) => state.user?._id);
|
2024-03-09 21:01:57 +00:00
|
|
|
const ticket = useTicketStore(
|
|
|
|
(state) => state[user ? "authData" : "unauthData"],
|
|
|
|
);
|
2024-03-06 11:13:28 +00:00
|
|
|
|
|
|
|
const { isActiveSSETab, updateSSEValue } = useSSETab<TicketMessage[]>(
|
|
|
|
"ticket",
|
|
|
|
addOrUpdateUnauthMessages,
|
|
|
|
);
|
|
|
|
|
2024-03-09 21:01:57 +00:00
|
|
|
const [modalWarningType, setModalWarningType] =
|
|
|
|
useState<ModalWarningType>(null);
|
2024-03-06 11:13:28 +00:00
|
|
|
const [isChatOpened, setIsChatOpened] = useState<boolean>(false);
|
2024-04-25 06:37:48 +00:00
|
|
|
const [sseEnabled, setSseEnabled] = useState(true);
|
|
|
|
|
2024-03-06 11:13:28 +00:00
|
|
|
const handleChatClickOpen = () => {
|
|
|
|
setIsChatOpened(true);
|
|
|
|
};
|
|
|
|
const handleChatClickClose = () => {
|
|
|
|
setIsChatOpened(false);
|
|
|
|
};
|
|
|
|
const handleChatClickSwitch = () => {
|
2024-03-09 21:01:57 +00:00
|
|
|
setIsChatOpened((state) => !state);
|
2024-03-06 11:13:28 +00:00
|
|
|
};
|
2024-04-25 06:37:48 +00:00
|
|
|
|
|
|
|
const getGreetingMessage: TicketMessage = useMemo(() => {
|
|
|
|
const workingHoursMessage =
|
|
|
|
"Здравствуйте, задайте ваш вопрос и наш оператор вам ответит в течение 10 минут";
|
|
|
|
const offHoursMessage =
|
|
|
|
"Здравствуйте, к сожалению, сейчас операторы не работают. Задайте ваш вопрос, и вам ответят в 10:00 по московскому времени";
|
|
|
|
const date = new Date();
|
|
|
|
const currentHourUTC = date.getUTCHours();
|
|
|
|
const MscTime = 3; // Москва UTC+3;
|
|
|
|
const moscowHour = (currentHourUTC + MscTime) % 24;
|
|
|
|
const greetingMessage =
|
2024-05-05 18:02:03 +00:00
|
|
|
moscowHour >= 3 && moscowHour < 10
|
|
|
|
? offHoursMessage
|
|
|
|
: workingHoursMessage;
|
2024-04-25 06:37:48 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
created_at: new Date().toISOString(),
|
|
|
|
files: [],
|
|
|
|
id: "111",
|
|
|
|
message: greetingMessage,
|
|
|
|
request_screenshot: "",
|
|
|
|
session_id: "greetingMessage",
|
|
|
|
shown: { me: 1 },
|
|
|
|
ticket_id: "111",
|
|
|
|
user_id: "greetingMessage",
|
|
|
|
};
|
|
|
|
}, [isChatOpened]);
|
|
|
|
|
2024-03-06 11:13:28 +00:00
|
|
|
useTicketsFetcher({
|
2024-06-02 23:29:40 +00:00
|
|
|
url: `${process.env.REACT_APP_DOMAIN}/heruvym/v1.0.0/getTickets`,
|
2024-03-06 11:13:28 +00:00
|
|
|
ticketsPerPage: 10,
|
|
|
|
ticketApiPage: 0,
|
|
|
|
onSuccess: (result) => {
|
2024-03-11 16:02:47 +00:00
|
|
|
if (result.data?.length) {
|
|
|
|
const currentTicket = result.data.find(
|
|
|
|
({ origin }) => !origin.includes("/support"),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!currentTicket) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-03-06 11:13:28 +00:00
|
|
|
setTicketData({
|
2024-03-11 16:02:47 +00:00
|
|
|
ticketId: currentTicket.id,
|
|
|
|
sessionId: currentTicket.sess,
|
2024-03-06 11:13:28 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onError: (error: Error) => {
|
2024-05-15 12:37:42 +00:00
|
|
|
const message = parseAxiosError(error);
|
2024-03-06 11:13:28 +00:00
|
|
|
if (message) enqueueSnackbar(message);
|
|
|
|
},
|
2024-05-13 13:24:41 +00:00
|
|
|
onFetchStateChange: () => {},
|
2024-03-09 21:01:57 +00:00
|
|
|
enabled: Boolean(user),
|
2024-03-06 11:13:28 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
useTicketMessages({
|
2024-06-02 23:29:40 +00:00
|
|
|
url: `${process.env.REACT_APP_DOMAIN}/heruvym/v1.0.0/getMessages`,
|
2024-03-06 11:13:28 +00:00
|
|
|
isUnauth: true,
|
|
|
|
ticketId: ticket.sessionData?.ticketId,
|
|
|
|
messagesPerPage: ticket.messagesPerPage,
|
|
|
|
messageApiPage: ticket.apiPage,
|
|
|
|
onSuccess: useCallback((messages) => {
|
|
|
|
addOrUpdateUnauthMessages(messages);
|
|
|
|
}, []),
|
|
|
|
onError: useCallback((error: Error) => {
|
2024-05-16 13:40:52 +00:00
|
|
|
if (error.name === "CanceledError") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-15 12:37:42 +00:00
|
|
|
const [message] = parseAxiosError(error);
|
2024-03-06 11:13:28 +00:00
|
|
|
if (message) enqueueSnackbar(message);
|
|
|
|
}, []),
|
|
|
|
onFetchStateChange: setUnauthTicketMessageFetchState,
|
|
|
|
});
|
|
|
|
|
|
|
|
useSSESubscription<TicketMessage>({
|
2024-04-25 06:37:48 +00:00
|
|
|
enabled:
|
|
|
|
sseEnabled && isActiveSSETab && Boolean(ticket.sessionData?.sessionId),
|
2024-06-02 23:29:40 +00:00
|
|
|
url: `${process.env.REACT_APP_DOMAIN}/heruvym/v1.0.0/ticket?ticket=${ticket.sessionData?.ticketId}&s=${ticket.sessionData?.sessionId}`,
|
2024-03-06 11:13:28 +00:00
|
|
|
onNewData: (ticketMessages) => {
|
2024-04-25 06:37:48 +00:00
|
|
|
const isTicketClosed = ticketMessages.some(
|
|
|
|
(message) => message.session_id === "close",
|
|
|
|
);
|
|
|
|
if (isTicketClosed) {
|
|
|
|
cleanAuthTicketData();
|
|
|
|
addOrUpdateUnauthMessages([getGreetingMessage]);
|
|
|
|
if (!user) {
|
|
|
|
cleanUnauthTicketData();
|
|
|
|
localStorage.removeItem("unauth-ticket");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2024-03-06 11:13:28 +00:00
|
|
|
updateSSEValue(ticketMessages);
|
|
|
|
addOrUpdateUnauthMessages(ticketMessages);
|
|
|
|
},
|
|
|
|
onDisconnect: useCallback(() => {
|
|
|
|
setUnauthIsPreventAutoscroll(false);
|
2024-04-25 06:37:48 +00:00
|
|
|
setSseEnabled(false);
|
2024-03-06 11:13:28 +00:00
|
|
|
}, []),
|
|
|
|
marker: "ticket",
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-03-09 21:01:57 +00:00
|
|
|
cleanAuthTicketData();
|
2024-04-25 06:37:48 +00:00
|
|
|
setSseEnabled(true);
|
2024-03-09 21:01:57 +00:00
|
|
|
}, [user]);
|
2024-03-06 11:13:28 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (isChatOpened) {
|
2024-03-09 21:01:57 +00:00
|
|
|
const newMessages = ticket.messages.filter(
|
|
|
|
({ shown }) => shown?.me !== 1,
|
|
|
|
);
|
2024-03-06 11:13:28 +00:00
|
|
|
|
|
|
|
newMessages.map(async ({ id }) => {
|
|
|
|
await shownMessage(id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}, [isChatOpened, ticket.messages]);
|
|
|
|
|
|
|
|
const sendMessage = async (messageField: string) => {
|
|
|
|
if (!messageField || ticket.isMessageSending) return false;
|
2024-04-25 06:37:48 +00:00
|
|
|
setSseEnabled(true);
|
2024-03-09 21:01:57 +00:00
|
|
|
let successful = false;
|
2024-03-06 11:13:28 +00:00
|
|
|
setIsMessageSending(true);
|
|
|
|
if (!ticket.sessionData?.ticketId) {
|
2024-05-15 11:44:10 +00:00
|
|
|
const [data, createError] = await createTicket(
|
|
|
|
messageField,
|
|
|
|
Boolean(user),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (createError || !data) {
|
2024-03-09 21:01:57 +00:00
|
|
|
successful = false;
|
2024-05-15 11:44:10 +00:00
|
|
|
|
|
|
|
enqueueSnackbar(createError);
|
|
|
|
} else {
|
|
|
|
successful = true;
|
|
|
|
|
|
|
|
setTicketData({ ticketId: data.Ticket, sessionId: data.sess });
|
2024-03-06 11:13:28 +00:00
|
|
|
}
|
2024-05-15 11:44:10 +00:00
|
|
|
|
2024-03-06 11:13:28 +00:00
|
|
|
setIsMessageSending(false);
|
|
|
|
} else {
|
|
|
|
const [_, sendTicketMessageError] = await sendTicketMessage(
|
|
|
|
ticket.sessionData?.ticketId,
|
|
|
|
messageField,
|
|
|
|
);
|
2024-03-09 21:01:57 +00:00
|
|
|
successful = true;
|
2024-03-06 11:13:28 +00:00
|
|
|
|
|
|
|
if (sendTicketMessageError) {
|
2024-03-09 21:01:57 +00:00
|
|
|
successful = false;
|
2024-03-06 11:13:28 +00:00
|
|
|
enqueueSnackbar(sendTicketMessageError);
|
|
|
|
}
|
|
|
|
setIsMessageSending(false);
|
|
|
|
}
|
|
|
|
|
2024-03-09 21:01:57 +00:00
|
|
|
return successful;
|
|
|
|
};
|
2024-03-06 11:13:28 +00:00
|
|
|
const sendFile = async (file: File) => {
|
2024-03-09 21:01:57 +00:00
|
|
|
if (file === undefined) return true;
|
|
|
|
|
2024-05-15 11:44:10 +00:00
|
|
|
let ticketId = ticket.sessionData?.ticketId;
|
2024-03-06 11:13:28 +00:00
|
|
|
if (!ticket.sessionData?.ticketId) {
|
2024-05-15 11:44:10 +00:00
|
|
|
const [data, createError] = await createTicket("", Boolean(user));
|
|
|
|
ticketId = data?.Ticket;
|
|
|
|
|
|
|
|
if (createError || !data) {
|
|
|
|
enqueueSnackbar(createError);
|
|
|
|
} else {
|
|
|
|
setTicketData({ ticketId: data.Ticket, sessionId: data.sess });
|
2024-03-06 11:13:28 +00:00
|
|
|
}
|
2024-05-15 11:44:10 +00:00
|
|
|
|
2024-03-06 11:13:28 +00:00
|
|
|
setIsMessageSending(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ticketId !== undefined) {
|
2024-03-09 21:01:57 +00:00
|
|
|
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
|
2024-03-06 11:13:28 +00:00
|
|
|
|
2024-05-15 11:44:10 +00:00
|
|
|
const [_, sendFileError] = await sendFileRequest(ticketId, file);
|
|
|
|
|
|
|
|
if (sendFileError) {
|
|
|
|
enqueueSnackbar(sendFileError);
|
2024-03-06 11:13:28 +00:00
|
|
|
}
|
2024-05-15 11:44:10 +00:00
|
|
|
|
2024-03-09 21:01:57 +00:00
|
|
|
return true;
|
2024-03-06 11:13:28 +00:00
|
|
|
}
|
2024-03-09 21:01:57 +00:00
|
|
|
};
|
2024-03-06 11:13:28 +00:00
|
|
|
|
2024-03-09 21:01:57 +00:00
|
|
|
return (
|
|
|
|
<FloatingSupportChat
|
|
|
|
isChatOpened={isChatOpened}
|
|
|
|
handleChatClickOpen={handleChatClickOpen}
|
|
|
|
handleChatClickClose={handleChatClickClose}
|
|
|
|
handleChatClickSwitch={handleChatClickSwitch}
|
|
|
|
sendMessage={sendMessage}
|
|
|
|
sendFile={sendFile}
|
|
|
|
modalWarningType={modalWarningType}
|
|
|
|
setModalWarningType={setModalWarningType}
|
2024-04-25 06:37:48 +00:00
|
|
|
greetingMessage={getGreetingMessage}
|
2024-03-09 21:01:57 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|