show amo token expired dialog on route changes

This commit is contained in:
nflnkr 2024-06-24 20:40:13 +03:00
parent 55ebc8a768
commit 1794ce032a
7 changed files with 93 additions and 112 deletions

@ -1,32 +1,30 @@
import type { SuspenseProps } from "react";
import { lazy, Suspense } from "react";
import { lazily } from "react-lazily";
import ContactFormModal from "@ui_kit/ContactForm";
import SigninDialog from "./pages/auth/Signin";
import SignupDialog from "./pages/auth/Signup";
import { Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
import "./index.css";
import Landing from "./pages/Landing/Landing";
import Main from "./pages/main";
import { clearAuthToken, getMessageFromFetchError, UserAccount, useUserFetcher } from "@frontend/kitui"; import { clearAuthToken, getMessageFromFetchError, UserAccount, useUserFetcher } from "@frontend/kitui";
import type { OriginalUserAccount } from "@root/user"; import type { OriginalUserAccount } from "@root/user";
import { clearUserData, setCustomerAccount, setUser, setUserAccount, useUserStore } from "@root/user"; import { clearUserData, setCustomerAccount, setUser, setUserAccount, useUserStore } from "@root/user";
import { enqueueSnackbar } from "notistack"; import ContactFormModal from "@ui_kit/ContactForm";
import PrivateRoute from "@ui_kit/PrivateRoute";
import FloatingSupportChat from "@ui_kit/FloatingSupportChat"; import FloatingSupportChat from "@ui_kit/FloatingSupportChat";
import PrivateRoute from "@ui_kit/PrivateRoute";
import { Restore } from "./pages/auth/Restore";
import { isAxiosError } from "axios";
import RecoverPassword from "./pages/auth/RecoverPassword";
import { InfoPrivilege } from "./pages/InfoPrivilege";
import OutdatedLink from "./pages/auth/OutdatedLink";
import { useAfterpay } from "@utils/hooks/useAfterpay"; import { useAfterpay } from "@utils/hooks/useAfterpay";
import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher"; import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
import { enqueueSnackbar } from "notistack";
import type { SuspenseProps } from "react";
import { lazy, Suspense } from "react";
import { lazily } from "react-lazily";
import { Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
import { useAmoAccount } from "./api/integration";
import ListPageDummy from "./components/Dummys/pageDummys/listPageDummy"; import ListPageDummy from "./components/Dummys/pageDummys/listPageDummy";
import "./index.css";
import OutdatedLink from "./pages/auth/OutdatedLink";
import RecoverPassword from "./pages/auth/RecoverPassword";
import { Restore } from "./pages/auth/Restore";
import SigninDialog from "./pages/auth/Signin";
import SignupDialog from "./pages/auth/Signup";
import { InfoPrivilege } from "./pages/InfoPrivilege";
import AmoTokenExpiredDialog from "./pages/IntegrationsPage/IntegrationsModal/AmoTokenExpiredDialog";
import Landing from "./pages/Landing/Landing";
import Main from "./pages/main";
const MyQuizzesFull = lazy(() => import("./pages/createQuize/MyQuizzesFull")); const MyQuizzesFull = lazy(() => import("./pages/createQuize/MyQuizzesFull"));
const QuizGallery = lazy(() => import("./pages/createQuize/QuizGallery")); const QuizGallery = lazy(() => import("./pages/createQuize/QuizGallery"));
const ViewPage = lazy(() => import("./pages/ViewPublicationPage")); const ViewPage = lazy(() => import("./pages/ViewPublicationPage"));
const Analytics = lazy(() => import("./pages/Analytics/Analytics")); const Analytics = lazy(() => import("./pages/Analytics/Analytics"));
@ -69,6 +67,7 @@ export default function App() {
const userId = useUserStore((state) => state.userId); const userId = useUserStore((state) => state.userId);
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const { data: amoAccount } = useAmoAccount();
useUserFetcher({ useUserFetcher({
url: `${process.env.REACT_APP_DOMAIN}/user/${userId}`, url: `${process.env.REACT_APP_DOMAIN}/user/${userId}`,
@ -127,6 +126,7 @@ export default function App() {
return ( return (
<> <>
{amoAccount && <AmoTokenExpiredDialog isAmoTokenExpired={amoAccount.stale} />}
<ContactFormModal /> <ContactFormModal />
<FloatingSupportChat /> <FloatingSupportChat />
{location.state?.backgroundLocation && ( {location.state?.backgroundLocation && (

@ -1,23 +1,22 @@
import { connectAmo } from "@/api/integration"; import { connectAmo } from "@/api/integration";
import { setTryShowAmoTokenExpiredDialog } from "@/stores/uiTools/actions";
import { useUiTools } from "@/stores/uiTools/store";
import CustomCheckbox from "@/ui_kit/CustomCheckbox"; import CustomCheckbox from "@/ui_kit/CustomCheckbox";
import { Box, Button, Dialog, Typography, useMediaQuery, useTheme } from "@mui/material"; import { Box, Button, Dialog, Typography, useTheme } from "@mui/material";
import { useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
const HIDE_DIALOG_EXPIRATION_PERIOD = 24 * 60 * 60 * 1000; const HIDE_DIALOG_EXPIRATION_PERIOD = 24 * 60 * 60 * 1000;
interface Props { interface Props {
initialOpen: boolean; isAmoTokenExpired: boolean;
} }
export default function AmoTokenExpiredDialog({ initialOpen }: Props) { export default function AmoTokenExpiredDialog({ isAmoTokenExpired }: Props) {
const theme = useTheme(); const theme = useTheme();
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(() => { const tryShowAmoTokenExpiredDialog = useUiTools((state) => state.tryShowAmoTokenExpiredDialog);
const hideExpirationTime = Number(localStorage.getItem("hideAmoTokenExpiredDialogExpirationTime"));
if (hideExpirationTime && hideExpirationTime > Date.now()) return false;
return initialOpen;
});
const [isHideDialogForADayChecked, setIsHideDialogForADayChecked] = useState<boolean>(false); const [isHideDialogForADayChecked, setIsHideDialogForADayChecked] = useState<boolean>(false);
const location = useLocation();
const onAmoClick = async () => { const onAmoClick = async () => {
const [url, error] = await connectAmo(); const [url, error] = await connectAmo();
@ -32,12 +31,16 @@ export default function AmoTokenExpiredDialog({ initialOpen }: Props) {
localStorage.setItem("hideAmoTokenExpiredDialogExpirationTime", expirationDate.toString()); localStorage.setItem("hideAmoTokenExpiredDialogExpirationTime", expirationDate.toString());
} }
setIsDialogOpen(false); setTryShowAmoTokenExpiredDialog(false);
} }
useEffect(() => {
setTryShowAmoTokenExpiredDialog(true);
}, [location]);
return ( return (
<Dialog <Dialog
open={isDialogOpen} open={isAmoTokenExpired && tryShowAmoTokenExpiredDialog}
onClose={handleDialogClose} onClose={handleDialogClose}
PaperProps={{ PaperProps={{
sx: { sx: {
@ -103,9 +106,7 @@ export default function AmoTokenExpiredDialog({ initialOpen }: Props) {
<CustomCheckbox <CustomCheckbox
label={"Не показывать сутки"} label={"Не показывать сутки"}
checked={isHideDialogForADayChecked} checked={isHideDialogForADayChecked}
handleChange={({ target }) => { handleChange={({ target }) => setIsHideDialogForADayChecked(target.checked)}
setIsHideDialogForADayChecked(target.checked);
}}
/> />
</Box> </Box>
</Dialog> </Dialog>

@ -43,7 +43,6 @@ export default function EditPage({
setScrollDown, setScrollDown,
}: Props) { }: Props) {
const quiz = useCurrentQuiz(); const quiz = useCurrentQuiz();
const { data: amoAccount } = useAmoAccount();
const { editQuizId } = useQuizStore(); const { editQuizId } = useQuizStore();
const { questions } = useQuestionsStore(); const { questions } = useQuestionsStore();
const { showConfirmLeaveModal, nextStep } = useUiTools(); const { showConfirmLeaveModal, nextStep } = useUiTools();
@ -108,7 +107,6 @@ export default function EditPage({
return ( return (
<> <>
{amoAccount && <AmoTokenExpiredDialog initialOpen={amoAccount.stale} />}
<Box <Box
sx={{ sx={{
display: isMobile ? "block" : "flex", display: isMobile ? "block" : "flex",

@ -28,27 +28,27 @@ export const updateEditSomeQuestion = (contentId?: string) => {
export const updateOpenedModalSettingsId = (id?: string) => export const updateOpenedModalSettingsId = (id?: string) =>
useUiTools.setState({ openedModalSettingsId: id ? id : null }); useUiTools.setState({ openedModalSettingsId: id ? id : null });
export const updateCanCreatePublic = (can: boolean) => export const updateCanCreatePublic = (can: boolean) => useUiTools.setState({ canCreatePublic: can });
useUiTools.setState({ canCreatePublic: can });
export const updateModalInfoWhyCantCreate = (can: boolean) => export const updateModalInfoWhyCantCreate = (can: boolean) => useUiTools.setState({ openModalInfoWhyCantCreate: can });
useUiTools.setState({ openModalInfoWhyCantCreate: can }); export const updateDeleteId = (deleteNodeId: string | null = null) => useUiTools.setState({ deleteNodeId });
export const updateDeleteId = (deleteNodeId: string | null = null) =>
useUiTools.setState({ deleteNodeId });
export const setShowConfirmLeaveModal = (showConfirmLeaveModal: boolean) => export const setShowConfirmLeaveModal = (showConfirmLeaveModal: boolean) =>
useUiTools.setState({ showConfirmLeaveModal }); useUiTools.setState({ showConfirmLeaveModal });
export const updateSomeWorkBackend = (someWorkBackend: boolean) => export const updateSomeWorkBackend = (someWorkBackend: boolean) => useUiTools.setState({ someWorkBackend });
useUiTools.setState({ someWorkBackend });
export const updateNextStep = (nextStep: number) => export const updateNextStep = (nextStep: number) => useUiTools.setState({ nextStep });
useUiTools.setState({ nextStep });
export const setModalQuestionParentContentId = ( export const setModalQuestionParentContentId = (modalQuestionParentContentId: string) =>
modalQuestionParentContentId: string, useUiTools.setState({ modalQuestionParentContentId });
) => useUiTools.setState({ modalQuestionParentContentId }); export const setModalQuestionTargetContentId = (modalQuestionTargetContentId: string) =>
export const setModalQuestionTargetContentId = ( useUiTools.setState({ modalQuestionTargetContentId });
modalQuestionTargetContentId: string, export const setOpenedModalQuestions = (open: boolean) => useUiTools.setState({ openedModalQuestions: open });
) => useUiTools.setState({ modalQuestionTargetContentId });
export const setOpenedModalQuestions = (open: boolean) => export const setTryShowAmoTokenExpiredDialog = (tryShowAmoTokenExpiredDialog: boolean) => {
useUiTools.setState({ openedModalQuestions: open }); const hideExpirationTime = Number(localStorage.getItem("hideAmoTokenExpiredDialogExpirationTime"));
useUiTools.setState({
tryShowAmoTokenExpiredDialog: hideExpirationTime > Date.now() ? false : tryShowAmoTokenExpiredDialog,
});
};

@ -16,6 +16,7 @@ export type UiTools = {
modalQuestionParentContentId: string; modalQuestionParentContentId: string;
modalQuestionTargetContentId: string; modalQuestionTargetContentId: string;
openedModalQuestions: boolean; openedModalQuestions: boolean;
tryShowAmoTokenExpiredDialog: boolean;
}; };
export type WhyCantCreatePublic = { export type WhyCantCreatePublic = {
@ -23,6 +24,9 @@ export type WhyCantCreatePublic = {
problems: string[]; problems: string[];
}; };
const hideAmoTokenExpiredDialogExpirationTime = Number(localStorage.getItem("hideAmoTokenExpiredDialogExpirationTime"));
const tryShowAmoTokenExpiredDialog = hideAmoTokenExpiredDialogExpirationTime > Date.now() ? false : true;
const initialState: UiTools = { const initialState: UiTools = {
openedModalSettingsId: null as null, openedModalSettingsId: null as null,
dragQuestionContentId: null, dragQuestionContentId: null,
@ -38,6 +42,7 @@ const initialState: UiTools = {
modalQuestionParentContentId: "", modalQuestionParentContentId: "",
modalQuestionTargetContentId: "", modalQuestionTargetContentId: "",
openedModalQuestions: false, openedModalQuestions: false,
tryShowAmoTokenExpiredDialog,
}; };
export const useUiTools = create<UiTools>()( export const useUiTools = create<UiTools>()(
@ -45,5 +50,5 @@ export const useUiTools = create<UiTools>()(
name: "UiTools", name: "UiTools",
enabled: process.env.NODE_ENV === "development", enabled: process.env.NODE_ENV === "development",
trace: process.env.NODE_ENV === "development", trace: process.env.NODE_ENV === "development",
}), })
); );

@ -11,7 +11,7 @@ import MenuItem from "../MenuItem";
import { useCurrentQuiz } from "@root/quizes/hooks"; import { useCurrentQuiz } from "@root/quizes/hooks";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { setCurrentStep } from "@root/quizes/actions"; import { setCurrentStep } from "@root/quizes/actions";
import { updateNextStep } from "@root/uiTools/actions"; import { setTryShowAmoTokenExpiredDialog, updateNextStep } from "@root/uiTools/actions";
const quizSettingsMenuItems = [ const quizSettingsMenuItems = [
[TagIcon, "Дополнения"], [TagIcon, "Дополнения"],
@ -40,6 +40,7 @@ export default function Sidebar({ changePage, disableCollapse }: SidebarProps) {
updateNextStep(index); updateNextStep(index);
changePage(index); changePage(index);
setTryShowAmoTokenExpiredDialog(true);
}; };
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev); const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
@ -109,13 +110,7 @@ export default function Sidebar({ changePage, disableCollapse }: SidebarProps) {
text={menuItem.sidebarText} text={menuItem.sidebarText}
isCollapsed={isMenuCollapsed} isCollapsed={isMenuCollapsed}
isActive={currentStep === index && pathname.startsWith("/edit")} isActive={currentStep === index && pathname.startsWith("/edit")}
disabled={ disabled={pathname.startsWith("/edit") ? false : quiz === undefined ? true : quiz?.config.type === null}
pathname.startsWith("/edit")
? false
: quiz === undefined
? true
: quiz?.config.type === null
}
icon={ icon={
<Icon <Icon
color={ color={
@ -153,17 +148,12 @@ export default function Sidebar({ changePage, disableCollapse }: SidebarProps) {
onClick={() => { onClick={() => {
navigate("/design"); navigate("/design");
setCurrentStep(15); setCurrentStep(15);
setTryShowAmoTokenExpiredDialog(true);
}} }}
text={"Дизайн"} text={"Дизайн"}
isCollapsed={isMenuCollapsed} isCollapsed={isMenuCollapsed}
isActive={pathname.startsWith("/design")} isActive={pathname.startsWith("/design")}
disabled={ disabled={pathname.startsWith("/design") ? false : quiz === undefined ? true : quiz?.config.type === null}
pathname.startsWith("/design")
? false
: quiz === undefined
? true
: quiz?.config.type === null
}
icon={ icon={
<PencilCircleIcon <PencilCircleIcon
color={ color={
@ -182,16 +172,13 @@ export default function Sidebar({ changePage, disableCollapse }: SidebarProps) {
onClick={() => { onClick={() => {
navigate("/integrations"); navigate("/integrations");
setCurrentStep(16); setCurrentStep(16);
setTryShowAmoTokenExpiredDialog(true);
}} }}
text={"Интеграции"} text={"Интеграции"}
isCollapsed={isMenuCollapsed} isCollapsed={isMenuCollapsed}
isActive={pathname.startsWith("/integrations")} isActive={pathname.startsWith("/integrations")}
disabled={ disabled={
pathname.startsWith("/integrations") pathname.startsWith("/integrations") ? false : quiz === undefined ? true : quiz?.config.type === null
? false
: quiz === undefined
? true
: quiz?.config.type === null
} }
icon={ icon={
<PuzzlePieceIcon <PuzzlePieceIcon

@ -1,13 +1,5 @@
import React, { ChangeEvent, FC, useEffect, useRef, useState } from "react"; import React, { ChangeEvent, FC, useEffect, useRef, useState } from "react";
import { import { Box, FormControl, IconButton, TextField, Typography, useMediaQuery, useTheme } from "@mui/material";
Box,
FormControl,
IconButton,
TextField,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { SidebarModal } from "./SidebarModal"; import { SidebarModal } from "./SidebarModal";
@ -21,7 +13,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
import { LogoutButton } from "@ui_kit/LogoutButton"; import { LogoutButton } from "@ui_kit/LogoutButton";
import PencilCircleIcon from "@icons/PencilCircleIcon"; import PencilCircleIcon from "@icons/PencilCircleIcon";
import { quizSetupSteps } from "@model/quizSettings"; import { quizSetupSteps } from "@model/quizSettings";
import { updateNextStep } from "@root/uiTools/actions"; import { setTryShowAmoTokenExpiredDialog, updateNextStep } from "@root/uiTools/actions";
import { handleLogoutClick } from "@utils/HandleLogoutClick"; import { handleLogoutClick } from "@utils/HandleLogoutClick";
interface SidebarIconProps { interface SidebarIconProps {
@ -37,12 +29,7 @@ interface Iprops {
scrollDown: boolean; scrollDown: boolean;
} }
export const SidebarMobile: FC<Iprops> = ({ export const SidebarMobile: FC<Iprops> = ({ open, changePage, setHeightSitebar, scrollDown }) => {
open,
changePage,
setHeightSitebar,
scrollDown,
}) => {
const theme = useTheme(); const theme = useTheme();
const isWrappSidebar = useMediaQuery(theme.breakpoints.down(400)); const isWrappSidebar = useMediaQuery(theme.breakpoints.down(400));
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
@ -58,7 +45,7 @@ export const SidebarMobile: FC<Iprops> = ({
new ResizeObserver((entries) => { new ResizeObserver((entries) => {
const { height } = entries[0].contentRect; const { height } = entries[0].contentRect;
setHeightSitebar(height); setHeightSitebar(height);
}), })
); );
useEffect(() => { useEffect(() => {
@ -72,8 +59,7 @@ export const SidebarMobile: FC<Iprops> = ({
}; };
const clickInput = (event: MouseEvent) => { const clickInput = (event: MouseEvent) => {
if (ref.current && !ref.current?.contains(event.target as Node)) if (ref.current && !ref.current?.contains(event.target as Node)) setInputOpen(false);
setInputOpen(false);
}; };
useEffect(() => { useEffect(() => {
document.addEventListener("mousedown", clickInput); document.addEventListener("mousedown", clickInput);
@ -89,6 +75,7 @@ export const SidebarMobile: FC<Iprops> = ({
updateNextStep(index); updateNextStep(index);
changePage(index); changePage(index);
setTryShowAmoTokenExpiredDialog(true);
}; };
const openPopper = Boolean(anchorEl); const openPopper = Boolean(anchorEl);
const id = openPopper ? "simple-popper" : ""; const id = openPopper ? "simple-popper" : "";
@ -130,11 +117,12 @@ export const SidebarMobile: FC<Iprops> = ({
}} }}
> >
<Box> <Box>
<Typography sx={{ fontSize: "12px", color: "#9A9AAF" }}> <Typography sx={{ fontSize: "12px", color: "#9A9AAF" }}>Название</Typography>
Название
</Typography>
{inputOpen ? ( {inputOpen ? (
<FormControl fullWidth variant="standard"> <FormControl
fullWidth
variant="standard"
>
<TextField <TextField
ref={ref} ref={ref}
value={inputValue} value={inputValue}
@ -173,12 +161,18 @@ export const SidebarMobile: FC<Iprops> = ({
/> />
</FormControl> </FormControl>
) : ( ) : (
<Typography color={"white"} sx={{ wordBreak: "break-word" }}> <Typography
color={"white"}
sx={{ wordBreak: "break-word" }}
>
{quiz.name} {quiz.name}
</Typography> </Typography>
)} )}
</Box> </Box>
<IconButton onClick={() => setInputOpen(true)} sx={{ mt: "10px" }}> <IconButton
onClick={() => setInputOpen(true)}
sx={{ mt: "10px" }}
>
<Pencil <Pencil
style={{ style={{
position: "absolute", position: "absolute",
@ -242,20 +236,18 @@ export const SidebarMobile: FC<Iprops> = ({
borderRadius: "8px", borderRadius: "8px",
}} }}
> >
{React.createElement( {React.createElement(sidebarIcon as React.FC<SidebarIconProps>, {
sidebarIcon as React.FC<SidebarIconProps>, height: isWrappSidebar ? "25px" : "32px",
{ width: isWrappSidebar ? "25px" : "32px",
height: isWrappSidebar ? "25px" : "32px", color: `${theme.palette.brightPurple.main}`,
width: isWrappSidebar ? "25px" : "32px", })}
color: `${theme.palette.brightPurple.main}`,
},
)}
</Box> </Box>
))} ))}
<Box <Box
onClick={() => { onClick={() => {
navigate("/design"); navigate("/design");
setCurrentStep(15); setCurrentStep(15);
setTryShowAmoTokenExpiredDialog(true);
}} }}
sx={{ sx={{
cursor: "pointer", cursor: "pointer",
@ -293,9 +285,7 @@ export const SidebarMobile: FC<Iprops> = ({
marginLeft: "0", marginLeft: "0",
}} }}
> >
<Settings <Settings style={{ color: "#974BFA", fontSize: "24px", marginLeft: "5px" }} />
style={{ color: "#974BFA", fontSize: "24px", marginLeft: "5px" }}
/>
<ArrowDown style={{ color: "#F2F3F7" }} /> <ArrowDown style={{ color: "#F2F3F7" }} />
</Box> </Box>
</Box> </Box>