Merge branch 'dev' into staging
This commit is contained in:
commit
4efadbc628
@ -36,6 +36,7 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600&display=swap" rel="stylesheet" />
|
||||
|
||||
@ -117,6 +117,7 @@ export interface QuizConfig {
|
||||
};
|
||||
meta: string;
|
||||
yandexMetricNumber: number | undefined;
|
||||
vkMetricNumber: number | undefined;
|
||||
}
|
||||
|
||||
export type FormContactFieldName =
|
||||
@ -225,4 +226,5 @@ export const defaultQuizConfig: QuizConfig = {
|
||||
},
|
||||
meta: "",
|
||||
yandexMetricNumber: undefined,
|
||||
vkMetricNumber: undefined,
|
||||
};
|
||||
|
||||
@ -116,33 +116,32 @@ const GeneralItemTimeConv = ({
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(700));
|
||||
|
||||
const data = Object.entries(general)
|
||||
.sort((a, b) => a[0] - b[0]);
|
||||
const data = Object.entries(general).sort((a, b) => a[0] - b[0]);
|
||||
|
||||
const days = [...data].map(e => e[0])
|
||||
const days = [...data].map((e) => e[0]);
|
||||
|
||||
let buffer = 0
|
||||
let buffer = 0;
|
||||
|
||||
const time = [...data].map(e => {
|
||||
const time = [...data].map((e) => {
|
||||
if (e[1] > 0) {
|
||||
buffer = e[1]
|
||||
buffer = e[1];
|
||||
}
|
||||
return buffer
|
||||
})
|
||||
|
||||
return buffer;
|
||||
});
|
||||
|
||||
console.log("data", data)
|
||||
console.log("time", time.reduce((a, b) => (Number(a) + Number(b)), 0))
|
||||
console.log("time", getCalculatedTime(time.reduce((a, b) => (Number(a) + Number(b)), 0)))
|
||||
console.log("days", days.length)
|
||||
const numberValue = calculateTime ?
|
||||
(
|
||||
(time.reduce((a, b) => (Number(a) + Number(b)), 0))
|
||||
/
|
||||
(days.length)
|
||||
) || 0
|
||||
:
|
||||
conversionValue
|
||||
console.log("data", data);
|
||||
console.log(
|
||||
"time",
|
||||
time.reduce((a, b) => Number(a) + Number(b), 0),
|
||||
);
|
||||
console.log(
|
||||
"time",
|
||||
getCalculatedTime(time.reduce((a, b) => Number(a) + Number(b), 0)),
|
||||
);
|
||||
console.log("days", days.length);
|
||||
const numberValue = calculateTime
|
||||
? time.reduce((a, b) => Number(a) + Number(b), 0) / days.length || 0
|
||||
: conversionValue;
|
||||
|
||||
if (
|
||||
Object.keys(general).length === 0 ||
|
||||
@ -153,7 +152,6 @@ const GeneralItemTimeConv = ({
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
@ -164,7 +162,9 @@ const GeneralItemTimeConv = ({
|
||||
>
|
||||
<Typography sx={{ margin: "20px 20px 0" }}>{title}</Typography>
|
||||
<Typography sx={{ margin: "10px 20px 0", fontWeight: "bold" }}>
|
||||
{calculateTime ? `${getCalculatedTime(numberValue)} с` : `${numberValue.toFixed(2)}%`}
|
||||
{calculateTime
|
||||
? `${getCalculatedTime(numberValue)} с`
|
||||
: `${numberValue.toFixed(2)}%`}
|
||||
</Typography>
|
||||
<LineChart
|
||||
xAxis={[
|
||||
@ -178,10 +178,11 @@ const GeneralItemTimeConv = ({
|
||||
{
|
||||
data: Object.values(time),
|
||||
valueFormatter: (value) => {
|
||||
console.log("log", value)
|
||||
return calculateTime ? getCalculatedTime(value) : String((value*100).toFixed(2)) + "%"
|
||||
}
|
||||
,
|
||||
console.log("log", value);
|
||||
return calculateTime
|
||||
? getCalculatedTime(value)
|
||||
: String((value * 100).toFixed(2)) + "%";
|
||||
},
|
||||
},
|
||||
]}
|
||||
// dataset={Object.entries(general).map(([, v]) => moment.unix(v).format("ss:mm:HH")).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})}
|
||||
@ -196,7 +197,6 @@ const GeneralItemTimeConv = ({
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export const General: FC<GeneralProps> = ({ data, day }) => {
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { Box, useTheme } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
import YandexMetric from "../mocks/YandexMetric.png";
|
||||
import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
|
||||
|
||||
type PartnerItemProps = {
|
||||
setIsModalOpen: (value: boolean) => void;
|
||||
@ -34,7 +34,7 @@ export const YandexButton: FC<PartnerItemProps> = ({
|
||||
}}
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
<img width={"100%"} src={YandexMetric} alt={"Yandex.Метрика"} />
|
||||
<YandexMetricaLogo />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -30,13 +30,24 @@ export default function YandexModal({ isModalOpen, handleCloseModal }: Props) {
|
||||
const [currentValue, setCurrentValue] = useState<string>(
|
||||
yandexNumber ? yandexNumber.toString() : "",
|
||||
);
|
||||
const handleClose = () => {
|
||||
handleCloseModal();
|
||||
if (!yandexNumber) {
|
||||
setIsSave(false);
|
||||
setCurrentValue("");
|
||||
return;
|
||||
}
|
||||
setIsSave(true);
|
||||
setCurrentValue(yandexNumber.toString());
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
handleCloseModal();
|
||||
updateQuiz(quiz?.id, (quiz) => {
|
||||
quiz.config.yandexMetricNumber = currentValue
|
||||
? Number(currentValue)
|
||||
: undefined;
|
||||
});
|
||||
handleCloseModal();
|
||||
if (!currentValue) {
|
||||
setIsSave(false);
|
||||
return;
|
||||
@ -55,7 +66,7 @@ export default function YandexModal({ isModalOpen, handleCloseModal }: Props) {
|
||||
return (
|
||||
<Dialog
|
||||
open={isModalOpen}
|
||||
onClose={handleCloseModal}
|
||||
onClose={handleClose}
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
@ -83,7 +94,7 @@ export default function YandexModal({ isModalOpen, handleCloseModal }: Props) {
|
||||
</Typography>
|
||||
</Box>
|
||||
<IconButton
|
||||
onClick={handleCloseModal}
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
width: "12px",
|
||||
height: "12px",
|
||||
@ -166,7 +177,7 @@ export default function YandexModal({ isModalOpen, handleCloseModal }: Props) {
|
||||
>
|
||||
<Button
|
||||
sx={{ width: isMobile ? "100%" : "130px" }}
|
||||
onClick={handleCloseModal}
|
||||
onClick={handleClose}
|
||||
variant={"outlined"}
|
||||
>
|
||||
Отмена
|
||||
|
||||
6
src/pages/IntegrationsPage/mocks/YandexMetricaLogo.tsx
Normal file
6
src/pages/IntegrationsPage/mocks/YandexMetricaLogo.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
import { ReactComponent as YandexLogo } from "./yandexMetricaLogo.svg";
|
||||
|
||||
export const YandexMetricaLogo = () => {
|
||||
return <YandexLogo />;
|
||||
};
|
||||
9
src/pages/IntegrationsPage/mocks/yandexMetricaLogo.svg
Normal file
9
src/pages/IntegrationsPage/mocks/yandexMetricaLogo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 168 KiB |
@ -8,7 +8,7 @@ import { isAxiosError } from "axios";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useSWR from "swr";
|
||||
import { useYandexMetrica } from "@utils/hooks/useYandexMetrica";
|
||||
import { useYandexMetrics } from "@utils/hooks/useYandexMetrics";
|
||||
|
||||
export default function ViewPublicationPage() {
|
||||
const quizId = useParams().quizId;
|
||||
@ -23,7 +23,7 @@ export default function ViewPublicationPage() {
|
||||
const quiz = quizes?.find((quiz) => quiz.qid === quizId);
|
||||
const yandexMetricNumber = quiz?.config.yandexMetricNumber;
|
||||
|
||||
useYandexMetrica(yandexMetricNumber);
|
||||
useYandexMetrics(yandexMetricNumber);
|
||||
|
||||
const {
|
||||
data: rawQuestions,
|
||||
|
||||
@ -41,7 +41,17 @@ export const setQuestions = (questions: RawQuestion[] | null | undefined) =>
|
||||
export const createUntypedQuestion = (
|
||||
quizId: number,
|
||||
insertAfterQuestionId?: string,
|
||||
) =>
|
||||
) => {
|
||||
const { questions } = useQuestionsStore.getState();
|
||||
|
||||
const questionsAmount = questions.filter(
|
||||
({ type }) => type !== "result",
|
||||
).length;
|
||||
|
||||
if (questionsAmount >= 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
setProducedState(
|
||||
(state) => {
|
||||
const newUntypedQuestion = {
|
||||
@ -70,6 +80,7 @@ export const createUntypedQuestion = (
|
||||
quizId,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const removeQuestion = (questionId: string) =>
|
||||
setProducedState(
|
||||
@ -530,66 +541,80 @@ export const deleteQuestion = async (questionId: string) =>
|
||||
}
|
||||
});
|
||||
|
||||
export const copyQuestion = async (questionId: string, quizId: number) =>
|
||||
requestQueue.enqueue(`copyQuestion-${quizId}-${questionId}`, async () => {
|
||||
const question = useQuestionsStore
|
||||
.getState()
|
||||
.questions.find((q) => q.id === questionId);
|
||||
if (!question) return;
|
||||
export const copyQuestion = async (questionId: string, quizId: number) => {
|
||||
const { questions } = useQuestionsStore.getState();
|
||||
|
||||
const frontId = nanoid();
|
||||
if (question.type === null) {
|
||||
const copiedQuestion = structuredClone(question);
|
||||
copiedQuestion.id = frontId;
|
||||
const questionsAmount = questions.filter(
|
||||
({ type }) => type !== "result",
|
||||
).length;
|
||||
|
||||
setProducedState(
|
||||
(state) => {
|
||||
state.questions.push(copiedQuestion);
|
||||
},
|
||||
{
|
||||
type: "copyQuestion",
|
||||
questionId,
|
||||
if (questionsAmount >= 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
return requestQueue.enqueue(
|
||||
`copyQuestion-${quizId}-${questionId}`,
|
||||
async () => {
|
||||
const question = useQuestionsStore
|
||||
.getState()
|
||||
.questions.find((q) => q.id === questionId);
|
||||
if (!question) return;
|
||||
|
||||
const frontId = nanoid();
|
||||
if (question.type === null) {
|
||||
const copiedQuestion = structuredClone(question);
|
||||
copiedQuestion.id = frontId;
|
||||
|
||||
setProducedState(
|
||||
(state) => {
|
||||
state.questions.push(copiedQuestion);
|
||||
},
|
||||
{
|
||||
type: "copyQuestion",
|
||||
questionId,
|
||||
quizId,
|
||||
},
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { updated: newQuestionId } = await questionApi.copy(
|
||||
question.backendId,
|
||||
quizId,
|
||||
},
|
||||
);
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
const copiedQuestion = structuredClone(question);
|
||||
copiedQuestion.backendId = newQuestionId;
|
||||
copiedQuestion.id = frontId;
|
||||
copiedQuestion.content.id = frontId;
|
||||
copiedQuestion.content.rule = {
|
||||
main: [],
|
||||
parentId: "",
|
||||
default: "",
|
||||
children: [],
|
||||
};
|
||||
|
||||
try {
|
||||
const { updated: newQuestionId } = await questionApi.copy(
|
||||
question.backendId,
|
||||
quizId,
|
||||
);
|
||||
setProducedState(
|
||||
(state) => {
|
||||
state.questions.push(copiedQuestion);
|
||||
},
|
||||
{
|
||||
type: "copyQuestion",
|
||||
questionId,
|
||||
quizId,
|
||||
},
|
||||
);
|
||||
|
||||
const copiedQuestion = structuredClone(question);
|
||||
copiedQuestion.backendId = newQuestionId;
|
||||
copiedQuestion.id = frontId;
|
||||
copiedQuestion.content.id = frontId;
|
||||
copiedQuestion.content.rule = {
|
||||
main: [],
|
||||
parentId: "",
|
||||
default: "",
|
||||
children: [],
|
||||
};
|
||||
|
||||
setProducedState(
|
||||
(state) => {
|
||||
state.questions.push(copiedQuestion);
|
||||
},
|
||||
{
|
||||
type: "copyQuestion",
|
||||
questionId,
|
||||
quizId,
|
||||
},
|
||||
);
|
||||
|
||||
updateQuestionOrders();
|
||||
} catch (error) {
|
||||
devlog("Error copying question", error);
|
||||
enqueueSnackbar("Не удалось скопировать вопрос");
|
||||
}
|
||||
});
|
||||
updateQuestionOrders();
|
||||
} catch (error) {
|
||||
devlog("Error copying question", error);
|
||||
enqueueSnackbar("Не удалось скопировать вопрос");
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
function setProducedState<A extends string | { type: string }>(
|
||||
recipe: (state: QuestionsStore) => void,
|
||||
|
||||
@ -54,6 +54,10 @@ export const cleanAuthTicketData = () => {
|
||||
useTicketStore.setState({ authData: initAuthData });
|
||||
};
|
||||
|
||||
export const cleanUnauthTicketData = () => {
|
||||
useTicketStore.setState({ unauthData: initAuthData });
|
||||
};
|
||||
|
||||
export const setTicketData = (sessionData: SessionData) =>
|
||||
updateTicket((ticket) => {
|
||||
ticket.sessionData = sessionData;
|
||||
|
||||
@ -11,36 +11,37 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
useTicketStore,
|
||||
addOrUpdateUnauthMessages,
|
||||
incrementUnauthMessage,
|
||||
setUnauthIsPreventAutoscroll,
|
||||
useTicketStore,
|
||||
} from "@root/ticket";
|
||||
import type { TouchEvent, WheelEvent } from "react";
|
||||
import * as React from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import ChatMessage from "./ChatMessage";
|
||||
import ChatVideo from "./ChatVideo";
|
||||
import SendIcon from "@icons/SendIcon";
|
||||
import UserCircleIcon from "./UserCircleIcon";
|
||||
import { throttle } from "@frontend/kitui";
|
||||
import { throttle, TicketMessage } from "@frontend/kitui";
|
||||
import ArrowLeft from "@icons/questionsPage/arrowLeft";
|
||||
import { useUserStore } from "@root/user";
|
||||
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||
import ChatImage from "./ChatImage";
|
||||
import ChatDocument from "@ui_kit/FloatingSupportChat/ChatDocument";
|
||||
import * as React from "react";
|
||||
import {
|
||||
checkAcceptableMediaType,
|
||||
ACCEPT_SEND_MEDIA_TYPES_MAP,
|
||||
checkAcceptableMediaType,
|
||||
} from "@utils/checkAcceptableMediaType";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import type { WheelEvent, TouchEvent } from "react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
sx?: SxProps<Theme>;
|
||||
onclickArrow?: () => void;
|
||||
sendMessage: (a: string) => Promise<boolean>;
|
||||
sendFile: (a: File | undefined) => Promise<true>;
|
||||
greetingMessage: TicketMessage;
|
||||
}
|
||||
|
||||
export default function Chat({
|
||||
@ -49,6 +50,7 @@ export default function Chat({
|
||||
onclickArrow,
|
||||
sendMessage,
|
||||
sendFile,
|
||||
greetingMessage,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
@ -69,6 +71,13 @@ export default function Chat({
|
||||
|
||||
const chatBoxRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
addOrUpdateUnauthMessages([greetingMessage]);
|
||||
if (open) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const sendMessageHC = async () => {
|
||||
const successful = await sendMessage(messageField);
|
||||
if (successful) {
|
||||
@ -193,7 +202,7 @@ export default function Chat({
|
||||
color: theme.palette.common.white,
|
||||
}}
|
||||
>
|
||||
<Typography>Мария</Typography>
|
||||
<Typography>Данила</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
@ -202,6 +211,14 @@ export default function Chat({
|
||||
>
|
||||
онлайн-консультант
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
lineHeight: "19px",
|
||||
}}
|
||||
>
|
||||
время работы 10:00-3:00 по мск
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
@ -310,6 +327,17 @@ export default function Chat({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{!ticket.sessionData?.ticketId && (
|
||||
<ChatMessage
|
||||
unAuthenticated
|
||||
text={greetingMessage.message}
|
||||
createdAt={greetingMessage.created_at}
|
||||
isSelf={
|
||||
(ticket.sessionData?.sessionId || user) ===
|
||||
greetingMessage.user_id
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<FormControl fullWidth sx={{ borderTop: "1px solid black" }}>
|
||||
<InputBase
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { useState, useEffect, forwardRef } from "react";
|
||||
import type { ReactNode, Ref } from "react";
|
||||
import { forwardRef, useEffect, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Fab,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
Slide,
|
||||
Dialog,
|
||||
Badge,
|
||||
Box,
|
||||
Dialog,
|
||||
Fab,
|
||||
Modal,
|
||||
Slide,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import CircleDoubleDown from "./QuestionIcon";
|
||||
import Chat from "./Chat";
|
||||
@ -16,9 +17,7 @@ import { TransitionProps } from "@mui/material/transitions";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useTicketStore } from "@root/ticket";
|
||||
import { useUserStore } from "@root/user";
|
||||
import { ACCEPT_SEND_FILE_TYPES_MAP } from "@frontend/squzanswerer/dist-package/components/ViewPublicationPage/tools/fileUpload";
|
||||
|
||||
import type { ReactNode, Ref } from "react";
|
||||
import { TicketMessage } from "@frontend/kitui";
|
||||
|
||||
const animation = {
|
||||
"@keyframes runningStripe": {
|
||||
@ -48,6 +47,7 @@ interface Props {
|
||||
sendFile: (a: File | undefined) => Promise<true>;
|
||||
modalWarningType: string | null;
|
||||
setModalWarningType: any;
|
||||
greetingMessage: TicketMessage;
|
||||
}
|
||||
|
||||
export default function FloatingSupportChat({
|
||||
@ -59,6 +59,7 @@ export default function FloatingSupportChat({
|
||||
sendFile,
|
||||
modalWarningType,
|
||||
setModalWarningType,
|
||||
greetingMessage,
|
||||
}: Props) {
|
||||
const [monitorType, setMonitorType] = useState<"desktop" | "mobile" | "">("");
|
||||
const theme = useTheme();
|
||||
@ -107,6 +108,7 @@ export default function FloatingSupportChat({
|
||||
sx={{ alignSelf: "start", width: "clamp(200px, 100%, 400px)" }}
|
||||
sendMessage={sendMessage}
|
||||
sendFile={sendFile}
|
||||
greetingMessage={greetingMessage}
|
||||
/>
|
||||
<Dialog
|
||||
fullScreen
|
||||
@ -119,6 +121,7 @@ export default function FloatingSupportChat({
|
||||
onclickArrow={handleChatClickClose}
|
||||
sendMessage={sendMessage}
|
||||
sendFile={sendFile}
|
||||
greetingMessage={greetingMessage}
|
||||
/>
|
||||
</Dialog>
|
||||
<Fab
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
createTicket,
|
||||
TicketMessage,
|
||||
useSSESubscription,
|
||||
useTicketMessages,
|
||||
@ -7,21 +8,21 @@ import {
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import FloatingSupportChat from "./FloatingSupportChat";
|
||||
import { useUserStore } from "@root/user";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { sendTicketMessage, shownMessage } from "../../api/ticket";
|
||||
import { useSSETab } from "../../utils/hooks/useSSETab";
|
||||
import {
|
||||
addOrUpdateUnauthMessages,
|
||||
useTicketStore,
|
||||
cleanAuthTicketData,
|
||||
cleanUnauthTicketData,
|
||||
setIsMessageSending,
|
||||
setTicketData,
|
||||
setUnauthIsPreventAutoscroll,
|
||||
setUnauthTicketMessageFetchState,
|
||||
cleanAuthTicketData,
|
||||
setTicketData,
|
||||
useTicketStore,
|
||||
} from "@root/ticket";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { getMessageFromFetchError } from "@utils/backendMessageHandler";
|
||||
import { createTicket } from "@frontend/kitui";
|
||||
import { setIsMessageSending } from "@root/ticket";
|
||||
|
||||
type ModalWarningType =
|
||||
| "errorType"
|
||||
@ -59,6 +60,8 @@ export default () => {
|
||||
const [modalWarningType, setModalWarningType] =
|
||||
useState<ModalWarningType>(null);
|
||||
const [isChatOpened, setIsChatOpened] = useState<boolean>(false);
|
||||
const [sseEnabled, setSseEnabled] = useState(true);
|
||||
|
||||
const handleChatClickOpen = () => {
|
||||
setIsChatOpened(true);
|
||||
};
|
||||
@ -68,6 +71,34 @@ export default () => {
|
||||
const handleChatClickSwitch = () => {
|
||||
setIsChatOpened((state) => !state);
|
||||
};
|
||||
|
||||
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 =
|
||||
moscowHour >= 10 && moscowHour < 15
|
||||
? workingHoursMessage
|
||||
: offHoursMessage;
|
||||
|
||||
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]);
|
||||
|
||||
useTicketsFetcher({
|
||||
url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets",
|
||||
ticketsPerPage: 10,
|
||||
@ -113,22 +144,37 @@ export default () => {
|
||||
});
|
||||
|
||||
useSSESubscription<TicketMessage>({
|
||||
enabled: isActiveSSETab && Boolean(ticket.sessionData?.sessionId),
|
||||
enabled:
|
||||
sseEnabled && isActiveSSETab && Boolean(ticket.sessionData?.sessionId),
|
||||
url:
|
||||
process.env.REACT_APP_DOMAIN +
|
||||
`/heruvym/ticket?ticket=${ticket.sessionData?.ticketId}&s=${ticket.sessionData?.sessionId}`,
|
||||
onNewData: (ticketMessages) => {
|
||||
const isTicketClosed = ticketMessages.some(
|
||||
(message) => message.session_id === "close",
|
||||
);
|
||||
if (isTicketClosed) {
|
||||
cleanAuthTicketData();
|
||||
addOrUpdateUnauthMessages([getGreetingMessage]);
|
||||
if (!user) {
|
||||
cleanUnauthTicketData();
|
||||
localStorage.removeItem("unauth-ticket");
|
||||
}
|
||||
return;
|
||||
}
|
||||
updateSSEValue(ticketMessages);
|
||||
addOrUpdateUnauthMessages(ticketMessages);
|
||||
},
|
||||
onDisconnect: useCallback(() => {
|
||||
setUnauthIsPreventAutoscroll(false);
|
||||
setSseEnabled(false);
|
||||
}, []),
|
||||
marker: "ticket",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
cleanAuthTicketData();
|
||||
setSseEnabled(true);
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -145,7 +191,7 @@ export default () => {
|
||||
|
||||
const sendMessage = async (messageField: string) => {
|
||||
if (!messageField || ticket.isMessageSending) return false;
|
||||
|
||||
setSseEnabled(true);
|
||||
let successful = false;
|
||||
setIsMessageSending(true);
|
||||
if (!ticket.sessionData?.ticketId) {
|
||||
@ -245,6 +291,7 @@ export default () => {
|
||||
sendFile={sendFile}
|
||||
modalWarningType={modalWarningType}
|
||||
setModalWarningType={setModalWarningType}
|
||||
greetingMessage={getGreetingMessage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const useYandexMetrica = (yandexMetricNumber: number | undefined) => {
|
||||
export const useYandexMetrics = (yandexMetricNumber: number | undefined) => {
|
||||
useEffect(() => {
|
||||
if (yandexMetricNumber) {
|
||||
if (
|
||||
yandexMetricNumber &&
|
||||
typeof yandexMetricNumber === "number" &&
|
||||
!Number.isNaN(yandexMetricNumber)
|
||||
) {
|
||||
const script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.innerHTML = `
|
||||
Loading…
Reference in New Issue
Block a user