запер и постбек подключены к api + мобилка
This commit is contained in:
parent
8a4e980ed4
commit
751d9eb4f3
@ -18,6 +18,7 @@ import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||
import { General } from "./General";
|
||||
import { AnswersStatistics } from "./Answers/AnswersStatistics";
|
||||
import { Devices } from "./Devices";
|
||||
import AnalyticsSkeleton from "./AnalyticsSkeleton";
|
||||
|
||||
import { setQuizes } from "@root/quizes/actions";
|
||||
import { useQuizStore } from "@root/quizes/store";
|
||||
@ -45,7 +46,7 @@ export default function Analytics() {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
const { devices, general, questions } = useAnalytics({
|
||||
const { devices, general, questions, isLoading } = useAnalytics({
|
||||
ready: Boolean(Object.keys(quiz).length),
|
||||
quizId: editQuizId?.toString() || "",
|
||||
from,
|
||||
@ -255,12 +256,18 @@ export default function Analytics() {
|
||||
{isMobile ? <ResetIcon /> : "Сбросить"}
|
||||
</Button>
|
||||
</Box>
|
||||
{isLoading ? (
|
||||
<AnalyticsSkeleton />
|
||||
) : (
|
||||
<>
|
||||
<General
|
||||
data={general}
|
||||
day={86400 - moment(to).unix() - moment(from).unix() > 0}
|
||||
/>
|
||||
<AnswersStatistics data={questions} />
|
||||
<Devices data={devices} />
|
||||
</>
|
||||
)}
|
||||
</SectionWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
102
src/pages/Analytics/AnalyticsSkeleton.tsx
Normal file
102
src/pages/Analytics/AnalyticsSkeleton.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { Box, Paper, Skeleton, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
|
||||
export default function AnalyticsSkeleton() {
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(700));
|
||||
|
||||
const card = (
|
||||
<Paper
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<Skeleton variant="text" sx={{ width: "40%", height: 28, mb: 1 }} />
|
||||
<Skeleton variant="text" sx={{ width: "20%", height: 24, mb: 2 }} />
|
||||
<Skeleton variant="rectangular" sx={{ width: "100%", height: 220, borderRadius: "8px" }} />
|
||||
</Paper>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box sx={{ marginTop: "45px" }}>
|
||||
<Typography component="h3" sx={{ fontSize: "24px", fontWeight: "bold", color: theme.palette.text.primary }}>
|
||||
Ключевые метрики
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: isTablet ? (isMobile ? "1fr" : "1fr 1fr") : "1fr 1fr 1fr",
|
||||
gap: "20px",
|
||||
marginTop: "40px",
|
||||
}}
|
||||
>
|
||||
{card}
|
||||
{card}
|
||||
{card}
|
||||
{card}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ marginTop: "120px" }}>
|
||||
<Typography component="h3" sx={{ fontSize: "24px", fontWeight: "bold", color: theme.palette.text.primary }}>
|
||||
Статистика по ответам
|
||||
</Typography>
|
||||
<Box sx={{ display: isTablet && !isMobile ? "flex" : "block", gap: "40px", mt: "20px" }}>
|
||||
<Paper sx={{ borderRadius: "12px", boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)", p: 2, mb: 2 }}>
|
||||
<Skeleton variant="text" sx={{ width: "60%", height: 28, mb: 1 }} />
|
||||
<Skeleton variant="rectangular" sx={{ width: "100%", height: 260, borderRadius: "8px" }} />
|
||||
</Paper>
|
||||
<Paper sx={{ borderRadius: "12px", boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)", p: 2 }}>
|
||||
<Skeleton variant="text" sx={{ width: "60%", height: 28, mb: 1 }} />
|
||||
<Skeleton variant="rectangular" sx={{ width: "100%", height: 260, borderRadius: "8px" }} />
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box sx={{ mt: "30px" }}>
|
||||
<Skeleton variant="text" sx={{ width: 300, height: 28, mb: 2 }} />
|
||||
<Paper sx={{ borderRadius: "12px", boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)", p: 2 }}>
|
||||
{[...Array(4)].map((_, idx) => (
|
||||
<Box key={idx} sx={{ p: "15px 25px" }}>
|
||||
<Skeleton variant="rectangular" sx={{ width: "100%", height: 44, borderRadius: "10px" }} />
|
||||
</Box>
|
||||
))}
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ marginTop: "120px" }}>
|
||||
<Typography component="h3" sx={{ fontSize: "24px", fontWeight: "bold", color: theme.palette.text.primary }}>
|
||||
Статистика пользователей
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: isTablet ? (isMobile ? "1fr" : "1fr 1fr") : "1fr 1fr 1fr",
|
||||
gap: "20px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
{[...Array(3)].map((_, i) => (
|
||||
<Paper key={i} sx={{ overflow: "hidden", minHeight: "500px", display: "flex", flexDirection: "column", gap: "30px", borderRadius: "12px", boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)", p: 2 }}>
|
||||
<Skeleton variant="text" sx={{ width: "50%", height: 28 }} />
|
||||
<Skeleton variant="circular" width={245} height={245} sx={{ alignSelf: "center" }} />
|
||||
<Box sx={{ background: theme.palette.background.default, padding: "20px" }}>
|
||||
{[...Array(4)].map((_, idx) => (
|
||||
<Box key={idx} sx={{ display: "flex", mb: "10px" }}>
|
||||
<Skeleton variant="text" sx={{ flexGrow: 1, height: 20 }} />
|
||||
<Skeleton variant="text" sx={{ width: 60, height: 20, ml: 2 }} />
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Paper>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
import { FC } from "react";
|
||||
import { Box, Link, BoxProps } from "@mui/material";
|
||||
import OrangeYoutube from "@/assets/icons/OrangeYoutube";
|
||||
|
||||
interface InstructionYoutubeLinkProps extends BoxProps {}
|
||||
|
||||
const InstructionYoutubeLink: FC<InstructionYoutubeLinkProps> = ({ sx, ...props }) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "right",
|
||||
...sx,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<Link
|
||||
href="https://youtube.com"
|
||||
underline="hover"
|
||||
sx={{
|
||||
color: "#FA590B",
|
||||
display: "inline-flex",
|
||||
gap: "10px",
|
||||
fontSize: "16px"
|
||||
}}
|
||||
>
|
||||
<OrangeYoutube sx={{
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
}} /> Смотреть инструкцию
|
||||
</Link>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstructionYoutubeLink;
|
||||
@ -1,10 +1,12 @@
|
||||
import { FC, useState } from "react";
|
||||
import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box, Button, Link } from "@mui/material";
|
||||
import { FC, useState, useEffect } from "react";
|
||||
import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box, Button } from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { Quiz } from "@/model/quiz/quiz";
|
||||
import CustomTextField from "@/ui_kit/CustomTextField";
|
||||
import OrangeYoutube from "@/assets/icons/OrangeYoutube";
|
||||
import { createLeadTarget } from "@/api/leadtarget";
|
||||
import { createLeadTarget, getLeadTargetsByQuiz, deleteLeadTarget } from "@/api/leadtarget";
|
||||
import { useFormik } from "formik";
|
||||
import InstructionYoutubeLink from "@/pages/IntegrationsPage/IntegrationsModal/InstructionYoutubeLink";
|
||||
import { useSnackbar } from "notistack";
|
||||
|
||||
type PostbackModalProps = {
|
||||
isModalOpen: boolean;
|
||||
@ -22,8 +24,60 @@ export const PostbackModal: FC<PostbackModalProps> = ({
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const [token, setToken] = useState<string>("");
|
||||
const [domain, setDomain] = useState<string>("");
|
||||
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const deleteLeadTargetsByQuizType = async (quizId: number, type: "webhook") => {
|
||||
const [targets] = await getLeadTargetsByQuiz(quizId);
|
||||
if (!targets || targets.length === 0) {
|
||||
console.log("No targets found for deletion");
|
||||
return;
|
||||
}
|
||||
const toDelete = targets.filter(t => t.Type === type);
|
||||
console.log("Targets to delete:", toDelete);
|
||||
for (const t of toDelete) {
|
||||
console.log("Deleting target with ID:", t.ID);
|
||||
await deleteLeadTarget(t.ID);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: { token: string; domain: string }) => {
|
||||
const tokenValue = (values.token || "").trim();
|
||||
const target = (values.domain || "").trim();
|
||||
try {
|
||||
setIsSaving(true);
|
||||
if (!tokenValue && !target) {
|
||||
await deleteLeadTargetsByQuizType(quiz.backendId, "webhook");
|
||||
enqueueSnackbar("Postback удален", { variant: "success" });
|
||||
} else {
|
||||
await createLeadTarget({
|
||||
type: "webhook",
|
||||
quizID: quiz.backendId,
|
||||
target,
|
||||
name: tokenValue || undefined,
|
||||
});
|
||||
enqueueSnackbar("Postback сохранен", { variant: "success" });
|
||||
}
|
||||
} catch (error) {
|
||||
enqueueSnackbar("Ошибка при сохранении", { variant: "error" });
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formik = useFormik<{ token: string; domain: string }>({
|
||||
initialValues: { token: "", domain: "" },
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
setTimeout(() => {
|
||||
const input = document.getElementById("postback-domain") as HTMLInputElement | null;
|
||||
input?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@ -33,7 +87,8 @@ export const PostbackModal: FC<PostbackModalProps> = ({
|
||||
PaperProps={{
|
||||
sx: {
|
||||
maxWidth: isTablet ? "100%" : "919px",
|
||||
height: "314px",
|
||||
height: isMobile ? "303px" : "214px",
|
||||
// height: "314px",
|
||||
borderRadius: "12px",
|
||||
},
|
||||
}}
|
||||
@ -69,32 +124,17 @@ export const PostbackModal: FC<PostbackModalProps> = ({
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "right"
|
||||
}}>
|
||||
<Link
|
||||
href="https://youtube.com"
|
||||
underline="hover"
|
||||
sx={{
|
||||
color: "#FA590B",
|
||||
display: "inline-flex",
|
||||
gap: "10px",
|
||||
fontSize: "16px"
|
||||
}}
|
||||
><OrangeYoutube sx={{
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
}} /> Смотреть инструкцию</Link>
|
||||
</Box>
|
||||
{!isMobile && <InstructionYoutubeLink />}
|
||||
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
sx={{
|
||||
marginTop: "-43px",
|
||||
display: "inline-flex",
|
||||
alignItems: "end",
|
||||
gap: "38px",
|
||||
marginTop: isMobile ? 0 : "-43px",
|
||||
display: isMobile ? "flex" : "inline-flex",
|
||||
flexDirection: isMobile ? "column" : "row",
|
||||
alignItems: isMobile ? "center" : "end",
|
||||
gap: isMobile ? "10px" : "38px",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
@ -107,25 +147,7 @@ export const PostbackModal: FC<PostbackModalProps> = ({
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
color: "black",
|
||||
mt: "11px",
|
||||
mb: "14px",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
Токен авторизации
|
||||
</Typography>
|
||||
<CustomTextField
|
||||
id="postback-auth-token"
|
||||
placeholder="токен в формате ХХХХХХ"
|
||||
value={token}
|
||||
onChange={(e) => setToken(e.target.value)}
|
||||
maxLength={150}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
color: "black",
|
||||
mt: "11px",
|
||||
mt: isMobile ? 0 : "11px",
|
||||
mb: "14px",
|
||||
}}
|
||||
>
|
||||
@ -134,30 +156,21 @@ export const PostbackModal: FC<PostbackModalProps> = ({
|
||||
<CustomTextField
|
||||
id="postback-domain"
|
||||
placeholder="токен в формате ХХХХХХ"
|
||||
value={domain}
|
||||
onChange={(e) => setDomain(e.target.value)}
|
||||
value={formik.values.domain}
|
||||
onChange={(e) => formik.setFieldValue("domain", e.target.value)}
|
||||
maxLength={150}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const target = domain.trim();
|
||||
if (!token.trim() || !target) return;
|
||||
// Отправляем webhook postback как целевой endpoint
|
||||
await createLeadTarget({
|
||||
type: "webhook",
|
||||
quizID: quiz.backendId,
|
||||
target,
|
||||
name: token.trim(),
|
||||
});
|
||||
}}
|
||||
disabled={isSaving}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: "#7E2AEA",
|
||||
fontSize: "18px",
|
||||
lineHeight: "18px",
|
||||
width: "216px",
|
||||
width: isMobile ? "100%" : "216px",
|
||||
height: "44px",
|
||||
|
||||
p: "10px 20px",
|
||||
@ -166,6 +179,7 @@ export const PostbackModal: FC<PostbackModalProps> = ({
|
||||
Сохранить
|
||||
</Button>
|
||||
</Box>
|
||||
{isMobile && <InstructionYoutubeLink sx={{ mt: "40px", justifyContent: "center" }} />}
|
||||
</Box>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { FC, useState } from "react";
|
||||
import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box, Link, Button } from "@mui/material";
|
||||
import { FC, useState, useEffect } from "react";
|
||||
import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box, Button } from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { Quiz } from "@/model/quiz/quiz";
|
||||
import OrangeYoutube from "@/assets/icons/OrangeYoutube";
|
||||
import CustomTextField from "@/ui_kit/CustomTextField";
|
||||
import { createLeadTarget } from "@/api/leadtarget";
|
||||
import { createLeadTarget, getLeadTargetsByQuiz, deleteLeadTarget } from "@/api/leadtarget";
|
||||
import { useFormik } from "formik";
|
||||
import InstructionYoutubeLink from "@/pages/IntegrationsPage/IntegrationsModal/InstructionYoutubeLink";
|
||||
import { useSnackbar } from "notistack";
|
||||
|
||||
type ZapierModalProps = {
|
||||
isModalOpen: boolean;
|
||||
@ -22,7 +24,58 @@ export const ZapierModal: FC<ZapierModalProps> = ({
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const [webhookUrl, setWebhookUrl] = useState<string>("");
|
||||
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const deleteLeadTargetsByQuizType = async (quizId: number, type: "webhook") => {
|
||||
const [targets] = await getLeadTargetsByQuiz(quizId);
|
||||
if (!targets || targets.length === 0) {
|
||||
console.log("No targets found for deletion");
|
||||
return;
|
||||
}
|
||||
const toDelete = targets.filter(t => t.Type === type);
|
||||
console.log("Targets to delete:", toDelete);
|
||||
for (const t of toDelete) {
|
||||
console.log("Deleting target with ID:", t.ID);
|
||||
await deleteLeadTarget(t.ID);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: { webhookUrl: string }) => {
|
||||
const target = (values.webhookUrl || "").trim();
|
||||
try {
|
||||
setIsSaving(true);
|
||||
if (!target) {
|
||||
await deleteLeadTargetsByQuizType(quiz.backendId, "webhook");
|
||||
enqueueSnackbar("Webhook удален", { variant: "success" });
|
||||
} else {
|
||||
await createLeadTarget({
|
||||
type: "webhook",
|
||||
quizID: quiz.backendId,
|
||||
target,
|
||||
});
|
||||
enqueueSnackbar("Webhook сохранен", { variant: "success" });
|
||||
}
|
||||
} catch (error) {
|
||||
enqueueSnackbar("Ошибка при сохранении", { variant: "error" });
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formik = useFormik<{ webhookUrl: string }>({
|
||||
initialValues: { webhookUrl: "" },
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
setTimeout(() => {
|
||||
const input = document.getElementById("zapier-webhook-url") as HTMLInputElement | null;
|
||||
input?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@ -32,7 +85,7 @@ export const ZapierModal: FC<ZapierModalProps> = ({
|
||||
PaperProps={{
|
||||
sx: {
|
||||
maxWidth: isTablet ? "100%" : "919px",
|
||||
height: "195px",
|
||||
height: isMobile ? "303px" : "195px",
|
||||
borderRadius: "12px",
|
||||
},
|
||||
}}
|
||||
@ -62,6 +115,7 @@ export const ZapierModal: FC<ZapierModalProps> = ({
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
padding: "20px",
|
||||
@ -69,69 +123,59 @@ export const ZapierModal: FC<ZapierModalProps> = ({
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "right"
|
||||
}}>
|
||||
<Link
|
||||
href="https://youtube.com"
|
||||
underline="hover"
|
||||
sx={{
|
||||
color: "#FA590B",
|
||||
display: "inline-flex",
|
||||
gap: "10px",
|
||||
fontSize: "16px"
|
||||
}}
|
||||
><OrangeYoutube sx={{
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
}} /> Смотреть инструкцию</Link>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{ marginTop: "-33px" }}
|
||||
>
|
||||
{!isMobile && <InstructionYoutubeLink />}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: isMobile ? 0 : "-33px",
|
||||
display: isMobile ? "flex" : "block",
|
||||
flexDirection: "column",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
color: "black",
|
||||
mt: "11px",
|
||||
mt: isMobile ? 0 : "11px",
|
||||
mb: "14px",
|
||||
}}
|
||||
>
|
||||
URL webhook
|
||||
</Typography>
|
||||
|
||||
</Box>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
sx={{
|
||||
display: "inline-flex",
|
||||
flexDirection: isMobile ? "column" : "row",
|
||||
width: "100%",
|
||||
gap: "38px"
|
||||
gap: isMobile ? "10px" : "38px"
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
id="zapier-webhook-url"
|
||||
placeholder="введите url здесь"
|
||||
value={webhookUrl}
|
||||
onChange={(e) => setWebhookUrl(e.target.value)}
|
||||
value={formik.values.webhookUrl}
|
||||
onChange={(e) => formik.setFieldValue("webhookUrl", e.target.value)}
|
||||
maxLength={150}
|
||||
/>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const target = webhookUrl.trim();
|
||||
if (!target) return;
|
||||
await createLeadTarget({
|
||||
type: "webhook",
|
||||
quizID: quiz.backendId,
|
||||
target,
|
||||
});
|
||||
}}
|
||||
disabled={isSaving}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: "#7E2AEA",
|
||||
fontSize: "18px",
|
||||
lineHeight: "18px",
|
||||
width: "216px",
|
||||
width: isMobile ? "100%" : "216px",
|
||||
height: "44px",
|
||||
|
||||
p: "10px 20px",
|
||||
@ -141,6 +185,7 @@ export const ZapierModal: FC<ZapierModalProps> = ({
|
||||
</Button>
|
||||
|
||||
</Box>
|
||||
{isMobile && <InstructionYoutubeLink sx={{ mt: "40px" }} />}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@ -25,20 +25,28 @@ export function useAnalytics({ ready, quizId, to, from }: useAnalyticsProps) {
|
||||
const [devices, setDevices] = useState<DevicesResponse | null>(null);
|
||||
const [general, setGeneral] = useState<GeneralResponse | null>(null);
|
||||
const [questions, setQuestions] = useState<QuestionsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!quizId || !ready) return;
|
||||
|
||||
const requestStatistics = async () => {
|
||||
if (!formatTo || !formatFrom) {
|
||||
if (!quizId || !ready) {
|
||||
setIsLoading(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const requestStatistics = async () => {
|
||||
if (!formatTo || !formatFrom) {
|
||||
setIsLoading(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const [gottenGeneral] = await getGeneral(quizId, formatTo, formatFrom);
|
||||
const [gottenDevices] = await getDevices(quizId, formatTo, formatFrom);
|
||||
const [gottenQuestions] = await getQuestions(quizId, formatTo, formatFrom);
|
||||
|
||||
getGraphics(quizId, formatTo, formatFrom)
|
||||
getGraphics(quizId, formatTo, formatFrom);
|
||||
|
||||
if (gottenGeneral) {
|
||||
setGeneral(gottenGeneral);
|
||||
@ -51,10 +59,13 @@ export function useAnalytics({ ready, quizId, to, from }: useAnalyticsProps) {
|
||||
if (gottenQuestions) {
|
||||
setQuestions(gottenQuestions);
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
requestStatistics();
|
||||
}, [ready, quizId, to, from]);
|
||||
|
||||
return { devices, general, questions };
|
||||
return { devices, general, questions, isLoading };
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user