From 4ad467ae7bd0830039fd89b64635d8789efe291d Mon Sep 17 00:00:00 2001 From: Nastya Date: Mon, 11 Mar 2024 22:48:38 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BC=D0=B5=D0=B4=D0=B8=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.tsx | 2 + .../dashboard/Content/Support/Chat/Chat.tsx | 154 +++++++++++++++++- .../Content/Support/Chat/ChatDocument.tsx | 61 +++++++ .../Content/Support/Chat/ChatImage.tsx | 67 ++++++++ .../Content/Support/Chat/ChatMessage.tsx | 52 ++++++ .../Content/Support/Chat/ChatVideo.tsx | 68 ++++++++ .../Content/Support/Chat/fileUpload.ts | 9 + .../Content/Support/ChatImageNewWindow.tsx | 20 +++ 8 files changed, 427 insertions(+), 6 deletions(-) create mode 100644 src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx create mode 100644 src/pages/dashboard/Content/Support/Chat/ChatImage.tsx create mode 100644 src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx create mode 100644 src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx create mode 100644 src/pages/dashboard/Content/Support/Chat/fileUpload.ts create mode 100644 src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx diff --git a/src/index.tsx b/src/index.tsx index 3f4aa62..0223de9 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,6 +23,7 @@ import DiscountManagement from "@root/pages/dashboard/Content/DiscountManagement import { PromocodeManagement } from "@root/pages/dashboard/Content/PromocodeManagement"; import { SettingRoles } from "@pages/Setting/SettingRoles"; import Support from "@pages/dashboard/Content/Support/Support"; +import ChatImageNewWindow from "@pages/dashboard/Content/Support/ChatImageNewWindow"; import theme from "./theme"; import "./index.css"; @@ -111,6 +112,7 @@ root.render( /> ))} + } /> } /> diff --git a/src/pages/dashboard/Content/Support/Chat/Chat.tsx b/src/pages/dashboard/Content/Support/Chat/Chat.tsx index ce6b471..8907b0a 100644 --- a/src/pages/dashboard/Content/Support/Chat/Chat.tsx +++ b/src/pages/dashboard/Content/Support/Chat/Chat.tsx @@ -9,8 +9,39 @@ import { TicketMessage } from "@root/model/ticket"; import { sendTicketMessage } from "@root/api/tickets"; import { enqueueSnackbar } from "notistack"; import { useTicketStore } from "@root/stores/tickets"; -import { getMessageFromFetchError, throttle, useEventListener, useSSESubscription, useTicketMessages, useToken } from "@frontend/kitui"; +import { getMessageFromFetchError, makeRequest, throttle, useEventListener, useSSESubscription, useTicketMessages, useToken } from "@frontend/kitui"; +import ChatImage from "./ChatImage"; +import ChatDocument from "./ChatDocument"; +import ChatVideo from "./ChatVideo"; +import ChatMessage from "./ChatMessage"; +import { ACCEPT_SEND_MEDIA_TYPES_MAP, MAX_FILE_SIZE, MAX_PHOTO_SIZE, MAX_VIDEO_SIZE } from "./fileUpload"; +const tooLarge = "Файл слишком большой" +const checkAcceptableMediaType = (file: File) => { + if (file === null) return "" + + const segments = file?.name.split('.'); + const extension = segments[segments.length - 1]; + const type = extension.toLowerCase(); + + console.log(type) + switch (type) { + case ACCEPT_SEND_MEDIA_TYPES_MAP.document.find(name => name === type): + if (file.size > MAX_FILE_SIZE) return tooLarge + return "" + + case ACCEPT_SEND_MEDIA_TYPES_MAP.picture.find(name => name === type): + if (file.size > MAX_PHOTO_SIZE) return tooLarge + return "" + + case ACCEPT_SEND_MEDIA_TYPES_MAP.video.find(name => name === type): + if (file.size > MAX_VIDEO_SIZE) return tooLarge + return "" + + default: + return "Не удалось отправить файл. Недопустимый тип" + } + } export default function Chat() { const token = useToken(); @@ -26,6 +57,8 @@ export default function Chat() { const isPreventAutoscroll = useMessageStore(state => state.isPreventAutoscroll); const fetchState = useMessageStore(state => state.ticketMessagesFetchState); const lastMessageId = useMessageStore(state => state.lastMessageId); + const fileInputRef = useRef(null); + const [disableFileButton, setDisableFileButton] = useState(false); const ticket = tickets.find(ticket => ticket.id === ticketId); @@ -107,7 +140,47 @@ export default function Chat() { setMessageField(""); } - function handleAddAttachment() { } + const sendFile = async (file: File) => { + if (file === undefined) return true; + + // const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP.some( + // fileType => file.name.toLowerCase().endsWith(fileType) + // ); + // console.log(file.name.toLowerCase().endsWith(".png")) + // if (!isFileTypeAccepted) return setModalWarningType("errorType"); + let data; + + const ticketId = ticket?.id + if (ticketId !== undefined) { + try { + const body = new FormData(); + + body.append(file.name, file); + body.append("ticket", ticketId); + await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles", + body: body, + method: "POST", + }); + } catch (error: any) { + const errorMessage = getMessageFromFetchError(error); + if (errorMessage) enqueueSnackbar(errorMessage); + } + return true; + } + }; + const sendFileHC = async (file: File) => { + console.log(file) + const check = checkAcceptableMediaType(file) + if (check.length > 0) { + enqueueSnackbar(check) + return + } + setDisableFileButton(true) + await sendFile(file) + setDisableFileButton(false) + console.log(disableFileButton) + }; function handleTextfieldKeyPress(e: KeyboardEvent) { if (e.key === "Enter" && !e.shiftKey) { @@ -145,9 +218,66 @@ export default function Chat() { colorScheme: "dark", }} > - {ticket && messages.map(message => - - )} + {ticket && + messages.map((message) => { + const isFileVideo = () => { + if (message.files) { + return (ACCEPT_SEND_MEDIA_TYPES_MAP.video.some((fileType) => + message.files[0].toLowerCase().endsWith(fileType), + )) + } + }; + const isFileImage = () => { + if (message.files) { + return (ACCEPT_SEND_MEDIA_TYPES_MAP.picture.some((fileType) => + message.files[0].toLowerCase().endsWith(fileType), + )) + } + }; + const isFileDocument = () => { + if (message.files) { + return (ACCEPT_SEND_MEDIA_TYPES_MAP.document.some((fileType) => + message.files[0].toLowerCase().endsWith(fileType), + )) + } + }; + if (message.files !== null && message.files.length > 0 && isFileImage()) { + return + } + if (message.files !== null && message.files.length > 0 && isFileVideo()) { + return + } + if (message.files !== null && message.files.length > 0 && isFileDocument()) { + return + } + return + + }) + } {ticket && { + console.log(disableFileButton) + if (!disableFileButton) fileInputRef.current?.click() + }} sx={{ height: "45px", width: "45px", p: 0, }} > + { + if (e.target.files?.[0]) sendFileHC(e.target.files?.[0]); + }} + style={{ display: "none" }} + type="file" + /> diff --git a/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx b/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx new file mode 100644 index 0000000..f180a67 --- /dev/null +++ b/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx @@ -0,0 +1,61 @@ +import { Box, Link, Typography, useMediaQuery, useTheme } from "@mui/material"; +import DownloadIcon from '@mui/icons-material/Download'; + +interface Props { + unAuthenticated?: boolean; + isSelf: boolean; + file: string; + createdAt: string; +} + +export default function ChatDocument({ + unAuthenticated = false, + isSelf, + file, + createdAt, +}: Props) { + const theme = useTheme(); + + const date = new Date(createdAt); + + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + + + + + + + + ); +} diff --git a/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx b/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx new file mode 100644 index 0000000..8842d48 --- /dev/null +++ b/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx @@ -0,0 +1,67 @@ +import { + Box, + ButtonBase, + Link, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; +import { useNavigate } from "react-router-dom"; + +interface Props { + unAuthenticated?: boolean; + isSelf: boolean; + file: string; + createdAt: string; +} + +export default function ChatImage({ + unAuthenticated = false, + isSelf, + file, + createdAt, +}: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); + + + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + + + + + + + + ); +} diff --git a/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx b/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx new file mode 100644 index 0000000..0c944a0 --- /dev/null +++ b/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx @@ -0,0 +1,52 @@ +import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; + +interface Props { + unAuthenticated?: boolean; + isSelf: boolean; + text: string; + createdAt: string; +} + +export default function ChatMessage({ + unAuthenticated = false, + isSelf, + text, + createdAt, +}: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + + + + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + + + + {text} + + + + ); +} diff --git a/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx b/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx new file mode 100644 index 0000000..5fb281f --- /dev/null +++ b/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx @@ -0,0 +1,68 @@ +import { + Box, + ButtonBase, + Link, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; +import { useNavigate } from "react-router-dom"; +import { useEffect } from "react"; + +interface Props { + unAuthenticated?: boolean; + isSelf: boolean; + file: string; + createdAt: string; +} + +export default function ChatImage({ + unAuthenticated = false, + isSelf, + file, + createdAt, +}: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); + + + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + + + + + + + + ); +} diff --git a/src/pages/dashboard/Content/Support/Chat/fileUpload.ts b/src/pages/dashboard/Content/Support/Chat/fileUpload.ts new file mode 100644 index 0000000..19410e1 --- /dev/null +++ b/src/pages/dashboard/Content/Support/Chat/fileUpload.ts @@ -0,0 +1,9 @@ +export const MAX_FILE_SIZE = 10485760; +export const MAX_PHOTO_SIZE = 5242880; +export const MAX_VIDEO_SIZE = 52428800; + +export const ACCEPT_SEND_MEDIA_TYPES_MAP = { + picture: ["jpg", "png"], + video: ["mp4"], + document: ["doc", "docx", "pdf", "txt", "xlsx", "csv"], + } as const; \ No newline at end of file diff --git a/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx b/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx new file mode 100644 index 0000000..3adc7e4 --- /dev/null +++ b/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx @@ -0,0 +1,20 @@ +import { Box } from "@mui/material"; +import { useLocation } from "react-router-dom"; + +export default function ChatImageNewWindow() { + const location = useLocation(); + console.log(location); + const srcImage = location.pathname.split("image/")[1]; + return ( + <> + + + ); +}