fix floating support chat
This commit is contained in:
parent
ac692fafd3
commit
2fecf6ca14
@ -6,7 +6,7 @@
|
||||
"@craco/craco": "^7.0.0",
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@frontend/kitui": "^1.0.110",
|
||||
"@frontend/kitui": "1.0.110",
|
||||
"@frontend/squzanswerer": "^1.0.57",
|
||||
"@mui/icons-material": "^5.10.14",
|
||||
"@mui/material": "^5.10.14",
|
||||
|
11
src/App.tsx
11
src/App.tsx
@ -1,4 +1,4 @@
|
||||
import { clearAuthToken, getMessageFromFetchError, handleComponentError, UserAccount, useTicketsFetcher, useUserFetcher } from "@frontend/kitui";
|
||||
import { clearAuthToken, createMakeRequestConfig, getMessageFromFetchError, handleComponentError, UserAccount, useTicketsFetcher, useUserFetcher } from "@frontend/kitui";
|
||||
import type { OriginalUserAccount } from "@root/user";
|
||||
import { clearUserData, setCustomerAccount, setUser, setUserAccount, useUserStore } from "@root/user";
|
||||
import ContactFormModal from "@ui_kit/ContactForm";
|
||||
@ -27,6 +27,7 @@ import Debug from "./pages/Debug";
|
||||
import { setTicketData, setTickets, useTicketStore } from "./stores/ticket";
|
||||
import { parseAxiosError } from "./utils/parse-error";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { handleLogoutClick } from "./utils/HandleLogoutClick";
|
||||
|
||||
const MyQuizzesFull = lazy(() => import("./pages/createQuize/MyQuizzesFull"));
|
||||
const QuizGallery = lazy(() => import("./pages/createQuize/QuizGallery"));
|
||||
@ -42,6 +43,12 @@ const PersonalizationAI = lazy(() => import("./pages/PersonalizationAI/Personali
|
||||
let params = new URLSearchParams(document.location.search);
|
||||
const isTest = Boolean(params.get("test"))
|
||||
|
||||
createMakeRequestConfig(
|
||||
handleLogoutClick,
|
||||
(error, info, getTickets) => handleComponentError(error, info, getTickets()),
|
||||
() => useTicketStore.getState().tickets
|
||||
);
|
||||
|
||||
const routeslink = [
|
||||
{
|
||||
path: "/edit",
|
||||
@ -185,7 +192,7 @@ export default function App() {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={ApologyPage}
|
||||
onError={(error, info) => handleComponentError(error, info, tickets)}
|
||||
onError={(error, info) => handleComponentError(error, info, () => useTicketStore.getState().tickets)}
|
||||
>
|
||||
{amoAccount && <AmoTokenExpiredDialog isAmoTokenExpired={amoAccount.stale} />}
|
||||
|
||||
|
@ -30,7 +30,18 @@ interface Props {
|
||||
sendFile: (a: File | undefined) => Promise<void>;
|
||||
}
|
||||
|
||||
const greetingMessage = "Здравствуйте, задайте ваш вопрос и наш оператор вам ответит в течение 10 минут";
|
||||
const greetingMessage: TicketMessage = {
|
||||
id: "greeting",
|
||||
ticket_id: "",
|
||||
user_id: "system",
|
||||
session_id: "",
|
||||
message: "Здравствуйте, задайте ваш вопрос и наш оператор вам ответит в течение 10 минут",
|
||||
files: [],
|
||||
shown: {},
|
||||
request_screenshot: "",
|
||||
created_at: new Date().toISOString(),
|
||||
system: false
|
||||
};
|
||||
|
||||
export default function Chat({
|
||||
open = false,
|
||||
@ -197,10 +208,7 @@ export default function Chat({
|
||||
>
|
||||
{ticket.sessionData?.ticketId &&
|
||||
messages.map((message) => {
|
||||
const isSelf = useMemo(() =>
|
||||
(ticket.sessionData?.sessionId || user) === message.user_id,
|
||||
[ticket.sessionData?.sessionId, user, message.user_id]
|
||||
);
|
||||
const isSelf = (ticket.sessionData?.sessionId || user) === message.user_id;
|
||||
|
||||
return (
|
||||
<ChatMessageRenderer
|
||||
@ -213,10 +221,7 @@ export default function Chat({
|
||||
{!ticket.sessionData?.ticketId && (
|
||||
<ChatMessageRenderer
|
||||
message={greetingMessage}
|
||||
isSelf={useMemo(() =>
|
||||
(ticket.sessionData?.sessionId || user) === greetingMessage.user_id,
|
||||
[ticket.sessionData?.sessionId, user, greetingMessage.user_id]
|
||||
)}
|
||||
isSelf={false}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
@ -130,9 +130,15 @@ export default () => {
|
||||
({ shown }) => shown?.me !== 1,
|
||||
);
|
||||
|
||||
newMessages.forEach(({ id, user_id }) => {
|
||||
if ((ticket.sessionData?.sessionId || user) === user_id) shownMessage(id);
|
||||
});
|
||||
// Находим последнее сообщение, которое не от пользователя
|
||||
const lastNonUserMessage = newMessages
|
||||
.filter(({ user_id }) => (ticket.sessionData?.sessionId || user) !== user_id)
|
||||
.pop();
|
||||
|
||||
// Отправляем shown только на последнее сообщение, которое не от пользователя
|
||||
if (lastNonUserMessage) {
|
||||
shownMessage(lastNonUserMessage.id);
|
||||
}
|
||||
}
|
||||
}, [isChatOpened, ticket.messages]);
|
||||
|
||||
|
109
src/utils/handleComponentError.ts
Normal file
109
src/utils/handleComponentError.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { ErrorInfo } from "react";
|
||||
import { Ticket, createTicket, getAuthToken, sendTicketMessage } from "..";
|
||||
|
||||
let errorsQueue: ComponentError[] = [];
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
|
||||
interface ComponentError {
|
||||
timestamp: number;
|
||||
message: string;
|
||||
callStack: string | undefined;
|
||||
componentStack: string | null | undefined;
|
||||
}
|
||||
|
||||
function isErrorReportingAllowed(error?: Error): boolean {
|
||||
// Если ошибка помечена как debug-override — всегда отправлять
|
||||
if (error && (error as any).__forceSend) return true;
|
||||
// Проверяем домен
|
||||
const currentDomain = window.location.hostname;
|
||||
return currentDomain !== 'localhost';
|
||||
}
|
||||
|
||||
// Новый API: getTickets — callback, возвращающий актуальные тикеты
|
||||
export function handleComponentError(error: Error, info: ErrorInfo, getTickets: () => Ticket[]) {
|
||||
//репортим только о авторизонышах
|
||||
if (!getAuthToken()) return;
|
||||
// Проверяем разрешение на отправку ошибок (по домену)
|
||||
if (!isErrorReportingAllowed(error)) {
|
||||
console.log('❌ Отправка ошибки заблокирована:', error.message);
|
||||
return;
|
||||
}
|
||||
console.log(`✅ Обработка ошибки: ${error.message}`);
|
||||
// Копируем __forceSend если есть
|
||||
const componentError: ComponentError & { __forceSend?: boolean } = {
|
||||
timestamp: Math.floor(Date.now() / 1000),
|
||||
message: error.message,
|
||||
callStack: error.stack,
|
||||
componentStack: info.componentStack,
|
||||
...(error && (error as any).__forceSend ? { __forceSend: true } : {})
|
||||
};
|
||||
queueErrorRequest(componentError, getTickets);
|
||||
}
|
||||
|
||||
// Ставит ошибку в очередь для отправки, через 1 секунду вызывает sendErrorsToServer
|
||||
export function queueErrorRequest(error: ComponentError, getTickets: () => Ticket[]) {
|
||||
errorsQueue.push(error);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
sendErrorsToServer(getTickets);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Отправляет накопленные ошибки в тикеты, ищет существующий тикет с system: true или создает новый
|
||||
export async function sendErrorsToServer(getTickets: () => Ticket[]) {
|
||||
if (errorsQueue.length === 0) return;
|
||||
// Проверяем разрешение на отправку ошибок (по домену и debug-override)
|
||||
// Если хотя бы одна ошибка в очереди с __forceSend, отправляем всё
|
||||
const forceSend = errorsQueue.some(e => (e as any).__forceSend);
|
||||
if (!forceSend && !isErrorReportingAllowed()) {
|
||||
console.log('❌ Отправка ошибок заблокирована, очищаем очередь');
|
||||
errorsQueue = [];
|
||||
return;
|
||||
}
|
||||
const tickets = getTickets();
|
||||
try {
|
||||
// Формируем сообщение об ошибке
|
||||
const errorMessage = errorsQueue.map(error => {
|
||||
return `[${new Date(error.timestamp * 1000).toISOString()}] ${error.message}\n\nCall Stack:\n${error.callStack || 'N/A'}\n\nComponent Stack:\n${error.componentStack || 'N/A'}`;
|
||||
}).join('\n\n---\n\n');
|
||||
// ВСЕГДА ищем тикет через API
|
||||
const existingSystemTicket = await findSystemTicket(tickets);
|
||||
if (existingSystemTicket) {
|
||||
sendTicketMessage({
|
||||
ticketId: existingSystemTicket,
|
||||
message: errorMessage,
|
||||
systemError: true,
|
||||
});
|
||||
} else {
|
||||
// Создаем новый тикет для ошибки
|
||||
createTicket({
|
||||
message: errorMessage,
|
||||
useToken: true,
|
||||
systemError: true,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in sendErrorsToServer:', error);
|
||||
} finally {
|
||||
// Очищаем очередь ошибок
|
||||
errorsQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Ищет существующий тикет с system: true
|
||||
export async function findSystemTicket(tickets: Ticket[]) {
|
||||
for (const ticket of tickets) {
|
||||
console.log("[findSystemTicket] Проверяем тикет:", ticket);
|
||||
if (!('messages' in ticket)) {
|
||||
if (ticket.top_message && ticket.top_message.system === true) {
|
||||
console.log("[findSystemTicket] Найден тикет по top_message.system:true:", ticket.id);
|
||||
return ticket.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function clearErrorHandlingConfig () {
|
||||
clearTimeout(timeoutId);
|
||||
errorsQueue = [];
|
||||
}
|
@ -28,13 +28,6 @@ export const useAfterPay = () => {
|
||||
let URLadditionalinformation = searchParams.get("additionalinformation");//его токен
|
||||
|
||||
useEffect(() => {
|
||||
console.log("useAutoPay: Processing return from payment", {
|
||||
URLaction,
|
||||
URLuserId,
|
||||
URLadditionalinformation,
|
||||
userId,
|
||||
wayback: searchParams.get("wayback")
|
||||
});
|
||||
|
||||
setSearchParams({}, { replace: true });
|
||||
|
||||
|
@ -1443,10 +1443,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429"
|
||||
integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==
|
||||
|
||||
"@frontend/kitui@^1.0.110":
|
||||
"@frontend/kitui@1.0.110":
|
||||
version "1.0.110"
|
||||
resolved "http://gitea.pena/api/packages/skeris/npm/%40frontend%2Fkitui/-/1.0.110/kitui-1.0.110.tgz#969f70636508e9efd6c8d81e62a6913b18a0c029"
|
||||
integrity sha512-M+U9a4qylLb9ZOUn57v7lm/Rutqicm04vJsJrxeAY/6G4ma1bC29toOZwTt/uJUlF4gk4ojyWIIjiGTVM1/hKQ==
|
||||
resolved "http://gitea.pena/api/packages/skeris/npm/%40frontend%2Fkitui/-/1.0.110/kitui-1.0.110.tgz#0c0a968293338537a2811e7761f8efe933893573"
|
||||
integrity sha512-XOCev5zNtNZ8fu3IfK6oFNOqT8lE9jlmUX1kQ3OO+H30/LBpnBrww9nV/aHV2TEm0wYXdRMvaEtU6VOb72sDdg==
|
||||
dependencies:
|
||||
immer "^10.0.2"
|
||||
reconnecting-eventsource "^1.6.2"
|
||||
|
Loading…
Reference in New Issue
Block a user