Merge branch 'support-chat-logic' into 'vis-fix'

feat: support chat logic

See merge request frontend/squiz!178
This commit is contained in:
Nastya 2024-02-14 19:04:40 +00:00
commit 5bbbfab6ca
3 changed files with 161 additions and 160 deletions

@ -1,6 +1,5 @@
import { useEffect, useState } from "react";
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
import { useDebouncedCallback } from "use-debounce";
import CustomNumberField from "@ui_kit/CustomNumberField";
@ -27,40 +26,8 @@ export default function SliderOptions({
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const [switchState, setSwitchState] = useState("setting");
const [stepError, setStepError] = useState("");
const [startError, setStartError] = useState<boolean>(false);
const [minError, setMinError] = useState<boolean>(false);
const [maxError, setMaxError] = useState<boolean>(false);
const startValueDebounce = useDebouncedCallback((value) => {
updateQuestion(question.id, (question) => {
if (question.type !== "number") return;
question.content.start = value;
});
}, 2000);
useEffect(() => {
const min = Number(question.content.range.split("—")[0]);
const max = Number(question.content.range.split("—")[1]);
const start = Number(question.content.start);
if (start < min) {
setStartError(true);
startValueDebounce(min);
return;
}
if (start > max && min < max) {
setStartError(true);
startValueDebounce(max);
return;
}
if (start >= min && start <= max) {
setStartError(false);
}
}, [question.content.range, question.content.start]);
useEffect(() => {
const min = Number(question.content.range.split("—")[0]);
@ -211,7 +178,6 @@ export default function SliderOptions({
sx={{ maxWidth: "310px", width: "100%" }}
placeholder={"50"}
value={String(question.content.start)}
emptyError={startError}
onChange={({ target }) => {
updateQuestion(question.id, (question) => {
if (question.type !== "number") return;

@ -33,15 +33,16 @@ import {
useEventListener,
createTicket,
} from "@frontend/kitui";
import { sendTicketMessage } from "../../api/ticket";
import { sendTicketMessage, shownMessage } from "../../api/ticket";
import ArrowLeft from "@icons/questionsPage/arrowLeft";
interface Props {
open: boolean;
sx?: SxProps<Theme>;
onclickArrow?: () => void;
}
export default function Chat({ sx, onclickArrow }: Props) {
export default function Chat({ open = false, sx, onclickArrow }: Props) {
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const isMobile = useMediaQuery(theme.breakpoints.down(800));
@ -129,6 +130,16 @@ export default function Chat({ sx, onclickArrow }: Props) {
[lastMessageId],
);
useEffect(() => {
if (open) {
const newMessages = messages.filter(({ shown }) => shown.me !== 1);
newMessages.map(async ({ id }) => {
await shownMessage(id);
});
}
}, [open, messages]);
async function handleSendMessage() {
if (!messageField || isMessageSending) return;
@ -194,137 +205,143 @@ export default function Chat({ sx, onclickArrow }: Props) {
};
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
height: isMobile ? "100%" : "clamp(250px, calc(100vh - 90px), 600px)",
backgroundColor: "#944FEE",
borderRadius: "8px",
...sx,
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "9px",
pl: "22px",
pt: "12px",
pb: "20px",
filter: "drop-shadow(0px 3px 12px rgba(37, 39, 52, 0.3))",
}}
>
{isMobile && (
<IconButton onClick={onclickArrow}>
<ArrowLeft color="white" />
</IconButton>
)}
<UserCircleIcon />
<>
{open && (
<Box
sx={{
mt: "5px",
display: "flex",
flexDirection: "column",
gap: "3px",
color: theme.palette.common.white,
height: isMobile
? "100%"
: "clamp(250px, calc(100vh - 90px), 600px)",
backgroundColor: "#944FEE",
borderRadius: "8px",
...sx,
}}
>
<Typography>Мария</Typography>
<Typography
<Box
sx={{
fontSize: "16px",
lineHeight: "19px",
display: "flex",
alignItems: "center",
gap: "9px",
pl: "22px",
pt: "12px",
pb: "20px",
filter: "drop-shadow(0px 3px 12px rgba(37, 39, 52, 0.3))",
}}
>
онлайн-консультант
</Typography>
</Box>
</Box>
<Box
sx={{
flexGrow: 1,
backgroundColor: "white",
borderRadius: "8px",
display: "flex",
flexDirection: "column",
}}
>
<Box
ref={chatBoxRef}
sx={{
display: "flex",
width: "100%",
flexBasis: 0,
flexDirection: "column",
gap: upMd ? "20px" : "16px",
px: upMd ? "20px" : "5px",
py: upMd ? "20px" : "13px",
overflowY: "auto",
flexGrow: 1,
}}
>
{sessionData &&
messages.map((message) => (
<ChatMessage
unAuthenticated
key={message.id}
text={message.message}
createdAt={message.created_at}
isSelf={sessionData.sessionId === message.user_id}
/>
))}
</Box>
<FormControl fullWidth sx={{ borderTop: "1px solid black" }}>
<InputBase
value={messageField}
fullWidth
placeholder="Введите сообщение..."
id="message"
multiline
onKeyDown={handleTextfieldKeyPress}
{isMobile && (
<IconButton onClick={onclickArrow}>
<ArrowLeft color="white" />
</IconButton>
)}
<UserCircleIcon />
<Box
sx={{
mt: "5px",
display: "flex",
flexDirection: "column",
gap: "3px",
color: theme.palette.common.white,
}}
>
<Typography>Мария</Typography>
<Typography
sx={{
fontSize: "16px",
lineHeight: "19px",
}}
>
онлайн-консультант
</Typography>
</Box>
</Box>
<Box
sx={{
width: "100%",
p: 0,
flexGrow: 1,
backgroundColor: "white",
borderRadius: "8px",
display: "flex",
flexDirection: "column",
}}
inputProps={{
sx: {
fontWeight: 400,
fontSize: "16px",
lineHeight: "19px",
pt: upMd ? "30px" : "28px",
pb: upMd ? "30px" : "24px",
px: "19px",
maxHeight: "calc(19px * 5)",
color: "black",
},
}}
onChange={(e) => setMessageField(e.target.value)}
endAdornment={
<InputAdornment position="end">
<IconButton
disabled={isMessageSending}
onClick={handleSendMessage}
sx={{
height: "53px",
width: "53px",
mr: "13px",
p: 0,
opacity: isMessageSending ? 0.3 : 1,
}}
>
<SendIcon
style={{
width: "100%",
height: "100%",
}}
>
<Box
ref={chatBoxRef}
sx={{
display: "flex",
width: "100%",
flexBasis: 0,
flexDirection: "column",
gap: upMd ? "20px" : "16px",
px: upMd ? "20px" : "5px",
py: upMd ? "20px" : "13px",
overflowY: "auto",
flexGrow: 1,
}}
>
{sessionData &&
messages.map((message) => (
<ChatMessage
unAuthenticated
key={message.id}
text={message.message}
createdAt={message.created_at}
isSelf={sessionData.sessionId === message.user_id}
/>
</IconButton>
</InputAdornment>
}
/>
</FormControl>
</Box>
</Box>
))}
</Box>
<FormControl fullWidth sx={{ borderTop: "1px solid black" }}>
<InputBase
value={messageField}
fullWidth
placeholder="Введите сообщение..."
id="message"
multiline
onKeyDown={handleTextfieldKeyPress}
sx={{
width: "100%",
p: 0,
}}
inputProps={{
sx: {
fontWeight: 400,
fontSize: "16px",
lineHeight: "19px",
pt: upMd ? "30px" : "28px",
pb: upMd ? "30px" : "24px",
px: "19px",
maxHeight: "calc(19px * 5)",
color: "black",
},
}}
onChange={(e) => setMessageField(e.target.value)}
endAdornment={
<InputAdornment position="end">
<IconButton
disabled={isMessageSending}
onClick={handleSendMessage}
sx={{
height: "53px",
width: "53px",
mr: "13px",
p: 0,
opacity: isMessageSending ? 0.3 : 1,
}}
>
<SendIcon
style={{
width: "100%",
height: "100%",
}}
/>
</IconButton>
</InputAdornment>
}
/>
</FormControl>
</Box>
</Box>
)}
</>
);
}

@ -5,6 +5,7 @@ import {
useMediaQuery,
Slide,
Dialog,
Badge,
} from "@mui/material";
import { ReactNode, useState } from "react";
import CircleDoubleDown from "./QuestionIcon";
@ -12,6 +13,7 @@ import * as React from "react";
import Chat from "./Chat";
import { TransitionProps } from "@mui/material/transitions";
import { useLocation } from "react-router-dom";
import { useUnauthTicketStore } from "@root/unauthTicket";
const animation = {
"@keyframes runningStripe": {
@ -39,6 +41,7 @@ export default function FloatingSupportChat() {
const [open, setOpen] = useState(false);
const location = useLocation();
const locationChat = location.pathname;
const { messages } = useUnauthTicketStore((state) => state);
const handleClickOpen = () => {
setOpen(true);
@ -60,9 +63,10 @@ export default function FloatingSupportChat() {
zIndex: 10,
}}
>
{isChatOpened && (
<Chat sx={{ alignSelf: "start", width: "clamp(200px, 100%, 400px)" }} />
)}
<Chat
open={isChatOpened}
sx={{ alignSelf: "start", width: "clamp(200px, 100%, 400px)" }}
/>
<Dialog
fullScreen
open={open}
@ -112,7 +116,21 @@ export default function FloatingSupportChat() {
}}
/>
)}
<CircleDoubleDown />
<Badge
badgeContent={messages.filter(({ shown }) => shown.me !== 1).length}
sx={{
"& .MuiBadge-badge": {
display: isChatOpened ? "none" : "flex",
color: "#FFFFFF",
background: theme.palette.brightPurple.main,
top: "4px",
right: "4px",
transform: "scale(0.8) translate(50%, -50%)",
},
}}
>
<CircleDoubleDown isUp={isChatOpened} />
</Badge>
</Fab>
</Box>
);