feat: Funnel and Answers
This commit is contained in:
parent
31a8b613bb
commit
16f36cf5d9
@ -16,6 +16,8 @@ import HeaderFull from "@ui_kit/Header/HeaderFull";
|
||||
import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||
|
||||
import { General } from "./General";
|
||||
import { Answers } from "./Answers";
|
||||
import { Funnel } from "./Funnel";
|
||||
import { Devices } from "./Devices";
|
||||
|
||||
import CalendarIcon from "@icons/CalendarIcon";
|
||||
@ -172,6 +174,8 @@ export default function Analytics() {
|
||||
</Button>
|
||||
</Box>
|
||||
<General />
|
||||
<Answers />
|
||||
<Funnel />
|
||||
<Devices />
|
||||
</SectionWrapper>
|
||||
</>
|
||||
|
||||
101
src/pages/Analytics/Answers.tsx
Normal file
101
src/pages/Analytics/Answers.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
Typography,
|
||||
LinearProgress,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
type AnswerProps = {
|
||||
title: string;
|
||||
percent: number;
|
||||
};
|
||||
|
||||
const ANSWERS_MOCK: Record<string, number> = {
|
||||
"Добавьте ответ": 67,
|
||||
"Вопрос пропущен": 7,
|
||||
Другое: 27,
|
||||
};
|
||||
|
||||
const Answer = ({ title, percent }: AnswerProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box sx={{ padding: "15px 25px" }}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
title={title}
|
||||
value={percent}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "44px",
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: "10px",
|
||||
border: `1px solid ${theme.palette.grey2.main}`,
|
||||
"& > span": { background: "#9A9AAF1A" },
|
||||
"&::before": {
|
||||
content: `"${title}"`,
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
left: "20px",
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
color: theme.palette.grey3.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Box sx={{ minWidth: 35 }}>
|
||||
<Typography
|
||||
sx={{
|
||||
minWidth: "45px",
|
||||
color:
|
||||
percent === 100
|
||||
? theme.palette.text.secondary
|
||||
: theme.palette.text.primary,
|
||||
}}
|
||||
>{`${percent}%`}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const Answers = () => {
|
||||
const [answers, setAnswers] = useState<Record<string, number>>(ANSWERS_MOCK);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
|
||||
marginTop: "60px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="h3"
|
||||
sx={{
|
||||
padding: "25px",
|
||||
fontSize: "24px",
|
||||
fontWeight: "bold",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
Заголовок вопроса. Варианты ответов
|
||||
</Typography>
|
||||
{Object.entries(answers).map(([title, percent]) => (
|
||||
<Answer key={title} title={title} percent={percent} />
|
||||
))}
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
@ -20,20 +20,9 @@ const COLORS: Record<number, string> = {
|
||||
};
|
||||
|
||||
const DEVICES_MOCK: DevicesResponse = {
|
||||
device: {
|
||||
PC: 75,
|
||||
Mobile: 25,
|
||||
},
|
||||
os: {
|
||||
Windows: 44,
|
||||
AndroidOS: 25,
|
||||
"OS X": 19,
|
||||
Linux: 13,
|
||||
},
|
||||
browser: {
|
||||
Chrome: 75,
|
||||
Firefox: 25,
|
||||
},
|
||||
device: { PC: 75, Mobile: 25 },
|
||||
os: { Windows: 44, AndroidOS: 25, "OS X": 19, Linux: 13 },
|
||||
browser: { Chrome: 75, Firefox: 25 },
|
||||
};
|
||||
|
||||
const Device = ({ title, devices }: DeviceProps) => {
|
||||
@ -72,6 +61,7 @@ const Device = ({ title, devices }: DeviceProps) => {
|
||||
>
|
||||
{data.map(({ id, value, color }) => (
|
||||
<Box
|
||||
key={id}
|
||||
sx={{
|
||||
display: "flex",
|
||||
marginBottom: "10px",
|
||||
|
||||
134
src/pages/Analytics/Funnel.tsx
Normal file
134
src/pages/Analytics/Funnel.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
Typography,
|
||||
LinearProgress,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
type FunnelItemProps = {
|
||||
title: string;
|
||||
percent: number;
|
||||
};
|
||||
|
||||
const FUNNEL_MOCK: Record<string, number> = {
|
||||
"Стартовая страница": 100,
|
||||
"Воронка квиза": 69,
|
||||
Заявки: 56,
|
||||
Результаты: 56,
|
||||
};
|
||||
|
||||
const FunnelItem = ({ title, percent }: FunnelItemProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
padding: "15px 25px",
|
||||
"&:last-child div::after": { display: "none" },
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ marginBottom: "10px", fontWeight: "bold" }}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
"&::after": {
|
||||
content: "''",
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
bottom: "-15px",
|
||||
height: "1px",
|
||||
width: "100%",
|
||||
maxWidth: "300px",
|
||||
background: "#9A9AAF80",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
border: "1px solid black",
|
||||
borderRadius: "8px",
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
1
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", alignItems: "center", flexGrow: 1 }}>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={percent}
|
||||
sx={{
|
||||
width: "100%",
|
||||
marginRight: "15px",
|
||||
|
||||
height: "12px",
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: "6px",
|
||||
border:
|
||||
percent === 100
|
||||
? `1px solid ${theme.palette.brightPurple.main}`
|
||||
: null,
|
||||
"& > span": { background: "#D9C0F9" },
|
||||
}}
|
||||
/>
|
||||
<Box sx={{ minWidth: 35 }}>
|
||||
<Typography
|
||||
sx={{
|
||||
minWidth: "45px",
|
||||
color:
|
||||
percent === 100
|
||||
? theme.palette.text.secondary
|
||||
: theme.palette.text.primary,
|
||||
}}
|
||||
>{`${percent}%`}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const Funnel = () => {
|
||||
const [funnel, setFunnel] = useState<Record<string, number>>(FUNNEL_MOCK);
|
||||
|
||||
useEffect(() => {
|
||||
// const requestFunnel = async () => {
|
||||
// const [funnelResponse, funnelError] = await getGeneral("14761");
|
||||
// if (funnelError) {
|
||||
// enqueueSnackbar(funnelError);
|
||||
// return;
|
||||
// }
|
||||
// if (!funnelResponse) {
|
||||
// enqueueSnackbar("Воронка пуста.");
|
||||
// return;
|
||||
// }
|
||||
// setFunnel(funnelResponse);
|
||||
// };
|
||||
// requestFunnel();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
|
||||
marginTop: "60px",
|
||||
}}
|
||||
>
|
||||
{Object.entries(funnel).map(([title, percent]) => (
|
||||
<FunnelItem key={title} title={title} percent={percent} />
|
||||
))}
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
@ -22,26 +22,10 @@ const COLORS: Record<number, string> = {
|
||||
};
|
||||
|
||||
const GENERAL_MOCK: GeneralResponse = {
|
||||
open: {
|
||||
100: 20,
|
||||
50: 10,
|
||||
60: 5,
|
||||
},
|
||||
result: {
|
||||
100: 90,
|
||||
10: 3,
|
||||
50: 48,
|
||||
},
|
||||
avtime: {
|
||||
100: 0,
|
||||
2000: 550,
|
||||
60: 0,
|
||||
},
|
||||
conversation: {
|
||||
100: 50,
|
||||
1000: 50,
|
||||
10000: 50,
|
||||
},
|
||||
open: { 100: 20, 50: 10, 60: 5 },
|
||||
result: { 100: 90, 10: 3, 50: 48 },
|
||||
avtime: { 100: 0, 2000: 550, 60: 0 },
|
||||
conversation: { 100: 50, 1000: 50, 10000: 50 },
|
||||
};
|
||||
|
||||
const GeneralItem = ({ title, general, color, numberType }: GeneralProps) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user