Untitled
unknown
plain_text
a year ago
18 kB
2
Indexable
Never
import { PaperAirplaneIcon, StarIcon } from "@heroicons/react/24/solid"; import React, { useEffect, useState, useRef } from "react"; import LoadingBar from "react-top-loading-bar"; import useFade from "/components/useFade"; import Welcome from "/components/Welcome"; import { useRouter } from "next/router"; import moment from "moment-timezone"; import CGU from "/components/CGU"; import cookie from "js-cookie"; import _ from "lodash"; type Choice = { title: string; // Include any other properties you expect in a choice object }; type Response = { id: string; type: string; text: string; user: boolean; date: string; choices?: Choice[]; }; interface Props { err?: string; cgu: string; } interface Payload { message: string; keypwd?: string | string[]; fiche?: boolean; [key: string]: string | string[] | boolean | undefined; } const Chat = (props: Props) => { const router = useRouter(); const msgCGU = `En conversant avec moi, vous acceptez les Conditions Générales d'Utilisation, toutes vos informations sont confidentielles et uniquement à destination de votre praticien et de son équipe. Cliquez sur ce message pour les consulter.`; const [activeConversation, setActiveConversation] = useState(false); const [alrt, setAlrt] = useState<string>(props.err ? props.err : "none"); const [messagesg, setMessagesg] = useState<Response[]>([]); const [isVisible, setVisible, fadeProps] = useFade(false); const [messages, setMessages] = useState<Response[]>([]); const [transition, setTransition] = useState(true); const [closing, setClosing] = useState(false); const [selected, setSelected] = useState([]); const [authed, setAuthed] = useState(false); const [typing, setTyping] = useState(false); const [progress, setProgress] = useState(0); const [clicked, setClicked] = useState([]); const [input, setInput] = useState(""); const [con, setCon] = useState(true); const lastMsg = useRef<HTMLLIElement>(null); const messagesEndRef = useRef<HTMLDivElement>(null); const responsivness = 0.5; const sessionTime = 5; const Timeout = (time: number): AbortController => { const controller = new AbortController(); setTimeout(() => controller.abort(), time * 1000); return controller; }; useEffect(() => { const interval = setInterval(async () => { try { const res = await fetch("https://api.github.com/users/microsoft", { signal: Timeout(5).signal, }); if (!res.ok) throw new Error("Fetch failed"); const ping = await res.json(); if (!ping) setCon(false); } catch (error) { console.error(error); } }, 10000); return () => clearInterval(interval); }, []); useEffect(() => { const getThatDamnData = async () => { await fetch(`/api/messages?keypwd=${router.query.pid}`) .then(async (res) => { if (res.ok) { return res.json(); } else { throw await res.json(); } }) .then(async (data) => { if (data && authed) { if ( data.length < 1 || (messages[0] && messages[0].text === "bonjour") ) { const pl1: Payload = { message: "Bonjour", keypwd: router.query.pid, fiche: router.query.f ? true : false, }; Object.entries(router.query).map((e) => { if (e[0] !== "pid" && e[0] !== "f") { pl1[e[0]] = e[1]; } }); await fetch("/api/messages", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(pl1), }) .then(async (res) => { if (res.ok) { setAlrt("none"); setActiveConversation(true); return await res.json(); } else { throw await res.json(); } }) .then((str) => { if (str) { setMessagesg(str); setMessages(str); setProgress(100); } }) .catch((err) => { setAlrt(err.alrt); }); } const temp = [...data] .map((m) => { if (m.includes("triggerbp-")) { window.location.href = "/fin?keypwd=" + router.query.pid + "&mesg=" + JSON.parse(m).text; } else return m[0] === "{" && JSON.parse(m); }) .filter((m) => m) .map((m) => { if (m.text.includes("_opencgu")) { return { ...m, text: msgCGU, }; } return m; }); if (activeConversation) { if (temp.length > messagesg.length) { setActiveConversation(true); setMessagesg(temp); } else if (messagesg.length > messages.length) { setTimeout(async () => { setMessages([...messages, messagesg[messages.length]]); }, responsivness * 1000); } else { messagesg.length === messages.length && setTyping(false); } } else { setMessages(temp); setMessagesg(temp); setActiveConversation(true); } cookie.set("auth", moment().format("YYYY-MM-DD HH:mm:ss"), { expires: new Date(new Date().getTime() + sessionTime * 60 * 1000), }); } }) .catch(async (err) => { setAlrt(await err.alrt); }); }; const timer = setInterval(() => { getThatDamnData(); }, 2000); getThatDamnData(); scrollDown(); return () => clearTimeout(timer); }, []); useEffect(() => { if (!cookie.get("auth")) { fetch("/api/messages", { method: "PATCH", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ del: true, keypwd: router.query.pid, j: router.query.j, }), }) .then(async (res) => { if (!res.ok) { throw await res.json(); } }) .catch((err) => { console.log(err); }); } setAuthed(cookie.get("auth") || router.query.f ? true : false); }, [router.query.f, router.query.j, router.query.pid]); const scrollDown = () => { setTimeout(() => { lastMsg.current?.scrollIntoView({ behavior: "smooth" } ); }, 350); }; useEffect(() => { if (authed || router.query.f) { cookie.set("auth", moment().format("YYYY-MM-DD HH:mm:ss"), { expires: new Date(new Date().getTime() + sessionTime * 60 * 1000), }); } }, [authed, messages, router.query.f]); const handleSubmit = async (val: string) => { if (val !== "") { setAlrt("none"); setTyping(true); scrollDown(); setProgress(70); setMessages([ ...messages, { id: new Date().valueOf() + Math.random().toString(), type: "text", text: val, user: true, date: moment().tz("Europe/Paris").format("HH:mm"), }, ]); const pl3: Payload = { message: val, keypwd: router.query.pid, fiche: router.query.f ? true : false, }; Object.entries(router.query).map((e) => { if (e[0] !== "pid" && e[0] !== "f") { pl3[e[0]] = e[1]; } }); await fetch("/api/messages", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(pl3), }) .then(async (res) => { if (res.ok) { return res.json(); } else { throw await res.json(); } }) .then((str) => { if (str) { scrollDown(); } }) .catch((err) => { setAlrt(err.alrt); }) .finally(() => { setInput(""); setProgress(100); }); } }; const handleState = () => { isVisible ? setClosing(true) : setClosing(false); setVisible(!isVisible); }; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { setTimeout(() => { scrollToBottom(); }, 300); }, [messages, authed]); return typeof router.query.f !== "undefined" && !authed ? ( <Welcome setAuthed={setAuthed} /> ) : ( authed && ( <div className="Chat"> <div className={`alrt ${alrt}`}></div> <header> <div className="img"> <img src="/images/chatbot2.png" alt="logo bot" width={42} height={46} /> </div> <span>En ligne</span> <h1>ASISPO</h1> {typing && ( <div className="typing"> <div className="inner"> <span></span> <span></span> <span></span> </div> </div> )} </header> {!con && <div className="alrtMsg">Vérifiez votre connexion...</div>} <div className="chat"> {transition && ( <ul> <> {authed && (messages.length > 0 ? ( messages.map((msg, mi: number) => { if ( typeof msg.text !== "undefined" && msg.text !== "Bonjour" && msg.text[0] !== "_" ) { return ( <li className={msg.user ? "user-msg" : ""} key={mi} ref={messages.length === mi + 1 ? lastMsg : null} onClick={(e) => { if (msg.text === msgCGU) { handleState(); } else { e.preventDefault(); } }} > <> <span className="date">{msg.date}</span> <div className="botImg"> <img src="/images/bot-mini.png" alt="bot image" className="img" /> </div> <p>{msg.text}</p> {msg.choices && ( <div className="pbtn"> <div className={ msg.choices.length > 4 && msg.choices[0]?.title === "5" && msg.choices[4]?.title === "1" && !msg.choices[5] ? "choices stars" : "choices" } > {msg.choices.map((choice, i: number) => { return ( <button disabled={ clicked.includes(mi) || messages.length > mi + 2 } className={ (_.some(selected, { i: i, mi: mi, }) ? "selected" : "") + (msg.choices?.length > 6 || (msg.choices?.length > 4 && msg.choices[0].title === "5" && msg.choices[4].title === "1" && !msg.choices[5]) ? "" : " full") } key={i} onClick={() => { handleSubmit(choice.title); setClicked([...clicked, mi]); setSelected([ ...selected, { i: i, mi: mi, }, ]); }} > {msg.choices.length > 6 || (msg.choices.length > 4 && msg.choices[0].title === "5" && msg.choices[4].title === "1" && !msg.choices[5] && ( <StarIcon className="star" /> ))} <p>{choice.title}</p> </button> ); })} </div> </div> )} </> </li> ); } }) ) : ( <p>Chargement...</p> ))} <div ref={messagesEndRef} /> </> </ul> )} </div> <form> <LoadingBar className="loading-bar" color={"#47d3f6"} progress={progress} onLoaderFinished={() => setProgress(0)} /> <input disabled={isVisible} type="text" value={input} onChange={(e) => { if (!authed) { e.target.value = e.target.value .replace(/^(\d\d)(\d)$/g, "$1/$2") .replace(/^(\d\d\/\d\d)(\d+)$/g, "$1/$2") .replace(/[^\d/]/g, ""); } setInput(e.target.value); }} placeholder="Entrez votre message" onFocus={() => scrollDown()} size={!authed ? 10 : undefined} maxLength={!authed ? 10 : 300} /> <button className={input === "" ? "disabled" : ""} onClick={(e) => { e.preventDefault(); if (input !== "") { setProgress(20); handleSubmit(input); if (!authed) { setTransition(false); setTimeout(() => { setTransition(true); scrollDown(); }, 100); } } }} > <PaperAirplaneIcon /> </button> </form> <div className={isVisible ? "botBar topped" : "botBar"} onClick={() => handleState()} > CGU - ASISPO.COM @2022 </div> {isVisible && ( <div className={!closing ? "CGUcontainer" : "CGUcontainer disabled"} {...fadeProps} > <CGU toggle={handleState} cgu={props.cgu} /> </div> )} </div> ) ); }; export default Chat; export async function getServerSideProps({ query }) { const ihm_api: string = query.j ? process.env.IHM_API_J : process.env.IHM_API, ihm_token: string = process.env.IHM_API_TOKEN; let cgu: string = null; let err = "none"; await fetch(ihm_api + "/a6po/api/common/get-cgu/", { method: "GET", headers: { "Content-Type": "application/json", Authorization: ihm_token, }, }) .then(async (res) => { if (res.ok) { return res.json(); } else { throw await res.json(); } }) .then(async (str) => { if (str) { cgu = await str.content; } }) .catch(() => { err = "red"; }); return { props: { cgu: cgu, err: err, }, }; }