Merge branch 'dev' into formcontact
This commit is contained in:
commit
477b7163b7
@ -282,5 +282,15 @@ describe("Форма Входа", () => {
|
|||||||
|
|
||||||
cy.wait(5000);
|
cy.wait(5000);
|
||||||
cy.get('[data-cy="create-question"]').click();
|
cy.get('[data-cy="create-question"]').click();
|
||||||
|
|
||||||
|
// Удаления Квиза
|
||||||
|
|
||||||
|
cy.visit("http://localhost:3000/list");
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get('[data-cy="delete-quiz"]').each(($button) => {
|
||||||
|
cy.wrap($button).click();
|
||||||
|
cy.wait(500);
|
||||||
|
cy.contains("button", "Удалить").click();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,40 +1,10 @@
|
|||||||
import { FC, SVGProps } from "react";
|
import { FC, SVGProps } from "react";
|
||||||
|
|
||||||
export const CropIcon: FC = () => (
|
export const CropIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
<svg
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<path d="M6 6H2.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
width="24"
|
<path d="M6 2.25V18H21.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
height="24"
|
<path d="M18 15V6H9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
viewBox="0 0 24 24"
|
<path d="M18 21.75V18" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M6 6H2.25"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6 2.25V18H21.75"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M18 15V6H9"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M18 21.75V18"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export default function Component() {
|
|||||||
const [select, setSelect] = React.useState(0);
|
const [select, setSelect] = React.useState(0);
|
||||||
const userId = useUserStore((state) => state.userId);
|
const userId = useUserStore((state) => state.userId);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation()
|
const location = useLocation();
|
||||||
|
|
||||||
const onClick = () => (userId ? navigate("/list") : navigate("/signin"));
|
const onClick = () => (userId ? navigate("/list") : navigate("/signin"));
|
||||||
|
|
||||||
@ -41,7 +41,9 @@ export default function Component() {
|
|||||||
padding: 0,
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<QuizLogo width={isMobile ? 100 : 150} />
|
<Link to="/">
|
||||||
|
<QuizLogo width={isMobile ? 100 : 150} />
|
||||||
|
</Link>
|
||||||
{/*<Box*/}
|
{/*<Box*/}
|
||||||
{/* sx={{*/}
|
{/* sx={{*/}
|
||||||
{/* maxWidth: '595px',*/}
|
{/* maxWidth: '595px',*/}
|
||||||
@ -67,7 +69,7 @@ export default function Component() {
|
|||||||
{/* ))}*/}
|
{/* ))}*/}
|
||||||
{/*</Box>*/}
|
{/*</Box>*/}
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
sx={{
|
sx={{
|
||||||
color: "black",
|
color: "black",
|
||||||
|
|||||||
@ -4,17 +4,17 @@ import {} from "react-router-dom";
|
|||||||
import { useLocation, Link } from "react-router-dom";
|
import { useLocation, Link } from "react-router-dom";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
ListItem,
|
ListItem,
|
||||||
AppBar,
|
AppBar,
|
||||||
List,
|
List,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
IconButton,
|
IconButton,
|
||||||
Box,
|
Box,
|
||||||
Slide,
|
Slide,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { TransitionProps } from "@mui/material/transitions";
|
import { TransitionProps } from "@mui/material/transitions";
|
||||||
|
|
||||||
@ -38,212 +38,202 @@ import Logotip from "./images/icons/QuizLogo";
|
|||||||
// ];
|
// ];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
theme?: "dark" | "light";
|
theme?: "dark" | "light";
|
||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Transition = React.forwardRef(function Transition(
|
const Transition = React.forwardRef(function Transition(
|
||||||
props: TransitionProps & {
|
props: TransitionProps & {
|
||||||
children: React.ReactElement;
|
children: React.ReactElement;
|
||||||
},
|
},
|
||||||
|
|
||||||
ref: React.Ref<unknown>
|
ref: React.Ref<unknown>
|
||||||
) {
|
) {
|
||||||
return <Slide direction={"left"} ref={ref} {...props} />;
|
return <Slide direction={"left"} ref={ref} {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
const height = "80px";
|
const height = "80px";
|
||||||
export default function FullScreenDialog({
|
export default function FullScreenDialog({ theme = "dark", bgColor = "#F2F3F7" }: Props) {
|
||||||
theme = "dark",
|
const [open, setOpen] = useState(false);
|
||||||
bgColor = "#F2F3F7",
|
const location = useLocation();
|
||||||
}: Props) {
|
const themeMUI = useTheme();
|
||||||
const [open, setOpen] = useState(false);
|
const isMobile = useMediaQuery(themeMUI.breakpoints.down("md"));
|
||||||
const location = useLocation();
|
|
||||||
const themeMUI = useTheme();
|
|
||||||
const isMobile = useMediaQuery(themeMUI.breakpoints.down("md"));
|
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SectionStyled
|
<SectionStyled
|
||||||
tag="header"
|
tag="header"
|
||||||
bg={bgColor}
|
bg={bgColor}
|
||||||
mwidth={"1200px"}
|
mwidth={"1200px"}
|
||||||
sxsect={{
|
sxsect={{
|
||||||
minHeight: isMobile? "50px" : {height},
|
minHeight: isMobile ? "50px" : { height },
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
zIndex: 11
|
zIndex: 11,
|
||||||
|
}}
|
||||||
|
sxcont={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
svg: { color: "#000000" },
|
||||||
|
padding: isMobile ? 0 : "0 22px 0 40px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ bgcolor: "none", paddingTop: isMobile ? "6px" : 0 }}>
|
||||||
|
<Link to="/">
|
||||||
|
<Logotip width={150} />
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
{!isMobile && (
|
||||||
|
<Button
|
||||||
|
// onClick={() => setIsContactFormOpen(true)}
|
||||||
|
disableRipple
|
||||||
|
sx={{
|
||||||
|
color: "black",
|
||||||
|
border: "1px solid black",
|
||||||
|
ml: "auto",
|
||||||
|
mr: "38px",
|
||||||
|
textTransform: "none",
|
||||||
|
fontWeight: "400",
|
||||||
|
fontSize: "18px",
|
||||||
|
lineHeight: "24px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
padding: "8px 17px",
|
||||||
|
"&:hover": {
|
||||||
|
background: "rgba(126, 42, 234, 0.07)",
|
||||||
|
bgcolor: "#7E2AEA",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Предрегистрация
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
}}
|
<Button disableRipple variant="text" onClick={handleClickOpen}>
|
||||||
sxcont={{
|
<MenuIcon />
|
||||||
display: "flex",
|
</Button>
|
||||||
justifyContent: "space-between",
|
<Dialog
|
||||||
alignItems: "center",
|
fullScreen
|
||||||
svg: { color: "#000000" },
|
sx={{ width: isMobile ? "100%" : "320px", ml: "auto", height: "100%" }}
|
||||||
padding: isMobile ? 0 : "0 22px 0 40px"
|
open={open}
|
||||||
}}
|
onClose={handleClose}
|
||||||
|
TransitionComponent={Transition}
|
||||||
|
>
|
||||||
|
<AppBar
|
||||||
|
sx={{
|
||||||
|
position: "relative",
|
||||||
|
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
||||||
|
boxShadow: "none",
|
||||||
|
height: isMobile ? "66px" : "100px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Toolbar
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
svg: { color: theme === "dark" ? "#ffffff" : "#000000" },
|
||||||
|
pt: "12px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ bgcolor: "none", paddingTop: isMobile? "6px" : 0 }}>
|
{isMobile && <Logotip width={150} />}
|
||||||
<Logotip width={150}/>
|
<IconButton sx={{ ml: "auto" }} edge="start" color="inherit" onClick={handleClose} aria-label="close">
|
||||||
</Box>
|
<CloseIcon />
|
||||||
{!isMobile && (
|
</IconButton>
|
||||||
<Button
|
</Toolbar>
|
||||||
// onClick={() => setIsContactFormOpen(true)}
|
</AppBar>
|
||||||
disableRipple
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
color: "black",
|
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
||||||
border: "1px solid black",
|
height: "100vh",
|
||||||
ml: "auto",
|
p: "0",
|
||||||
mr: "38px",
|
}}
|
||||||
textTransform: "none",
|
>
|
||||||
fontWeight: "400",
|
{/*<ListItem*/}
|
||||||
fontSize: "18px",
|
{/* sx={{*/}
|
||||||
lineHeight: "24px",
|
{/* pl: "40px",*/}
|
||||||
borderRadius: "8px",
|
{/* flexDirection: "column",*/}
|
||||||
padding: "8px 17px",
|
{/* alignItems: isMobile ? "stretch" : "end",*/}
|
||||||
"&:hover": {
|
{/* }}*/}
|
||||||
background: "rgba(126, 42, 234, 0.07)",
|
{/*>*/}
|
||||||
bgcolor: "#7E2AEA",
|
{/* {buttonMenu.map(({ path, title }) => (*/}
|
||||||
},
|
{/* <Link*/}
|
||||||
}}
|
{/* key={path}*/}
|
||||||
>
|
{/* to={path}*/}
|
||||||
Предрегистрация
|
{/* style={{*/}
|
||||||
</Button>
|
{/* textDecoration: "none",*/}
|
||||||
)}
|
{/* height: "20px",*/}
|
||||||
|
{/* marginBottom: "25px",*/}
|
||||||
<Button disableRipple variant="text" onClick={handleClickOpen}>
|
{/* }}*/}
|
||||||
<MenuIcon />
|
{/* >*/}
|
||||||
</Button>
|
{/* <Button*/}
|
||||||
<Dialog
|
{/* disableRipple*/}
|
||||||
fullScreen
|
{/* variant="text"*/}
|
||||||
sx={{ width: isMobile ? "100%" : "320px", ml: "auto", height: "100%" }}
|
{/* sx={{*/}
|
||||||
open={open}
|
{/* color:*/}
|
||||||
onClose={handleClose}
|
{/* location.pathname === path*/}
|
||||||
TransitionComponent={Transition}
|
{/* ? "#7E2AEA"*/}
|
||||||
>
|
{/* : theme === "dark"*/}
|
||||||
<AppBar
|
{/* ? "white"*/}
|
||||||
sx={{
|
{/* : "black",*/}
|
||||||
position: "relative",
|
{/* height: "20px",*/}
|
||||||
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
{/* textTransform: "none",*/}
|
||||||
boxShadow: "none",
|
{/* fontSize: "16px",*/}
|
||||||
height: isMobile ? "66px" : "100px",
|
{/* "&:hover": {*/}
|
||||||
}}
|
{/* background: "none",*/}
|
||||||
>
|
{/* color: "#7E2AEA",*/}
|
||||||
<Toolbar
|
{/* },*/}
|
||||||
sx={{
|
{/* }}*/}
|
||||||
display: "flex",
|
{/* >*/}
|
||||||
justifyContent: "space-between",
|
{/* {title}*/}
|
||||||
svg: { color: theme === "dark" ? "#ffffff" : "#000000" },
|
{/* </Button>*/}
|
||||||
pt: "12px",
|
{/* </Link>*/}
|
||||||
}}
|
{/* ))}*/}
|
||||||
>
|
{/*</ListItem>*/}
|
||||||
{isMobile && (
|
{isMobile ? (
|
||||||
<Logotip width={150}/>
|
<Button
|
||||||
)}
|
// onClick={() => setIsContactFormOpen(true)}
|
||||||
<IconButton
|
variant="outlined"
|
||||||
sx={{ ml: "auto" }}
|
sx={{
|
||||||
edge="start"
|
position: "absolute",
|
||||||
color="inherit"
|
bottom: 0,
|
||||||
onClick={handleClose}
|
mb: "60px",
|
||||||
aria-label="close"
|
width: "188px",
|
||||||
>
|
color: theme === "dark" ? "white" : "black",
|
||||||
<CloseIcon />
|
border: "1px solid black",
|
||||||
</IconButton>
|
ml: "40px",
|
||||||
</Toolbar>
|
mt: "180px",
|
||||||
</AppBar>
|
textTransform: "none",
|
||||||
<List
|
fontWeight: "400",
|
||||||
sx={{
|
fontSize: "18px",
|
||||||
background: theme === "dark" ? "#252734" : "#F2F3F7",
|
lineHeight: "24px",
|
||||||
height: "100vh",
|
borderRadius: "8px",
|
||||||
p: "0",
|
padding: "8px 17px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/*<ListItem*/}
|
Предрегистрация
|
||||||
{/* sx={{*/}
|
</Button>
|
||||||
{/* pl: "40px",*/}
|
) : (
|
||||||
{/* flexDirection: "column",*/}
|
<Box
|
||||||
{/* alignItems: isMobile ? "stretch" : "end",*/}
|
sx={{
|
||||||
{/* }}*/}
|
position: "absolute",
|
||||||
{/*>*/}
|
right: "40px",
|
||||||
{/* {buttonMenu.map(({ path, title }) => (*/}
|
bottom: "60px",
|
||||||
{/* <Link*/}
|
}}
|
||||||
{/* key={path}*/}
|
>
|
||||||
{/* to={path}*/}
|
<Logotip width={150} />
|
||||||
{/* style={{*/}
|
</Box>
|
||||||
{/* textDecoration: "none",*/}
|
)}
|
||||||
{/* height: "20px",*/}
|
</List>
|
||||||
{/* marginBottom: "25px",*/}
|
</Dialog>
|
||||||
{/* }}*/}
|
</SectionStyled>
|
||||||
{/* >*/}
|
<Box sx={{ height: isMobile ? "50px" : { height } }} />
|
||||||
{/* <Button*/}
|
</>
|
||||||
{/* disableRipple*/}
|
);
|
||||||
{/* variant="text"*/}
|
|
||||||
{/* sx={{*/}
|
|
||||||
{/* color:*/}
|
|
||||||
{/* location.pathname === path*/}
|
|
||||||
{/* ? "#7E2AEA"*/}
|
|
||||||
{/* : theme === "dark"*/}
|
|
||||||
{/* ? "white"*/}
|
|
||||||
{/* : "black",*/}
|
|
||||||
{/* height: "20px",*/}
|
|
||||||
{/* textTransform: "none",*/}
|
|
||||||
{/* fontSize: "16px",*/}
|
|
||||||
{/* "&:hover": {*/}
|
|
||||||
{/* background: "none",*/}
|
|
||||||
{/* color: "#7E2AEA",*/}
|
|
||||||
{/* },*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/* >*/}
|
|
||||||
{/* {title}*/}
|
|
||||||
{/* </Button>*/}
|
|
||||||
{/* </Link>*/}
|
|
||||||
{/* ))}*/}
|
|
||||||
{/*</ListItem>*/}
|
|
||||||
{isMobile ? (
|
|
||||||
<Button
|
|
||||||
// onClick={() => setIsContactFormOpen(true)}
|
|
||||||
variant="outlined"
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
bottom: 0,
|
|
||||||
mb: "60px",
|
|
||||||
width: "188px",
|
|
||||||
color: theme === "dark" ? "white" : "black",
|
|
||||||
border: "1px solid black",
|
|
||||||
ml: "40px",
|
|
||||||
mt: "180px",
|
|
||||||
textTransform: "none",
|
|
||||||
fontWeight: "400",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
padding: "8px 17px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Предрегистрация
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
right: "40px",
|
|
||||||
bottom: "60px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Logotip width={150}/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Dialog>
|
|
||||||
</SectionStyled>
|
|
||||||
<Box sx={{height: isMobile ? "50px" : {height}}} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
|
import { ResultSettings } from "./ResultSettings";
|
||||||
import { ResultSettings } from "./ResultSettings"
|
|
||||||
import { createFrontResult } from "@root/questions/actions";
|
import { createFrontResult } from "@root/questions/actions";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
@ -7,6 +6,8 @@ import { Box, Typography, useTheme, useMediaQuery, Button } from "@mui/material"
|
|||||||
import image from "../../assets/Rectangle 110.png";
|
import image from "../../assets/Rectangle 110.png";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||||
|
import { decrementCurrentStep } from "@root/quizes/actions";
|
||||||
|
|
||||||
export const FirstEntry = () => {
|
export const FirstEntry = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -17,16 +18,20 @@ export const FirstEntry = () => {
|
|||||||
const create = () => {
|
const create = () => {
|
||||||
if (quiz?.config.haveRoot) {
|
if (quiz?.config.haveRoot) {
|
||||||
questions
|
questions
|
||||||
.filter((question:AnyTypedQuizQuestion) => {
|
.filter((question: AnyTypedQuizQuestion) => {
|
||||||
return question.type !== null && question.content.rule.parentId.length !== 0 && question.content.rule.children.length === 0
|
return (
|
||||||
})
|
question.type !== null &&
|
||||||
.forEach(question => {
|
question.content.rule.parentId.length !== 0 &&
|
||||||
createFrontResult(quiz.id, question.content.id)
|
question.content.rule.children.length === 0
|
||||||
})
|
);
|
||||||
|
})
|
||||||
|
.forEach((question) => {
|
||||||
|
createFrontResult(quiz.id, question.content.id);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
createFrontResult(quiz.id, "line")
|
createFrontResult(quiz.id, "line");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -53,7 +58,7 @@ export const FirstEntry = () => {
|
|||||||
mr: !isSmallMonitor ? "104px" : 0,
|
mr: !isSmallMonitor ? "104px" : 0,
|
||||||
marginBottom: isSmallMonitor ? "20px" : 0,
|
marginBottom: isSmallMonitor ? "20px" : 0,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
height: "100%"
|
height: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h5" sx={{ marginBottom: "20px" }}>
|
<Typography variant="h5" sx={{ marginBottom: "20px" }}>
|
||||||
@ -69,7 +74,10 @@ export const FirstEntry = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ color: "#4D4D4D", width: "95%" }}>
|
<Typography sx={{ color: "#4D4D4D", width: "95%" }}>
|
||||||
Вы можете показывать разные результаты квиза (добавьте описание, изображение, стоимость и т.п.) разным пользователям, нужно только их создать и проставить условия. Таким образом ваш квиз получится максимально индивидуальным для каждого клиента. Показывайте картинку/видео вместо результата или переадресовывайте пользователя по нужной ссылке.
|
Вы можете показывать разные результаты квиза (добавьте описание, изображение, стоимость и т.п.) разным
|
||||||
|
пользователям, нужно только их создать и проставить условия. Таким образом ваш квиз получится максимально
|
||||||
|
индивидуальным для каждого клиента. Показывайте картинку/видео вместо результата или переадресовывайте
|
||||||
|
пользователя по нужной ссылке.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -91,21 +99,35 @@ export const FirstEntry = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
|
||||||
onClick={create}
|
<Box sx={{ display: "flex", justifyContent: "flex-start", alignItems: "center", gap: "8px", mt: "30px" }}>
|
||||||
variant="contained"
|
<Button
|
||||||
sx={{
|
variant="outlined"
|
||||||
backgroundColor: "#7E2AEA",
|
sx={{
|
||||||
fontSize: "18px",
|
padding: "10px 20px",
|
||||||
lineHeight: "18px",
|
borderRadius: "8px",
|
||||||
width: "216px",
|
height: "44px",
|
||||||
height: "44px",
|
}}
|
||||||
mt: "30px",
|
onClick={decrementCurrentStep}
|
||||||
p: "10px 20px"
|
>
|
||||||
}}
|
<ArrowLeft />
|
||||||
>
|
</Button>
|
||||||
Создать результаты
|
<Button
|
||||||
</Button>
|
onClick={create}
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#7E2AEA",
|
||||||
|
fontSize: "18px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
width: "216px",
|
||||||
|
height: "44px",
|
||||||
|
|
||||||
|
p: "10px 20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Создать результаты
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@ -1,12 +1,4 @@
|
|||||||
import {
|
import { Box, Button, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
SxProps,
|
|
||||||
Theme,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { createQuiz } from "@root/quizes/actions";
|
import { createQuiz } from "@root/quizes/actions";
|
||||||
import { useQuizes } from "@root/quizes/hooks";
|
import { useQuizes } from "@root/quizes/hooks";
|
||||||
import SectionWrapper from "@ui_kit/SectionWrapper";
|
import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||||
@ -16,72 +8,62 @@ import ComplexNavText from "./ComplexNavText";
|
|||||||
import FirstQuiz from "./FirstQuiz";
|
import FirstQuiz from "./FirstQuiz";
|
||||||
import QuizCard from "./QuizCard";
|
import QuizCard from "./QuizCard";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
outerContainerSx?: SxProps<Theme>;
|
outerContainerSx?: SxProps<Theme>;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MyQuizzesFull({
|
export default function MyQuizzesFull({ outerContainerSx: sx, children }: Props) {
|
||||||
outerContainerSx: sx,
|
const { quizes } = useQuizes();
|
||||||
children,
|
const navigate = useNavigate();
|
||||||
}: Props) {
|
const theme = useTheme();
|
||||||
const { quizes } = useQuizes();
|
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
||||||
const navigate = useNavigate();
|
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{quizes.length === 0 ? (
|
{quizes.length === 0 ? (
|
||||||
<FirstQuiz />
|
<FirstQuiz />
|
||||||
) : (
|
) : (
|
||||||
<SectionWrapper maxWidth="lg">
|
<SectionWrapper maxWidth="lg">
|
||||||
<ComplexNavText text1="Кабинет квизов" />
|
<ComplexNavText text1="Кабинет квизов" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
mt: "20px",
|
mt: "20px",
|
||||||
mb: "30px",
|
mb: "30px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h4">Мои квизы</Typography>
|
<Typography variant="h4">Мои квизы</Typography>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile ? "10px" : "10px 47px",
|
padding: isMobile ? "10px" : "10px 47px",
|
||||||
minWidth: "44px",
|
minWidth: "44px",
|
||||||
}}
|
}}
|
||||||
onClick={() => createQuiz(navigate)}
|
onClick={() => createQuiz(navigate)}
|
||||||
data-cy="create-quiz"
|
data-cy="create-quiz"
|
||||||
>
|
>
|
||||||
{isMobile ? "+" : "Создать +"}
|
{isMobile ? "+" : "Создать +"}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
py: "10px",
|
py: "10px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
gap: "40px",
|
gap: "40px",
|
||||||
mb: "60px",
|
mb: "60px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{quizes.map(quiz => (
|
{quizes.map((quiz) => (
|
||||||
<QuizCard
|
<QuizCard key={quiz.id} quiz={quiz} openCount={0} applicationCount={0} conversionPercent={0} />
|
||||||
key={quiz.id}
|
))}
|
||||||
quiz={quiz}
|
</Box>
|
||||||
openCount={0}
|
{children}
|
||||||
applicationCount={0}
|
</SectionWrapper>
|
||||||
conversionPercent={0}
|
)}
|
||||||
/>
|
</>
|
||||||
))}
|
);
|
||||||
</Box>
|
|
||||||
{children}
|
|
||||||
</SectionWrapper>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { decrementCurrentStep } from "@root/quizes/actions";
|
|||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo";
|
||||||
import CustomAvatar from "./Avatar";
|
import CustomAvatar from "./Avatar";
|
||||||
import NavMenuItem from "./NavMenuItem";
|
import NavMenuItem from "./NavMenuItem";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -25,7 +26,9 @@ export default function Header() {
|
|||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PenaLogo width={124} />
|
<Link to="/">
|
||||||
|
<PenaLogo width={124} />
|
||||||
|
</Link>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|||||||
@ -1,11 +1,4 @@
|
|||||||
import {
|
import { Box, Container, IconButton, Typography, useTheme, useMediaQuery } from "@mui/material";
|
||||||
Box,
|
|
||||||
Container,
|
|
||||||
IconButton,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
useMediaQuery,
|
|
||||||
} from "@mui/material";
|
|
||||||
import NavMenuItem from "./NavMenuItem";
|
import NavMenuItem from "./NavMenuItem";
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo";
|
||||||
import WalletIcon from "@icons/WalletIcon";
|
import WalletIcon from "@icons/WalletIcon";
|
||||||
@ -13,7 +6,7 @@ import CustomAvatar from "./Avatar";
|
|||||||
import { Burger } from "@icons/Burger";
|
import { Burger } from "@icons/Burger";
|
||||||
import { clearAuthToken } from "@frontend/kitui";
|
import { clearAuthToken } from "@frontend/kitui";
|
||||||
import { logout } from "@api/auth";
|
import { logout } from "@api/auth";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { clearUserData } from "@root/user";
|
import { clearUserData } from "@root/user";
|
||||||
import { LogoutButton } from "@ui_kit/LogoutButton";
|
import { LogoutButton } from "@ui_kit/LogoutButton";
|
||||||
@ -59,7 +52,9 @@ export default function HeaderFull() {
|
|||||||
style={{ fontSize: "30px", color: "#000000", cursor: "pointer" }}
|
style={{ fontSize: "30px", color: "#000000", cursor: "pointer" }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<PenaLogo width={124} />
|
<Link to="/">
|
||||||
|
<PenaLogo width={124} />
|
||||||
|
</Link>
|
||||||
{!isTablet && (
|
{!isTablet && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -93,10 +88,7 @@ export default function HeaderFull() {
|
|||||||
>
|
>
|
||||||
Мой баланс
|
Мой баланс
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography variant="body2" color={theme.palette.brightPurple.main}>
|
||||||
variant="body2"
|
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
>
|
|
||||||
00.00 руб.
|
00.00 руб.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -111,13 +103,13 @@ export default function HeaderFull() {
|
|||||||
height: "36px",
|
height: "36px",
|
||||||
width: "36px",
|
width: "36px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<LogoutButton
|
<LogoutButton
|
||||||
onClick={handleLogoutClick}
|
onClick={handleLogoutClick}
|
||||||
sx={{
|
sx={{
|
||||||
ml: "20px",
|
ml: "20px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,330 +1,344 @@
|
|||||||
import { CropIcon } from "@icons/CropIcon";
|
import { CropIcon } from "@icons/CropIcon";
|
||||||
import { ResetIcon } from "@icons/ResetIcon";
|
import { ResetIcon } from "@icons/ResetIcon";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
IconButton,
|
||||||
Modal,
|
Modal,
|
||||||
Slider,
|
Slider,
|
||||||
SxProps,
|
SxProps,
|
||||||
Theme,
|
Theme,
|
||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { FC, useMemo, useRef, useState } from "react";
|
import { FC, useMemo, useRef, useState } from "react";
|
||||||
import ReactCrop, { Crop, PixelCrop } from "react-image-crop";
|
import ReactCrop, { Crop, PixelCrop } from "react-image-crop";
|
||||||
import "react-image-crop/dist/ReactCrop.css";
|
import "react-image-crop/dist/ReactCrop.css";
|
||||||
import { canvasPreview } from "./utils/canvasPreview";
|
import { canvasPreview } from "./utils/canvasPreview";
|
||||||
|
|
||||||
|
|
||||||
const styleSlider: SxProps<Theme> = {
|
const styleSlider: SxProps<Theme> = {
|
||||||
color: "#7E2AEA",
|
color: "#7E2AEA",
|
||||||
height: "12px",
|
height: "12px",
|
||||||
"& .MuiSlider-track": {
|
"& .MuiSlider-track": {
|
||||||
border: "none",
|
border: "none",
|
||||||
},
|
},
|
||||||
"& .MuiSlider-rail": {
|
"& .MuiSlider-rail": {
|
||||||
backgroundColor: "#F2F3F7",
|
backgroundColor: "#F2F3F7",
|
||||||
border: `1px solid #9A9AAF`,
|
border: `1px solid #9A9AAF`,
|
||||||
},
|
},
|
||||||
"& .MuiSlider-thumb": {
|
"& .MuiSlider-thumb": {
|
||||||
height: 26,
|
height: 26,
|
||||||
width: 26,
|
width: 26,
|
||||||
border: `6px solid #7E2AEA`,
|
border: `6px solid #7E2AEA`,
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
boxShadow: `0px 0px 0px 3px white,
|
boxShadow: `0px 0px 0px 3px white,
|
||||||
0px 4px 4px 3px #C3C8DD`,
|
0px 4px 4px 3px #C3C8DD`,
|
||||||
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
||||||
boxShadow: `0px 0px 0px 3px white,
|
boxShadow: `0px 0px 0px 3px white,
|
||||||
0px 4px 4px 3px #C3C8DD`,
|
0px 4px 4px 3px #C3C8DD`,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
imageBlob: Blob | null;
|
imageBlob: Blob | null;
|
||||||
originalImageUrl: string | null;
|
originalImageUrl: string | null;
|
||||||
setCropModalImageBlob: (imageBlob: Blob) => void;
|
setCropModalImageBlob: (imageBlob: Blob) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSaveImageClick: (imageBlob: Blob) => void;
|
onSaveImageClick: (imageBlob: Blob) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CropModal: FC<Props> = ({ isOpen, imageBlob, originalImageUrl, setCropModalImageBlob, onSaveImageClick, onClose }) => {
|
export const CropModal: FC<Props> = ({
|
||||||
const theme = useTheme();
|
isOpen,
|
||||||
const [crop, setCrop] = useState<Crop>();
|
imageBlob,
|
||||||
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
originalImageUrl,
|
||||||
const [darken, setDarken] = useState(0);
|
setCropModalImageBlob,
|
||||||
const [rotate, setRotate] = useState(0);
|
onSaveImageClick,
|
||||||
const [width, setWidth] = useState<number>(240);
|
onClose,
|
||||||
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
}) => {
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
const theme = useTheme();
|
||||||
|
const [crop, setCrop] = useState<Crop>();
|
||||||
|
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
||||||
|
const [darken, setDarken] = useState(0);
|
||||||
|
const [rotate, setRotate] = useState(0);
|
||||||
|
const [width, setWidth] = useState<number>(240);
|
||||||
|
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
||||||
|
|
||||||
const imageUrl = useMemo(() => imageBlob && URL.createObjectURL(imageBlob), [imageBlob]);
|
const imageUrl = useMemo(() => imageBlob && URL.createObjectURL(imageBlob), [imageBlob]);
|
||||||
|
|
||||||
const handleCropClick = async () => {
|
const handleCropClick = async () => {
|
||||||
if (!completedCrop) throw new Error("No completed crop");
|
if (!completedCrop) throw new Error("No completed crop");
|
||||||
if (!cropImageElementRef.current) throw new Error("No image");
|
if (!cropImageElementRef.current) throw new Error("No image");
|
||||||
|
|
||||||
const canvasCopy = document.createElement("canvas");
|
const canvasCopy = document.createElement("canvas");
|
||||||
const ctx = canvasCopy.getContext("2d");
|
const ctx = canvasCopy.getContext("2d");
|
||||||
if (!ctx) throw new Error("No 2d context");
|
if (!ctx) throw new Error("No 2d context");
|
||||||
|
|
||||||
canvasCopy.width = completedCrop.width;
|
canvasCopy.width = completedCrop.width;
|
||||||
canvasCopy.height = completedCrop.height;
|
canvasCopy.height = completedCrop.height;
|
||||||
ctx.filter = `brightness(${100 - darken}%)`;
|
ctx.filter = `brightness(${100 - darken}%)`;
|
||||||
|
|
||||||
await canvasPreview(cropImageElementRef.current, canvasCopy, completedCrop, rotate);
|
await canvasPreview(cropImageElementRef.current, canvasCopy, completedCrop, rotate);
|
||||||
|
|
||||||
canvasCopy.toBlob((blob) => {
|
canvasCopy.toBlob((blob) => {
|
||||||
if (!blob) throw new Error("Failed to create blob");
|
if (!blob) throw new Error("Failed to create blob");
|
||||||
|
|
||||||
setCropModalImageBlob(blob);
|
setCropModalImageBlob(blob);
|
||||||
setCrop(undefined);
|
setCrop(undefined);
|
||||||
setCompletedCrop(undefined);
|
setCompletedCrop(undefined);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleSaveClick() {
|
function handleSaveClick() {
|
||||||
if (imageBlob) onSaveImageClick?.(imageBlob);
|
if (imageBlob) onSaveImageClick?.(imageBlob);
|
||||||
setCrop(undefined);
|
setCrop(undefined);
|
||||||
setCompletedCrop(undefined);
|
setCompletedCrop(undefined);
|
||||||
onClose();
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleLoadOriginalImage() {
|
||||||
|
if (!originalImageUrl) return;
|
||||||
|
|
||||||
|
const response = await fetch(originalImageUrl);
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
setCropModalImageBlob(blob);
|
||||||
|
setCrop(undefined);
|
||||||
|
setCompletedCrop(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageSize = () => {
|
||||||
|
if (cropImageElementRef.current) {
|
||||||
|
const imageWidth = cropImageElementRef.current.naturalWidth;
|
||||||
|
const imageHeight = cropImageElementRef.current.naturalHeight;
|
||||||
|
|
||||||
|
const aspect = imageWidth / imageHeight;
|
||||||
|
|
||||||
|
if (aspect <= 1.333) {
|
||||||
|
setWidth(240);
|
||||||
|
}
|
||||||
|
if (aspect >= 1.5) {
|
||||||
|
setWidth(580);
|
||||||
|
}
|
||||||
|
if (aspect >= 1.778) {
|
||||||
|
setWidth(580);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function handleLoadOriginalImage() {
|
return (
|
||||||
if (!originalImageUrl) return;
|
<Modal
|
||||||
|
open={isOpen}
|
||||||
const response = await fetch(originalImageUrl);
|
onClose={onClose}
|
||||||
const blob = await response.blob();
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description"
|
||||||
setCropModalImageBlob(blob);
|
>
|
||||||
setCrop(undefined);
|
<Box
|
||||||
setCompletedCrop(undefined);
|
sx={{
|
||||||
}
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
const getImageSize = () => {
|
left: "50%",
|
||||||
if (cropImageElementRef.current) {
|
transform: "translate(-50%, -50%)",
|
||||||
const imageWidth = cropImageElementRef.current.naturalWidth;
|
bgcolor: "background.paper",
|
||||||
const imageHeight = cropImageElementRef.current.naturalHeight;
|
boxShadow: 24,
|
||||||
|
padding: "20px",
|
||||||
const aspect = imageWidth / imageHeight;
|
borderRadius: "8px",
|
||||||
|
width: isMobile ? "343px" : "620px",
|
||||||
if (aspect <= 1.333) {
|
}}
|
||||||
setWidth(240);
|
>
|
||||||
}
|
<Box
|
||||||
if (aspect >= 1.5) {
|
sx={{
|
||||||
setWidth(580);
|
height: "320px",
|
||||||
}
|
padding: "10px",
|
||||||
if (aspect >= 1.778) {
|
backgroundSize: "cover",
|
||||||
setWidth(580);
|
backgroundRepeat: "no-repeat",
|
||||||
}
|
display: "flex",
|
||||||
}
|
alignItems: "center",
|
||||||
};
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
open={isOpen}
|
|
||||||
onClose={onClose}
|
|
||||||
aria-labelledby="modal-modal-title"
|
|
||||||
aria-describedby="modal-modal-description"
|
|
||||||
>
|
>
|
||||||
<Box sx={{
|
{imageUrl && (
|
||||||
position: "absolute",
|
<ReactCrop
|
||||||
top: "50%",
|
crop={crop}
|
||||||
left: "50%",
|
onChange={(_, percentCrop) => setCrop(percentCrop)}
|
||||||
transform: "translate(-50%, -50%)",
|
onComplete={(c) => setCompletedCrop(c)}
|
||||||
bgcolor: "background.paper",
|
maxWidth={500}
|
||||||
boxShadow: 24,
|
minWidth={50}
|
||||||
padding: "20px",
|
maxHeight={320}
|
||||||
borderRadius: "8px",
|
minHeight={50}
|
||||||
width: isMobile ? "343px" : "620px",
|
>
|
||||||
}}>
|
<img
|
||||||
<Box
|
onLoad={getImageSize}
|
||||||
sx={{
|
ref={cropImageElementRef}
|
||||||
height: "320px",
|
alt="Crop me"
|
||||||
padding: "10px",
|
src={imageUrl}
|
||||||
backgroundSize: "cover",
|
style={{
|
||||||
backgroundRepeat: "no-repeat",
|
filter: `brightness(${100 - darken}%)`,
|
||||||
display: "flex",
|
transform: ` rotate(${rotate}deg)`,
|
||||||
alignItems: "center",
|
maxWidth: "580px",
|
||||||
justifyContent: "center",
|
maxHeight: "320px",
|
||||||
}}
|
}}
|
||||||
>
|
width={width}
|
||||||
{imageUrl && (
|
/>
|
||||||
<ReactCrop
|
</ReactCrop>
|
||||||
crop={crop}
|
)}
|
||||||
onChange={(_, percentCrop) => setCrop(percentCrop)}
|
</Box>
|
||||||
onComplete={(c) => setCompletedCrop(c)}
|
<Box
|
||||||
maxWidth={500}
|
sx={{
|
||||||
minWidth={50}
|
color: "#7E2AEA",
|
||||||
maxHeight={320}
|
display: "flex",
|
||||||
minHeight={50}
|
alignItems: "center",
|
||||||
>
|
justifyContent: "center",
|
||||||
<img
|
fontSize: "16xp",
|
||||||
onLoad={getImageSize}
|
fontWeight: "600",
|
||||||
ref={cropImageElementRef}
|
marginBottom: "50px",
|
||||||
alt="Crop me"
|
}}
|
||||||
src={imageUrl}
|
>
|
||||||
style={{
|
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
||||||
filter: `brightness(${100 - darken}%)`,
|
{crop?.width ? Math.round(crop.width) + "px" : ""}
|
||||||
transform: ` rotate(${rotate}deg)`,
|
</Typography>
|
||||||
maxWidth: "580px",
|
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
||||||
maxHeight: "320px",
|
{crop?.height ? Math.round(crop.height) + "px" : ""}
|
||||||
}}
|
</Typography>
|
||||||
width={width}
|
</Box>
|
||||||
/>
|
|
||||||
</ReactCrop>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
color: "#7E2AEA",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
fontSize: "16xp",
|
|
||||||
fontWeight: "600",
|
|
||||||
marginBottom: "50px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
|
||||||
{crop?.width ? Math.round(crop.width) + "px" : ""}
|
|
||||||
</Typography>
|
|
||||||
<Typography sx={{ color: "#7E2AEA", lineHeight: "0px" }}>
|
|
||||||
{crop?.height ? Math.round(crop.height) + "px" : ""}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: isMobile ? "block" : "flex",
|
display: isMobile ? "block" : "flex",
|
||||||
alignItems: "end",
|
alignItems: "end",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconButton onClick={() => setRotate(r => (r + 90) % 360)}>
|
<IconButton onClick={() => setRotate((r) => (r + 90) % 360)}>
|
||||||
<ResetIcon />
|
<ResetIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>Размер</Typography>
|
||||||
Размер
|
<Slider
|
||||||
</Typography>
|
sx={[
|
||||||
<Slider
|
styleSlider,
|
||||||
sx={[styleSlider, {
|
{
|
||||||
width: isMobile ? "350px" : "250px",
|
width: isMobile ? "350px" : "250px",
|
||||||
}]}
|
},
|
||||||
value={width}
|
]}
|
||||||
min={50}
|
value={width}
|
||||||
max={580}
|
min={50}
|
||||||
step={1}
|
max={580}
|
||||||
onChange={(_, newValue) => {
|
step={1}
|
||||||
setWidth(newValue as number);
|
onChange={(_, newValue) => {
|
||||||
}}
|
setWidth(newValue as number);
|
||||||
/>
|
}}
|
||||||
</Box>
|
/>
|
||||||
<Box>
|
</Box>
|
||||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
<Box>
|
||||||
Затемнение
|
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>Затемнение</Typography>
|
||||||
</Typography>
|
<Slider
|
||||||
<Slider
|
sx={[
|
||||||
sx={[styleSlider, {
|
styleSlider,
|
||||||
width: isMobile ? "350px" : "250px",
|
{
|
||||||
}]}
|
width: isMobile ? "350px" : "250px",
|
||||||
value={darken}
|
},
|
||||||
min={0}
|
]}
|
||||||
max={100}
|
value={darken}
|
||||||
step={1}
|
min={0}
|
||||||
onChange={(_, newValue) => setDarken(newValue as number)}
|
max={100}
|
||||||
/>
|
step={1}
|
||||||
</Box>
|
onChange={(_, newValue) => setDarken(newValue as number)}
|
||||||
</Box>
|
/>
|
||||||
<Box
|
</Box>
|
||||||
sx={{
|
</Box>
|
||||||
marginTop: "40px",
|
<Box
|
||||||
width: "100%",
|
sx={{
|
||||||
display: "flex",
|
marginTop: "40px",
|
||||||
}}
|
width: "100%",
|
||||||
>
|
display: "flex",
|
||||||
<Button
|
gap: "10px",
|
||||||
onClick={handleSaveClick}
|
}}
|
||||||
disableRipple
|
>
|
||||||
data-cy="crop-modal-save-button"
|
<Button
|
||||||
sx={{
|
onClick={handleCropClick}
|
||||||
height: "48px",
|
disableRipple
|
||||||
color: "#7E2AEA",
|
disabled={!completedCrop}
|
||||||
borderRadius: "8px",
|
sx={{
|
||||||
border: "1px solid #7E2AEA",
|
padding: "10px 20px",
|
||||||
marginRight: "10px",
|
borderRadius: "8px",
|
||||||
px: "20px",
|
background: theme.palette.brightPurple.main,
|
||||||
}}
|
fontSize: "18px",
|
||||||
>Сохранить</Button>
|
color: "#7E2AEA",
|
||||||
<Button
|
border: `1px solid ${!completedCrop ? "rgba(0, 0, 0, 0.26)" : "#7E2AEA"}`,
|
||||||
onClick={handleLoadOriginalImage}
|
backgroundColor: "transparent",
|
||||||
disableRipple
|
}}
|
||||||
disabled={!originalImageUrl}
|
>
|
||||||
sx={{
|
<CropIcon color={!completedCrop ? "rgba(0, 0, 0, 0.26)" : "#7E2AEA"} />
|
||||||
width: "215px",
|
Обрезать
|
||||||
height: "48px",
|
</Button>
|
||||||
color: "#7E2AEA",
|
<Button
|
||||||
borderRadius: "8px",
|
onClick={handleLoadOriginalImage}
|
||||||
border: "1px solid #7E2AEA",
|
disableRipple
|
||||||
marginRight: "10px",
|
disabled={!originalImageUrl}
|
||||||
ml: "auto",
|
sx={{
|
||||||
}}
|
width: "215px",
|
||||||
>
|
height: "48px",
|
||||||
Загрузить оригинал
|
color: "#7E2AEA",
|
||||||
</Button>
|
borderRadius: "8px",
|
||||||
<Button
|
border: "1px solid #7E2AEA",
|
||||||
onClick={handleCropClick}
|
}}
|
||||||
disableRipple
|
>
|
||||||
variant="contained"
|
Загрузить оригинал
|
||||||
disabled={!completedCrop}
|
</Button>
|
||||||
sx={{
|
<Button
|
||||||
padding: "10px 20px",
|
onClick={handleSaveClick}
|
||||||
borderRadius: "8px",
|
disableRipple
|
||||||
background: theme.palette.brightPurple.main,
|
variant="contained"
|
||||||
fontSize: "18px",
|
data-cy="crop-modal-save-button"
|
||||||
}}
|
sx={{
|
||||||
>
|
height: "48px",
|
||||||
<CropIcon />
|
borderRadius: "8px",
|
||||||
Обрезать
|
border: "1px solid #7E2AEA",
|
||||||
</Button>
|
marginRight: "10px",
|
||||||
</Box>
|
px: "20px",
|
||||||
</Box>
|
ml: "auto",
|
||||||
</Modal>
|
}}
|
||||||
);
|
>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCropModalState(initialOpenState = false) {
|
export function useCropModalState(initialOpenState = false) {
|
||||||
const [isCropModalOpen, setOpened] = useState(initialOpenState);
|
const [isCropModalOpen, setOpened] = useState(initialOpenState);
|
||||||
const [imageBlob, setCropModalImageBlob] = useState<Blob | null>(null);
|
const [imageBlob, setCropModalImageBlob] = useState<Blob | null>(null);
|
||||||
const [originalImageUrl, setOriginalImageUrl] = useState<string | null>(null);
|
const [originalImageUrl, setOriginalImageUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
const closeCropModal = () => {
|
const closeCropModal = () => {
|
||||||
setOpened(false);
|
setOpened(false);
|
||||||
setCropModalImageBlob(null);
|
setCropModalImageBlob(null);
|
||||||
setOriginalImageUrl(null);
|
setOriginalImageUrl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function openCropModal(image: Blob | string, originalImageUrl: string | null | undefined = null) {
|
async function openCropModal(image: Blob | string, originalImageUrl: string | null | undefined = null) {
|
||||||
if (typeof image === "string") {
|
if (typeof image === "string") {
|
||||||
const response = await fetch(image);
|
const response = await fetch(image);
|
||||||
image = await response.blob();
|
image = await response.blob();
|
||||||
}
|
|
||||||
|
|
||||||
setCropModalImageBlob(image);
|
|
||||||
setOriginalImageUrl(originalImageUrl);
|
|
||||||
setOpened(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
setCropModalImageBlob(image);
|
||||||
isCropModalOpen,
|
setOriginalImageUrl(originalImageUrl);
|
||||||
openCropModal,
|
setOpened(true);
|
||||||
closeCropModal,
|
}
|
||||||
imageBlob,
|
|
||||||
setCropModalImageBlob,
|
return {
|
||||||
originalImageUrl,
|
isCropModalOpen,
|
||||||
} as const;
|
openCropModal,
|
||||||
|
closeCropModal,
|
||||||
|
imageBlob,
|
||||||
|
setCropModalImageBlob,
|
||||||
|
originalImageUrl,
|
||||||
|
} as const;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user