first AI page
This commit is contained in:
parent
e2e391325b
commit
262337272f
@ -35,6 +35,7 @@ const { DesignPage } = lazily(() => import("./pages/DesignPage/DesignPage"));
|
||||
const { IntegrationsPage } = lazily(() => import("./pages/IntegrationsPage/IntegrationsPage"));
|
||||
const { QuizAnswersPage } = lazily(() => import("./pages/QuizAnswersPage/QuizAnswersPage"));
|
||||
const ChatImageNewWindow = lazy(() => import("@ui_kit/FloatingSupportChat/ChatImageNewWindow"));
|
||||
const PersonalizationAI = lazy(() => import("./pages/PersonalizationAI/PersonalizationAI"));
|
||||
let params = new URLSearchParams(document.location.search);
|
||||
const isTest = Boolean(params.get("test"))
|
||||
|
||||
@ -60,6 +61,12 @@ const routeslink = [
|
||||
sidebar: true,
|
||||
footer: true,
|
||||
},
|
||||
{
|
||||
path: "/personalization-ai",
|
||||
page: PersonalizationAI,
|
||||
header: true,
|
||||
sidebar: true,
|
||||
},
|
||||
] as const;
|
||||
|
||||
const LazyLoading = ({ children, fallback }: SuspenseProps) => (
|
||||
|
5
src/assets/icons/AiPersonalizationIcon.svg
Normal file
5
src/assets/icons/AiPersonalizationIcon.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 15L19.6668 16.2577C20.1354 17.1416 20.8584 17.8646 21.7423 18.3332L23 19L21.7423 19.6668C20.8584 20.1354 20.1354 20.8584 19.6668 21.7423L19 23L18.3332 21.7423C17.8646 20.8584 17.1416 20.1354 16.2577 19.6668L15 19L16.2577 18.3332C17.1416 17.8646 17.8646 17.1416 18.3332 16.2577L19 15Z" stroke="#7E2AEA" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M20 11V7C20 4.23858 17.7614 2 15 2H7C4.23858 2 2 4.23858 2 7V15C2 17.7614 4.23858 20 7 20H11" stroke="#7E2AEA" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M7.5 14.5V9.25C7.5 8.78587 7.68437 8.34075 8.01256 8.01256C8.34075 7.68437 8.78587 7.5 9.25 7.5C9.71413 7.5 10.1592 7.68437 10.4874 8.01256C10.8156 8.34075 11 8.78587 11 9.25V14.5M7.5 11.875H11M14.5 7.5V14.5" stroke="#7E2AEA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 930 B |
12
src/assets/icons/AiPersonalizationIcon.tsx
Normal file
12
src/assets/icons/AiPersonalizationIcon.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import * as React from "react";
|
||||
import { SvgIcon, SvgIconProps } from "@mui/material";
|
||||
|
||||
const AiPersonalizationIcon = (props: SvgIconProps) => (
|
||||
<SvgIcon {...props} viewBox="0 0 24 24" fill="none">
|
||||
<path d="M19 15L19.6668 16.2577C20.1354 17.1416 20.8584 17.8646 21.7423 18.3332L23 19L21.7423 19.6668C20.8584 20.1354 20.1354 20.8584 19.6668 21.7423L19 23L18.3332 21.7423C17.8646 20.8584 17.1416 20.1354 16.2577 19.6668L15 19L16.2577 18.3332C17.1416 17.8646 17.8646 17.1416 18.3332 16.2577L19 15Z" stroke="#7E2AEA" strokeWidth="1.5" strokeLinejoin="round"/>
|
||||
<path d="M20 11V7C20 4.23858 17.7614 2 15 2H7C4.23858 2 2 4.23858 2 7V15C2 17.7614 4.23858 20 7 20H11" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round"/>
|
||||
<path d="M7.5 14.5V9.25C7.5 8.78587 7.68437 8.34075 8.01256 8.01256C8.34075 7.68437 8.78587 7.5 9.25 7.5C9.71413 7.5 10.1592 7.68437 10.4874 8.01256C10.8156 8.34075 11 8.78587 11 9.25V14.5M7.5 11.875H11M14.5 7.5V14.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</SvgIcon>
|
||||
);
|
||||
|
||||
export default AiPersonalizationIcon;
|
172
src/pages/PersonalizationAI/GenderAndAgeSelector.tsx
Normal file
172
src/pages/PersonalizationAI/GenderAndAgeSelector.tsx
Normal file
@ -0,0 +1,172 @@
|
||||
import { Box, FormControl, FormLabel, RadioGroup, FormControlLabel, Radio, Select, MenuItem, useTheme } from "@mui/material";
|
||||
import { InfoPopover } from '@ui_kit/InfoPopover';
|
||||
import CheckboxIcon from "@icons/Checkbox";
|
||||
|
||||
interface GenderAndAgeSelectorProps {
|
||||
gender: string;
|
||||
setGender: (gender: string) => void;
|
||||
}
|
||||
|
||||
export default function GenderAndAgeSelector({ gender, setGender }: GenderAndAgeSelectorProps) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box sx={{ display: 'flex', gap: 4 }}>
|
||||
|
||||
|
||||
|
||||
<FormControl component="fieldset" variant="standard">
|
||||
<Box sx={{ display: 'flex', alignItems: "center", gap: '4px' }}>
|
||||
<FormLabel
|
||||
sx={{
|
||||
'&.Mui-focused': {
|
||||
color: '#4D4D4D',
|
||||
},
|
||||
color: '#4D4D4D',
|
||||
fontSize: '18px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1,
|
||||
letterSpacing: 0,
|
||||
mr: '3px',
|
||||
}}
|
||||
|
||||
component="legend" >Пол</FormLabel>
|
||||
<InfoPopover />
|
||||
</Box>
|
||||
<RadioGroup
|
||||
sx={{
|
||||
width: "155px",
|
||||
justifyContent: "space-between",
|
||||
mt: "9px",
|
||||
ml: "-9px"
|
||||
}}
|
||||
row aria-label="gender" name="row-radio-buttons-group" value={gender} onChange={(e) => setGender(e.target.value)}>
|
||||
|
||||
<FormControlLabel
|
||||
|
||||
sx={{
|
||||
// 1. Уменьшаем общий размер блока
|
||||
padding: 0,
|
||||
|
||||
// 2. Но сохраняем пространство для Ripple
|
||||
'& .MuiTouchRipple-root': {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'visible',
|
||||
},
|
||||
|
||||
// 3. Позиционируем иконку абсолютно
|
||||
'& .MuiSvgIcon-root': {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
},
|
||||
|
||||
'& .MuiFormControlLabel-label': {
|
||||
fontSize: '18px',
|
||||
fontWeight: 400,
|
||||
fontFamily: 'Rubik',
|
||||
lineHeight: 1,
|
||||
letterSpacing: 0,
|
||||
color: '#4D4D4D',
|
||||
ml: "6px"
|
||||
},
|
||||
m: 0,
|
||||
}}
|
||||
|
||||
value="male" control={<Radio icon={<CheckboxIcon />} checkedIcon={<CheckboxIcon checked />} />} label="М" />
|
||||
<FormControlLabel
|
||||
|
||||
sx={{
|
||||
// 1. Уменьшаем общий размер блока
|
||||
padding: 0,
|
||||
|
||||
// 2. Но сохраняем пространство для Ripple
|
||||
'& .MuiTouchRipple-root': {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'visible',
|
||||
},
|
||||
|
||||
// 3. Позиционируем иконку абсолютно
|
||||
'& .MuiSvgIcon-root': {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
},
|
||||
|
||||
'& .MuiFormControlLabel-label': {
|
||||
fontSize: '18px',
|
||||
fontWeight: 400,
|
||||
fontFamily: 'Rubik',
|
||||
lineHeight: 1,
|
||||
letterSpacing: 0,
|
||||
color: '#4D4D4D',
|
||||
},
|
||||
m: 0,
|
||||
}}
|
||||
|
||||
value="female" control={<Radio icon={<CheckboxIcon />} checkedIcon={<CheckboxIcon checked />} />} label="Ж" />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
<FormControl sx={{ minWidth: "420px", marginLeft: "15px" }} variant="filled">
|
||||
<Box sx={{
|
||||
|
||||
display: 'flex', alignItems: 'center', gap: '4px'
|
||||
}}>
|
||||
<FormLabel sx={{
|
||||
color: '#4D4D4D',
|
||||
fontSize: "18px",
|
||||
fontWeight: 500,
|
||||
lineHeight: "100%",
|
||||
'&.Mui-focused': {
|
||||
color: '#4D4D4D',
|
||||
},
|
||||
}}>Возраст</FormLabel>
|
||||
<InfoPopover />
|
||||
</Box>
|
||||
<Select
|
||||
value="35-50"
|
||||
displayEmpty
|
||||
inputProps={{ 'aria-label': 'age', disableUnderline: true }}
|
||||
disableUnderline
|
||||
sx={{
|
||||
height: "48px",
|
||||
maxWidth: "420px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #9A9AAF",
|
||||
'& .MuiSelect-filled': {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
|
||||
},
|
||||
'& .MuiSelect-select': {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
p: "10px 20px"
|
||||
},
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: '#E0E0E0',
|
||||
},
|
||||
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: '#B0B0B0',
|
||||
},
|
||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: '#7E2AEA',
|
||||
},
|
||||
mt: "17px",
|
||||
}}
|
||||
>
|
||||
|
||||
<MenuItem value="">Выберите возраст</MenuItem>
|
||||
<MenuItem value="35-50">35-50</MenuItem>
|
||||
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
);
|
||||
}
|
167
src/pages/PersonalizationAI/PersonalizationAI.tsx
Normal file
167
src/pages/PersonalizationAI/PersonalizationAI.tsx
Normal file
@ -0,0 +1,167 @@
|
||||
import { Box, Container, Typography, TextField, Button, List, ListItem, IconButton } from "@mui/material";
|
||||
import { InfoPopover } from '@ui_kit/InfoPopover';
|
||||
import CopyIcon from "@/assets/icons/CopyIcon";
|
||||
import GenderAndAgeSelector from "./GenderAndAgeSelector";
|
||||
import { useState } from "react";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import { ArrowDownIcon } from "../../assets/icons/questionsPage/ArrowDownIcon";
|
||||
import { useTheme } from "@mui/material";
|
||||
|
||||
const PURPLE = "#7E2AEA";
|
||||
const GREY_TEXT = "#A0A0A0";
|
||||
const GREY_BORDER = "#E0E0E0";
|
||||
const GREY_ICON = "#B0B0B0";
|
||||
const BLOCK_RADIUS = "16px";
|
||||
const BLOCK_PX = "32px";
|
||||
const BLOCK_PY = "24px";
|
||||
|
||||
export default function PersonalizationAI() {
|
||||
const [gender, setGender] = useState('');
|
||||
const [linksOpen, setLinksOpen] = useState(true);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Container id="PersonalizationAI" maxWidth={false} sx={{ minHeight: "100%", p: "20px" }}>
|
||||
<Typography variant="h5" color={theme.palette.grey3.main} fontWeight={700} sx={{ fontSize: 24, letterSpacing: "-0.2px" }}>
|
||||
Персонализация вопросов с помощью AI
|
||||
</Typography>
|
||||
<Typography sx={{
|
||||
color: theme.palette.grey3.main, fontSize: "18px", maxWidth: 796, m: 0,
|
||||
mt: "19px",
|
||||
letterSpacing: "0.009px",
|
||||
wordSpacing: "0.1px",
|
||||
lineHeight: "21.4px"
|
||||
}}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
</Typography>
|
||||
|
||||
{/* Первый белый блок */}
|
||||
<Box sx={{
|
||||
bgcolor: "#fff",
|
||||
borderRadius: "12px",
|
||||
mt: "40px",
|
||||
p: "20px 20px 30px",
|
||||
boxShadow: "none",
|
||||
maxWidth: "796px"
|
||||
}}>
|
||||
<GenderAndAgeSelector gender={gender} setGender={setGender} />
|
||||
|
||||
|
||||
|
||||
|
||||
{/* Ссылка */}
|
||||
<Box sx={{ mt: "34px" }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<Typography sx={{ color: theme.palette.grey3.main, fontSize: "18px", fontWeight: 500 }}>Ссылка</Typography>
|
||||
<InfoPopover />
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
lineHeight: "100%",
|
||||
letterSpacing: "0 %",
|
||||
color: theme.palette.grey2.main,
|
||||
mt: "16px"
|
||||
}}
|
||||
>
|
||||
Вставьте ссылку со всеми utm-метками
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mt: "6px" }}>
|
||||
<CustomTextField
|
||||
placeholder="linkexample.com"
|
||||
maxLength={5}
|
||||
sxForm={{
|
||||
width: "615px",
|
||||
}}
|
||||
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
bgcolor: PURPLE,
|
||||
borderRadius: "8px",
|
||||
width: "130px",
|
||||
height: "48px",
|
||||
boxShadow: "none",
|
||||
textTransform: "none",
|
||||
fontSize: "18px",
|
||||
'&:hover': { bgcolor: PURPLE },
|
||||
}}
|
||||
>
|
||||
Ок
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Второй белый блок */}
|
||||
<Box sx={{
|
||||
maxWidth: "796px",
|
||||
bgcolor: "#fff",
|
||||
borderRadius: "12px",
|
||||
p: "20px",
|
||||
boxShadow: "0px 4px 32px 0px #7E2AEA14",
|
||||
mt: "24px"
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography sx={{ fontSize: "18px", fontWeight: 500, color: theme.palette.grey3.main }}>
|
||||
Ваши сохраненные ссылки
|
||||
</Typography>
|
||||
<IconButton
|
||||
sx={{ cursor: 'pointer', color: PURPLE, display: 'flex', alignItems: 'center', transition: 'transform 0.2s', transform: linksOpen ? 'rotate(0deg)' : 'rotate(180deg)' }}
|
||||
onClick={() => setLinksOpen((prev) => !prev)}
|
||||
size="large"
|
||||
>
|
||||
<ArrowDownIcon style={{ width: "18px", height: "18px" }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Collapse in={linksOpen} timeout="auto" unmountOnExit sx={{ mt: "3px" }}>
|
||||
<List sx={{ gap: '8px', p: 0, m: 0 }}>
|
||||
{[1, 2, 3, 4, 5].map((_, idx) => (
|
||||
<ListItem
|
||||
key={idx}
|
||||
disablePadding
|
||||
sx={{
|
||||
bgcolor: "#F2F3F7",
|
||||
borderRadius: "10px",
|
||||
p: "13px 14px 13px 20px",
|
||||
mb: "8px",
|
||||
maxWidth: "756px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
transition: 'background 0.2s, border 0.2s',
|
||||
'& .MuiListItemSecondaryAction-root': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
width: "60px",
|
||||
justifyContent: "space-between",
|
||||
|
||||
},
|
||||
}}
|
||||
secondaryAction={
|
||||
<>
|
||||
<IconButton edge="end" aria-label="info" sx={{ color: PURPLE, p: 0, width: 18, height: 18 }}>
|
||||
<InfoPopover />
|
||||
</IconButton>
|
||||
<IconButton edge="end" aria-label="copy" sx={{ color: PURPLE, p: 0, width: 18, height: 18, marginRight: "-2px" }}>
|
||||
|
||||
<CopyIcon
|
||||
color={PURPLE}
|
||||
/>
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography sx={{ color: 'black', fontWeight: 400, fontSize: "16px" }}>
|
||||
linkexample.ru
|
||||
</Typography>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
53
src/ui_kit/InfoPopover.tsx
Normal file
53
src/ui_kit/InfoPopover.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { useState, MouseEvent } from "react";
|
||||
import Info from "@icons/Info";
|
||||
|
||||
import { Paper, Popover, SxProps, Typography } from "@mui/material";
|
||||
|
||||
export const InfoPopover = ({ blink = false, sx }: {blink?: boolean, sx?: SxProps}) => {
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const id = open ? "simple-popover" : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Info
|
||||
className={blink ? "blink" : ""}
|
||||
onClick={handleClick}
|
||||
sx={{p:0, height: "20px", ...sx}}
|
||||
/>
|
||||
<Popover
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
sx={{
|
||||
p: "20px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
подсказка
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
@ -12,6 +12,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { setCurrentStep } from "@root/quizes/actions";
|
||||
import { setTryShowAmoTokenExpiredDialog, updateNextStep } from "@root/uiTools/actions";
|
||||
import AiPersonalizationIcon from "../../assets/icons/AiPersonalizationIcon";
|
||||
|
||||
const quizSettingsMenuItems = [
|
||||
[TagIcon, "Дополнения"],
|
||||
@ -168,6 +169,20 @@ export default function Sidebar({ changePage, disableCollapse }: SidebarProps) {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
navigate("/personalization-ai");
|
||||
setCurrentStep(17);
|
||||
setTryShowAmoTokenExpiredDialog(true);
|
||||
}}
|
||||
text={"Персонализация вопросов с помощью AI"}
|
||||
isCollapsed={isMenuCollapsed}
|
||||
isActive={pathname.startsWith("/personalization-ai")}
|
||||
disabled={pathname.startsWith("/personalization-ai") ? false : quiz === undefined ? true : quiz?.config.type === null}
|
||||
icon={
|
||||
<AiPersonalizationIcon />
|
||||
}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
navigate("/integrations");
|
||||
|
Loading…
Reference in New Issue
Block a user