тултип для показа таймера
This commit is contained in:
parent
a7c78e57e7
commit
8604daf6a4
@ -1,12 +1,11 @@
|
|||||||
import { AuditoryItem } from "@/api/auditory";
|
import { AuditoryItem } from "@/api/auditory";
|
||||||
import CopyIcon from "@/assets/icons/CopyIcon";
|
|
||||||
import Trash from "@/assets/icons/trash";
|
import Trash from "@/assets/icons/trash";
|
||||||
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||||
import { InfoPopover } from "@/ui_kit/InfoPopover";
|
import { InfoPopover } from "@/ui_kit/InfoPopover";
|
||||||
import TooltipClickInfo from "@/ui_kit/Toolbars/TooltipClickInfo";
|
import TooltipClickInfo from "@/ui_kit/Toolbars/TooltipClickInfo";
|
||||||
import { useDomainDefine } from "@/utils/hooks/useDomainDefine";
|
import { useDomainDefine } from "@/utils/hooks/useDomainDefine";
|
||||||
import { IconButton, ListItem, Skeleton, Typography, useTheme } from "@mui/material";
|
import { IconButton, ListItem, Typography, useTheme } from "@mui/material";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { CopyButton } from "./CopyButton";
|
||||||
|
|
||||||
interface AuditoryLinkProps {
|
interface AuditoryLinkProps {
|
||||||
item: AuditoryItem;
|
item: AuditoryItem;
|
||||||
@ -20,66 +19,7 @@ export const AuditoryLink = ({ utmParams, item, index, onDelete }: AuditoryLinkP
|
|||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const { isTestServer } = useDomainDefine();
|
const { isTestServer } = useDomainDefine();
|
||||||
|
|
||||||
const getCreatedTime = (timestamp: number) => {
|
|
||||||
// Если timestamp в секундах (10 цифр)
|
|
||||||
if (timestamp.toString().length === 10) {
|
|
||||||
return new Date(timestamp * 1000).getTime();
|
|
||||||
}
|
|
||||||
// Если timestamp в миллисекундах (13 цифр)
|
|
||||||
return new Date(timestamp).getTime();
|
|
||||||
};
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(() => {
|
|
||||||
if (!item.created_at) return false;
|
|
||||||
const now = new Date().getTime();
|
|
||||||
const created = getCreatedTime(item.created_at);
|
|
||||||
const diffInMinutes = (now - created) / (1000 * 60);
|
|
||||||
console.log('Initial state:', {
|
|
||||||
created_at: item.created_at,
|
|
||||||
format: item.created_at.toString().length === 10 ? 'seconds' : 'milliseconds',
|
|
||||||
now,
|
|
||||||
created,
|
|
||||||
diffInMinutes,
|
|
||||||
isLoading: diffInMinutes < 2
|
|
||||||
});
|
|
||||||
return diffInMinutes < 2;
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!item.created_at) return;
|
|
||||||
|
|
||||||
const now = new Date().getTime();
|
|
||||||
const created = getCreatedTime(item.created_at);
|
|
||||||
const diffInMinutes = (now - created) / (1000 * 60);
|
|
||||||
|
|
||||||
console.log('Effect check:', {
|
|
||||||
created_at: item.created_at,
|
|
||||||
format: item.created_at.toString().length === 10 ? 'seconds' : 'milliseconds',
|
|
||||||
now,
|
|
||||||
created,
|
|
||||||
diffInMinutes,
|
|
||||||
timeDiff: now - created
|
|
||||||
});
|
|
||||||
|
|
||||||
if (now - created < 1000) {
|
|
||||||
console.log('Setting loading to true for new link');
|
|
||||||
setIsLoading(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diffInMinutes < 2) {
|
|
||||||
const timeLeft = Math.ceil((2 - diffInMinutes) * 60 * 1000);
|
|
||||||
console.log('Setting timer for:', timeLeft, 'ms');
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
console.log('Timer finished, setting loading to false');
|
|
||||||
setIsLoading(false);
|
|
||||||
}, timeLeft);
|
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
|
||||||
}
|
|
||||||
}, [item.created_at]);
|
|
||||||
|
|
||||||
const handleCopy = (text: string) => {
|
const handleCopy = (text: string) => {
|
||||||
if (isLoading) return;
|
|
||||||
navigator.clipboard.writeText(text);
|
navigator.clipboard.writeText(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -127,32 +67,11 @@ export const AuditoryLink = ({ utmParams, item, index, onDelete }: AuditoryLinkP
|
|||||||
<IconButton edge="end" aria-label="info" sx={{ color: theme.palette.brightPurple.main, p: 0, width: 18, height: 18 }}>
|
<IconButton edge="end" aria-label="info" sx={{ color: theme.palette.brightPurple.main, p: 0, width: 18, height: 18 }}>
|
||||||
<TooltipClickInfo title={`Пол: ${item.sex === 0 ? "женский" : item.sex === 1 ? "мужской" : "оба"} \n Возраст: ${item.age}`} />
|
<TooltipClickInfo title={`Пол: ${item.sex === 0 ? "женский" : item.sex === 1 ? "мужской" : "оба"} \n Возраст: ${item.age}`} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{isLoading ? (
|
<CopyButton
|
||||||
<Skeleton
|
created_at={item.created_at}
|
||||||
variant="circular"
|
onCopy={handleCopy}
|
||||||
width={18}
|
text={linkText}
|
||||||
height={18}
|
/>
|
||||||
sx={{
|
|
||||||
bgcolor: theme.palette.grey[400],
|
|
||||||
marginRight: "-2px"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconButton
|
|
||||||
edge="end"
|
|
||||||
aria-label="copy"
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.brightPurple.main,
|
|
||||||
p: 0,
|
|
||||||
width: 18,
|
|
||||||
height: 18,
|
|
||||||
marginRight: "-2px"
|
|
||||||
}}
|
|
||||||
onClick={() => handleCopy(linkText)}
|
|
||||||
>
|
|
||||||
<CopyIcon color={theme.palette.brightPurple.main} />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
161
src/pages/PersonalizationAI/CopyButton.tsx
Normal file
161
src/pages/PersonalizationAI/CopyButton.tsx
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import { IconButton, Skeleton, useTheme, Tooltip, ClickAwayListener } from "@mui/material";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import CopyIcon from "@/assets/icons/CopyIcon";
|
||||||
|
import { useSnackbar } from "notistack";
|
||||||
|
|
||||||
|
interface CopyButtonProps {
|
||||||
|
created_at: number;
|
||||||
|
onCopy: (text: string) => void;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CopyButton = ({ created_at, onCopy, text }: CopyButtonProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [timeLeft, setTimeLeft] = useState<string>("");
|
||||||
|
|
||||||
|
const getCreatedTime = (timestamp: number) => {
|
||||||
|
// Если timestamp в секундах (10 цифр)
|
||||||
|
if (timestamp.toString().length === 10) {
|
||||||
|
return new Date(timestamp * 1000).getTime();
|
||||||
|
}
|
||||||
|
// Если timestamp в миллисекундах (13 цифр)
|
||||||
|
return new Date(timestamp).getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatTimeLeft = (milliseconds: number) => {
|
||||||
|
const minutes = Math.floor(milliseconds / (1000 * 60));
|
||||||
|
const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
|
||||||
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(() => {
|
||||||
|
if (!created_at) return false;
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const created = getCreatedTime(created_at);
|
||||||
|
const diffInMinutes = (now - created) / (1000 * 60);
|
||||||
|
return diffInMinutes < 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!created_at) return;
|
||||||
|
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const created = getCreatedTime(created_at);
|
||||||
|
const diffInMinutes = (now - created) / (1000 * 60);
|
||||||
|
|
||||||
|
if (now - created < 1000) {
|
||||||
|
setIsLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffInMinutes < 2) {
|
||||||
|
const timeLeft = Math.ceil((2 - diffInMinutes) * 60 * 1000);
|
||||||
|
setTimeLeft(formatTimeLeft(timeLeft));
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
const currentTime = new Date().getTime();
|
||||||
|
const elapsed = currentTime - created;
|
||||||
|
const remaining = 2 * 60 * 1000 - elapsed;
|
||||||
|
|
||||||
|
if (remaining <= 0) {
|
||||||
|
setIsLoading(false);
|
||||||
|
clearInterval(timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeLeft(formatTimeLeft(remaining));
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}
|
||||||
|
}, [created_at]);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (isLoading) return;
|
||||||
|
onCopy(text);
|
||||||
|
enqueueSnackbar("Ссылка успешно скопирована", { variant: "success" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTooltipClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTooltipOpen = () => {
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<ClickAwayListener onClickAway={handleTooltipClose}>
|
||||||
|
<div>
|
||||||
|
<Tooltip
|
||||||
|
PopperProps={{
|
||||||
|
disablePortal: true,
|
||||||
|
sx: {
|
||||||
|
"& .MuiTooltip-tooltip": {
|
||||||
|
minWidth: "175px",
|
||||||
|
maxWidth: "175px",
|
||||||
|
whiteSpace: "normal",
|
||||||
|
textAlign: "center"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placement="top"
|
||||||
|
onClose={handleTooltipClose}
|
||||||
|
open={open}
|
||||||
|
title={`Идёт процесс генерации вопросов, он будет закончен через ${timeLeft}`}
|
||||||
|
onMouseEnter={handleTooltipOpen}
|
||||||
|
onMouseLeave={handleTooltipClose}
|
||||||
|
sx={{
|
||||||
|
fontSize: "12px",
|
||||||
|
p: "10px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Skeleton
|
||||||
|
variant="circular"
|
||||||
|
width={18}
|
||||||
|
height={18}
|
||||||
|
sx={{
|
||||||
|
bgcolor: theme.palette.grey[400],
|
||||||
|
marginRight: "-2px"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</ClickAwayListener>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
edge="end"
|
||||||
|
aria-label="copy"
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
p: 0,
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
marginRight: "-2px",
|
||||||
|
position: "relative",
|
||||||
|
"&:hover": {
|
||||||
|
"&::after": {
|
||||||
|
content: '""',
|
||||||
|
position: "absolute",
|
||||||
|
top: "-15px",
|
||||||
|
left: "-15px",
|
||||||
|
right: "-15px",
|
||||||
|
bottom: "-15px",
|
||||||
|
borderRadius: "50%",
|
||||||
|
backgroundColor: theme.palette.grey[500],
|
||||||
|
opacity: 0.1,
|
||||||
|
zIndex: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<CopyIcon color={theme.palette.brightPurple.main} />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user