Untitled

mail@pastecode.io avatar
unknown
plain_text
2 years ago
18 kB
2
Indexable
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,
    },
  };
}