Merge remote-tracking branch 'origin/staging'
This commit is contained in:
commit
49dcee8deb
@ -7,6 +7,7 @@
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<title>Quiz</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -38,7 +38,13 @@ const DeviceType = device.type;
|
||||
let Device = md.mobile();
|
||||
if (Device === null) { Device = userAgent; }
|
||||
|
||||
export const publicationMakeRequest = ({ url, body }: any) => {
|
||||
type PublicationMakeRequestParams = {
|
||||
url: string;
|
||||
body: FormData;
|
||||
method: "POST";
|
||||
};
|
||||
|
||||
export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestParams) => {
|
||||
return axios(url, {
|
||||
data: body,
|
||||
headers: {
|
||||
@ -56,7 +62,7 @@ export const publicationMakeRequest = ({ url, body }: any) => {
|
||||
export async function getData(quizId: string): Promise<{
|
||||
data: GetQuizDataResponse | null;
|
||||
isRecentlyCompleted: boolean;
|
||||
error?: any;
|
||||
error?: AxiosError;
|
||||
}> {
|
||||
try {
|
||||
const { data, headers } = await axios<GetQuizDataResponse>(
|
||||
@ -99,6 +105,8 @@ export async function getData(quizId: string): Promise<{
|
||||
}
|
||||
|
||||
export async function getQuizData(quizId: string) {
|
||||
if (!quizId) throw new Error("No quiz id");
|
||||
|
||||
const response = await getData(quizId);
|
||||
const quizDataResponse = response.data;
|
||||
|
||||
@ -116,7 +124,14 @@ export async function getQuizData(quizId: string) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function sendAnswer({ questionId, body, qid, preview }: any) {
|
||||
type SendAnswerProps = {
|
||||
questionId: string;
|
||||
body: string | string[];
|
||||
qid: string;
|
||||
preview: boolean;
|
||||
};
|
||||
|
||||
export function sendAnswer({ questionId, body, qid, preview }: SendAnswerProps) {
|
||||
if (preview) return;
|
||||
const formData = new FormData();
|
||||
|
||||
@ -138,11 +153,26 @@ export function sendAnswer({ questionId, body, qid, preview }: any) {
|
||||
}
|
||||
|
||||
//body ={file, filename}
|
||||
export function sendFile({ questionId, body, qid, preview }: any) {
|
||||
if (preview) return;
|
||||
type SendFileParams = {
|
||||
questionId: string;
|
||||
body: {
|
||||
name: string;
|
||||
file: File;
|
||||
preview: boolean;
|
||||
};
|
||||
qid: string;
|
||||
};
|
||||
|
||||
type Answer = {
|
||||
question_id: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export function sendFile({ questionId, body, qid }: SendFileParams) {
|
||||
if (body.preview) return;
|
||||
const formData = new FormData();
|
||||
|
||||
const answers: any = [
|
||||
const answers: Answer[] = [
|
||||
{
|
||||
question_id: questionId,
|
||||
content: "file:" + body.name,
|
||||
@ -162,7 +192,20 @@ export function sendFile({ questionId, body, qid, preview }: any) {
|
||||
}
|
||||
|
||||
//форма контактов
|
||||
export function sendFC({ questionId, body, qid, preview }: any) {
|
||||
export type SendFCParams = {
|
||||
questionId: string;
|
||||
body: {
|
||||
name?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
address?: string;
|
||||
customs?: Record<string, string>;
|
||||
};
|
||||
qid: string;
|
||||
preview: boolean;
|
||||
};
|
||||
|
||||
export function sendFC({ questionId, body, qid, preview }: SendFCParams) {
|
||||
if (preview) return;
|
||||
const formData = new FormData();
|
||||
|
||||
|
@ -9,13 +9,11 @@ export default function AddressIcon({ color, backgroundColor }: Props) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
// height: "38px",
|
||||
// width: "24px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "58px",
|
||||
width: "42px",
|
||||
width: "45px",
|
||||
backgroundColor: {backgroundColor},
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderTopLeftRadius: "12px",
|
||||
|
@ -9,13 +9,11 @@ export default function EmailIcon({ color, backgroundColor }: Props) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
// height: "38px",
|
||||
// width: "24px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "58px",
|
||||
width: "42px",
|
||||
width: "45px",
|
||||
backgroundColor: {backgroundColor},
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderTopLeftRadius: "12px",
|
||||
|
@ -9,13 +9,11 @@ export default function NameIcon({ color, backgroundColor }: Props) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
// height: "38px",
|
||||
// width: "45px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "58px",
|
||||
width: "42px",
|
||||
width: "45px",
|
||||
backgroundColor: {backgroundColor},
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderTopLeftRadius: "12px",
|
||||
|
@ -9,13 +9,11 @@ export default function PhoneIcon({ color, backgroundColor }: Props) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
// height: "38px",
|
||||
// width: "24px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "58px",
|
||||
width: "42px",
|
||||
width: "45px",
|
||||
backgroundColor: {backgroundColor},
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderTopLeftRadius: "12px",
|
||||
|
@ -9,13 +9,11 @@ export default function TextIcon({ color, backgroundColor }: Props) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
// height: "38px",
|
||||
// width: "45px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "58px",
|
||||
width: "42px",
|
||||
width: "45px",
|
||||
backgroundColor: {backgroundColor},
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderTopLeftRadius: "12px",
|
||||
|
@ -4,7 +4,7 @@ type InfoProps = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
sx?: SxProps;
|
||||
onClick?: any;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
color?: string
|
||||
};
|
||||
|
@ -1,18 +1,28 @@
|
||||
import { FC, SVGProps } from "react";
|
||||
|
||||
export const NameplateLogo: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||
<svg {...props} height="1em" viewBox="0 0 89 81" fill="none">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M34.5604 0.0885217C24.3105 -1.09878 17.239 9.91147 10.6615 17.8703C4.88905 24.8549 0.590996 32.7441 0.0917664 41.7955C-0.439369 51.4255 1.3075 61.4545 7.86805 68.5172C14.6258 75.7923 24.7486 80.3889 34.5604 78.9028C43.5576 77.54 47.5388 67.6598 54.2372 61.4939C61.5105 54.799 75.061 51.6851 74.8731 41.7955C74.6848 31.8908 60.3836 29.952 53.5024 22.8328C46.3747 15.4585 44.7425 1.26797 34.5604 0.0885217Z"
|
||||
fill="#7E2AEA"
|
||||
/>
|
||||
<circle cx="60.0543" cy="75.1555" r="5.65583" fill="#7E2AEA" />
|
||||
<circle cx="54.4046" cy="12.3947" r="2.1546" fill="#7E2AEA" />
|
||||
<path
|
||||
d="M88.866 39.4685C88.2378 33.3607 85.3643 27.7037 80.8025 23.594C76.2408 19.4843 70.3156 17.2146 64.1757 17.2248C63.3039 17.2252 62.4328 17.2708 61.5658 17.3614C55.4608 18.0025 49.8093 20.8814 45.7015 25.443C41.5937 30.0046 39.3205 35.9256 39.3203 42.0642V42.0642V77.549H49.9658V62.468C54.128 65.3636 59.0787 66.9119 64.1491 66.9036C65.0208 66.9033 65.8919 66.8577 66.759 66.767C70.0031 66.426 73.1483 65.4494 76.0151 63.8929C78.8818 62.3364 81.4138 60.2305 83.4667 57.6955C85.5195 55.1604 87.0529 52.2458 87.9793 49.1181C88.9058 45.9904 89.2071 42.7109 88.866 39.4667V39.4685ZM75.1937 51.0011C74.0243 52.4537 72.5783 53.6599 70.9395 54.5498C69.3007 55.4397 67.5017 55.9956 65.6465 56.1854C65.149 56.2371 64.6492 56.2631 64.1491 56.2635C60.9296 56.2605 57.8068 55.1631 55.2932 53.1515C52.7796 51.1398 51.0245 48.3334 50.3161 45.1929C49.6077 42.0523 49.988 38.7642 51.3945 35.8683C52.8011 32.9723 55.1504 30.6406 58.0568 29.2558C60.9632 27.871 64.2541 27.5154 67.3892 28.2473C70.5244 28.9793 73.3176 30.7553 75.3103 33.284C77.303 35.8126 78.3769 38.9436 78.3558 42.1629C78.3346 45.3823 77.2196 48.4989 75.1937 51.0011Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<svg {...props} width="168" height="20" viewBox="0 0 168 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M162.063 14.9047C161.972 14.9047 161.895 14.8775 161.832 14.823C161.777 14.7595 161.75 14.6824 161.75 14.5917V14.1153C161.75 14.0337 161.768 13.9565 161.804 13.884C161.85 13.8114 161.895 13.7433 161.941 13.6798L165.697 8.9028H162.186C162.095 8.9028 162.018 8.87558 161.954 8.82115C161.9 8.76671 161.872 8.68958 161.872 8.58978V8.14066C161.872 8.04993 161.9 7.97734 161.954 7.92291C162.018 7.85939 162.095 7.82764 162.186 7.82764H167.031C167.121 7.82764 167.194 7.85939 167.248 7.92291C167.312 7.97734 167.344 8.04993 167.344 8.14066V8.64422C167.344 8.72588 167.321 8.79846 167.276 8.86197C167.239 8.92549 167.198 8.99353 167.153 9.06612L163.438 13.8295H167.303C167.393 13.8295 167.466 13.8567 167.521 13.9112C167.584 13.9656 167.616 14.0427 167.616 14.1425V14.5917C167.616 14.6824 167.584 14.7595 167.521 14.823C167.466 14.8775 167.393 14.9047 167.303 14.9047H162.063Z" fill="currentColor"/>
|
||||
<path d="M159.148 14.9037C159.058 14.9037 158.981 14.8764 158.917 14.822C158.863 14.7585 158.835 14.6814 158.835 14.5906V8.13964C158.835 8.04891 158.863 7.97633 158.917 7.92189C158.981 7.85837 159.058 7.82662 159.148 7.82662H159.802C159.892 7.82662 159.965 7.85837 160.019 7.92189C160.074 7.97633 160.101 8.04891 160.101 8.13964V14.5906C160.101 14.6814 160.074 14.7585 160.019 14.822C159.965 14.8764 159.892 14.9037 159.802 14.9037H159.148ZM159.04 6.50648C158.949 6.50648 158.872 6.47926 158.808 6.42482C158.754 6.36131 158.727 6.28419 158.727 6.19345V5.45853C158.727 5.3678 158.754 5.29521 158.808 5.24078C158.872 5.17726 158.949 5.14551 159.04 5.14551H159.897C159.988 5.14551 160.06 5.17726 160.115 5.24078C160.178 5.29521 160.21 5.3678 160.21 5.45853V6.19345C160.21 6.28419 160.178 6.36131 160.115 6.42482C160.06 6.47926 159.988 6.50648 159.897 6.50648H159.04Z" fill="currentColor"/>
|
||||
<path d="M153.294 15.0408C152.714 15.0408 152.219 14.9138 151.811 14.6597C151.412 14.3966 151.108 14.0337 150.899 13.5709C150.69 13.1082 150.586 12.5774 150.586 11.9786V8.14066C150.586 8.04993 150.613 7.97734 150.668 7.92291C150.731 7.85939 150.808 7.82764 150.899 7.82764H151.579C151.67 7.82764 151.743 7.85939 151.797 7.92291C151.861 7.97734 151.892 8.04993 151.892 8.14066V11.9105C151.892 13.2624 152.482 13.9384 153.662 13.9384C154.224 13.9384 154.673 13.7615 155.009 13.4076C155.354 13.0447 155.526 12.5457 155.526 11.9105V8.14066C155.526 8.04993 155.553 7.97734 155.608 7.92291C155.671 7.85939 155.749 7.82764 155.839 7.82764H156.506C156.606 7.82764 156.683 7.85939 156.738 7.92291C156.792 7.97734 156.819 8.04993 156.819 8.14066V14.5917C156.819 14.6824 156.792 14.7595 156.738 14.823C156.683 14.8775 156.606 14.9047 156.506 14.9047H155.88C155.789 14.9047 155.712 14.8775 155.649 14.823C155.594 14.7595 155.567 14.6824 155.567 14.5917V13.9928C155.322 14.3104 155.023 14.5644 154.669 14.755C154.324 14.9455 153.866 15.0408 153.294 15.0408Z" fill="currentColor"/>
|
||||
<path d="M147.755 15.7912C147.637 15.7912 147.542 15.7503 147.47 15.6687C147.397 15.5961 147.338 15.5326 147.293 15.4781L146.735 14.716C146.245 14.9338 145.678 15.0426 145.033 15.0426C144.253 15.0426 143.586 14.9111 143.033 14.648C142.488 14.3758 142.062 13.9629 141.754 13.4095C141.454 12.8469 141.291 12.1438 141.264 11.3C141.255 10.9008 141.25 10.5197 141.25 10.1568C141.25 9.78476 141.255 9.39915 141.264 8.99993C141.291 8.1652 141.463 7.47111 141.781 6.91765C142.098 6.35511 142.534 5.93775 143.087 5.66555C143.65 5.38429 144.299 5.24365 145.033 5.24365C145.777 5.24365 146.426 5.38429 146.98 5.66555C147.542 5.93775 147.982 6.35511 148.3 6.91765C148.617 7.47111 148.785 8.1652 148.803 8.99993C148.822 9.39915 148.831 9.78476 148.831 10.1568C148.831 10.5197 148.822 10.9008 148.803 11.3C148.758 12.5521 148.431 13.4821 147.823 14.09L148.722 15.3693C148.731 15.3783 148.74 15.392 148.749 15.4101C148.758 15.4373 148.763 15.4736 148.763 15.519C148.772 15.5916 148.749 15.6551 148.695 15.7095C148.64 15.764 148.572 15.7912 148.49 15.7912H147.755ZM145.033 13.8858C145.723 13.8858 146.29 13.6771 146.735 13.2598C147.179 12.8424 147.42 12.1665 147.456 11.2319C147.474 10.8236 147.483 10.4607 147.483 10.1431C147.483 9.81651 147.474 9.45359 147.456 9.05437C147.438 8.42832 147.32 7.92023 147.102 7.53008C146.893 7.13994 146.612 6.85413 146.258 6.67267C145.905 6.49121 145.496 6.40048 145.033 6.40048C144.589 6.40048 144.185 6.49121 143.822 6.67267C143.468 6.85413 143.187 7.13994 142.978 7.53008C142.77 7.92023 142.652 8.42832 142.625 9.05437C142.616 9.45359 142.611 9.81651 142.611 10.1431C142.611 10.4607 142.616 10.8236 142.625 11.2319C142.661 12.1665 142.901 12.8424 143.346 13.2598C143.79 13.6771 144.353 13.8858 145.033 13.8858Z" fill="currentColor"/>
|
||||
<path d="M135.907 15.0367C135.453 15.0367 135.04 14.946 134.668 14.7645C134.296 14.5831 133.997 14.3381 133.77 14.0296C133.543 13.7211 133.43 13.3718 133.43 12.9817C133.43 12.3556 133.684 11.8566 134.192 11.4846C134.7 11.1126 135.362 10.8676 136.179 10.7497L138.207 10.4639V10.0692C138.207 9.63369 138.08 9.29344 137.826 9.04847C137.581 8.8035 137.177 8.68101 136.614 8.68101C136.206 8.68101 135.875 8.76267 135.621 8.92598C135.376 9.0893 135.203 9.29798 135.104 9.55203C135.049 9.68813 134.954 9.75617 134.818 9.75617H134.205C134.106 9.75617 134.029 9.72895 133.974 9.67452C133.929 9.611 133.906 9.53842 133.906 9.45676C133.906 9.32066 133.956 9.15281 134.056 8.9532C134.165 8.75359 134.328 8.55852 134.546 8.36798C134.763 8.17745 135.04 8.01867 135.376 7.89165C135.721 7.75555 136.138 7.6875 136.628 7.6875C137.172 7.6875 137.631 7.76009 138.003 7.90526C138.375 8.04135 138.665 8.22735 138.874 8.46325C139.091 8.69915 139.246 8.96681 139.336 9.26622C139.436 9.56564 139.486 9.86959 139.486 10.1781V14.5876C139.486 14.6783 139.454 14.7555 139.391 14.819C139.336 14.8734 139.264 14.9006 139.173 14.9006H138.547C138.447 14.9006 138.37 14.8734 138.316 14.819C138.261 14.7555 138.234 14.6783 138.234 14.5876V14.0024C138.116 14.1657 137.957 14.329 137.758 14.4923C137.558 14.6466 137.308 14.7782 137.009 14.887C136.71 14.9868 136.342 15.0367 135.907 15.0367ZM136.192 14.016C136.564 14.016 136.905 13.9389 137.213 13.7846C137.522 13.6213 137.762 13.3718 137.934 13.0361C138.116 12.7004 138.207 12.2785 138.207 11.7704V11.3893L136.628 11.6207C135.984 11.7114 135.498 11.8657 135.172 12.0834C134.845 12.2921 134.682 12.5598 134.682 12.8864C134.682 13.1405 134.754 13.3537 134.9 13.5261C135.054 13.6894 135.244 13.8119 135.471 13.8935C135.707 13.9752 135.947 14.016 136.192 14.016Z" fill="currentColor"/>
|
||||
<path d="M125.993 14.9006C125.902 14.9006 125.825 14.8734 125.761 14.819C125.707 14.7555 125.68 14.6783 125.68 14.5876V8.13662C125.68 8.04589 125.707 7.9733 125.761 7.91887C125.825 7.85535 125.902 7.8236 125.993 7.8236H126.632C126.723 7.8236 126.796 7.85535 126.85 7.91887C126.914 7.9733 126.945 8.04589 126.945 8.13662V8.73545C127.181 8.42696 127.476 8.17745 127.83 7.98691C128.193 7.7873 128.656 7.6875 129.218 7.6875C129.808 7.6875 130.307 7.81906 130.715 8.08218C131.133 8.33623 131.446 8.69462 131.654 9.15735C131.863 9.611 131.967 10.1418 131.967 10.7497V14.5876C131.967 14.6783 131.94 14.7555 131.886 14.819C131.831 14.8734 131.759 14.9006 131.668 14.9006H130.987C130.897 14.9006 130.82 14.8734 130.756 14.819C130.702 14.7555 130.674 14.6783 130.674 14.5876V10.8177C130.674 10.1826 130.52 9.68813 130.212 9.33427C129.903 8.97135 129.45 8.78989 128.851 8.78989C128.288 8.78989 127.835 8.97135 127.49 9.33427C127.154 9.68813 126.986 10.1826 126.986 10.8177V14.5876C126.986 14.6783 126.954 14.7555 126.891 14.819C126.837 14.8734 126.764 14.9006 126.673 14.9006H125.993Z" fill="currentColor"/>
|
||||
<path d="M120.884 15.0367C119.949 15.0367 119.205 14.7509 118.652 14.1793C118.099 13.5986 117.795 12.8093 117.74 11.8112C117.731 11.6933 117.727 11.5436 117.727 11.3621C117.727 11.1716 117.731 11.0173 117.74 10.8994C117.776 10.2552 117.926 9.69266 118.189 9.21179C118.452 8.72184 118.811 8.3453 119.264 8.08218C119.727 7.81906 120.267 7.6875 120.884 7.6875C121.574 7.6875 122.15 7.83267 122.612 8.12301C123.084 8.41335 123.443 8.82618 123.688 9.36149C123.933 9.89681 124.055 10.5229 124.055 11.2396V11.471C124.055 11.5708 124.023 11.6479 123.96 11.7024C123.905 11.7568 123.833 11.784 123.742 11.784H119.033C119.033 11.7931 119.033 11.8112 119.033 11.8385C119.033 11.8657 119.033 11.8884 119.033 11.9065C119.051 12.2785 119.133 12.6278 119.278 12.9545C119.423 13.272 119.632 13.5306 119.904 13.7302C120.176 13.9298 120.503 14.0296 120.884 14.0296C121.211 14.0296 121.483 13.9797 121.701 13.8799C121.918 13.7801 122.095 13.6712 122.231 13.5533C122.367 13.4263 122.458 13.331 122.504 13.2675C122.585 13.1495 122.649 13.0815 122.694 13.0633C122.739 13.0361 122.812 13.0225 122.912 13.0225H123.565C123.656 13.0225 123.728 13.0497 123.783 13.1042C123.846 13.1495 123.874 13.2176 123.865 13.3083C123.855 13.4444 123.783 13.6123 123.647 13.8119C123.511 14.0024 123.316 14.1929 123.062 14.3835C122.808 14.574 122.499 14.7328 122.136 14.8598C121.773 14.9778 121.356 15.0367 120.884 15.0367ZM119.033 10.845H122.762V10.8041C122.762 10.3958 122.685 10.0329 122.531 9.71535C122.386 9.39779 122.172 9.14827 121.891 8.96681C121.61 8.77628 121.274 8.68101 120.884 8.68101C120.494 8.68101 120.158 8.77628 119.877 8.96681C119.605 9.14827 119.396 9.39779 119.251 9.71535C119.106 10.0329 119.033 10.3958 119.033 10.8041V10.845Z" fill="currentColor"/>
|
||||
<path d="M109.915 14.9033C109.815 14.9033 109.738 14.876 109.683 14.8216C109.629 14.7581 109.602 14.681 109.602 14.5902V5.7031C109.602 5.60329 109.629 5.52617 109.683 5.47173C109.738 5.40822 109.815 5.37646 109.915 5.37646H113.371C114.043 5.37646 114.624 5.48534 115.113 5.7031C115.613 5.92085 115.998 6.24749 116.27 6.683C116.543 7.10943 116.679 7.64475 116.679 8.28894C116.679 8.93313 116.543 9.46845 116.27 9.89489C115.998 10.3213 115.613 10.6434 115.113 10.8612C114.624 11.0789 114.043 11.1878 113.371 11.1878H110.963V14.5902C110.963 14.681 110.931 14.7581 110.867 14.8216C110.813 14.876 110.736 14.9033 110.636 14.9033H109.915ZM110.949 10.0446H113.303C113.975 10.0446 114.478 9.89489 114.814 9.59547C115.15 9.29606 115.318 8.86055 115.318 8.28894C115.318 7.72641 115.154 7.2909 114.828 6.98241C114.501 6.67392 113.993 6.51968 113.303 6.51968H110.949V10.0446Z" fill="currentColor"/>
|
||||
<path d="M100.555 15.0367C100.101 15.0367 99.6886 14.946 99.3166 14.7645C98.9446 14.5831 98.6452 14.3381 98.4184 14.0296C98.1915 13.7211 98.0781 13.3718 98.0781 12.9817C98.0781 12.3556 98.3322 11.8566 98.8403 11.4846C99.3484 11.1126 100.011 10.8676 100.827 10.7497L102.855 10.4639V10.0692C102.855 9.63369 102.728 9.29344 102.474 9.04847C102.229 8.8035 101.825 8.68101 101.263 8.68101C100.855 8.68101 100.523 8.76267 100.269 8.92598C100.024 9.0893 99.8519 9.29798 99.7521 9.55203C99.6977 9.68813 99.6024 9.75617 99.4663 9.75617H98.8539C98.7541 9.75617 98.6769 9.72895 98.6225 9.67452C98.5771 9.611 98.5545 9.53842 98.5545 9.45676C98.5545 9.32066 98.6044 9.15281 98.7042 8.9532C98.813 8.75359 98.9764 8.55852 99.1941 8.36798C99.4119 8.17745 99.6886 8.01867 100.024 7.89165C100.369 7.75555 100.786 7.6875 101.276 7.6875C101.821 7.6875 102.279 7.76009 102.651 7.90526C103.023 8.04135 103.313 8.22735 103.522 8.46325C103.74 8.69915 103.894 8.96681 103.985 9.26622C104.085 9.56564 104.134 9.86959 104.134 10.1781V14.5876C104.134 14.6783 104.103 14.7555 104.039 14.819C103.985 14.8734 103.912 14.9006 103.821 14.9006H103.195C103.096 14.9006 103.018 14.8734 102.964 14.819C102.91 14.7555 102.882 14.6783 102.882 14.5876V14.0024C102.764 14.1657 102.606 14.329 102.406 14.4923C102.206 14.6466 101.957 14.7782 101.657 14.887C101.358 14.9868 100.991 15.0367 100.555 15.0367ZM100.841 14.016C101.213 14.016 101.553 13.9389 101.862 13.7846C102.17 13.6213 102.411 13.3718 102.583 13.0361C102.764 12.7004 102.855 12.2785 102.855 11.7704V11.3893L101.276 11.6207C100.632 11.7114 100.147 11.8657 99.8202 12.0834C99.4935 12.2921 99.3302 12.5598 99.3302 12.8864C99.3302 13.1405 99.4028 13.3537 99.548 13.5261C99.7022 13.6894 99.8928 13.8119 100.12 13.8935C100.355 13.9752 100.596 14.016 100.841 14.016Z" fill="currentColor"/>
|
||||
<path d="M90.6021 14.9047C90.5114 14.9047 90.4342 14.8775 90.3707 14.823C90.3163 14.7595 90.2891 14.6824 90.2891 14.5917V8.14066C90.2891 8.04993 90.3163 7.97734 90.3707 7.92291C90.4342 7.85939 90.5114 7.82764 90.6021 7.82764H91.2554C91.3461 7.82764 91.4187 7.85939 91.4731 7.92291C91.5275 7.97734 91.5548 8.04993 91.5548 8.14066V10.7537H95.2702V8.14066C95.2702 8.04993 95.2974 7.97734 95.3519 7.92291C95.4154 7.85939 95.4925 7.82764 95.5832 7.82764H96.2229C96.3136 7.82764 96.3862 7.85939 96.4406 7.92291C96.5042 7.97734 96.5359 8.04993 96.5359 8.14066V14.5917C96.5359 14.6824 96.5042 14.7595 96.4406 14.823C96.3862 14.8775 96.3136 14.9047 96.2229 14.9047H95.5832C95.4925 14.9047 95.4154 14.8775 95.3519 14.823C95.2974 14.7595 95.2702 14.6824 95.2702 14.5917V11.8425H91.5548V14.5917C91.5548 14.6824 91.5275 14.7595 91.4731 14.823C91.4187 14.8775 91.3461 14.9047 91.2554 14.9047H90.6021Z" fill="currentColor"/>
|
||||
<path d="M82.0322 15.0367C81.3426 15.0367 80.7665 14.9052 80.3038 14.6421C79.841 14.3789 79.4872 14.016 79.2422 13.5533C78.9972 13.0815 78.8611 12.5462 78.8339 11.9473C78.8249 11.7931 78.8203 11.598 78.8203 11.3621C78.8203 11.1171 78.8249 10.9221 78.8339 10.7769C78.8611 10.169 78.9972 9.63369 79.2422 9.17096C79.4963 8.70823 79.8547 8.3453 80.3174 8.08218C80.7801 7.81906 81.3517 7.6875 82.0322 7.6875C82.7127 7.6875 83.2843 7.81906 83.747 8.08218C84.2098 8.3453 84.5636 8.70823 84.8086 9.17096C85.0626 9.63369 85.2033 10.169 85.2305 10.7769C85.2396 10.9221 85.2441 11.1171 85.2441 11.3621C85.2441 11.598 85.2396 11.7931 85.2305 11.9473C85.2033 12.5462 85.0672 13.0815 84.8222 13.5533C84.5772 14.016 84.2234 14.3789 83.7606 14.6421C83.2979 14.9052 82.7218 15.0367 82.0322 15.0367ZM82.0322 13.9888C82.5947 13.9888 83.0439 13.8119 83.3796 13.458C83.7153 13.0951 83.8967 12.5688 83.924 11.8793C83.933 11.7432 83.9376 11.5708 83.9376 11.3621C83.9376 11.1534 83.933 10.981 83.924 10.845C83.8967 10.1554 83.7153 9.63369 83.3796 9.27984C83.0439 8.91691 82.5947 8.73545 82.0322 8.73545C81.4697 8.73545 81.016 8.91691 80.6712 9.27984C80.3355 9.63369 80.1586 10.1554 80.1405 10.845C80.1314 10.981 80.1268 11.1534 80.1268 11.3621C80.1268 11.5708 80.1314 11.7432 80.1405 11.8793C80.1586 12.5688 80.3355 13.0951 80.6712 13.458C81.016 13.8119 81.4697 13.9888 82.0322 13.9888Z" fill="currentColor"/>
|
||||
<path d="M71.1568 14.9047C71.066 14.9047 70.9889 14.8775 70.9254 14.823C70.871 14.7595 70.8438 14.6824 70.8438 14.5917V8.14066C70.8438 8.04993 70.871 7.97734 70.9254 7.92291C70.9889 7.85939 71.066 7.82764 71.1568 7.82764H71.81C71.9008 7.82764 71.9734 7.85939 72.0278 7.92291C72.0822 7.97734 72.1094 8.04993 72.1094 8.14066V10.7537H75.8249V8.14066C75.8249 8.04993 75.8521 7.97734 75.9066 7.92291C75.9701 7.85939 76.0472 7.82764 76.1379 7.82764H76.7776C76.8683 7.82764 76.9409 7.85939 76.9953 7.92291C77.0588 7.97734 77.0906 8.04993 77.0906 8.14066V14.5917C77.0906 14.6824 77.0588 14.7595 76.9953 14.823C76.9409 14.8775 76.8683 14.9047 76.7776 14.9047H76.1379C76.0472 14.9047 75.9701 14.8775 75.9066 14.823C75.8521 14.7595 75.8249 14.6824 75.8249 14.5917V11.8425H72.1094V14.5917C72.1094 14.6824 72.0822 14.7595 72.0278 14.823C71.9734 14.8775 71.9008 14.9047 71.81 14.9047H71.1568Z" fill="currentColor"/>
|
||||
<path d="M65.3051 15.0367C64.8514 15.0367 64.4386 14.946 64.0666 14.7645C63.6946 14.5831 63.3952 14.3381 63.1684 14.0296C62.9415 13.7211 62.8281 13.3718 62.8281 12.9817C62.8281 12.3556 63.0822 11.8566 63.5903 11.4846C64.0984 11.1126 64.7607 10.8676 65.5773 10.7497L67.6051 10.4639V10.0692C67.6051 9.63369 67.4781 9.29344 67.2241 9.04847C66.9791 8.8035 66.5753 8.68101 66.0128 8.68101C65.6045 8.68101 65.2733 8.76267 65.0193 8.92598C64.7743 9.0893 64.6019 9.29798 64.5021 9.55203C64.4477 9.68813 64.3524 9.75617 64.2163 9.75617H63.6039C63.5041 9.75617 63.427 9.72895 63.3725 9.67452C63.3271 9.611 63.3045 9.53842 63.3045 9.45676C63.3045 9.32066 63.3544 9.15281 63.4542 8.9532C63.563 8.75359 63.7264 8.55852 63.9441 8.36798C64.1619 8.17745 64.4386 8.01867 64.7743 7.89165C65.1191 7.75555 65.5365 7.6875 66.0264 7.6875C66.5708 7.6875 67.029 7.76009 67.401 7.90526C67.773 8.04135 68.0633 8.22735 68.272 8.46325C68.4898 8.69915 68.644 8.96681 68.7347 9.26622C68.8345 9.56564 68.8844 9.86959 68.8844 10.1781V14.5876C68.8844 14.6783 68.8527 14.7555 68.7892 14.819C68.7347 14.8734 68.6621 14.9006 68.5714 14.9006H67.9454C67.8456 14.9006 67.7684 14.8734 67.714 14.819C67.6596 14.7555 67.6323 14.6783 67.6323 14.5876V14.0024C67.5144 14.1657 67.3556 14.329 67.156 14.4923C66.9564 14.6466 66.7069 14.7782 66.4075 14.887C66.1081 14.9868 65.7406 15.0367 65.3051 15.0367ZM65.5909 14.016C65.9629 14.016 66.3031 13.9389 66.6116 13.7846C66.9201 13.6213 67.1605 13.3718 67.3329 13.0361C67.5144 12.7004 67.6051 12.2785 67.6051 11.7704V11.3893L66.0264 11.6207C65.3822 11.7114 64.8968 11.8657 64.5702 12.0834C64.2435 12.2921 64.0802 12.5598 64.0802 12.8864C64.0802 13.1405 64.1528 13.3537 64.298 13.5261C64.4522 13.6894 64.6428 13.8119 64.8696 13.8935C65.1055 13.9752 65.3459 14.016 65.5909 14.016Z" fill="currentColor"/>
|
||||
<path d="M55.1001 14.9047C55.0093 14.9047 54.9322 14.8729 54.8687 14.8094C54.8052 14.7459 54.7734 14.6688 54.7734 14.578V14.0745C54.7734 13.884 54.8687 13.7841 55.0592 13.7751C55.3405 13.766 55.5764 13.6344 55.7669 13.3804C55.9575 13.1173 56.1027 12.7044 56.2025 12.1419C56.3023 11.5703 56.3522 10.8172 56.3522 9.8827V8.14066C56.3522 8.04993 56.3794 7.97734 56.4338 7.92291C56.4973 7.85939 56.5745 7.82764 56.6652 7.82764H60.9931C61.0838 7.82764 61.1564 7.85939 61.2108 7.92291C61.2743 7.97734 61.3061 8.04993 61.3061 8.14066V14.5917C61.3061 14.6824 61.2743 14.7595 61.2108 14.823C61.1564 14.8775 61.0838 14.9047 60.9931 14.9047H60.3398C60.2491 14.9047 60.1765 14.8775 60.122 14.823C60.0676 14.7595 60.0404 14.6824 60.0404 14.5917V8.91641H57.5906V10.0324C57.5906 10.9034 57.5407 11.652 57.4409 12.278C57.3411 12.895 57.1869 13.3985 56.9782 13.7887C56.7786 14.1698 56.52 14.451 56.2025 14.6325C55.894 14.814 55.5265 14.9047 55.1001 14.9047Z" fill="currentColor"/>
|
||||
<path d="M50.6418 15.0367C49.7073 15.0367 48.9633 14.7509 48.4098 14.1793C47.8564 13.5986 47.5524 12.8093 47.498 11.8112C47.4889 11.6933 47.4844 11.5436 47.4844 11.3621C47.4844 11.1716 47.4889 11.0173 47.498 10.8994C47.5343 10.2552 47.684 9.69266 47.9471 9.21179C48.2102 8.72184 48.5686 8.3453 49.0223 8.08218C49.485 7.81906 50.0249 7.6875 50.6418 7.6875C51.3314 7.6875 51.9075 7.83267 52.3703 8.12301C52.8421 8.41335 53.2004 8.82618 53.4454 9.36149C53.6904 9.89681 53.8129 10.5229 53.8129 11.2396V11.471C53.8129 11.5708 53.7811 11.6479 53.7176 11.7024C53.6632 11.7568 53.5906 11.784 53.4999 11.784H48.7909C48.7909 11.7931 48.7909 11.8112 48.7909 11.8385C48.7909 11.8657 48.7909 11.8884 48.7909 11.9065C48.8091 12.2785 48.8907 12.6278 49.0359 12.9545C49.181 13.272 49.3897 13.5306 49.6619 13.7302C49.9341 13.9298 50.2608 14.0296 50.6418 14.0296C50.9685 14.0296 51.2407 13.9797 51.4584 13.8799C51.6762 13.7801 51.8531 13.6712 51.9892 13.5533C52.1253 13.4263 52.216 13.331 52.2614 13.2675C52.343 13.1495 52.4065 13.0815 52.4519 13.0633C52.4973 13.0361 52.5699 13.0225 52.6697 13.0225H53.3229C53.4137 13.0225 53.4863 13.0497 53.5407 13.1042C53.6042 13.1495 53.6314 13.2176 53.6223 13.3083C53.6133 13.4444 53.5407 13.6123 53.4046 13.8119C53.2685 14.0024 53.0734 14.1929 52.8194 14.3835C52.5653 14.574 52.2568 14.7328 51.8939 14.8598C51.531 14.9778 51.1136 15.0367 50.6418 15.0367ZM48.7909 10.845H52.52V10.8041C52.52 10.3958 52.4428 10.0329 52.2886 9.71535C52.1434 9.39779 51.9302 9.14827 51.6489 8.96681C51.3677 8.77628 51.032 8.68101 50.6418 8.68101C50.2517 8.68101 49.916 8.77628 49.6347 8.96681C49.3625 9.14827 49.1538 9.39779 49.0087 9.71535C48.8635 10.0329 48.7909 10.3958 48.7909 10.8041V10.845Z" fill="currentColor"/>
|
||||
<path d="M39.2505 16.4017C39.1598 16.4017 39.0827 16.37 39.0192 16.3065C38.9647 16.252 38.9375 16.1795 38.9375 16.0887V14.1289C38.9375 14.0382 38.9647 13.9656 39.0192 13.9112C39.0827 13.8477 39.1598 13.8159 39.2505 13.8159H39.3186C39.5635 13.8068 39.7722 13.6707 39.9446 13.4076C40.117 13.1354 40.2486 12.7135 40.3393 12.1419C40.43 11.5612 40.4754 10.8082 40.4754 9.8827V8.14066C40.4754 8.04993 40.5026 7.97734 40.5571 7.92291C40.6206 7.85939 40.6977 7.82764 40.7884 7.82764H45.1299C45.2206 7.82764 45.2932 7.85939 45.3477 7.92291C45.4112 7.97734 45.4429 8.04993 45.4429 8.14066V13.7887H46.0826C46.1824 13.7887 46.2595 13.8204 46.314 13.884C46.3684 13.9384 46.3956 14.011 46.3956 14.1017V16.0887C46.3956 16.1795 46.3684 16.252 46.314 16.3065C46.2595 16.37 46.1824 16.4017 46.0826 16.4017H45.4429C45.3522 16.4017 45.2751 16.37 45.2116 16.3065C45.1571 16.252 45.1299 16.1795 45.1299 16.0887V14.9047H40.2032V16.0887C40.2032 16.1795 40.1714 16.252 40.1079 16.3065C40.0535 16.37 39.9809 16.4017 39.8902 16.4017H39.2505ZM40.9381 13.8159L44.1772 13.7887V8.91641H41.7139V10.0324C41.7139 10.9942 41.6458 11.7835 41.5097 12.4005C41.3736 13.0084 41.1831 13.4802 40.9381 13.8159Z" fill="currentColor"/>
|
||||
<path d="M34.2034 15.0426C33.3686 15.0426 32.6745 14.8884 32.1211 14.5799C31.5767 14.2714 31.1639 13.8404 30.8826 13.287C30.6013 12.7335 30.4471 12.0893 30.4199 11.3544C30.4108 10.9824 30.4062 10.5832 30.4062 10.1568C30.4062 9.73032 30.4108 9.32203 30.4199 8.93188C30.4471 8.19696 30.6013 7.55276 30.8826 6.9993C31.1639 6.44584 31.5767 6.01487 32.1211 5.70638C32.6745 5.3979 33.3686 5.24365 34.2034 5.24365C34.8294 5.24365 35.3783 5.32985 35.8501 5.50224C36.3219 5.67463 36.7121 5.90599 37.0206 6.19633C37.3381 6.48667 37.5786 6.8133 37.7419 7.17623C37.9052 7.53008 37.9959 7.89754 38.0141 8.27862C38.0231 8.36027 37.9959 8.42832 37.9324 8.48276C37.878 8.5372 37.8099 8.56442 37.7283 8.56442H36.9797C36.8981 8.56442 36.8255 8.54174 36.762 8.49637C36.7075 8.45101 36.6667 8.36935 36.6395 8.2514C36.4852 7.54369 36.1949 7.05828 35.7685 6.79516C35.3511 6.53204 34.8249 6.40048 34.1897 6.40048C33.4639 6.40048 32.8878 6.60916 32.4613 7.02652C32.0349 7.43481 31.8035 8.09262 31.7672 8.99993C31.74 9.74393 31.74 10.5061 31.7672 11.2864C31.8035 12.1937 32.0349 12.856 32.4613 13.2734C32.8878 13.6817 33.4639 13.8858 34.1897 13.8858C34.8249 13.8858 35.3511 13.7543 35.7685 13.4911C36.1949 13.228 36.4852 12.7426 36.6395 12.0349C36.6667 11.9169 36.7075 11.8353 36.762 11.7899C36.8255 11.7446 36.8981 11.7219 36.9797 11.7219H37.7283C37.8099 11.7219 37.878 11.7491 37.9324 11.8035C37.9959 11.858 38.0231 11.926 38.0141 12.0077C37.9959 12.3887 37.9052 12.7607 37.7419 13.1237C37.5786 13.4775 37.3381 13.7996 37.0206 14.09C36.7121 14.3803 36.3219 14.6117 35.8501 14.7841C35.3783 14.9564 34.8294 15.0426 34.2034 15.0426Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.54763 0.0219046C6.0113 -0.271892 4.26146 2.45258 2.63386 4.42198C1.20548 6.15032 0.14193 8.10248 0.0183964 10.3423C-0.113033 12.7252 0.319225 15.2068 1.94263 16.9545C3.61482 18.7547 6.1197 19.8922 8.54763 19.5244C10.774 19.1872 11.7591 16.7423 13.4166 15.2166C15.2164 13.56 18.5695 12.7894 18.523 10.3423C18.4764 7.89135 14.9376 7.41159 13.2348 5.64994C11.4711 3.8252 11.0672 0.313757 8.54763 0.0219046Z" fill="#7E2AEA"/>
|
||||
<circle cx="14.8605" cy="18.5983" r="1.39953" fill="#7E2AEA"/>
|
||||
<circle cx="13.4628" cy="3.06831" r="0.533154" fill="#7E2AEA"/>
|
||||
<path d="M21.9866 9.76883C21.8311 8.25747 21.1201 6.85765 19.9913 5.84071C18.8625 4.82376 17.3963 4.26212 15.877 4.26466C15.6613 4.26475 15.4457 4.27604 15.2312 4.29846C13.7205 4.45709 12.3221 5.16948 11.3056 6.29824C10.2891 7.42699 9.72661 8.89216 9.72656 10.4111V10.4111V19.1918H12.3608V15.46C13.3907 16.1766 14.6158 16.5597 15.8704 16.5576C16.0861 16.5575 16.3017 16.5463 16.5162 16.5238C17.319 16.4394 18.0973 16.1978 18.8066 15.8126C19.516 15.4275 20.1426 14.9064 20.6505 14.2791C21.1585 13.6518 21.538 12.9306 21.7672 12.1566C21.9964 11.3827 22.071 10.5712 21.9866 9.76839V9.76883ZM18.6034 12.6226C18.314 12.982 17.9562 13.2805 17.5507 13.5007C17.1452 13.7209 16.7 13.8585 16.241 13.9054C16.1179 13.9182 15.9942 13.9246 15.8704 13.9247C15.0738 13.924 14.301 13.6525 13.679 13.1547C13.057 12.6569 12.6228 11.9624 12.4475 11.1853C12.2722 10.4082 12.3663 9.59455 12.7143 8.87796C13.0624 8.16136 13.6437 7.58437 14.3629 7.2417C15.0821 6.89903 15.8964 6.81104 16.6722 6.99216C17.448 7.17329 18.1391 7.61277 18.6323 8.23848C19.1254 8.86418 19.3911 9.63893 19.3858 10.4356C19.3806 11.2322 19.1047 12.0034 18.6034 12.6226Z" fill="currentColor"/>
|
||||
|
||||
</svg>
|
||||
);
|
||||
|
@ -1,22 +1,23 @@
|
||||
import { getQuizData } from "@/api/quizRelase";
|
||||
import { QuizViewContext, createQuizViewStore } from "@/stores/quizView";
|
||||
import LoadingSkeleton from "@/ui_kit/LoadingSkeleton";
|
||||
import { QuizDataContext } from "@contexts/QuizDataContext";
|
||||
import { RootContainerWidthContext } from "@contexts/RootContainerWidthContext";
|
||||
import { QuizSettings } from "@model/settingsData";
|
||||
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
|
||||
import ScopedCssBaseline from "@mui/material/ScopedCssBaseline";
|
||||
import { LocalizationProvider } from "@mui/x-date-pickers";
|
||||
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
|
||||
import { ruRU } from '@mui/x-date-pickers/locales';
|
||||
import { ruRU } from "@mui/x-date-pickers/locales";
|
||||
import { handleComponentError } from "@utils/handleComponentError";
|
||||
import lightTheme from "@utils/themes/light";
|
||||
import moment from "moment";
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
import { SnackbarProvider } from "notistack";
|
||||
import { startTransition, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import useSWR from "swr";
|
||||
import { ApologyPage } from "./ViewPublicationPage/ApologyPage";
|
||||
import ViewPublicationPage from "./ViewPublicationPage/ViewPublicationPage";
|
||||
import { QuizViewContext, createQuizViewStore } from "@/stores/quizView";
|
||||
|
||||
|
||||
moment.locale("ru");
|
||||
@ -28,9 +29,10 @@ type Props = {
|
||||
preview?: boolean;
|
||||
changeFaviconAndTitle?: boolean;
|
||||
className?: string;
|
||||
disableGlobalCss?: boolean;
|
||||
};
|
||||
|
||||
export default function QuizAnswerer({ quizSettings, quizId, preview = false, changeFaviconAndTitle = true, className }: Props) {
|
||||
function QuizAnswererInner({ quizSettings, quizId, preview = false, changeFaviconAndTitle = true, className, disableGlobalCss = false }: Props) {
|
||||
const [quizViewStore] = useState(createQuizViewStore);
|
||||
const [rootContainerWidth, setRootContainerWidth] = useState<number>(() => window.innerWidth);
|
||||
const rootContainerRef = useRef<HTMLDivElement>(null);
|
||||
@ -41,6 +43,30 @@ export default function QuizAnswerer({ quizSettings, quizId, preview = false, ch
|
||||
refreshInterval: 0,
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
//@ts-ignore
|
||||
let YM = window?.ym;
|
||||
//@ts-ignore
|
||||
let VP = window?._tmr;
|
||||
if (YM !== undefined && quizSettings?.settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
quizSettings.settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
"penaquiz-start"
|
||||
);
|
||||
};
|
||||
if (VP !== undefined && quizSettings?.settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: quizSettings.settings.cfg.vkMetricNumber,
|
||||
goal: "penaquiz-start"
|
||||
});
|
||||
};
|
||||
}, 4000)
|
||||
}, [])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth);
|
||||
}, []);
|
||||
@ -58,49 +84,23 @@ export default function QuizAnswerer({ quizSettings, quizId, preview = false, ch
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (isLoading) return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<LoadingSkeleton />
|
||||
</ThemeProvider>
|
||||
);
|
||||
if (error) return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApologyPage error={error} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
if (isLoading) return <LoadingSkeleton />;
|
||||
if (error) return <ApologyPage error={error} />;
|
||||
|
||||
quizSettings ??= data;
|
||||
if (!quizSettings) throw new Error("Quiz data is null");
|
||||
|
||||
if (quizSettings.questions.length === 0) return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApologyPage error={new Error("No questions found")} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
if(!quizId) return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApologyPage error={error} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
if (quizSettings.questions.length === 0) return <ApologyPage error={new Error("No questions found")} />;
|
||||
if (!quizId) return <ApologyPage error={new Error("No quiz id")} />;
|
||||
|
||||
|
||||
return (
|
||||
<QuizViewContext.Provider value={quizViewStore}>
|
||||
<RootContainerWidthContext.Provider value={rootContainerWidth}>
|
||||
<QuizDataContext.Provider value={{ ...quizSettings, quizId, preview, changeFaviconAndTitle }}>
|
||||
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="ru" localeText={localeText}>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<SnackbarProvider
|
||||
preventDuplicate={true}
|
||||
style={{ backgroundColor: lightTheme.palette.brightPurple.main }}
|
||||
>
|
||||
<CssBaseline />
|
||||
const quizContainer = (
|
||||
<Box
|
||||
ref={rootContainerRef}
|
||||
className={className}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<ErrorBoundary
|
||||
@ -110,11 +110,45 @@ export default function QuizAnswerer({ quizSettings, quizId, preview = false, ch
|
||||
<ViewPublicationPage />
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
|
||||
return (
|
||||
<QuizViewContext.Provider value={quizViewStore}>
|
||||
<RootContainerWidthContext.Provider value={rootContainerWidth}>
|
||||
<QuizDataContext.Provider value={{ ...quizSettings, quizId, preview, changeFaviconAndTitle }}>
|
||||
{disableGlobalCss ? (
|
||||
<ScopedCssBaseline
|
||||
sx={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: "transparent",
|
||||
}}
|
||||
>
|
||||
{quizContainer}
|
||||
</ScopedCssBaseline>
|
||||
) : (
|
||||
<CssBaseline>
|
||||
{quizContainer}
|
||||
</CssBaseline>
|
||||
)}
|
||||
</QuizDataContext.Provider>
|
||||
</RootContainerWidthContext.Provider>
|
||||
</QuizViewContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default function QuizAnswerer(props: Props) {
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="ru" localeText={localeText}>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<SnackbarProvider
|
||||
preventDuplicate={true}
|
||||
style={{ backgroundColor: lightTheme.palette.brightPurple.main }}
|
||||
>
|
||||
<QuizAnswererInner {...props} />
|
||||
</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export const ApologyPage = ({ error }: Props) => {
|
||||
if (error.response?.data === "quiz is inactive") message = "Квиз не активирован";
|
||||
if (error.message === "No questions found") message = "Нет созданных вопросов";
|
||||
if (error.message === "Quiz already completed") message = "Вы уже прошли этот опрос";
|
||||
if (error.message === "No questions found") message = "Вопросы отсутствуют";
|
||||
if (error.message === "No quiz id") message = "Отсутствует id квиза";
|
||||
if (error.response?.data === "Invalid request data") message = "Такого квиза не существует";
|
||||
|
||||
return (
|
||||
|
@ -1,35 +1,26 @@
|
||||
import { FC, useRef, useState, useEffect } from "react";
|
||||
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
|
||||
import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
|
||||
import NameIcon from "@icons/ContactFormIcon/NameIcon";
|
||||
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
||||
import TextIcon from "@icons/ContactFormIcon/TextIcon";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
InputAdornment,
|
||||
Link,
|
||||
TextField as MuiTextField,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { useEffect, useRef, useState, } from "react";
|
||||
import { Box, Button, Link, Typography, useTheme, } from "@mui/material";
|
||||
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox.tsx";
|
||||
|
||||
import { DESIGN_LIST } from "@/utils/designList";
|
||||
import { sendFC } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { DESIGN_LIST } from "@utils/designList.ts";
|
||||
import { sendFC, SendFCParams } from "@api/quizRelase.ts";
|
||||
import { useQuizData } from "@contexts/QuizDataContext.ts";
|
||||
import { NameplateLogo } from "@icons/NameplateLogo.tsx";
|
||||
import { QuizQuestionResult } from "@model/questionTypes/result.ts";
|
||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared.ts";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication.ts";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useRootContainerSize } from "../../contexts/RootContainerWidthContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts";
|
||||
import {
|
||||
FormContactFieldData,
|
||||
FormContactFieldName,
|
||||
} from "@model/settingsData.ts";
|
||||
import {
|
||||
Inputs
|
||||
} from "@/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx";
|
||||
import { EMAIL_REGEXP } from "@utils/emailRegexp.tsx";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
const EMAIL_REGEXP =
|
||||
/^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu;
|
||||
|
||||
type Props = {
|
||||
currentQuestion: AnyTypedQuizQuestion;
|
||||
@ -86,7 +77,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
|
||||
const inputHC = async () => {
|
||||
const FC = settings.cfg.formContact.fields || settings.cfg.formContact;
|
||||
const body = {} as any;
|
||||
const body: SendFCParams["body"] = {};
|
||||
if (name.length > 0) body.name = name;
|
||||
if (email.length > 0) body.email = email;
|
||||
if (phone.length > 0) body.phone = phone;
|
||||
@ -113,19 +104,21 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const FCcopy: any =
|
||||
const FCcopy: Record<FormContactFieldName, FormContactFieldData> =
|
||||
settings.cfg.formContact.fields || settings.cfg.formContact;
|
||||
|
||||
const filteredFC: any = {};
|
||||
const filteredFC: Partial<
|
||||
Record<FormContactFieldName, FormContactFieldData>
|
||||
> = {};
|
||||
for (const i in FCcopy) {
|
||||
const field = FCcopy[i];
|
||||
const field = FCcopy[i as keyof typeof FCcopy];
|
||||
if (field.used) {
|
||||
filteredFC[i] = field;
|
||||
filteredFC[i as FormContactFieldName] = field;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleShowResultsClick() {
|
||||
const FC: any = settings.cfg.formContact.fields;
|
||||
const FC = settings.cfg.formContact.fields;
|
||||
if (FC["email"].used !== EMAIL_REGEXP.test(email)) {
|
||||
return enqueueSnackbar("введена некорректная почта");
|
||||
}
|
||||
@ -145,12 +138,28 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
try {
|
||||
await inputHC();
|
||||
fireOnce.current = false;
|
||||
const sessions: any = JSON.parse(
|
||||
localStorage.getItem("sessions") || "{}"
|
||||
);
|
||||
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
|
||||
sessions[quizId] = Date.now();
|
||||
localStorage.setItem("sessions", JSON.stringify(sessions));
|
||||
enqueueSnackbar("Данные успешно отправлены");
|
||||
|
||||
//@ts-ignore
|
||||
let YM = window?.ym;
|
||||
//@ts-ignore
|
||||
let VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
"penaquiz-contacts"
|
||||
);
|
||||
};
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: "penaquiz-contacts"
|
||||
});
|
||||
};
|
||||
} catch (e) {
|
||||
enqueueSnackbar("повторите попытку позже");
|
||||
}
|
||||
@ -161,6 +170,26 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
|
||||
setFire(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
//@ts-ignore
|
||||
let YM = window?.ym;
|
||||
//@ts-ignore
|
||||
let VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
"penaquiz-form"
|
||||
);
|
||||
};
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: "penaquiz-form"
|
||||
});
|
||||
};
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -184,8 +213,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
settings.cfg.design && !isMobile
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? `url(${DESIGN_LIST[settings.cfg.theme]})`
|
||||
: `linear-gradient(90deg, #272626, transparent), url(${
|
||||
DESIGN_LIST[settings.cfg.theme]
|
||||
: `linear-gradient(90deg, #272626, transparent), url(${DESIGN_LIST[settings.cfg.theme]
|
||||
})`
|
||||
: null,
|
||||
}}
|
||||
@ -212,6 +240,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
justifyContent: "center",
|
||||
borderRight: isMobile ? undefined : "1px solid #9A9AAF80",
|
||||
margin: isMobile ? 0 : "40px 0",
|
||||
padding: isMobile ? "0" : "0 40px"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@ -222,14 +251,14 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "center",
|
||||
padding: isMobile ? "20px 20px 0 20px" : "0 0 0 40px",
|
||||
padding: isMobile ? "40px 20px 0 20px" : "0",
|
||||
mt: isMobile ? 0 : isTablet ? "-180px" : "-47px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: isTablet ? undefined : "center",
|
||||
m: "20px 0",
|
||||
fontSize: isTablet ? "24px" : "28px",
|
||||
fontSize: "24px",
|
||||
lineHeight: "normal",
|
||||
fontWeight: 501,
|
||||
color: theme.palette.text.primary,
|
||||
@ -257,18 +286,19 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
alignItems: isMobile ? undefined : "center",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
p: isMobile ? "40px" : "125px 60px 30px 60px",
|
||||
p: isMobile ? "0 20px" : isTablet ? "0px 40px 30px 60px" : "125px 60px 30px 60px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
my: "20px",
|
||||
mt: isMobile ? "10px" : "20px",
|
||||
mb: "20px"
|
||||
}}
|
||||
>
|
||||
<Inputs
|
||||
@ -284,30 +314,9 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
setAdress={setAdress}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{
|
||||
// resultQuestion &&
|
||||
// settings.cfg.resultInfo.when === "after" &&
|
||||
<Button
|
||||
disabled={!(ready && !fire)}
|
||||
variant="contained"
|
||||
onClick={handleShowResultsClick}
|
||||
sx={{
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
"&:disabled": {
|
||||
border: "1px solid #9A9AAF",
|
||||
color: "#9A9AAF",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{settings.cfg.formContact?.button || "Получить результаты"}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
mt: "20px",
|
||||
width: isMobile ? "300px" : "390px",
|
||||
}}
|
||||
>
|
||||
@ -318,15 +327,16 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
}}
|
||||
checked={ready}
|
||||
colorIcon={theme.palette.primary.main}
|
||||
sx={{ marginRight: "0" }}
|
||||
/>
|
||||
<Typography sx={{ color: theme.palette.text.primary }}>
|
||||
<Typography sx={{ color: theme.palette.text.primary, lineHeight: "18.96px" }} fontSize={"16px"} >
|
||||
С 
|
||||
<Link href={"https://shub.pena.digital/ppdd"} target="_blank">
|
||||
<Link href={`https://${window.location.hostname.includes("s") ? "s" : ""}hub.pena.digital/ppdd`} target="_blank">
|
||||
Положением об обработке персональных данных{" "}
|
||||
</Link>
|
||||
 и 
|
||||
<Link
|
||||
href={"https://shub.pena.digital/docs/privacy"}
|
||||
href={`https://${window.location.hostname.includes("s") ? "s" : ""}hub.pena.digital/docs/privacy`}
|
||||
target="_blank"
|
||||
>
|
||||
{" "}
|
||||
@ -335,41 +345,65 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
 ознакомлен
|
||||
</Typography>
|
||||
</Box>
|
||||
{
|
||||
// resultQuestion &&
|
||||
// settings.cfg.resultInfo.when === "after" &&
|
||||
<Button
|
||||
disabled={!(ready && !fire)}
|
||||
variant="contained"
|
||||
onClick={handleShowResultsClick}
|
||||
sx={{
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
margin: isMobile ? "auto" : undefined,
|
||||
mt: "20px",
|
||||
p: "10px 20px",
|
||||
"&:disabled": {
|
||||
border: "1px solid #9A9AAF",
|
||||
color: "#9A9AAF",
|
||||
},
|
||||
|
||||
}}
|
||||
>
|
||||
{settings.cfg.formContact?.button || "Получить результаты"}
|
||||
</Button>
|
||||
}
|
||||
{show_badge && (
|
||||
<Box
|
||||
component={Link}
|
||||
target={"_blank"}
|
||||
href={`https://${
|
||||
window.location.hostname.includes("s") ? "s" : ""
|
||||
href={`https://${window.location.hostname.includes("s") ? "s" : ""
|
||||
}quiz.pena.digital/squiz/quiz/logo?q=${quizId}`}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
mt: "55px",
|
||||
mb: "50px",
|
||||
gap: "15px",
|
||||
mb: "40px",
|
||||
gap: "10px",
|
||||
textDecoration: "none",
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: isMobile ? "28%" : undefined
|
||||
}}
|
||||
>
|
||||
<NameplateLogo
|
||||
style={{
|
||||
fontSize: "23px",
|
||||
fontSize: "20px",
|
||||
color: quizThemes[settings.cfg.theme].isLight
|
||||
? "#151515"
|
||||
: "#FFFFFF",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
color: quizThemes[settings.cfg.theme].isLight
|
||||
? "#4D4D4D"
|
||||
: "#F5F7FF",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
Сделано на PenaQuiz
|
||||
</Typography>
|
||||
{/*<Typography*/}
|
||||
{/* sx={{*/}
|
||||
{/* fontSize: "14px",*/}
|
||||
{/* color: quizThemes[settings.cfg.theme].isLight*/}
|
||||
{/* ? "#4D4D4D"*/}
|
||||
{/* : "#F5F7FF",*/}
|
||||
{/* whiteSpace: "nowrap",*/}
|
||||
{/* }}*/}
|
||||
{/*>*/}
|
||||
{/* Сделано на PenaQuiz*/}
|
||||
{/*</Typography>*/}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@ -378,147 +412,3 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Inputs = ({
|
||||
name,
|
||||
setName,
|
||||
email,
|
||||
setEmail,
|
||||
phone,
|
||||
setPhone,
|
||||
text,
|
||||
setText,
|
||||
adress,
|
||||
setAdress,
|
||||
}: any) => {
|
||||
const { settings } = useQuizData();
|
||||
|
||||
const FC = settings.cfg.formContact.fields;
|
||||
|
||||
if (!FC) return null;
|
||||
console.log(email);
|
||||
const Name = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setName(target.value)}
|
||||
id={name}
|
||||
title={FC["name"].innerText || "Введите имя"}
|
||||
desc={FC["name"].text || "Имя"}
|
||||
Icon={NameIcon}
|
||||
/>
|
||||
);
|
||||
const Email = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setEmail(target.value.replaceAll(/\s/g, ""))}
|
||||
id={email}
|
||||
title={FC["email"].innerText || "Введите Email"}
|
||||
desc={FC["email"].text || "Email"}
|
||||
Icon={EmailIcon}
|
||||
/>
|
||||
);
|
||||
const Phone = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setPhone(target.value)}
|
||||
id={phone}
|
||||
title={FC["phone"].innerText || "Введите номер телефона"}
|
||||
desc={FC["phone"].text || "Номер телефона"}
|
||||
Icon={PhoneIcon}
|
||||
/>
|
||||
);
|
||||
const Text = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setText(target.value)}
|
||||
id={text}
|
||||
title={FC["text"].text || "Введите фамилию"}
|
||||
desc={FC["text"].innerText || "Фамилия"}
|
||||
Icon={TextIcon}
|
||||
/>
|
||||
);
|
||||
const Adress = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setAdress(target.value)}
|
||||
id={adress}
|
||||
title={FC["address"].innerText || "Введите адрес"}
|
||||
desc={FC["address"].text || "Адрес"}
|
||||
Icon={AddressIcon}
|
||||
/>
|
||||
);
|
||||
|
||||
if (Object.values(FC).some((data) => data.used)) {
|
||||
return (
|
||||
<>
|
||||
{FC["name"].used ? Name : <></>}
|
||||
{FC["email"].used ? Email : <></>}
|
||||
{FC["phone"].used ? Phone : <></>}
|
||||
{FC["text"].used ? Text : <></>}
|
||||
{FC["address"].used ? Adress : <></>}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{Name}
|
||||
{Email}
|
||||
{Phone}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const CustomInput = ({
|
||||
title,
|
||||
desc,
|
||||
Icon,
|
||||
onChange,
|
||||
id,
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
desc: string;
|
||||
Icon: FC<{ color: string; backgroundColor: string }>;
|
||||
onChange: TextFieldProps["onChange"];
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 600;
|
||||
const { settings } = useQuizData();
|
||||
return (
|
||||
<Box m="10px 0">
|
||||
<Typography mb="7px" color={theme.palette.text.primary}>
|
||||
{title}
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
onChange={onChange}
|
||||
sx={{
|
||||
width: isMobile ? "300px" : "390px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#9A9AAF80",
|
||||
borderRadius: "12px",
|
||||
},
|
||||
"& .MuiInputBase-root": {
|
||||
paddingLeft: 0,
|
||||
},
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"&:hover fieldset": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
}}
|
||||
placeholder={desc}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Icon
|
||||
color="gray"
|
||||
backgroundColor={
|
||||
quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "#F2F3F71A"
|
||||
}
|
||||
/>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
56
lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx
Normal file
56
lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import {MenuItem, Select, SelectChangeEvent, useTheme} from "@mui/material";
|
||||
import {Dispatch, FC, SetStateAction, useState} from "react";
|
||||
import {phoneMasksByCountry} from "@utils/phoneMasksByCountry.tsx";
|
||||
import {Value} from "react-phone-number-input";
|
||||
|
||||
type CountrySelectorProps = {
|
||||
setMask: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export const CountrySelector:FC<CountrySelectorProps> = ({setMask}) => {
|
||||
const theme = useTheme();
|
||||
const [country, setCountry] = useState('RU');
|
||||
|
||||
const handleChange = (e: SelectChangeEvent<Value>) => {
|
||||
setCountry(e.target.value);
|
||||
setMask(phoneMasksByCountry[e.target.value][1]);
|
||||
};
|
||||
return (
|
||||
<Select
|
||||
value={country}
|
||||
onChange={handleChange}
|
||||
renderValue={(value) => value}
|
||||
sx={{
|
||||
minWidth: 50,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
"& .MuiSelect-select": {
|
||||
paddingLeft: "5px",
|
||||
paddingRight: "5px",
|
||||
color: 'gray',
|
||||
fontSize: "12px",
|
||||
border: "none",
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: "none",
|
||||
},
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
border: "none",
|
||||
},
|
||||
"&:hover:before": {
|
||||
border: "none",
|
||||
},
|
||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||
border: "none",
|
||||
},
|
||||
"&.Mui-focused:hover .MuiOutlinedInput-notchedOutline": {
|
||||
border: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{Object.keys(phoneMasksByCountry).map((countryCode) => {
|
||||
return <MenuItem value={countryCode}>{phoneMasksByCountry[countryCode][0]}</MenuItem>
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,91 @@
|
||||
import {
|
||||
Box,
|
||||
InputAdornment,
|
||||
TextField as MuiTextField,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme
|
||||
} from "@mui/material";
|
||||
import {useRootContainerSize} from "@contexts/RootContainerWidthContext.ts";
|
||||
import {useQuizData} from "@contexts/QuizDataContext.ts";
|
||||
import {useIMask} from "react-imask";
|
||||
import {quizThemes} from "@utils/themes/Publication/themePublication.ts";
|
||||
import {FC, useState} from "react";
|
||||
import {
|
||||
CountrySelector
|
||||
} from "@/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx";
|
||||
import {phoneMasksByCountry} from "@utils/phoneMasksByCountry.tsx";
|
||||
|
||||
type InputProps = {
|
||||
title: string;
|
||||
desc: string;
|
||||
Icon: FC<{ color: string; backgroundColor: string }>;
|
||||
onChange: TextFieldProps["onChange"];
|
||||
id: string;
|
||||
isPhone?:boolean
|
||||
};
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||||
|
||||
|
||||
export const CustomInput = ({ title, desc, Icon, onChange ,isPhone}: InputProps) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 600;
|
||||
const { settings } = useQuizData();
|
||||
const [mask, setMask] = useState(phoneMasksByCountry['RU'][1]);
|
||||
const { ref } = useIMask({mask});
|
||||
return (
|
||||
<Box m="10px 0">
|
||||
<Typography mb="7px" color={theme.palette.text.primary} fontSize={"16px"}>
|
||||
{title}
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
inputRef={isPhone? ref : null}
|
||||
onChange={onChange}
|
||||
sx={{
|
||||
width: isMobile ? "100%" : "390px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
fontSize: "16px",
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#9A9AAF80",
|
||||
borderRadius: "12px",
|
||||
},
|
||||
"& .MuiInputBase-root": {
|
||||
paddingLeft: 0,
|
||||
},
|
||||
"& .MuiOutlinedInput-input": {
|
||||
paddingLeft: "10px",
|
||||
},
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"&:hover fieldset": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
}}
|
||||
placeholder={desc}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start" >
|
||||
<Icon
|
||||
color="gray"
|
||||
backgroundColor={
|
||||
quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "#F2F3F71A"
|
||||
}
|
||||
/>
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
{isPhone && (
|
||||
<CountrySelector setMask={setMask} />)}
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
</Box>
|
||||
);
|
||||
};
|
107
lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx
Normal file
107
lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import {useQuizData} from "@contexts/QuizDataContext.ts";
|
||||
import NameIcon from "@icons/ContactFormIcon/NameIcon.tsx";
|
||||
import EmailIcon from "@icons/ContactFormIcon/EmailIcon.tsx";
|
||||
import TextIcon from "@icons/ContactFormIcon/TextIcon.tsx";
|
||||
import AddressIcon from "@icons/ContactFormIcon/AddressIcon.tsx";
|
||||
import {Dispatch, SetStateAction} from "react";
|
||||
import {
|
||||
CustomInput
|
||||
} from "@/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx";
|
||||
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon.tsx";
|
||||
|
||||
type InputsProps = {
|
||||
name: string;
|
||||
setName: Dispatch<SetStateAction<string>>;
|
||||
email: string;
|
||||
setEmail: Dispatch<SetStateAction<string>>;
|
||||
phone: string;
|
||||
setPhone: Dispatch<SetStateAction<string>>;
|
||||
text: string;
|
||||
setText: Dispatch<SetStateAction<string>>;
|
||||
adress: string;
|
||||
setAdress: Dispatch<SetStateAction<string>>;
|
||||
};
|
||||
|
||||
export const Inputs = ({
|
||||
name,
|
||||
setName,
|
||||
email,
|
||||
setEmail,
|
||||
phone,
|
||||
setPhone,
|
||||
text,
|
||||
setText,
|
||||
adress,
|
||||
setAdress,
|
||||
}: InputsProps) => {
|
||||
const { settings } = useQuizData();
|
||||
const FC = settings.cfg.formContact.fields;
|
||||
|
||||
if (!FC) return null;
|
||||
const Name = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setName(target.value)}
|
||||
id={name}
|
||||
title={FC["name"].innerText || "Введите имя"}
|
||||
desc={FC["name"].text || "Имя"}
|
||||
Icon={NameIcon}
|
||||
/>
|
||||
);
|
||||
const Email = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setEmail(target.value.replaceAll(/\s/g, ""))}
|
||||
id={email}
|
||||
title={FC["email"].innerText || "Введите Email"}
|
||||
desc={FC["email"].text || "Email"}
|
||||
Icon={EmailIcon}
|
||||
/>
|
||||
);
|
||||
const Phone = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setPhone(target.value)}
|
||||
id={phone}
|
||||
title={FC["phone"].innerText || "Введите номер телефона"}
|
||||
desc={FC["phone"].text || "Номер телефона"}
|
||||
Icon={PhoneIcon}
|
||||
isPhone={true}
|
||||
/>
|
||||
);
|
||||
const Text = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setText(target.value)}
|
||||
id={text}
|
||||
title={FC["text"].text || "Введите фамилию"}
|
||||
desc={FC["text"].innerText || "Фамилия"}
|
||||
Icon={TextIcon}
|
||||
/>
|
||||
);
|
||||
const Adress = (
|
||||
<CustomInput
|
||||
onChange={({ target }) => setAdress(target.value)}
|
||||
id={adress}
|
||||
title={FC["address"].innerText || "Введите адрес"}
|
||||
desc={FC["address"].text || "Адрес"}
|
||||
Icon={AddressIcon}
|
||||
/>
|
||||
);
|
||||
|
||||
if (Object.values(FC).some((data) => data.used)) {
|
||||
return (
|
||||
<>
|
||||
{FC["name"].used ? Name : <></>}
|
||||
{FC["email"].used ? Email : <></>}
|
||||
{FC["phone"].used ? Phone : <></>}
|
||||
{FC["text"].used ? Text : <></>}
|
||||
{FC["address"].used ? Adress : <></>}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{Name}
|
||||
{Email}
|
||||
{Phone}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
@ -63,7 +63,7 @@ export const Question = ({
|
||||
? "transparent"
|
||||
: "linear-gradient(90deg,#272626, transparent)"
|
||||
: theme.palette.background.default,
|
||||
overflow: "hidden"
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
@ -9,6 +9,7 @@ import { useRootContainerSize } from "../../contexts/RootContainerWidthContext";
|
||||
import type { QuizQuestionResult } from "../../model/questionTypes/result";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import { DESIGN_LIST } from "@/utils/designList";
|
||||
import { useEffect } from "react";
|
||||
|
||||
type ResultFormProps = {
|
||||
resultQuestion: QuizQuestionResult;
|
||||
@ -23,7 +24,27 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
(state) => state.setCurrentQuizStep
|
||||
);
|
||||
const spec = settings.cfg.spec;
|
||||
console.log(quizThemes[settings.cfg.theme].isLight);
|
||||
|
||||
useEffect(() => {
|
||||
//@ts-ignore
|
||||
let YM = window?.ym;
|
||||
//@ts-ignore
|
||||
let VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
`penaquiz-result-{${resultQuestion.id}}`
|
||||
);
|
||||
};
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: `penaquiz-result-{${resultQuestion.id}}`
|
||||
});
|
||||
};
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -75,17 +96,6 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
mb: "7px",
|
||||
}}
|
||||
>
|
||||
{settings.cfg.startpage.logo && (
|
||||
<img
|
||||
src={settings.cfg.startpage.logo}
|
||||
style={{
|
||||
height: "40px",
|
||||
maxWidth: "110px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
@ -93,7 +103,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.orgname}
|
||||
Ваш результат:
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
@ -138,9 +148,9 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
resultQuestion.description !== " " && (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "23px",
|
||||
fontSize: "24px",
|
||||
fontWeight: 700,
|
||||
m: "20px 0",
|
||||
mt: "25px",
|
||||
color: theme.palette.text.primary,
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
@ -151,7 +161,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
m: "20px 0",
|
||||
mt: "20px",
|
||||
color: theme.palette.text.primary,
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
@ -164,7 +174,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
m: "20px 0",
|
||||
mt: "30px ",
|
||||
wordBreak: "break-word",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
@ -187,8 +197,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
<Box
|
||||
component={Link}
|
||||
target={"_blank"}
|
||||
href={`https://${
|
||||
window.location.hostname.includes("s") ? "s" : ""
|
||||
href={`https://${window.location.hostname.includes("s") ? "s" : ""
|
||||
}quiz.pena.digital/squiz/quiz/logo?q=${quizId}`}
|
||||
sx={{
|
||||
display: "flex",
|
||||
@ -207,17 +216,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
||||
: "#F5F7FF",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
color: quizThemes[settings.cfg.theme].isLight
|
||||
? "#4D4D4D"
|
||||
: "#F5F7FF",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
Сделано на PenaQuiz
|
||||
</Typography>
|
||||
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
@ -28,6 +28,8 @@ const StandartLayout = ({
|
||||
quizMainBlock,
|
||||
backgroundBlock,
|
||||
}: LayoutProps) => {
|
||||
const size = useRootContainerSize();
|
||||
const isTablet = size >= 700 && size < 1100;
|
||||
const { settings } = useQuizData();
|
||||
|
||||
return (
|
||||
@ -47,6 +49,7 @@ const StandartLayout = ({
|
||||
width: 0,
|
||||
},
|
||||
overflowY: "auto",
|
||||
padding: isTablet ? "15px" : "0",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@ -57,7 +60,7 @@ const StandartLayout = ({
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
p: "25px",
|
||||
p: isTablet? "25px" : alignType === 'left'? "25px 25px 25px 35px" : "25px 35px 25px 25px" ,
|
||||
overflowY: "auto",
|
||||
background:
|
||||
settings.cfg.design && !quizThemes[settings.cfg.theme].isLight
|
||||
@ -73,7 +76,16 @@ const StandartLayout = ({
|
||||
{quizMainBlock}
|
||||
</Box>
|
||||
{settings.cfg.startpage.background.desktop && (
|
||||
<Box sx={{ width: "60%", overflow: "hidden" }}>{backgroundBlock}</Box>
|
||||
<Box sx={{ width: "60%", overflow: "hidden" }}><Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: alignType === "left"? "25px 25px 25px 15px" : "25px 15px 25px 25px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
"& > img": { width: "100%", borderRadius: "12px" },
|
||||
}}
|
||||
>{backgroundBlock}</Box></Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
@ -84,13 +96,22 @@ const ExpandedLayout = ({
|
||||
quizHeaderBlock,
|
||||
quizMainBlock,
|
||||
backgroundBlock,
|
||||
}: LayoutProps) => (
|
||||
}: LayoutProps) => {
|
||||
const size = useRootContainerSize();
|
||||
const isTablet = size >= 700 && size < 1100;
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
height: "100%",
|
||||
width: "40%",
|
||||
padding: "16px",
|
||||
width: alignType === "center" ? "100%" : isTablet ? "46%" : "42%",
|
||||
display: "flex",
|
||||
padding:
|
||||
alignType === "center"
|
||||
? isTablet ? "30px 40px" : "30px 35px"
|
||||
: alignType === "left"
|
||||
? isTablet ? "25px 0 31px 40px" : "25px 0 31px 35px"
|
||||
: isTablet ? "25px 40px 31px 0" : "25px 35px 31px 0",
|
||||
margin:
|
||||
alignType === "center"
|
||||
? "0 auto"
|
||||
@ -106,9 +127,16 @@ const ExpandedLayout = ({
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "16px",
|
||||
minHeight: "calc(100% - 32px)",
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
|
||||
padding:
|
||||
alignType === "center"
|
||||
? "0"
|
||||
: alignType === "left"
|
||||
? "0 40px 0 0"
|
||||
: "0 0 0 40px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
@ -121,7 +149,7 @@ const ExpandedLayout = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
{quizHeaderBlock}
|
||||
{alignType !== "center" && quizHeaderBlock}
|
||||
{quizMainBlock}
|
||||
</Box>
|
||||
</Box>
|
||||
@ -140,6 +168,7 @@ const ExpandedLayout = ({
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const CenteredLayout = ({
|
||||
quizHeaderBlock,
|
||||
@ -152,7 +181,7 @@ const CenteredLayout = ({
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "auto",
|
||||
padding: "10px 25px 25px",
|
||||
padding: isTablet ? "25px 40px 40px" : "25px 25px 25px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
|
@ -28,7 +28,7 @@ const StandartMobileLayout = ({
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column-reverse",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
justifyContent: "flex-end",
|
||||
minHeight: "100%",
|
||||
@ -49,7 +49,7 @@ const StandartMobileLayout = ({
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
p: "25px",
|
||||
p: "20px",
|
||||
height: "100%",
|
||||
overflowY: "auto",
|
||||
overflowX: "hidden",
|
||||
@ -65,7 +65,24 @@ const StandartMobileLayout = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{marginBottom: "13px"}}>
|
||||
{quizHeaderBlock}
|
||||
</Box>
|
||||
{settings.cfg.startpage.background.desktop && (
|
||||
<Box sx={{ width: "100%", overflow: "hidden" }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
"& > img": { width: "100%", borderRadius: "12px" },
|
||||
}}
|
||||
>
|
||||
{backgroundBlock}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
height: "80%",
|
||||
@ -74,14 +91,12 @@ const StandartMobileLayout = ({
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
marginTop: "30px"
|
||||
}}
|
||||
>
|
||||
{quizMainBlock}
|
||||
</Box>
|
||||
</Box>
|
||||
{settings.cfg.startpage.background.desktop && (
|
||||
<Box sx={{ width: "100%", overflow: "hidden" }}>{backgroundBlock}</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@ -118,10 +133,9 @@ const ExpandedMobileLayout = ({
|
||||
"&::-webkit-scrollbar-thumb": { backgroundColor: "#b8babf" },
|
||||
}}
|
||||
>
|
||||
{quizHeaderBlock}
|
||||
<Box
|
||||
sx={{
|
||||
padding: "16px",
|
||||
padding: "20px",
|
||||
height: "80%",
|
||||
display: "flex",
|
||||
flexGrow: 1,
|
||||
@ -130,6 +144,7 @@ const ExpandedMobileLayout = ({
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{quizHeaderBlock}
|
||||
{quizMainBlock}
|
||||
</Box>
|
||||
</Box>
|
||||
@ -185,7 +200,7 @@ const CenteredMobileLayout = ({
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
padding: "10px 25px 25px",
|
||||
padding: "20px",
|
||||
height: "100%",
|
||||
overflowY: "auto",
|
||||
overflowX: "hidden",
|
||||
|
@ -27,11 +27,30 @@ export const StartPageViewPublication = () => {
|
||||
const { isMobileDevice } = useUADevice();
|
||||
const setCurrentQuizStep = useQuizViewStore(state => state.setCurrentQuizStep);
|
||||
|
||||
const isMobile = useRootContainerSize() < 700;
|
||||
const isTablet = useRootContainerSize() < 800;
|
||||
|
||||
const size = useRootContainerSize();
|
||||
const isMobile = size < 700;
|
||||
const isTablet = size >= 700 && size < 1100;
|
||||
console.log(settings)
|
||||
const handleCopyNumber = () => {
|
||||
navigator.clipboard.writeText(settings.cfg.info.phonenumber);
|
||||
//@ts-ignore
|
||||
const YM = window?.ym;
|
||||
//@ts-ignore
|
||||
const VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
"penaquiz-phone"
|
||||
);
|
||||
}
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: "penaquiz-phone"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const background =
|
||||
@ -82,6 +101,90 @@ export const StartPageViewPublication = () => {
|
||||
) : null
|
||||
) : null;
|
||||
|
||||
const quizHeaderBlock = (<Box
|
||||
sx={{
|
||||
margin:
|
||||
settings.cfg.startpageType === "centered" ? "0 auto" : null,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexWrap: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "nowrap" : "wrap",
|
||||
gap: isMobile ? "20px" : "30px",
|
||||
mb: settings.cfg.startpageType === "centered" ? isMobile ? "20px" : "25px" : settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile ? 0 : "7px",
|
||||
justifyContent: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && isMobile ? "center" : undefined
|
||||
}}
|
||||
>
|
||||
{settings.cfg.startpage.logo &&
|
||||
<img
|
||||
src={settings.cfg.startpage.logo}
|
||||
style={{
|
||||
maxHeight: isMobile ? "30px" : "40px",
|
||||
maxWidth: isMobile ? "100px" : "110px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
alt=""
|
||||
/>
|
||||
}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "white"
|
||||
: theme.palette.text.primary,
|
||||
wordBreak: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "normal" : "break-word",
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.orgname}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>)
|
||||
|
||||
const PenaBadge = (
|
||||
<Box
|
||||
component={Link}
|
||||
target={"_blank"}
|
||||
href={
|
||||
`https://${window.location.hostname.includes("s") ? "s" : ""}quiz.pena.digital/squiz/quiz/logo?q=${quizId}`
|
||||
}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "7px",
|
||||
textDecoration: "none",
|
||||
marginLeft: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isTablet && !isMobile ? "61px" : undefined
|
||||
}}
|
||||
>
|
||||
<NameplateLogo
|
||||
style={{
|
||||
fontSize: "23px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "#FFFFFF"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "#151515"
|
||||
: "#FFFFFF",
|
||||
}}
|
||||
/>
|
||||
{/*<Typography*/}
|
||||
{/* sx={{*/}
|
||||
{/* fontSize: "13px",*/}
|
||||
{/* color:*/}
|
||||
{/* settings.cfg.startpageType === "expanded"*/}
|
||||
{/* ? "#F5F7FF"*/}
|
||||
{/* : quizThemes[settings.cfg.theme].isLight*/}
|
||||
{/* ? "#4D4D4D"*/}
|
||||
{/* : "#F5F7FF",*/}
|
||||
{/* whiteSpace: "nowrap",*/}
|
||||
{/* }}*/}
|
||||
{/*>*/}
|
||||
{/* Сделано на PenaQuiz*/}
|
||||
{/*</Typography>*/}
|
||||
</Box>)
|
||||
|
||||
const realQuestionsCount = questions.filter((question) => question.type !== null && question.type !== "result").length;
|
||||
|
||||
return (
|
||||
@ -93,59 +196,18 @@ export const StartPageViewPublication = () => {
|
||||
width: "100%",
|
||||
background:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? settings.cfg.startpage.position === "left"
|
||||
? "linear-gradient(90deg,#272626,transparent)"
|
||||
? settings.cfg.startpage.position === "left" || isMobile && settings.cfg.startpage.position === "right"
|
||||
? "linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%)"
|
||||
: settings.cfg.startpage.position === "center"
|
||||
? "linear-gradient(180deg,transparent,#272626)"
|
||||
: "linear-gradient(270deg,#272626,transparent)"
|
||||
? "linear-gradient(0deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%)"
|
||||
: "linear-gradient(-90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%)"
|
||||
: theme.palette.background.default,
|
||||
|
||||
color: settings.cfg.startpageType === "expanded" ? "white" : "black",
|
||||
}}
|
||||
>
|
||||
<QuizPreviewLayoutByType
|
||||
quizHeaderBlock={
|
||||
<Box
|
||||
sx={{
|
||||
margin:
|
||||
settings.cfg.startpageType === "centered" ? "0 auto" : null,
|
||||
padding: settings.cfg.startpageType === "standard" ? "" : "16px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap",
|
||||
gap: "30px",
|
||||
mb: "7px",
|
||||
}}
|
||||
>
|
||||
{settings.cfg.startpage.logo &&
|
||||
<img
|
||||
src={settings.cfg.startpage.logo}
|
||||
style={{
|
||||
height: "40px",
|
||||
maxWidth: "110px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
alt=""
|
||||
/>
|
||||
}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "white"
|
||||
: theme.palette.text.primary,
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.orgname}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
quizHeaderBlock={quizHeaderBlock
|
||||
}
|
||||
quizMainBlock={
|
||||
<>
|
||||
@ -153,7 +215,7 @@ export const StartPageViewPublication = () => {
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
justifyContent: settings.cfg.startpageType === "standard" && isMobile ? "start" : "center",
|
||||
flexGrow: settings.cfg.startpageType === "centered" ? 0 : 1,
|
||||
alignItems:
|
||||
settings.cfg.startpageType === "centered"
|
||||
@ -163,17 +225,17 @@ export const StartPageViewPublication = () => {
|
||||
? "center"
|
||||
: "start"
|
||||
: "start",
|
||||
marginTop: "28px",
|
||||
width: "100%",
|
||||
marginTop: settings.cfg.startpageType === "centered" ? "30px" : isMobile ? "0px" : "5px",
|
||||
maxWidth: isMobile ? "100%" : settings.cfg.startpageType === "centered" ? "700px" : isTablet && settings.cfg.startpageType !== "expanded" && settings.cfg.startpage.position !== "center" ? "380px" : "531px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
fontSize: "26px",
|
||||
fontWeight: "700",
|
||||
fontSize: isMobile ? "24px" : "27px",
|
||||
fontStyle: "normal",
|
||||
fontStretch: "normal",
|
||||
lineHeight: "1.2",
|
||||
lineHeight: isMobile ? "26.4px" : "normal",
|
||||
overflowWrap: "break-word",
|
||||
width: "100%",
|
||||
textAlign:
|
||||
@ -191,8 +253,10 @@ export const StartPageViewPublication = () => {
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
margin: "16px 0 30px",
|
||||
fontSize: isMobile ? "16px" : "17px",
|
||||
fontWeight: "400",
|
||||
lineHeight: isMobile ? "19.2px" : "normal",
|
||||
margin: "12px 0 30px",
|
||||
overflowWrap: "break-word",
|
||||
width: "100%",
|
||||
textAlign:
|
||||
@ -218,22 +282,44 @@ export const StartPageViewPublication = () => {
|
||||
disabled={realQuestionsCount === 0}
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
padding: "10px 30px",
|
||||
padding: "10px 20px",
|
||||
width: "auto",
|
||||
background: theme.palette.primary.main,
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
onClick={() => {
|
||||
setCurrentQuizStep("question")
|
||||
|
||||
|
||||
//@ts-ignore
|
||||
const YM = window?.ym;
|
||||
//@ts-ignore
|
||||
const VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
"penaquiz-startquiz"
|
||||
);
|
||||
}
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: "penaquiz-startquiz"
|
||||
});
|
||||
}
|
||||
}}
|
||||
onClick={() => setCurrentQuizStep("question")}
|
||||
>
|
||||
{settings.cfg.startpage.button.trim()
|
||||
? settings.cfg.startpage.button
|
||||
: "Пройти тест"}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "46px",
|
||||
display: "flex",
|
||||
flexGrow:
|
||||
settings.cfg.startpageType === "centered"
|
||||
@ -241,35 +327,73 @@ export const StartPageViewPublication = () => {
|
||||
? 0
|
||||
: 1
|
||||
: 0,
|
||||
gap: "20px",
|
||||
gap: isMobile ? "30px" : "40px",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
justifyContent: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && isMobile || settings.cfg.startpageType === "centered" && isMobile ? "center" : "space-between",
|
||||
width: "100%",
|
||||
flexWrap: "wrap",
|
||||
flexWrap: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? isMobile ? "wrap-reverse" : "nowrap" : "wrap",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ maxWidth: "300px" }}>
|
||||
{settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile && quizHeaderBlock}
|
||||
<Box sx={{
|
||||
maxWidth: "300px",
|
||||
display: settings.cfg.startpageType === "centered" && isMobile || settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && isMobile ? "flex" : "block",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
order: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "2" : "0"
|
||||
}}>
|
||||
{settings.cfg.info.site && (
|
||||
<Link mb="16px" href={settings.cfg.info.site}>
|
||||
<ButtonBase
|
||||
sx={{ display:"block",marginLeft: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile ? "auto" : undefined,}}
|
||||
onClick={async () => {
|
||||
//@ts-ignore
|
||||
const YM = window?.ym;
|
||||
//@ts-ignore
|
||||
const VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
await YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
"penaquiz-email"
|
||||
);
|
||||
}
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
await VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: "penaquiz-email"
|
||||
});
|
||||
}
|
||||
location.href = (
|
||||
settings.cfg.info.site.includes("https")
|
||||
? settings.cfg.info.site
|
||||
: `https://${settings.cfg.info.site}`
|
||||
).replace(/\s+/g, '')
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
lineHeight: "19px",
|
||||
fontSize: "16px",
|
||||
textAlign: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile ? "end" : "none",
|
||||
color: theme.palette.primary.main,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: isTablet ? "200px" : "300px",
|
||||
}}
|
||||
>
|
||||
{settings.cfg.info.site}
|
||||
</Typography>
|
||||
</Link>
|
||||
</ButtonBase>
|
||||
|
||||
)}
|
||||
{settings.cfg.info.clickable ? (
|
||||
isMobileDevice ? (
|
||||
<Link href={`tel:${settings.cfg.info.phonenumber}`}>
|
||||
<Typography
|
||||
sx={{
|
||||
lineHeight: "19px",
|
||||
textAlign: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "end" : "none",
|
||||
fontSize: "16px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
@ -281,10 +405,15 @@ export const StartPageViewPublication = () => {
|
||||
</Typography>
|
||||
</Link>
|
||||
) : (
|
||||
<ButtonBase onClick={handleCopyNumber}>
|
||||
<ButtonBase sx={{ display:"block", marginTop:"10px",
|
||||
marginLeft: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile ? "auto" : undefined,
|
||||
}}
|
||||
onClick={handleCopyNumber} >
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "end" : "none",
|
||||
fontSize: "16px",
|
||||
lineHeight: "19px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "#FFFFFF"
|
||||
@ -298,8 +427,10 @@ export const StartPageViewPublication = () => {
|
||||
) : (
|
||||
<Typography
|
||||
sx={{
|
||||
lineHeight: "19px",
|
||||
textAlign: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" ? "end" : "none",
|
||||
fontSize: "16px",
|
||||
marginTop: "5px",
|
||||
marginTop: "10px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "#FFFFFF"
|
||||
@ -311,12 +442,14 @@ export const StartPageViewPublication = () => {
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
lineHeight: "14px",
|
||||
width: "100%",
|
||||
overflowWrap: "break-word",
|
||||
fontSize: "12px",
|
||||
textAlign: settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && !isMobile ? "end" : settings.cfg.startpageType === "expanded" && settings.cfg.startpage.position === "center" && isMobile || settings.cfg.startpageType === "centered" && isMobile ? "center" : "none",
|
||||
maxHeight: "120px",
|
||||
overflow: "auto",
|
||||
marginTop: "5px",
|
||||
marginTop: "10px",
|
||||
"&::-webkit-scrollbar": { width: 0 },
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
@ -328,47 +461,7 @@ export const StartPageViewPublication = () => {
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{show_badge && (
|
||||
<Box
|
||||
component={Link}
|
||||
target={"_blank"}
|
||||
href={
|
||||
`https://${window.location.hostname.includes("s") ? "s" : ""}quiz.pena.digital/squiz/quiz/logo?q=${quizId}`
|
||||
}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
textDecoration: "none",
|
||||
}}
|
||||
>
|
||||
<NameplateLogo
|
||||
style={{
|
||||
fontSize: "23px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "#FFFFFF"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "#151515"
|
||||
: "#FFFFFF",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
color:
|
||||
settings.cfg.startpageType === "expanded"
|
||||
? "#F5F7FF"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "#4D4D4D"
|
||||
: "#F5F7FF",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
Сделано на PenaQuiz
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{show_badge && PenaBadge}
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
|
@ -7,18 +7,27 @@ import { notReachable } from "@utils/notReachable";
|
||||
import {quizThemes} from "@utils/themes/Publication/themePublication";
|
||||
import {enqueueSnackbar} from "notistack";
|
||||
import {ReactElement, useEffect} from "react";
|
||||
import { ContactForm } from "./ContactForm";
|
||||
import {Question} from "./Question";
|
||||
import {ResultForm} from "./ResultForm";
|
||||
import {StartPageViewPublication} from "./StartPageViewPublication";
|
||||
import NextButton from "./tools/NextButton";
|
||||
import PrevButton from "./tools/PrevButton";
|
||||
import QuestionSelect from "./QuestionSelect";
|
||||
import {useYandexMetrica} from "@utils/hooks/useYandexMetrica.tsx";
|
||||
import {useYandexMetrics} from "@/utils/hooks/useYandexMetrics";
|
||||
import {useVKMetrics} from "@/utils/hooks/useVKMetrics";
|
||||
import {
|
||||
ContactForm
|
||||
} from "@/components/ViewPublicationPage/ContactForm/ContactForm.tsx";
|
||||
|
||||
export default function ViewPublicationPage() {
|
||||
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const {
|
||||
settings,
|
||||
recentlyCompleted,
|
||||
quizId,
|
||||
preview,
|
||||
changeFaviconAndTitle,
|
||||
} = useQuizData();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
let currentQuizStep = useQuizViewStore((state) => state.currentQuizStep);
|
||||
const {
|
||||
currentQuestion,
|
||||
@ -30,13 +39,15 @@ export default function ViewPublicationPage() {
|
||||
showResultAfterContactForm,
|
||||
setQuestion,
|
||||
} = useQuestionFlowControl();
|
||||
useYandexMetrics(settings?.cfg?.yandexMetricNumber);
|
||||
useVKMetrics(settings?.cfg?.vkMetricNumber);
|
||||
|
||||
const isAnswer = answers.some(ans => ans.questionId === currentQuestion?.id);
|
||||
const isAnswer = answers.some(
|
||||
(ans) => ans.questionId === currentQuestion?.id
|
||||
);
|
||||
|
||||
const yandexMetricNumber = settings?.cfg?.yandexMetricNumber
|
||||
useYandexMetrica(yandexMetricNumber);
|
||||
|
||||
useEffect(function setFaviconAndTitle() {
|
||||
useEffect(
|
||||
function setFaviconAndTitle() {
|
||||
if (!changeFaviconAndTitle) return;
|
||||
|
||||
const link = document.querySelector('link[rel="icon"]');
|
||||
@ -45,19 +56,26 @@ export default function ViewPublicationPage() {
|
||||
}
|
||||
|
||||
document.title = settings.name;
|
||||
}, [changeFaviconAndTitle, settings.cfg.startpage.favIcon, settings.name]);
|
||||
},
|
||||
[changeFaviconAndTitle, settings.cfg.startpage.favIcon, settings.name]
|
||||
);
|
||||
|
||||
if (recentlyCompleted) throw new Error("Quiz already completed");
|
||||
if (currentQuizStep === "startpage" && settings.cfg.noStartPage) currentQuizStep = "question";
|
||||
if (currentQuizStep === "startpage" && settings.cfg.noStartPage)
|
||||
currentQuizStep = "question";
|
||||
|
||||
if (!currentQuestion) return (
|
||||
if (!currentQuestion)
|
||||
return (
|
||||
<ThemeProvider
|
||||
theme={quizThemes[settings.cfg.theme || "StandardTheme"].theme}
|
||||
>
|
||||
<Typography textAlign={"center"} mt="50px">Вопрос не выбран</Typography>
|
||||
<Typography textAlign={"center"} mt="50px">
|
||||
Вопрос не выбран
|
||||
</Typography>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
|
||||
let quizStepElement: ReactElement;
|
||||
switch (currentQuizStep) {
|
||||
case "startpage": {
|
||||
@ -74,7 +92,12 @@ export default function ViewPublicationPage() {
|
||||
<Question
|
||||
currentQuestion={currentQuestion}
|
||||
currentQuestionStepNumber={currentQuestionStepNumber}
|
||||
prevButton={<PrevButton isPreviousButtonEnabled={isPreviousButtonEnabled} moveToPrevQuestion={moveToPrevQuestion} />}
|
||||
prevButton={
|
||||
<PrevButton
|
||||
isPreviousButtonEnabled={isPreviousButtonEnabled}
|
||||
moveToPrevQuestion={moveToPrevQuestion}
|
||||
/>
|
||||
}
|
||||
nextButton={
|
||||
<NextButton
|
||||
isNextButtonEnabled={isNextButtonEnabled}
|
||||
@ -85,7 +108,7 @@ export default function ViewPublicationPage() {
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
preview,
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
|
@ -1,116 +0,0 @@
|
||||
import moment from "moment";
|
||||
import { DatePicker } from "@mui/x-date-pickers";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
||||
import CalendarIcon from "@icons/CalendarIcon";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useState } from "react";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
|
||||
type DateProps = {
|
||||
currentQuestion: QuizQuestionDate;
|
||||
};
|
||||
|
||||
export const Date = ({ currentQuestion }: DateProps) => {
|
||||
const theme = useTheme();
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
const currentAnswer = moment(answer) || moment();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
format="DD/MM/YYYY"
|
||||
slots={{
|
||||
openPickerIcon: () => (
|
||||
<CalendarIcon
|
||||
sx={{
|
||||
"& path": { stroke: theme.palette.primary.main },
|
||||
"& rect": { stroke: theme.palette.primary.main },
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
value={currentAnswer}
|
||||
onChange={async (date) => {
|
||||
if (isSending || !date) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: moment(date).format("YYYY.MM.DD"),
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, date, 0);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
slotProps={{
|
||||
openPickerButton: {
|
||||
sx: {
|
||||
p: 0,
|
||||
},
|
||||
"data-cy": "open-datepicker",
|
||||
},
|
||||
layout: {
|
||||
sx: { backgroundColor: theme.palette.background.default },
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderRadius: "10px",
|
||||
maxWidth: "250px",
|
||||
pr: "30px",
|
||||
"& input": {
|
||||
py: "11px",
|
||||
pl: "20px",
|
||||
lineHeight: "19px",
|
||||
},
|
||||
"& fieldset": {
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
110
lib/components/ViewPublicationPage/questions/Date/index.tsx
Normal file
110
lib/components/ViewPublicationPage/questions/Date/index.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { useState } from "react";
|
||||
import moment from "moment";
|
||||
import { DatePicker } from "@mui/x-date-pickers";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import CalendarIcon from "@icons/CalendarIcon";
|
||||
|
||||
import type { Moment } from "moment";
|
||||
import type { QuizQuestionDate } from "@model/questionTypes/date";
|
||||
|
||||
type DateProps = {
|
||||
currentQuestion: QuizQuestionDate;
|
||||
};
|
||||
|
||||
export const Date = ({ currentQuestion }: DateProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
const currentAnswer = moment(answer) || moment();
|
||||
|
||||
const onDateChange = async (date: Moment | null) => {
|
||||
if (isSending || !date) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: moment(date).format("YYYY.MM.DD"),
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, date, 0);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
format="DD/MM/YYYY"
|
||||
slots={{
|
||||
openPickerIcon: () => (
|
||||
<CalendarIcon
|
||||
sx={{
|
||||
"& path": { stroke: theme.palette.primary.main },
|
||||
"& rect": { stroke: theme.palette.primary.main },
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
value={currentAnswer}
|
||||
onChange={onDateChange}
|
||||
slotProps={{
|
||||
openPickerButton: { sx: { p: 0 }, "data-cy": "open-datepicker" },
|
||||
layout: {
|
||||
sx: { backgroundColor: theme.palette.background.default },
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(154,154,175, 0.2)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderRadius: "10px",
|
||||
maxWidth: "250px",
|
||||
pr: "30px",
|
||||
"& input": { py: "11px", pl: "20px", lineHeight: "19px" },
|
||||
"& fieldset": { borderColor: "#9A9AAF" },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,221 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
polyfillCountryFlagEmojis();
|
||||
import { useState } from "react";
|
||||
|
||||
type EmojiProps = {
|
||||
currentQuestion: QuizQuestionEmoji;
|
||||
};
|
||||
|
||||
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
||||
const theme = useTheme();
|
||||
const { quizId, settings, preview } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const deleteAnswer = useQuizViewStore(state => state.deleteAnswer);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
onChange={({ target }) => {
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[Number(target.value)].answer,
|
||||
currentQuestion.content.variants[Number(target.value)].points || 0
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{ display: "flex", width: "100%", gap: "42px", flexWrap: "wrap" }}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<FormControl
|
||||
key={index}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id
|
||||
? theme.palette.primary.main
|
||||
: "#9A9AAF",
|
||||
overflow: "hidden",
|
||||
maxWidth: "317px",
|
||||
width: "100%",
|
||||
height: "255px",
|
||||
background: settings.cfg.design && !quizThemes[settings.cfg.theme].isLight
|
||||
? "rgba(255,255,255, 0.3)" : settings.cfg.design && quizThemes[settings.cfg.theme].isLight || quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "transparent",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
}}
|
||||
// value={index}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body:
|
||||
currentQuestion.content.variants[index].extendedText +
|
||||
" " +
|
||||
currentQuestion.content.variants[index].answer,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
deleteAnswer(currentQuestion.id);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "193px",
|
||||
background: "#ffffff",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{variant.extendedText && (
|
||||
<Typography fontSize={"100px"}>
|
||||
{variant.extendedText}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
sx={{
|
||||
margin: 0,
|
||||
padding: "15px",
|
||||
color: theme.palette.text.primary,
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems:
|
||||
variant.answer.length <= 60 ? "center" : "flex-start",
|
||||
position: "relative",
|
||||
height: "80px",
|
||||
justifyContent: "center",
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
// paddingLeft: "45px",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
"& .MuiFormControlLabel-label.Mui-disabled": {
|
||||
color: theme.palette.text.primary,
|
||||
}
|
||||
}}
|
||||
value={index}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={
|
||||
<RadioCheck color={theme.palette.primary.main} />
|
||||
}
|
||||
icon={<RadioIcon />}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "-162px",
|
||||
right: "12px",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Box sx={{ display: "flex", gap: "10px" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
wordBreak: "break-word",
|
||||
lineHeight: "normal",
|
||||
}}
|
||||
>
|
||||
{variant.answer}
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,179 @@
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import type { MouseEvent } from "react";
|
||||
import type { QuestionVariant } from "@/model/questionTypes/shared";
|
||||
import type { QuizQuestionEmoji } from "@model/questionTypes/emoji";
|
||||
|
||||
polyfillCountryFlagEmojis();
|
||||
|
||||
type EmojiVariantProps = {
|
||||
currentQuestion: QuizQuestionEmoji;
|
||||
variant: QuestionVariant;
|
||||
index: number;
|
||||
isSending: boolean;
|
||||
setIsSending: (isSending: boolean) => void;
|
||||
};
|
||||
|
||||
export const EmojiVariant = ({
|
||||
currentQuestion,
|
||||
variant,
|
||||
index,
|
||||
isSending,
|
||||
setIsSending,
|
||||
}: EmojiVariantProps) => {
|
||||
const { quizId, settings, preview } = useQuizData();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
const onVariantClick = async (event: MouseEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body:
|
||||
currentQuestion.content.variants[index].extendedText +
|
||||
" " +
|
||||
currentQuestion.content.variants[index].answer,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
deleteAnswer(currentQuestion.id);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
key={index}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||
overflow: "hidden",
|
||||
maxWidth: "317px",
|
||||
width: "100%",
|
||||
height: "255px",
|
||||
background:
|
||||
settings.cfg.design && !quizThemes[settings.cfg.theme].isLight
|
||||
? "rgba(255,255,255, 0.3)"
|
||||
: (settings.cfg.design && quizThemes[settings.cfg.theme].isLight) ||
|
||||
quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "transparent",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
}}
|
||||
// value={index}
|
||||
onClick={onVariantClick}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "193px",
|
||||
background: "#ffffff",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{variant.extendedText && (
|
||||
<Typography fontSize="100px">{variant.extendedText}</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
sx={{
|
||||
margin: 0,
|
||||
padding: "15px",
|
||||
color: theme.palette.text.primary,
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: variant.answer.length <= 60 ? "center" : "flex-start",
|
||||
position: "relative",
|
||||
height: "80px",
|
||||
justifyContent: "center",
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
"&::-webkit-scrollbar": { width: "4px" },
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
"& .MuiFormControlLabel-label.Mui-disabled": {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
|
||||
icon={<RadioIcon />}
|
||||
sx={{ position: "absolute", top: "-162px", right: "12px" }}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Box sx={{ display: "flex", gap: "10px" }}>
|
||||
<Typography sx={{ wordBreak: "break-word", lineHeight: "normal" }}>
|
||||
{variant.answer}
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
70
lib/components/ViewPublicationPage/questions/Emoji/index.tsx
Normal file
70
lib/components/ViewPublicationPage/questions/Emoji/index.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { useState } from "react";
|
||||
import { Box, RadioGroup, Typography, useTheme } from "@mui/material";
|
||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import type { QuizQuestionEmoji } from "@model/questionTypes/emoji";
|
||||
import { EmojiVariant } from "./EmojiVariant";
|
||||
|
||||
polyfillCountryFlagEmojis();
|
||||
|
||||
type EmojiProps = {
|
||||
currentQuestion: QuizQuestionEmoji;
|
||||
};
|
||||
|
||||
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
onChange={({ target }) =>
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[Number(target.value)].answer,
|
||||
currentQuestion.content.variants[Number(target.value)].points || 0
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{ display: "flex", width: "100%", gap: "42px", flexWrap: "wrap" }}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<EmojiVariant
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,292 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
ButtonBase,
|
||||
IconButton,
|
||||
Modal,
|
||||
Skeleton,
|
||||
Typography,
|
||||
useTheme
|
||||
} from "@mui/material";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import CloseBold from "@icons/CloseBold";
|
||||
import UploadIcon from "@icons/UploadIcon";
|
||||
|
||||
import { sendAnswer, sendFile } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import Info from "@icons/Info";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useState } from "react";
|
||||
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
|
||||
import type { QuizQuestionFile } from "../../../model/questionTypes/file";
|
||||
import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE, UPLOAD_FILE_DESCRIPTIONS_MAP } from "../tools/fileUpload";
|
||||
|
||||
type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null;
|
||||
|
||||
type FileProps = {
|
||||
currentQuestion: QuizQuestionFile;
|
||||
};
|
||||
|
||||
export const File = ({ currentQuestion }: FileProps) => {
|
||||
const theme = useTheme();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const { quizId, preview } = useQuizData();
|
||||
const [modalWarningType, setModalWarningType] = useState<ModalWarningType>(null);
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false);
|
||||
const isMobile = useRootContainerSize() < 500;
|
||||
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const uploadFile = async (file: File | undefined) => {
|
||||
if (isSending) return;
|
||||
if (!file) return;
|
||||
console.log(file.size);
|
||||
console.log(MAX_FILE_SIZE);
|
||||
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
|
||||
|
||||
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].some(
|
||||
fileType => file.name.toLowerCase().endsWith(fileType)
|
||||
);
|
||||
|
||||
if (!isFileTypeAccepted) return setModalWarningType("errorType");
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
const data = await sendFile({
|
||||
questionId: currentQuestion.id,
|
||||
body: {
|
||||
file: file,
|
||||
name: file.name,
|
||||
preview
|
||||
},
|
||||
qid: quizId,
|
||||
});
|
||||
console.log(data);
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `https://storage.yandexcloud.net/squizanswer/${quizId}/${currentQuestion.id}/${data!.data.fileIDMap[currentQuestion.id]}`,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, `${file.name}|${URL.createObjectURL(file)}`, 0);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
setIsDropzoneHighlighted(false);
|
||||
|
||||
const file = event.dataTransfer.files[0];
|
||||
|
||||
uploadFile(file);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>{currentQuestion.title}</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
maxWidth: answer?.split("|")[0] ? "640px" : "600px",
|
||||
}}
|
||||
>
|
||||
{answer?.split("|")[0] ? (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
||||
<Typography color={theme.palette.text.primary}>Вы загрузили:</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px 5px 5px 16px",
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
borderRadius: "8px",
|
||||
color: "#FFFFFF",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
overflow: "hidden",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{answer?.split("|")[0]}</Typography>
|
||||
<IconButton
|
||||
sx={{ p: 0 }}
|
||||
onClick={async () => {
|
||||
if (answer.length > 0) {
|
||||
setIsSending(true);
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
}
|
||||
console.log(answer);
|
||||
updateAnswer(currentQuestion.id, "", 0);
|
||||
setIsSending(false);
|
||||
}}
|
||||
>
|
||||
<CloseBold />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{isSending ?
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "120px",
|
||||
maxWidth: "560px",
|
||||
}}
|
||||
/>
|
||||
:
|
||||
<ButtonBase
|
||||
component="label"
|
||||
sx={{ justifyContent: "flex-start", width: "100%" }}
|
||||
>
|
||||
<input
|
||||
onChange={e => uploadFile(e.target.files?.[0])}
|
||||
hidden
|
||||
accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")}
|
||||
multiple
|
||||
type="file"
|
||||
/>
|
||||
<Box
|
||||
onDragEnter={() => !answer?.split("|")[0] && setIsDropzoneHighlighted(true)}
|
||||
onDragLeave={() => setIsDropzoneHighlighted(false)}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onDrop={onDrop}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: isMobile ? undefined : "120px",
|
||||
display: "flex",
|
||||
gap: "50px",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
padding: "33px 44px 33px 55px",
|
||||
backgroundColor: "#F2F3F7",
|
||||
border: `1px solid ${isDropzoneHighlighted ? "red" : "#9A9AAF"}`,
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
<UploadIcon />
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#9A9AAF",
|
||||
// color: theme.palette.grey2.main,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].title}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#9A9AAF",
|
||||
// color: theme.palette.grey2.main,
|
||||
fontSize: "16px",
|
||||
lineHeight: "19px",
|
||||
}}
|
||||
>
|
||||
{UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
}
|
||||
<Info
|
||||
sx={{ width: "40px", height: "40px" }}
|
||||
color={theme.palette.primary.main}
|
||||
onClick={() => setModalWarningType(currentQuestion.content.type)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{answer && currentQuestion.content.type === "picture" && (
|
||||
<img
|
||||
src={answer.split("|")[1]}
|
||||
alt=""
|
||||
style={{
|
||||
marginTop: "15px",
|
||||
maxWidth: "300px",
|
||||
maxHeight: "300px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{answer && currentQuestion.content.type === "video" && (
|
||||
<video
|
||||
src={answer.split("|")[1]}
|
||||
style={{
|
||||
marginTop: "15px",
|
||||
maxWidth: "300px",
|
||||
maxHeight: "300px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Modal
|
||||
open={modalWarningType !== null}
|
||||
onClose={() => setModalWarningType(null)}
|
||||
>
|
||||
<Box sx={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: isMobile ? 300 : 400,
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: 3,
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
}}>
|
||||
<CurrentModal status={modalWarningType} />
|
||||
</Box>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const CurrentModal = ({ status }: { status: ModalWarningType; }) => {
|
||||
|
||||
switch (status) {
|
||||
case null: return null;
|
||||
case 'errorType': return <Typography>Выбран некорректный тип файла</Typography>;
|
||||
case 'errorSize': return <Typography>Файл слишком большой. Максимальный размер 50 МБ</Typography>;
|
||||
default: return (
|
||||
<>
|
||||
<Typography>Допустимые расширения файлов:</Typography>
|
||||
<Typography>{
|
||||
ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
173
lib/components/ViewPublicationPage/questions/File/UploadFile.tsx
Normal file
173
lib/components/ViewPublicationPage/questions/File/UploadFile.tsx
Normal file
@ -0,0 +1,173 @@
|
||||
import { useState } from "react";
|
||||
import { Box, ButtonBase, Skeleton, Typography, useTheme } from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { sendAnswer, sendFile } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import {
|
||||
ACCEPT_SEND_FILE_TYPES_MAP,
|
||||
MAX_FILE_SIZE,
|
||||
UPLOAD_FILE_DESCRIPTIONS_MAP,
|
||||
} from "@/components/ViewPublicationPage/tools/fileUpload";
|
||||
|
||||
import Info from "@icons/Info";
|
||||
import UploadIcon from "@icons/UploadIcon";
|
||||
|
||||
import type { QuizQuestionFile } from "@model/questionTypes/file";
|
||||
import type { ModalWarningType } from "./index";
|
||||
|
||||
type UploadFileProps = {
|
||||
currentQuestion: QuizQuestionFile;
|
||||
setModalWarningType: (modalType: ModalWarningType) => void;
|
||||
isSending: boolean;
|
||||
setIsSending: (isSending: boolean) => void;
|
||||
};
|
||||
|
||||
export const UploadFile = ({
|
||||
currentQuestion,
|
||||
setModalWarningType,
|
||||
isSending,
|
||||
setIsSending,
|
||||
}: UploadFileProps) => {
|
||||
const { quizId, preview } = useQuizData();
|
||||
const [isDropzoneHighlighted, setIsDropzoneHighlighted] =
|
||||
useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const isMobile = useRootContainerSize() < 500;
|
||||
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const uploadFile = async (file: File | undefined) => {
|
||||
if (isSending) return;
|
||||
if (!file) return;
|
||||
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
|
||||
|
||||
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[
|
||||
currentQuestion.content.type
|
||||
].some((fileType) => file.name.toLowerCase().endsWith(fileType));
|
||||
|
||||
if (!isFileTypeAccepted) return setModalWarningType("errorType");
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
const data = await sendFile({
|
||||
questionId: currentQuestion.id,
|
||||
body: {
|
||||
file: file,
|
||||
name: file.name,
|
||||
preview,
|
||||
},
|
||||
qid: quizId,
|
||||
});
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `https://storage.yandexcloud.net/squizanswer/${quizId}/${
|
||||
currentQuestion.id
|
||||
}/${data!.data.fileIDMap[currentQuestion.id]}`,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${file.name}|${URL.createObjectURL(file)}`,
|
||||
0
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
setIsDropzoneHighlighted(false);
|
||||
|
||||
const file = event.dataTransfer.files[0];
|
||||
|
||||
uploadFile(file);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
{isSending ? (
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ width: "100%", height: "120px", maxWidth: "560px" }}
|
||||
/>
|
||||
) : (
|
||||
<ButtonBase
|
||||
component="label"
|
||||
sx={{ justifyContent: "flex-start", width: "100%" }}
|
||||
>
|
||||
<input
|
||||
onChange={({ target }) => uploadFile(target.files?.[0])}
|
||||
hidden
|
||||
accept={ACCEPT_SEND_FILE_TYPES_MAP[
|
||||
currentQuestion.content.type
|
||||
].join(",")}
|
||||
multiple
|
||||
type="file"
|
||||
/>
|
||||
<Box
|
||||
onDragEnter={() =>
|
||||
!answer?.split("|")[0] && setIsDropzoneHighlighted(true)
|
||||
}
|
||||
onDragLeave={() => setIsDropzoneHighlighted(false)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDrop={onDrop}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: isMobile ? undefined : "120px",
|
||||
display: "flex",
|
||||
gap: "50px",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
padding: "33px 44px 33px 55px",
|
||||
backgroundColor: "#F2F3F7",
|
||||
border: `1px solid ${isDropzoneHighlighted ? "red" : "#9A9AAF"}`,
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
<UploadIcon />
|
||||
<Box>
|
||||
<Typography sx={{ color: "#9A9AAF", fontWeight: 500 }}>
|
||||
{
|
||||
UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
|
||||
.title
|
||||
}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#9A9AAF",
|
||||
fontSize: "16px",
|
||||
lineHeight: "19px",
|
||||
}}
|
||||
>
|
||||
{
|
||||
UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type]
|
||||
.description
|
||||
}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
)}
|
||||
<Info
|
||||
sx={{ width: "40px", height: "40px" }}
|
||||
color={theme.palette.primary.main}
|
||||
onClick={() => setModalWarningType(currentQuestion.content.type)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,75 @@
|
||||
import { Box, IconButton, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import CloseBold from "@icons/CloseBold";
|
||||
|
||||
import type { QuizQuestionFile } from "@model/questionTypes/file";
|
||||
|
||||
type UploadedFileProps = {
|
||||
currentQuestion: QuizQuestionFile;
|
||||
setIsSending: (isSending: boolean) => void;
|
||||
};
|
||||
|
||||
export const UploadedFile = ({
|
||||
currentQuestion,
|
||||
setIsSending,
|
||||
}: UploadedFileProps) => {
|
||||
const { quizId, preview } = useQuizData();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const deleteFile = async () => {
|
||||
if (answer.length > 0) {
|
||||
setIsSending(true);
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
}
|
||||
|
||||
updateAnswer(currentQuestion.id, "", 0);
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
||||
<Typography color={theme.palette.text.primary}>Вы загрузили:</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px 5px 5px 16px",
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
borderRadius: "8px",
|
||||
color: "#FFFFFF",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
overflow: "hidden",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{answer?.split("|")[0]}
|
||||
</Typography>
|
||||
<IconButton sx={{ p: 0 }} onClick={deleteFile}>
|
||||
<CloseBold />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
134
lib/components/ViewPublicationPage/questions/File/index.tsx
Normal file
134
lib/components/ViewPublicationPage/questions/File/index.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
import { useState } from "react";
|
||||
import { Box, Modal, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { UploadFile } from "./UploadFile";
|
||||
import { UploadedFile } from "./UploadedFile";
|
||||
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { ACCEPT_SEND_FILE_TYPES_MAP } from "@/components/ViewPublicationPage/tools/fileUpload";
|
||||
|
||||
import type { QuizQuestionFile } from "@model/questionTypes/file";
|
||||
|
||||
export type ModalWarningType =
|
||||
| "errorType"
|
||||
| "errorSize"
|
||||
| "picture"
|
||||
| "video"
|
||||
| "audio"
|
||||
| "document"
|
||||
| null;
|
||||
|
||||
type FileProps = {
|
||||
currentQuestion: QuizQuestionFile;
|
||||
};
|
||||
|
||||
export const File = ({ currentQuestion }: FileProps) => {
|
||||
const theme = useTheme();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const [modalWarningType, setModalWarningType] =
|
||||
useState<ModalWarningType>(null);
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const isMobile = useRootContainerSize() < 500;
|
||||
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
maxWidth: answer?.split("|")[0] ? "640px" : "600px",
|
||||
}}
|
||||
>
|
||||
{answer?.split("|")[0] ? (
|
||||
<UploadedFile
|
||||
currentQuestion={currentQuestion}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
) : (
|
||||
<UploadFile
|
||||
currentQuestion={currentQuestion}
|
||||
setModalWarningType={setModalWarningType}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
)}
|
||||
{answer && currentQuestion.content.type === "picture" && (
|
||||
<img
|
||||
src={answer.split("|")[1]}
|
||||
style={{ marginTop: "15px", maxWidth: "300px", maxHeight: "300px" }}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
{answer && currentQuestion.content.type === "video" && (
|
||||
<video
|
||||
src={answer.split("|")[1]}
|
||||
style={{
|
||||
marginTop: "15px",
|
||||
maxWidth: "300px",
|
||||
maxHeight: "300px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Modal
|
||||
open={modalWarningType !== null}
|
||||
onClose={() => setModalWarningType(null)}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: isMobile ? 300 : 400,
|
||||
bgcolor: "background.paper",
|
||||
borderRadius: 3,
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
}}
|
||||
>
|
||||
<CurrentModal status={modalWarningType} />
|
||||
</Box>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const CurrentModal = ({ status }: { status: ModalWarningType }) => {
|
||||
switch (status) {
|
||||
case null:
|
||||
return null;
|
||||
case "errorType":
|
||||
return <Typography>Выбран некорректный тип файла</Typography>;
|
||||
case "errorSize":
|
||||
return (
|
||||
<Typography>Файл слишком большой. Максимальный размер 50 МБ</Typography>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<Typography>Допустимые расширения файлов:</Typography>
|
||||
<Typography>
|
||||
{ACCEPT_SEND_FILE_TYPES_MAP[status].join(" ")}
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
@ -1,195 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
|
||||
import type { QuizQuestionImages } from "../../../model/questionTypes/images";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useState } from "react";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
type ImagesProps = {
|
||||
currentQuestion: QuizQuestionImages;
|
||||
};
|
||||
|
||||
export const Images = ({ currentQuestion }: ImagesProps) => {
|
||||
const { quizId, preview } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const deleteAnswer = useQuizViewStore(state => state.deleteAnswer);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const theme = useTheme();
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer;
|
||||
const { settings } = useQuizData();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const isTablet = useRootContainerSize() < 1000;
|
||||
const isMobile = useRootContainerSize() < 500;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gap: "15px",
|
||||
gridTemplateColumns: isTablet
|
||||
? isMobile
|
||||
? "repeat(1, 1fr)"
|
||||
: "repeat(2, 1fr)"
|
||||
: "repeat(3, 1fr)",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id
|
||||
? theme.palette.primary.main
|
||||
: "#9A9AAF",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
background: settings.cfg.design && !quizThemes[settings.cfg.theme].isLight
|
||||
? "rgba(255,255,255, 0.3)" : settings.cfg.design && quizThemes[settings.cfg.theme].isLight || quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "transparent",
|
||||
}}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
deleteAnswer(currentQuestion.id);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||
<Box sx={{ width: "100%", height: "300px" }}>
|
||||
{variant.extendedText && (
|
||||
<img
|
||||
src={variant.extendedText}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
borderRadius: "12px 12px 0 0"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
color: theme.palette.text.primary,
|
||||
marginTop: "10px",
|
||||
marginLeft: 0,
|
||||
padding: "10px",
|
||||
display: "flex",
|
||||
alignItems:
|
||||
variant.answer.length <= 60 ? "center" : "flex-start",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
height: "80px",
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={
|
||||
<RadioCheck color={theme.palette.primary.main} />
|
||||
}
|
||||
icon={<RadioIcon />}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "-297px",
|
||||
right: 0
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={variant.answer}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,156 @@
|
||||
import { Box, FormControlLabel, Radio, useTheme } from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import type { MouseEvent } from "react";
|
||||
import type { QuestionVariant } from "@/model/questionTypes/shared";
|
||||
import type { QuizQuestionImages } from "@model/questionTypes/images";
|
||||
|
||||
type ImagesProps = {
|
||||
currentQuestion: QuizQuestionImages;
|
||||
variant: QuestionVariant;
|
||||
isSending: boolean;
|
||||
setIsSending: (isSending: boolean) => void;
|
||||
index: number;
|
||||
};
|
||||
|
||||
export const ImageVariant = ({
|
||||
currentQuestion,
|
||||
variant,
|
||||
isSending,
|
||||
setIsSending,
|
||||
index,
|
||||
}: ImagesProps) => {
|
||||
const { quizId, preview } = useQuizData();
|
||||
const { settings } = useQuizData();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { deleteAnswer, updateAnswer } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer;
|
||||
|
||||
const onVariantClick = async (event: MouseEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
deleteAnswer(currentQuestion.id);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
background:
|
||||
settings.cfg.design && !quizThemes[settings.cfg.theme].isLight
|
||||
? "rgba(255,255,255, 0.3)"
|
||||
: (settings.cfg.design && quizThemes[settings.cfg.theme].isLight) ||
|
||||
quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "transparent",
|
||||
}}
|
||||
onClick={onVariantClick}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||
<Box sx={{ width: "100%", height: "300px" }}>
|
||||
{variant.extendedText && (
|
||||
<img
|
||||
src={variant.extendedText}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
borderRadius: "12px 12px 0 0",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
color: theme.palette.text.primary,
|
||||
marginTop: "10px",
|
||||
marginLeft: 0,
|
||||
padding: "10px",
|
||||
display: "flex",
|
||||
alignItems: variant.answer.length <= 60 ? "center" : "flex-start",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
height: "80px",
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
|
||||
icon={<RadioIcon />}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "-297px",
|
||||
right: 0,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={variant.answer}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,73 @@
|
||||
import { useState } from "react";
|
||||
import { Box, RadioGroup, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { ImageVariant } from "./ImageVariant";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import type { QuizQuestionImages } from "@model/questionTypes/images";
|
||||
|
||||
type ImagesProps = {
|
||||
currentQuestion: QuizQuestionImages;
|
||||
};
|
||||
|
||||
export const Images = ({ currentQuestion }: ImagesProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const theme = useTheme();
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer;
|
||||
const isTablet = useRootContainerSize() < 1000;
|
||||
const isMobile = useRootContainerSize() < 500;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gap: "15px",
|
||||
gridTemplateColumns: isTablet
|
||||
? isMobile
|
||||
? "repeat(1, 1fr)"
|
||||
: "repeat(2, 1fr)"
|
||||
: "repeat(3, 1fr)",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<ImageVariant
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,477 +0,0 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
import { CustomSlider } from "@ui_kit/CustomSlider";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type { QuizQuestionNumber } from "@model/questionTypes/number";
|
||||
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import type { ChangeEvent, SyntheticEvent } from "react";
|
||||
|
||||
type NumberProps = {
|
||||
currentQuestion: QuizQuestionNumber;
|
||||
};
|
||||
|
||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const [inputValue, setInputValue] = useState<string>("0");
|
||||
const [minRange, setMinRange] = useState<string>("0");
|
||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||
const [reversedInputValue, setReversedInputValue] = useState<string>("0");
|
||||
const [reversedMinRange, setReversedMinRange] = useState<string>("0");
|
||||
const [reversedMaxRange, setReversedMaxRange] =
|
||||
useState<string>("100000000000");
|
||||
const theme = useTheme();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const deleteAnswer = useQuizViewStore(state => state.deleteAnswer);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const [minBorder, maxBorder] = currentQuestion.content.range
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
const min = minBorder < maxBorder ? minBorder : maxBorder;
|
||||
const max = minBorder < maxBorder ? maxBorder : minBorder;
|
||||
const reversed = minBorder > maxBorder;
|
||||
|
||||
useEffect(() => {
|
||||
console.log("reversed:", reversed);
|
||||
}, [reversed]);
|
||||
|
||||
const sendAnswerToBackend = async (value: string, noUpdate = false) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: value,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
if (!noUpdate) {
|
||||
updateAnswer(currentQuestion.id, value, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
const updateValueDebounced = useDebouncedCallback(async (value: string) => {
|
||||
if (reversed) {
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(min)
|
||||
? String(min)
|
||||
: window.Number(value) > window.Number(max)
|
||||
? String(max)
|
||||
: value;
|
||||
|
||||
setReversedInputValue(newValue);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
String(max + min - window.Number(newValue)),
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(String(window.Number(newValue)), true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(minRange)
|
||||
? minRange
|
||||
: window.Number(value) > window.Number(maxRange)
|
||||
? maxRange
|
||||
: value;
|
||||
|
||||
setInputValue(newValue);
|
||||
await sendAnswerToBackend(newValue);
|
||||
}, 1000);
|
||||
const updateMinRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMinRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[0]) < min
|
||||
? min
|
||||
: max + min - window.Number(value.split("—")[0]);
|
||||
|
||||
const newMinValue =
|
||||
window.Number(value.split("—")[0]) > max
|
||||
? String(max)
|
||||
: value.split("—")[0];
|
||||
|
||||
setReversedMinRange(
|
||||
crowded ? String(max + min - window.Number(newMinValue)) : newMinValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${newMinRange}—${value.split("—")[1]}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinValue}—${value.split("—")[1]}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMinValue = crowded
|
||||
? maxRange
|
||||
: window.Number(value.split("—")[0]) < min
|
||||
? String(min)
|
||||
: value.split("—")[0];
|
||||
|
||||
setMinRange(newMinValue);
|
||||
await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
const updateMaxRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMaxRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[1]) > max
|
||||
? max
|
||||
: max + min - window.Number(value.split("—")[1]);
|
||||
|
||||
const newMaxValue =
|
||||
window.Number(value.split("—")[1]) < min
|
||||
? String(min)
|
||||
: value.split("—")[1];
|
||||
|
||||
setReversedMaxRange(
|
||||
crowded ? String(max + min - window.Number(newMaxValue)) : newMaxValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${value.split("—")[0]}—${newMaxRange}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${value.split("—")[0]}—${newMaxValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMaxValue = crowded
|
||||
? minRange
|
||||
: window.Number(value.split("—")[1]) > max
|
||||
? String(max)
|
||||
: value.split("—")[1];
|
||||
|
||||
setMaxRange(newMaxValue);
|
||||
await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const sliderValue =
|
||||
answer ||
|
||||
(reversed
|
||||
? max + min - currentQuestion.content.start + "—" + max
|
||||
: currentQuestion.content.start + "—" + max);
|
||||
|
||||
useEffect(() => {
|
||||
if (answer) {
|
||||
if (answer.includes("—")) {
|
||||
if (reversed) {
|
||||
setReversedMinRange(
|
||||
String(max + min - window.Number(answer.split("—")[0]))
|
||||
);
|
||||
setReversedMaxRange(
|
||||
String(max + min - window.Number(answer.split("—")[1]))
|
||||
);
|
||||
} else {
|
||||
setMinRange(answer.split("—")[0]);
|
||||
setMaxRange(answer.split("—")[1]);
|
||||
}
|
||||
} else {
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(answer)));
|
||||
} else {
|
||||
setInputValue(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!answer) {
|
||||
setMinRange(String(currentQuestion.content.start));
|
||||
setMaxRange(String(max));
|
||||
|
||||
if (currentQuestion.content.chooseRange) {
|
||||
setReversedMinRange(String(currentQuestion.content.start));
|
||||
setReversedMaxRange(String(min));
|
||||
}
|
||||
|
||||
setReversedInputValue(String(currentQuestion.content.start));
|
||||
setInputValue(String(currentQuestion.content.start));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onSliderChange = (_: Event, value: number | number[]) => {
|
||||
const range = Array.isArray(value)
|
||||
? `${value[0]}—${value[1]}`
|
||||
: String(value);
|
||||
|
||||
updateAnswer(currentQuestion.id, range, 0);
|
||||
};
|
||||
|
||||
const onChangeCommitted = async (
|
||||
_: Event | SyntheticEvent<Element, Event>,
|
||||
value: number | number[]
|
||||
) => {
|
||||
if (currentQuestion.content.chooseRange && Array.isArray(value)) {
|
||||
if (reversed) {
|
||||
const newMinReversedValue = String(max + min - value[0]);
|
||||
const newMaxReversedValue = String(max + min - value[1]);
|
||||
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
setReversedMinRange(newMinReversedValue);
|
||||
setReversedMaxRange(newMaxReversedValue);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinReversedValue}—${newMaxReversedValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
await sendAnswerToBackend(`${value[0]}—${value[1]}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(value)));
|
||||
} else {
|
||||
setInputValue(String(value));
|
||||
}
|
||||
|
||||
await sendAnswerToBackend(String(value));
|
||||
};
|
||||
|
||||
const changeValueLabelFormat = (value: number) => {
|
||||
if (!reversed) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const [minSliderBorder, maxSliderBorder] = sliderValue
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
|
||||
if (value === minSliderBorder) {
|
||||
return max + min - minSliderBorder;
|
||||
}
|
||||
|
||||
return max + min - maxSliderBorder;
|
||||
};
|
||||
|
||||
const onInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedInputValue(value);
|
||||
} else {
|
||||
setInputValue(value);
|
||||
}
|
||||
|
||||
updateValueDebounced(value);
|
||||
};
|
||||
|
||||
const onMinInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedMinRange(newValue);
|
||||
|
||||
if (window.Number(newValue) <= window.Number(reversedMaxRange)) {
|
||||
const value = max + min - window.Number(reversedMaxRange);
|
||||
updateMinRangeDebounced(`${value}—${value}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMinRangeDebounced(
|
||||
`${newValue}—${max + min - window.Number(reversedMaxRange)}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMinRange(newValue);
|
||||
|
||||
if (window.Number(newValue) >= window.Number(maxRange)) {
|
||||
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMinRangeDebounced(`${newValue}—${maxRange}`);
|
||||
};
|
||||
|
||||
const onMaxInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedMaxRange(newValue);
|
||||
|
||||
if (window.Number(newValue) >= window.Number(reversedMinRange)) {
|
||||
const value = max + min - window.Number(reversedMinRange);
|
||||
updateMaxRangeDebounced(`${value}—${value}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(
|
||||
`${max + min - window.Number(reversedMinRange)}—${newValue}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMaxRange(newValue);
|
||||
|
||||
if (window.Number(newValue) <= window.Number(minRange)) {
|
||||
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(`${minRange}—${newValue}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
gap: "30px",
|
||||
padding: "0 30px",
|
||||
}}
|
||||
>
|
||||
<CustomSlider
|
||||
value={
|
||||
currentQuestion.content.chooseRange
|
||||
? sliderValue.split("—").length || 0 > 1
|
||||
? sliderValue.split("—").map((item) => window.Number(item))
|
||||
: [min, min + 1]
|
||||
: window.Number(sliderValue.split("—")[0])
|
||||
}
|
||||
min={min}
|
||||
max={max}
|
||||
step={currentQuestion.content.step || 1}
|
||||
onChange={onSliderChange}
|
||||
onChangeCommitted={onChangeCommitted}
|
||||
valueLabelFormat={changeValueLabelFormat}
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
"& .MuiSlider-valueLabel": {
|
||||
background: theme.palette.primary.main,
|
||||
borderRadius: "8px",
|
||||
minWidth: "60px",
|
||||
height: "36px"
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{!currentQuestion.content.chooseRange && (
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? reversedInputValue : inputValue}
|
||||
onChange={onInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderColor: "#9A9AAF"
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentQuestion.content.chooseRange && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
"& .MuiFormControl-root": { width: "auto" },
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMinRange) : minRange}
|
||||
onChange={onMinInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderColor: "#9A9AAF"
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Typography color={theme.palette.text.primary}>до</Typography>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMaxRange) : maxRange}
|
||||
onChange={onMaxInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderColor: "#9A9AAF"
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
477
lib/components/ViewPublicationPage/questions/Number/index.tsx
Normal file
477
lib/components/ViewPublicationPage/questions/Number/index.tsx
Normal file
@ -0,0 +1,477 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
import { CustomSlider } from "@ui_kit/CustomSlider";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type { QuizQuestionNumber } from "@model/questionTypes/number";
|
||||
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import type { ChangeEvent, SyntheticEvent } from "react";
|
||||
|
||||
type NumberProps = {
|
||||
currentQuestion: QuizQuestionNumber;
|
||||
};
|
||||
|
||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const [inputValue, setInputValue] = useState<string>("0");
|
||||
const [minRange, setMinRange] = useState<string>("0");
|
||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||
const [reversedInputValue, setReversedInputValue] = useState<string>("0");
|
||||
const [reversedMinRange, setReversedMinRange] = useState<string>("0");
|
||||
const [reversedMaxRange, setReversedMaxRange] =
|
||||
useState<string>("100000000000");
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const theme = useTheme();
|
||||
|
||||
const [minBorder, maxBorder] = currentQuestion.content.range
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
const min = minBorder < maxBorder ? minBorder : maxBorder;
|
||||
const max = minBorder < maxBorder ? maxBorder : minBorder;
|
||||
const reversed = minBorder > maxBorder;
|
||||
|
||||
const answer = answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
)?.answer as string;
|
||||
|
||||
const sliderValue =
|
||||
answer ||
|
||||
(reversed
|
||||
? max + min - currentQuestion.content.start + "—" + max
|
||||
: currentQuestion.content.start + "—" + max);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("reversed:", reversed);
|
||||
}, [reversed]);
|
||||
|
||||
const sendAnswerToBackend = async (value: string, noUpdate = false) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: value,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
if (!noUpdate) {
|
||||
updateAnswer(currentQuestion.id, value, 0);
|
||||
}
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
const updateValueDebounced = useDebouncedCallback(async (value: string) => {
|
||||
if (reversed) {
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(min)
|
||||
? String(min)
|
||||
: window.Number(value) > window.Number(max)
|
||||
? String(max)
|
||||
: value;
|
||||
|
||||
setReversedInputValue(newValue);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
String(max + min - window.Number(newValue)),
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(String(window.Number(newValue)), true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue =
|
||||
window.Number(value) < window.Number(minRange)
|
||||
? minRange
|
||||
: window.Number(value) > window.Number(maxRange)
|
||||
? maxRange
|
||||
: value;
|
||||
|
||||
setInputValue(newValue);
|
||||
await sendAnswerToBackend(newValue);
|
||||
}, 1000);
|
||||
|
||||
const updateMinRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMinRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[0]) < min
|
||||
? min
|
||||
: max + min - window.Number(value.split("—")[0]);
|
||||
|
||||
const newMinValue =
|
||||
window.Number(value.split("—")[0]) > max
|
||||
? String(max)
|
||||
: value.split("—")[0];
|
||||
|
||||
setReversedMinRange(
|
||||
crowded ? String(max + min - window.Number(newMinValue)) : newMinValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${newMinRange}—${value.split("—")[1]}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinValue}—${value.split("—")[1]}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMinValue = crowded
|
||||
? maxRange
|
||||
: window.Number(value.split("—")[0]) < min
|
||||
? String(min)
|
||||
: value.split("—")[0];
|
||||
|
||||
setMinRange(newMinValue);
|
||||
await sendAnswerToBackend(`${newMinValue}—${value.split("—")[1]}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
|
||||
const updateMaxRangeDebounced = useDebouncedCallback(
|
||||
async (value: string, crowded = false) => {
|
||||
if (reversed) {
|
||||
const newMaxRange = crowded
|
||||
? window.Number(value.split("—")[1])
|
||||
: max + min - window.Number(value.split("—")[1]) > max
|
||||
? max
|
||||
: max + min - window.Number(value.split("—")[1]);
|
||||
|
||||
const newMaxValue =
|
||||
window.Number(value.split("—")[1]) < min
|
||||
? String(min)
|
||||
: value.split("—")[1];
|
||||
|
||||
setReversedMaxRange(
|
||||
crowded ? String(max + min - window.Number(newMaxValue)) : newMaxValue
|
||||
);
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
`${value.split("—")[0]}—${newMaxRange}`,
|
||||
0
|
||||
);
|
||||
await sendAnswerToBackend(
|
||||
`${value.split("—")[0]}—${newMaxValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const newMaxValue = crowded
|
||||
? minRange
|
||||
: window.Number(value.split("—")[1]) > max
|
||||
? String(max)
|
||||
: value.split("—")[1];
|
||||
|
||||
setMaxRange(newMaxValue);
|
||||
await sendAnswerToBackend(`${value.split("—")[0]}—${newMaxValue}`);
|
||||
},
|
||||
1000
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (answer) {
|
||||
if (answer.includes("—")) {
|
||||
if (reversed) {
|
||||
setReversedMinRange(
|
||||
String(max + min - window.Number(answer.split("—")[0]))
|
||||
);
|
||||
setReversedMaxRange(
|
||||
String(max + min - window.Number(answer.split("—")[1]))
|
||||
);
|
||||
} else {
|
||||
setMinRange(answer.split("—")[0]);
|
||||
setMaxRange(answer.split("—")[1]);
|
||||
}
|
||||
} else {
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(answer)));
|
||||
} else {
|
||||
setInputValue(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!answer) {
|
||||
setMinRange(String(currentQuestion.content.start));
|
||||
setMaxRange(String(max));
|
||||
|
||||
if (currentQuestion.content.chooseRange) {
|
||||
setReversedMinRange(String(currentQuestion.content.start));
|
||||
setReversedMaxRange(String(min));
|
||||
}
|
||||
|
||||
setReversedInputValue(String(currentQuestion.content.start));
|
||||
setInputValue(String(currentQuestion.content.start));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onSliderChange = (_: Event, value: number | number[]) => {
|
||||
const range = Array.isArray(value)
|
||||
? `${value[0]}—${value[1]}`
|
||||
: String(value);
|
||||
|
||||
updateAnswer(currentQuestion.id, range, 0);
|
||||
};
|
||||
|
||||
const onChangeCommitted = async (
|
||||
_: Event | SyntheticEvent<Element, Event>,
|
||||
value: number | number[]
|
||||
) => {
|
||||
if (currentQuestion.content.chooseRange && Array.isArray(value)) {
|
||||
if (reversed) {
|
||||
const newMinReversedValue = String(max + min - value[0]);
|
||||
const newMaxReversedValue = String(max + min - value[1]);
|
||||
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
setReversedMinRange(newMinReversedValue);
|
||||
setReversedMaxRange(newMaxReversedValue);
|
||||
await sendAnswerToBackend(
|
||||
`${newMinReversedValue}—${newMaxReversedValue}`,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMinRange(String(value[0]));
|
||||
setMaxRange(String(value[1]));
|
||||
await sendAnswerToBackend(`${value[0]}—${value[1]}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
setReversedInputValue(String(max + min - window.Number(value)));
|
||||
} else {
|
||||
setInputValue(String(value));
|
||||
}
|
||||
|
||||
await sendAnswerToBackend(String(value));
|
||||
};
|
||||
|
||||
const changeValueLabelFormat = (value: number) => {
|
||||
if (!reversed) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const [minSliderBorder, maxSliderBorder] = sliderValue
|
||||
.split("—")
|
||||
.map(window.Number);
|
||||
|
||||
if (value === minSliderBorder) {
|
||||
return max + min - minSliderBorder;
|
||||
}
|
||||
|
||||
return max + min - maxSliderBorder;
|
||||
};
|
||||
|
||||
const onInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedInputValue(value);
|
||||
} else {
|
||||
setInputValue(value);
|
||||
}
|
||||
|
||||
updateValueDebounced(value);
|
||||
};
|
||||
|
||||
const onMinInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedMinRange(newValue);
|
||||
|
||||
if (window.Number(newValue) <= window.Number(reversedMaxRange)) {
|
||||
const value = max + min - window.Number(reversedMaxRange);
|
||||
updateMinRangeDebounced(`${value}—${value}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMinRangeDebounced(
|
||||
`${newValue}—${max + min - window.Number(reversedMaxRange)}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMinRange(newValue);
|
||||
|
||||
if (window.Number(newValue) >= window.Number(maxRange)) {
|
||||
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMinRangeDebounced(`${newValue}—${maxRange}`);
|
||||
};
|
||||
|
||||
const onMaxInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = target.value.replace(/\D/g, "");
|
||||
|
||||
if (reversed) {
|
||||
setReversedMaxRange(newValue);
|
||||
|
||||
if (window.Number(newValue) >= window.Number(reversedMinRange)) {
|
||||
const value = max + min - window.Number(reversedMinRange);
|
||||
updateMaxRangeDebounced(`${value}—${value}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(
|
||||
`${max + min - window.Number(reversedMinRange)}—${newValue}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMaxRange(newValue);
|
||||
|
||||
if (window.Number(newValue) <= window.Number(minRange)) {
|
||||
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateMaxRangeDebounced(`${minRange}—${newValue}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
gap: "30px",
|
||||
padding: "0 30px",
|
||||
}}
|
||||
>
|
||||
<CustomSlider
|
||||
value={
|
||||
currentQuestion.content.chooseRange
|
||||
? sliderValue.split("—").length || 0 > 1
|
||||
? sliderValue.split("—").map((item) => window.Number(item))
|
||||
: [min, min + 1]
|
||||
: window.Number(sliderValue.split("—")[0])
|
||||
}
|
||||
min={min}
|
||||
max={max}
|
||||
step={currentQuestion.content.step || 1}
|
||||
onChange={onSliderChange}
|
||||
onChangeCommitted={onChangeCommitted}
|
||||
valueLabelFormat={changeValueLabelFormat}
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
"& .MuiSlider-valueLabel": {
|
||||
background: theme.palette.primary.main,
|
||||
borderRadius: "8px",
|
||||
minWidth: "60px",
|
||||
height: "36px",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{!currentQuestion.content.chooseRange && (
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? reversedInputValue : inputValue}
|
||||
onChange={onInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentQuestion.content.chooseRange && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
"& .MuiFormControl-root": { width: "auto" },
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMinRange) : minRange}
|
||||
onChange={onMinInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
borderColor: theme.palette.text.primary,
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Typography color={theme.palette.text.primary}>до</Typography>
|
||||
<CustomTextField
|
||||
placeholder="0"
|
||||
value={reversed ? String(reversedMaxRange) : maxRange}
|
||||
onChange={onMaxInputChange}
|
||||
sx={{
|
||||
maxWidth: "80px",
|
||||
"& .MuiOutlinedInput-root": { background: "transparent" },
|
||||
"& .MuiInputBase-input": { textAlign: "center", zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
backgroundColor: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,54 +0,0 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
||||
import YoutubeEmbedIframe from "../tools/YoutubeEmbedIframe";
|
||||
|
||||
type PageProps = {
|
||||
currentQuestion: QuizQuestionPage;
|
||||
};
|
||||
|
||||
export const Page = ({ currentQuestion }: PageProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" sx={{ paddingBottom: "25px", color: theme.palette.text.primary, wordBreak: "break-word"}} >{currentQuestion.title}</Typography>
|
||||
<Typography color={theme.palette.text.primary} sx={{wordBreak: "break-word"}}>{currentQuestion.content.text}</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
{
|
||||
currentQuestion.content.useImage ? (
|
||||
<Box sx={{ borderRadius: "12px", border: "1px solid #9A9AAF", overflow: "hidden" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<YoutubeEmbedIframe
|
||||
containerSX={{
|
||||
width: "100%",
|
||||
height: "calc(100% - 270px)",
|
||||
maxHeight: "80%",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
videoUrl={currentQuestion.content.video}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
74
lib/components/ViewPublicationPage/questions/Page/index.tsx
Normal file
74
lib/components/ViewPublicationPage/questions/Page/index.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import YoutubeEmbedIframe from "@/components/ViewPublicationPage/tools/YoutubeEmbedIframe";
|
||||
|
||||
import type { QuizQuestionPage } from "@model/questionTypes/page";
|
||||
|
||||
type PageProps = {
|
||||
currentQuestion: QuizQuestionPage;
|
||||
};
|
||||
|
||||
export const Page = ({ currentQuestion }: PageProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
paddingBottom: "25px",
|
||||
color: theme.palette.text.primary,
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.content.text}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.useImage ? (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
border: "1px solid #9A9AAF",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<YoutubeEmbedIframe
|
||||
containerSX={{
|
||||
width: "100%",
|
||||
height: "calc(100% - 270px)",
|
||||
maxHeight: "80%",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
videoUrl={currentQuestion.content.video}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,148 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
Rating as RatingComponent,
|
||||
Typography,
|
||||
useTheme
|
||||
} from "@mui/material";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import FlagIcon from "@icons/questionsPage/FlagIcon";
|
||||
import StarIconMini from "@icons/questionsPage/StarIconMini";
|
||||
import HashtagIcon from "@icons/questionsPage/hashtagIcon";
|
||||
import HeartIcon from "@icons/questionsPage/heartIcon";
|
||||
import LightbulbIcon from "@icons/questionsPage/lightbulbIcon";
|
||||
import LikeIcon from "@icons/questionsPage/likeIcon";
|
||||
import TropfyIcon from "@icons/questionsPage/tropfyIcon";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
|
||||
import type { QuizQuestionRating } from "../../../model/questionTypes/rating";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useState } from "react";
|
||||
|
||||
type RatingProps = {
|
||||
currentQuestion: QuizQuestionRating;
|
||||
};
|
||||
|
||||
const buttonRatingForm = [
|
||||
{
|
||||
name: "star",
|
||||
icon: (color: string, width: number) => <StarIconMini width={width} color={color} />,
|
||||
},
|
||||
{
|
||||
name: "trophie",
|
||||
icon: (color: string, width: number) => <TropfyIcon width={width} color={color} />,
|
||||
},
|
||||
{
|
||||
name: "flag",
|
||||
icon: (color: string, width: number) => <FlagIcon width={width} color={color} />,
|
||||
},
|
||||
{
|
||||
name: "heart",
|
||||
icon: (color: string, width: number) => <HeartIcon width={width} color={color} />,
|
||||
},
|
||||
{
|
||||
name: "like",
|
||||
icon: (color: string, width: number) => <LikeIcon width={width} color={color} />,
|
||||
},
|
||||
{
|
||||
name: "bubble",
|
||||
icon: (color: string, width: number) => <LightbulbIcon width={width} color={color} />,
|
||||
},
|
||||
{
|
||||
name: "hashtag",
|
||||
icon: (color: string, width: number) => <HashtagIcon width={width} color={color} />,
|
||||
},
|
||||
];
|
||||
|
||||
export const Rating = ({ currentQuestion }: RatingProps) => {
|
||||
const { quizId, preview } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isTablet = useRootContainerSize() < 750;
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { answer } =
|
||||
answers.find(
|
||||
({ questionId }) => questionId === currentQuestion.id
|
||||
) ?? {};
|
||||
const form = buttonRatingForm.find(
|
||||
({ name }) => name === currentQuestion.content.form
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h5" color={theme.palette.text.primary} sx={{ wordBreak: "break-word" }}>{currentQuestion.title}</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
marginTop: "20px",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "inline-block",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<RatingComponent
|
||||
disabled={isSending}
|
||||
value={Number(answer || 0)}
|
||||
onChange={async (_, value) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: String(value) + " из " + currentQuestion.content.steps,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, String(value), 0);
|
||||
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
sx={{
|
||||
height: "50px",
|
||||
opacity: "1!important",
|
||||
"& .MuiRating-root.Mui-disabled": { opacity: "1!important" },
|
||||
"& .MuiRating-icon": {mr: isMobile ? undefined : "15px"}
|
||||
}}
|
||||
max={currentQuestion.content.steps}
|
||||
icon={form?.icon(theme.palette.primary.main, isMobile ? 30 : isTablet ? 40 : 50)}
|
||||
emptyIcon={form?.icon("#9A9AAF", isMobile ? 30 : isTablet ? 40 : 50)}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: 2,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
color: "#9A9AAF"
|
||||
}}>
|
||||
{currentQuestion.content.ratingNegativeDescription}
|
||||
</Typography>
|
||||
<Typography sx={{ color: "#9A9AAF" }}>
|
||||
{currentQuestion.content.ratingPositiveDescription}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
};
|
165
lib/components/ViewPublicationPage/questions/Rating/index.tsx
Normal file
165
lib/components/ViewPublicationPage/questions/Rating/index.tsx
Normal file
@ -0,0 +1,165 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Rating as RatingComponent,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import FlagIcon from "@icons/questionsPage/FlagIcon";
|
||||
import StarIconMini from "@icons/questionsPage/StarIconMini";
|
||||
import HashtagIcon from "@icons/questionsPage/hashtagIcon";
|
||||
import HeartIcon from "@icons/questionsPage/heartIcon";
|
||||
import LightbulbIcon from "@icons/questionsPage/lightbulbIcon";
|
||||
import LikeIcon from "@icons/questionsPage/likeIcon";
|
||||
import TropfyIcon from "@icons/questionsPage/tropfyIcon";
|
||||
|
||||
import type { QuizQuestionRating } from "@model/questionTypes/rating";
|
||||
|
||||
const RATING_FORM_BUTTONS = [
|
||||
{
|
||||
name: "star",
|
||||
icon: (color: string, width: number) => (
|
||||
<StarIconMini width={width} color={color} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "trophie",
|
||||
icon: (color: string, width: number) => (
|
||||
<TropfyIcon width={width} color={color} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "flag",
|
||||
icon: (color: string, width: number) => (
|
||||
<FlagIcon width={width} color={color} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "heart",
|
||||
icon: (color: string, width: number) => (
|
||||
<HeartIcon width={width} color={color} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "like",
|
||||
icon: (color: string, width: number) => (
|
||||
<LikeIcon width={width} color={color} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "bubble",
|
||||
icon: (color: string, width: number) => (
|
||||
<LightbulbIcon width={width} color={color} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "hashtag",
|
||||
icon: (color: string, width: number) => (
|
||||
<HashtagIcon width={width} color={color} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
type RatingProps = {
|
||||
currentQuestion: QuizQuestionRating;
|
||||
};
|
||||
|
||||
export const Rating = ({ currentQuestion }: RatingProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { quizId, preview } = useQuizData();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isTablet = useRootContainerSize() < 750;
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const form = RATING_FORM_BUTTONS.find(
|
||||
({ name }) => name === currentQuestion.content.form
|
||||
);
|
||||
|
||||
const sendRating = async (value: number | null) => {
|
||||
setIsSending(true);
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: String(value) + " из " + currentQuestion.content.steps,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, String(value), 0);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
marginTop: "20px",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "inline-block", width: "100%" }}>
|
||||
<RatingComponent
|
||||
disabled={isSending}
|
||||
value={Number(answer || 0)}
|
||||
onChange={(_, value) => sendRating(value)}
|
||||
sx={{
|
||||
height: "50px",
|
||||
opacity: "1!important",
|
||||
"& .MuiRating-root.Mui-disabled": { opacity: "1!important" },
|
||||
"& .MuiRating-icon": { mr: isMobile ? undefined : "15px" },
|
||||
}}
|
||||
max={currentQuestion.content.steps}
|
||||
icon={form?.icon(
|
||||
theme.palette.primary.main,
|
||||
isMobile ? 30 : isTablet ? 40 : 50
|
||||
)}
|
||||
emptyIcon={form?.icon(
|
||||
"#9A9AAF",
|
||||
isMobile ? 30 : isTablet ? 40 : 50
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: 2,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: "#9A9AAF" }}>
|
||||
{currentQuestion.content.ratingNegativeDescription}
|
||||
</Typography>
|
||||
<Typography sx={{ color: "#9A9AAF" }}>
|
||||
{currentQuestion.content.ratingPositiveDescription}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,99 +0,0 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { Select as SelectComponent } from "../tools//Select";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type { QuizQuestionSelect } from "../../../model/questionTypes/select";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useState } from "react";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
type SelectProps = {
|
||||
currentQuestion: QuizQuestionSelect;
|
||||
};
|
||||
|
||||
export const Select = ({ currentQuestion }: SelectProps) => {
|
||||
const theme = useTheme();
|
||||
const { quizId, settings, preview } = useQuizData();
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const deleteAnswer = useQuizViewStore(state => state.deleteAnswer);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<SelectComponent
|
||||
disabled={isSending}
|
||||
placeholder={currentQuestion.content.default}
|
||||
activeItemIndex={answer ? Number(answer) : -1}
|
||||
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
||||
colorMain={theme.palette.primary.main}
|
||||
sx={{
|
||||
"& .MuiSelect-select.MuiSelect-outlined": { zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
background: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: "transparent",
|
||||
},
|
||||
}}
|
||||
onChange={async (_, value) => {
|
||||
setIsSending(true);
|
||||
if (value < 0) {
|
||||
deleteAnswer(currentQuestion.id);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
return setIsSending(false);
|
||||
}
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: String(
|
||||
currentQuestion.content.variants[Number(value)].answer
|
||||
),
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, String(value), 0);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
102
lib/components/ViewPublicationPage/questions/Select/index.tsx
Normal file
102
lib/components/ViewPublicationPage/questions/Select/index.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { useState } from "react";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { Select as SelectComponent } from "@/components/ViewPublicationPage/tools/Select";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import type { QuizQuestionSelect } from "@model/questionTypes/select";
|
||||
|
||||
type SelectProps = {
|
||||
currentQuestion: QuizQuestionSelect;
|
||||
};
|
||||
|
||||
export const Select = ({ currentQuestion }: SelectProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { quizId, settings, preview } = useQuizData();
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const theme = useTheme();
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
const sendSelectedAnswer = async (value: number) => {
|
||||
setIsSending(true);
|
||||
|
||||
if (value < 0) {
|
||||
deleteAnswer(currentQuestion.id);
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
return setIsSending(false);
|
||||
}
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: String(currentQuestion.content.variants[Number(value)].answer),
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(currentQuestion.id, String(value), 0);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<SelectComponent
|
||||
disabled={isSending}
|
||||
placeholder={currentQuestion.content.default}
|
||||
activeItemIndex={answer ? Number(answer) : -1}
|
||||
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
||||
colorMain={theme.palette.primary.main}
|
||||
sx={{
|
||||
"& .MuiSelect-select.MuiSelect-outlined": { zIndex: 1 },
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
background: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: "transparent",
|
||||
},
|
||||
}}
|
||||
onChange={(_, value) => sendSelectedAnswer(value)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,286 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
TextField as MuiTextField,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { ChangeEvent, FC, useEffect, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import type { QuizQuestionText } from "../../../model/questionTypes/text";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
|
||||
type TextProps = {
|
||||
currentQuestion: QuizQuestionText;
|
||||
stepNumber: number | null;
|
||||
};
|
||||
|
||||
const Orientation = [
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
];
|
||||
|
||||
export const Text = ({ currentQuestion, stepNumber }: TextProps) => {
|
||||
const { settings, preview } = useQuizData();
|
||||
const spec = settings.cfg.spec;
|
||||
const { quizId } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
const inputHC = useDebouncedCallback(async (text) => {
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: text,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}, 400);
|
||||
useEffect(
|
||||
() => () => {
|
||||
inputHC.flush();
|
||||
},
|
||||
[inputHC]
|
||||
);
|
||||
switch (spec) {
|
||||
case true:
|
||||
return (
|
||||
<TextSpecial
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
stepNumber={stepNumber}
|
||||
/>
|
||||
);
|
||||
case undefined:
|
||||
return (
|
||||
<TextNormal
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<TextNormal
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface Props {
|
||||
currentQuestion: QuizQuestionText;
|
||||
answer: any;
|
||||
inputHC: (a: string) => void;
|
||||
stepNumber?: number | null;
|
||||
}
|
||||
|
||||
const TextNormal = ({ currentQuestion, answer, inputHC }: Props) => {
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const theme = useTheme();
|
||||
const { settings } = useQuizData();
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={async ({ target }) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiOutlinedInput-root": {
|
||||
background: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: "transparent",
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#9A9AAF"
|
||||
},
|
||||
"&:focus-visible": { borderColor: theme.palette.primary.main },
|
||||
}}
|
||||
/>
|
||||
{currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "400px",
|
||||
width: "100%",
|
||||
height: "300px",
|
||||
margin: "15px",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const TextSpecial = ({
|
||||
currentQuestion,
|
||||
answer,
|
||||
inputHC,
|
||||
stepNumber,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const isHorizontal = Orientation[Number(stepNumber) - 1].horizontal;
|
||||
const { settings } = useQuizData();
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: isMobile ? "column" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
{isHorizontal &&
|
||||
currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ margin: "30px", width: "50vw", maxHeight: "550px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{
|
||||
<TextField
|
||||
autoFocus={true}
|
||||
multiline
|
||||
maxRows={4}
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={async ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
}}
|
||||
inputProps={{
|
||||
maxLength: 400,
|
||||
background: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(154,154,175, 0.2)"
|
||||
: "transparent",
|
||||
}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
"& .MuiOutlinedInput-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: "#FFFFFF",
|
||||
},
|
||||
"&:focus-visible": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</Box>
|
||||
{!isHorizontal &&
|
||||
currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ margin: "15px", width: "40vw" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,91 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
|
||||
import { Answer, useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import type { ChangeEvent } from "react";
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
|
||||
interface TextNormalProps {
|
||||
currentQuestion: QuizQuestionText;
|
||||
answer?: Answer;
|
||||
inputHC: (text: string) => void;
|
||||
stepNumber?: number | null;
|
||||
}
|
||||
|
||||
export const TextNormal = ({
|
||||
currentQuestion,
|
||||
answer,
|
||||
inputHC,
|
||||
}: TextNormalProps) => {
|
||||
const { settings } = useQuizData();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const theme = useTheme();
|
||||
|
||||
const onInputChange = async ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={onInputChange}
|
||||
sx={{
|
||||
"& .MuiOutlinedInput-root": {
|
||||
background: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: "transparent",
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": { borderColor: "#9A9AAF" },
|
||||
"&:focus-visible": { borderColor: theme.palette.primary.main },
|
||||
}}
|
||||
/>
|
||||
{currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "400px",
|
||||
width: "100%",
|
||||
height: "300px",
|
||||
margin: "15px",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,152 @@
|
||||
import {
|
||||
Box,
|
||||
TextField as MuiTextField,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import { Answer, useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import type { ChangeEvent, FC } from "react";
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
|
||||
const ORIENTATION = [
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: false },
|
||||
{ horizontal: false },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
{ horizontal: true },
|
||||
];
|
||||
|
||||
interface TextSpecialProps {
|
||||
currentQuestion: QuizQuestionText;
|
||||
answer?: Answer;
|
||||
inputHC: (text: string) => void;
|
||||
stepNumber?: number | null;
|
||||
}
|
||||
|
||||
export const TextSpecial = ({
|
||||
currentQuestion,
|
||||
answer,
|
||||
inputHC,
|
||||
stepNumber,
|
||||
}: TextSpecialProps) => {
|
||||
const { settings } = useQuizData();
|
||||
const { updateAnswer } = useQuizViewStore((state) => state);
|
||||
const isHorizontal = ORIENTATION[Number(stepNumber) - 1].horizontal;
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
|
||||
const onInputChange = async ({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
updateAnswer(currentQuestion.id, target.value, 0);
|
||||
inputHC(target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: isMobile ? "column" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
marginTop: "20px",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
{isHorizontal &&
|
||||
currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ margin: "30px", width: "50vw", maxHeight: "550px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{
|
||||
<TextField
|
||||
autoFocus={true}
|
||||
multiline
|
||||
maxRows={4}
|
||||
placeholder={currentQuestion.content.placeholder}
|
||||
value={answer || ""}
|
||||
onChange={onInputChange}
|
||||
inputProps={{
|
||||
maxLength: 400,
|
||||
background: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#F2F3F7"
|
||||
: "rgba(154,154,175, 0.2)"
|
||||
: "transparent",
|
||||
}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
"& .MuiOutlinedInput-root": {
|
||||
backgroundColor: settings.cfg.design
|
||||
? "rgba(154,154,175, 0.2)"
|
||||
: "#FFFFFF",
|
||||
},
|
||||
"&:focus-visible": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</Box>
|
||||
{!isHorizontal &&
|
||||
currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ margin: "15px", width: "40vw" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
77
lib/components/ViewPublicationPage/questions/Text/index.tsx
Normal file
77
lib/components/ViewPublicationPage/questions/Text/index.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { TextSpecial } from "./TextSpecial";
|
||||
import { TextNormal } from "./TextNormal";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import type { QuizQuestionText } from "@model/questionTypes/text";
|
||||
|
||||
type TextProps = {
|
||||
currentQuestion: QuizQuestionText;
|
||||
stepNumber: number | null;
|
||||
};
|
||||
|
||||
export const Text = ({ currentQuestion, stepNumber }: TextProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const { settings, preview } = useQuizData();
|
||||
const { quizId } = useQuizData();
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
const inputHC = useDebouncedCallback(async (text) => {
|
||||
setIsSending(true);
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: text,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}, 400);
|
||||
|
||||
useEffect(() => {
|
||||
inputHC.flush();
|
||||
}, [inputHC]);
|
||||
|
||||
switch (settings.cfg.spec) {
|
||||
case true:
|
||||
return (
|
||||
<TextSpecial
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
stepNumber={stepNumber}
|
||||
/>
|
||||
);
|
||||
|
||||
case undefined:
|
||||
return (
|
||||
<TextNormal
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<TextNormal
|
||||
currentQuestion={currentQuestion}
|
||||
answer={answer}
|
||||
inputHC={inputHC}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
@ -1,307 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
TextField as MuiTextField,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TextFieldProps,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
useQuizViewStore,
|
||||
} from "@stores/quizView";
|
||||
|
||||
import { CheckboxIcon } from "@icons/Checkbox";
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
||||
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
|
||||
import moment from "moment";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||||
|
||||
type VariantProps = {
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
};
|
||||
|
||||
export const Variant = ({ currentQuestion }: VariantProps) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const ownVariants = useQuizViewStore(state => state.ownVariants);
|
||||
const updateOwnVariant = useQuizViewStore(state => state.updateOwnVariant);
|
||||
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const ownVariant = ownVariants.find(
|
||||
(variant) => variant.id === currentQuestion.id
|
||||
);
|
||||
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
|
||||
const Group = currentQuestion.content.multi ? FormGroup : RadioGroup;
|
||||
|
||||
useEffect(() => {
|
||||
if (!ownVariant) {
|
||||
updateOwnVariant(currentQuestion.id, "");
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (moment.isMoment(answer))
|
||||
throw new Error("Answer is Moment in Variant question");
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
name={currentQuestion.id.toString()}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
marginTop: "20px",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<VariantItem
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
answer={answer}
|
||||
index={index}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
))}
|
||||
{currentQuestion.content.own && ownVariant && (
|
||||
<VariantItem
|
||||
own
|
||||
currentQuestion={currentQuestion}
|
||||
variant={ownVariant.variant}
|
||||
answer={answer}
|
||||
index={currentQuestion.content.variants.length + 2}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Group>
|
||||
{currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const VariantItem = ({
|
||||
currentQuestion,
|
||||
variant,
|
||||
answer,
|
||||
index,
|
||||
own = false,
|
||||
isSending,
|
||||
setIsSending,
|
||||
}: {
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
variant: QuestionVariant;
|
||||
answer: string | string[] | undefined;
|
||||
index: number;
|
||||
own?: boolean;
|
||||
isSending: boolean;
|
||||
setIsSending: (a: boolean) => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const deleteAnswer = useQuizViewStore(state => state.deleteAnswer);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
margin: "0",
|
||||
borderRadius: "12px",
|
||||
color: theme.palette.text.primary,
|
||||
padding: "15px",
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||
backgroundColor: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
display: "flex",
|
||||
maxWidth: "685px",
|
||||
maxHeight: "85px",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
"&.MuiFormControl-root": {
|
||||
width: "100%",
|
||||
},
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
"& .MuiFormControlLabel-label.Mui-disabled": {
|
||||
color: theme.palette.text.primary,
|
||||
}
|
||||
}}
|
||||
value={index}
|
||||
labelPlacement="start"
|
||||
control={
|
||||
currentQuestion.content.multi ? (
|
||||
<Checkbox
|
||||
checked={!!answer?.includes(variant.id)}
|
||||
checkedIcon={
|
||||
<CheckboxIcon checked color={theme.palette.primary.main} />
|
||||
}
|
||||
icon={<CheckboxIcon />}
|
||||
/>
|
||||
) : (
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
label={own ? <TextField label="Другое..." /> : variant.answer}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
if (isSending) return;
|
||||
|
||||
setIsSending(true);
|
||||
const variantId = currentQuestion.content.variants[index].id;
|
||||
console.log(answer);
|
||||
|
||||
if (currentQuestion.content.multi) {
|
||||
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentQuestion.content.variants[index].answer,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
variantId,
|
||||
answer === variantId
|
||||
? 0
|
||||
: currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === variantId) {
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,186 @@
|
||||
import {
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
TextField as MuiTextField,
|
||||
Radio,
|
||||
TextFieldProps,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import { CheckboxIcon } from "@icons/Checkbox";
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import type { FC, MouseEvent } from "react";
|
||||
import type { QuestionVariant } from "@model/questionTypes/shared";
|
||||
import type { QuizQuestionVariant } from "@model/questionTypes/variant";
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||||
|
||||
export const VariantItem = ({
|
||||
currentQuestion,
|
||||
variant,
|
||||
answer,
|
||||
index,
|
||||
own = false,
|
||||
isSending,
|
||||
setIsSending,
|
||||
}: {
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
variant: QuestionVariant;
|
||||
answer: string | string[] | undefined;
|
||||
index: number;
|
||||
own?: boolean;
|
||||
isSending: boolean;
|
||||
setIsSending: (a: boolean) => void;
|
||||
}) => {
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const theme = useTheme();
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
|
||||
const sendVariant = async (event: MouseEvent<HTMLLabelElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (isSending) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSending(true);
|
||||
|
||||
const variantId = currentQuestion.content.variants[index].id;
|
||||
|
||||
if (currentQuestion.content.multi) {
|
||||
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentAnswer.includes(variantId)
|
||||
? currentAnswer?.filter((item) => item !== variantId)
|
||||
: [...currentAnswer, variantId],
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: currentQuestion.content.variants[index].answer,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
variantId,
|
||||
answer === variantId
|
||||
? 0
|
||||
: currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === variantId) {
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
margin: "0",
|
||||
borderRadius: "12px",
|
||||
color: theme.palette.text.primary,
|
||||
padding: "15px",
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||
backgroundColor: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
display: "flex",
|
||||
maxWidth: "685px",
|
||||
maxHeight: "85px",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
"&.MuiFormControl-root": { width: "100%" },
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": { width: "4px" },
|
||||
"&::-webkit-scrollbar-thumb": { backgroundColor: "#b8babf" },
|
||||
},
|
||||
"& .MuiFormControlLabel-label.Mui-disabled": {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
value={index}
|
||||
labelPlacement="start"
|
||||
control={
|
||||
currentQuestion.content.multi ? (
|
||||
<Checkbox
|
||||
checked={!!answer?.includes(variant.id)}
|
||||
checkedIcon={
|
||||
<CheckboxIcon checked color={theme.palette.primary.main} />
|
||||
}
|
||||
icon={<CheckboxIcon />}
|
||||
/>
|
||||
) : (
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
label={own ? <TextField label="Другое..." /> : variant.answer}
|
||||
onClick={sendVariant}
|
||||
/>
|
||||
);
|
||||
};
|
124
lib/components/ViewPublicationPage/questions/Variant/index.tsx
Normal file
124
lib/components/ViewPublicationPage/questions/Variant/index.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
FormGroup,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import { VariantItem } from "./VariantItem";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import type { QuizQuestionVariant } from "@model/questionTypes/variant";
|
||||
import moment from "moment";
|
||||
|
||||
type VariantProps = {
|
||||
currentQuestion: QuizQuestionVariant;
|
||||
};
|
||||
|
||||
export const Variant = ({ currentQuestion }: VariantProps) => {
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
const { ownVariants, updateOwnVariant } = useQuizViewStore((state) => state);
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const ownVariant = ownVariants.find(
|
||||
(variant) => variant.id === currentQuestion.id
|
||||
);
|
||||
|
||||
const Group = currentQuestion.content.multi ? FormGroup : RadioGroup;
|
||||
|
||||
useEffect(() => {
|
||||
if (!ownVariant) {
|
||||
updateOwnVariant(currentQuestion.id, "");
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
name={currentQuestion.id.toString()}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
marginTop: "20px",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<VariantItem
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
answer={answer}
|
||||
index={index}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
))}
|
||||
{currentQuestion.content.own && ownVariant && (
|
||||
<VariantItem
|
||||
own
|
||||
currentQuestion={currentQuestion}
|
||||
variant={ownVariant.variant}
|
||||
answer={answer}
|
||||
index={currentQuestion.content.variants.length + 2}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Group>
|
||||
{currentQuestion.content.back &&
|
||||
currentQuestion.content.back !== " " && (
|
||||
<Box sx={{ maxWidth: "400px", width: "100%", height: "300px" }}>
|
||||
<img
|
||||
key={currentQuestion.id}
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,226 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import BlankImage from "@icons/BlankImage";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
|
||||
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
||||
import { useState } from "react";
|
||||
|
||||
type VarimgProps = {
|
||||
currentQuestion: QuizQuestionVarImg;
|
||||
};
|
||||
|
||||
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const answers = useQuizViewStore(state => state.answers);
|
||||
const deleteAnswer = useQuizViewStore(state => state.deleteAnswer);
|
||||
const updateAnswer = useQuizViewStore(state => state.updateAnswer);
|
||||
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const variant = currentQuestion.content.variants.find(
|
||||
({ id }) => answer === id
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
gap: "30px",
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
gap: "20px",
|
||||
"&:focus": { color: theme.palette.text.primary },
|
||||
"&:active": { color: theme.palette.text.primary }
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
marginBottom: "15px",
|
||||
borderRadius: "12px",
|
||||
padding: "20px",
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id
|
||||
? theme.palette.primary.main
|
||||
: "#9A9AAF",
|
||||
display: "flex",
|
||||
margin: 0,
|
||||
justifyContent: "space-between",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "4px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: "#b8babf",
|
||||
},
|
||||
},
|
||||
"& .MuiFormControlLabel-label.Mui-disabled": {
|
||||
color: theme.palette.text.primary,
|
||||
}
|
||||
}}
|
||||
labelPlacement="start"
|
||||
value={index}
|
||||
onClick={async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
}}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={
|
||||
<RadioCheck color={theme.palette.primary.main} />
|
||||
}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
label={variant.answer}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
{/* {(variant?.extendedText || currentQuestion.content.back) && ( */}
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "450px",
|
||||
width: "100%",
|
||||
height: "450px",
|
||||
border: "1px solid #9A9AAF",
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#9A9AAF30",
|
||||
color: theme.palette.text.primary,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{answer ? (
|
||||
variant?.extendedText ? (
|
||||
<img
|
||||
src={variant?.extendedText}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<BlankImage />
|
||||
)
|
||||
) : currentQuestion.content.back !== " " &&
|
||||
currentQuestion.content.back !== null &&
|
||||
currentQuestion.content.back.length > 0 ? (
|
||||
<img
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : currentQuestion.content.replText !== " " &&
|
||||
currentQuestion.content.replText.length > 0 ? (
|
||||
currentQuestion.content.replText
|
||||
) : variant?.extendedText || isMobile ? (
|
||||
"Выберите вариант ответа ниже"
|
||||
) : (
|
||||
"Выберите вариант ответа слева"
|
||||
)}
|
||||
</Box>
|
||||
{/* )} */}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,129 @@
|
||||
import { FormControlLabel, Radio, useTheme } from "@mui/material";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { sendAnswer } from "@api/quizRelase";
|
||||
import { useQuizData } from "@contexts/QuizDataContext";
|
||||
|
||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
import type { MouseEvent } from "react";
|
||||
import type { QuestionVariant } from "@/model/questionTypes/shared";
|
||||
import type { QuizQuestionVarImg } from "@model/questionTypes/varimg";
|
||||
|
||||
type VarimgVariantProps = {
|
||||
currentQuestion: QuizQuestionVarImg;
|
||||
variant: QuestionVariant;
|
||||
index: number;
|
||||
isSending: boolean;
|
||||
setIsSending: (isSending: boolean) => void;
|
||||
};
|
||||
|
||||
export const VarimgVariant = ({
|
||||
currentQuestion,
|
||||
variant,
|
||||
index,
|
||||
isSending,
|
||||
setIsSending,
|
||||
}: VarimgVariantProps) => {
|
||||
const { settings, quizId, preview } = useQuizData();
|
||||
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
|
||||
const sendVariant = async (event: MouseEvent<HTMLLabelElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
setIsSending(true);
|
||||
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: `${currentQuestion.content.variants[index].answer} <img style="width:100%; max-width:250px; max-height:250px" src="${currentQuestion.content.variants[index].extendedText}"/>`,
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
|
||||
updateAnswer(
|
||||
currentQuestion.id,
|
||||
currentQuestion.content.variants[index].id,
|
||||
currentQuestion.content.variants[index].points || 0
|
||||
);
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
if (answer === currentQuestion.content.variants[index].id) {
|
||||
try {
|
||||
await sendAnswer({
|
||||
questionId: currentQuestion.id,
|
||||
body: "",
|
||||
qid: quizId,
|
||||
preview,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackbar("ответ не был засчитан");
|
||||
}
|
||||
|
||||
deleteAnswer(currentQuestion.id);
|
||||
}
|
||||
|
||||
setIsSending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={variant.id}
|
||||
disabled={isSending}
|
||||
sx={{
|
||||
marginBottom: "15px",
|
||||
borderRadius: "12px",
|
||||
padding: "20px",
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: settings.cfg.design
|
||||
? quizThemes[settings.cfg.theme].isLight
|
||||
? "#FFFFFF"
|
||||
: "rgba(255,255,255, 0.3)"
|
||||
: quizThemes[settings.cfg.theme].isLight
|
||||
? "white"
|
||||
: theme.palette.background.default,
|
||||
border: `1px solid`,
|
||||
borderColor:
|
||||
answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||
display: "flex",
|
||||
margin: 0,
|
||||
justifyContent: "space-between",
|
||||
"&:hover": { borderColor: theme.palette.primary.main },
|
||||
"& .MuiFormControlLabel-label": {
|
||||
wordBreak: "break-word",
|
||||
height: variant.answer.length <= 60 ? undefined : "60px",
|
||||
overflow: "auto",
|
||||
lineHeight: "normal",
|
||||
"&::-webkit-scrollbar": { width: "4px" },
|
||||
"&::-webkit-scrollbar-thumb": { backgroundColor: "#b8babf" },
|
||||
},
|
||||
"& .MuiFormControlLabel-label.Mui-disabled": {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
labelPlacement="start"
|
||||
value={index}
|
||||
onClick={sendVariant}
|
||||
label={variant.answer}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
130
lib/components/ViewPublicationPage/questions/Varimg/index.tsx
Normal file
130
lib/components/ViewPublicationPage/questions/Varimg/index.tsx
Normal file
@ -0,0 +1,130 @@
|
||||
import { useState } from "react";
|
||||
import { Box, RadioGroup, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { VarimgVariant } from "./VarimgVariant";
|
||||
|
||||
import { useQuizViewStore } from "@stores/quizView";
|
||||
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
|
||||
|
||||
import BlankImage from "@icons/BlankImage";
|
||||
|
||||
import type { QuizQuestionVarImg } from "@model/questionTypes/varimg";
|
||||
|
||||
type VarimgProps = {
|
||||
currentQuestion: QuizQuestionVarImg;
|
||||
};
|
||||
|
||||
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
const [isSending, setIsSending] = useState<boolean>(false);
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
|
||||
const theme = useTheme();
|
||||
const isMobile = useRootContainerSize() < 650;
|
||||
|
||||
const { answer } =
|
||||
answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const variant = currentQuestion.content.variants.find(
|
||||
({ id }) => answer === id
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ wordBreak: "break-word" }}
|
||||
>
|
||||
{currentQuestion.title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
marginTop: "20px",
|
||||
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||
gap: "30px",
|
||||
alignItems: isMobile ? "center" : undefined,
|
||||
}}
|
||||
>
|
||||
<RadioGroup
|
||||
name={currentQuestion.id}
|
||||
value={currentQuestion.content.variants.findIndex(
|
||||
({ id }) => answer === id
|
||||
)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexBasis: "100%",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
gap: "20px",
|
||||
"&:focus": { color: theme.palette.text.primary },
|
||||
"&:active": { color: theme.palette.text.primary },
|
||||
}}
|
||||
>
|
||||
{currentQuestion.content.variants.map((variant, index) => (
|
||||
<VarimgVariant
|
||||
key={variant.id}
|
||||
currentQuestion={currentQuestion}
|
||||
variant={variant}
|
||||
isSending={isSending}
|
||||
setIsSending={setIsSending}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "450px",
|
||||
width: "100%",
|
||||
height: "450px",
|
||||
border: "1px solid #9A9AAF",
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#9A9AAF30",
|
||||
color: theme.palette.text.primary,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{answer ? (
|
||||
variant?.extendedText ? (
|
||||
<img
|
||||
src={variant?.extendedText}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<BlankImage />
|
||||
)
|
||||
) : currentQuestion.content.back !== " " &&
|
||||
currentQuestion.content.back !== null &&
|
||||
currentQuestion.content.back.length > 0 ? (
|
||||
<img
|
||||
src={currentQuestion.content.back}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : currentQuestion.content.replText !== " " &&
|
||||
currentQuestion.content.replText.length > 0 ? (
|
||||
currentQuestion.content.replText
|
||||
) : variant?.extendedText || isMobile ? (
|
||||
"Выберите вариант ответа ниже"
|
||||
) : (
|
||||
"Выберите вариант ответа слева"
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -109,6 +109,7 @@ export interface QuizConfig {
|
||||
};
|
||||
meta: string;
|
||||
yandexMetricNumber: number | undefined;
|
||||
vkMetricNumber: number | undefined;
|
||||
}
|
||||
|
||||
export type FormContactFieldName =
|
||||
@ -118,7 +119,7 @@ export type FormContactFieldName =
|
||||
| "text"
|
||||
| "address";
|
||||
|
||||
type FormContactFieldData = {
|
||||
export type FormContactFieldData = {
|
||||
text: string;
|
||||
innerText: string;
|
||||
key: string;
|
||||
|
@ -6,9 +6,11 @@ import { createContext, useContext } from "react";
|
||||
import { createStore, useStore } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
export type Answer = string | string[] | Moment;
|
||||
|
||||
type QuestionAnswer = {
|
||||
questionId: string;
|
||||
answer: string | string[] | Moment;
|
||||
answer: Answer
|
||||
};
|
||||
|
||||
type OwnVariant = {
|
||||
|
@ -2,13 +2,14 @@ import { FormControl, TextField as MuiTextField, SxProps, Theme, useTheme } from
|
||||
|
||||
import type { InputProps, TextFieldProps } from "@mui/material";
|
||||
import type { ChangeEvent, FC, FocusEvent, KeyboardEvent } from "react";
|
||||
import {Answer} from "@stores/quizView.ts";
|
||||
|
||||
|
||||
const TextField = MuiTextField as unknown as FC<TextFieldProps>; // temporary fix ts(2590)
|
||||
|
||||
interface CustomTextFieldProps {
|
||||
placeholder: string;
|
||||
value?: string;
|
||||
value?: Answer;
|
||||
error?: string;
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
||||
|
@ -6,10 +6,11 @@ let domain = "https://hbpn.link";
|
||||
const currentDomain = location.hostname;
|
||||
|
||||
if (
|
||||
currentDomain === "s.hbpn.link" ||
|
||||
currentDomain === "s.hbpn.link"
|
||||
//Исключение - туризм. Он на стейджинговом квизе и на чужом для публикации домене
|
||||
currentDomain === "tourism.pena.digital" ||
|
||||
currentDomain.includes("localhost")
|
||||
|| currentDomain === "tourism.pena.digital"
|
||||
|| currentDomain.includes("localhost")
|
||||
|| currentDomain.includes("127.0.0.1")
|
||||
) domain = "https://s.hbpn.link";
|
||||
|
||||
export { domain };
|
2
lib/utils/emailRegexp.tsx
Normal file
2
lib/utils/emailRegexp.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export const EMAIL_REGEXP =
|
||||
/^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu;
|
BIN
lib/utils/fonts/Lato/Lato-Black.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Black.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-BlackItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-BlackItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-Bold.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Bold.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-BoldItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-BoldItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-ExtraBold.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-ExtraBold.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-ExtraBoldItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-ExtraBoldItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-ExtraLight.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-ExtraLight.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-ExtraLightItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-ExtraLightItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-Italic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Italic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-Light.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Light.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-LightItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-LightItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-Medium.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Medium.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-MediumItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-MediumItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-Regular.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Regular.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-SemiBold.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-SemiBold.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-SemiBoldItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-SemiBoldItalic.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-Thin.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-Thin.ttf
Executable file
Binary file not shown.
BIN
lib/utils/fonts/Lato/Lato-ThinItalic.ttf
Executable file
BIN
lib/utils/fonts/Lato/Lato-ThinItalic.ttf
Executable file
Binary file not shown.
39
lib/utils/fonts/importsFonts.ts
Normal file
39
lib/utils/fonts/importsFonts.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import BlackItalic from "./Lato/Lato-BlackItalic.ttf"
|
||||
import ExtraBold from "./Lato/Lato-ExtraBold.ttf"
|
||||
import Light from "./Lato/Lato-Light.ttf"
|
||||
import SemiBold from "./Lato/Lato-SemiBold.ttf"
|
||||
import Black from "./Lato/Lato-Black.ttf"
|
||||
import ExtraLightItalic from "./Lato/Lato-ExtraLightItalic.ttf"
|
||||
import MediumItalic from "./Lato/Lato-MediumItalic.ttf"
|
||||
import ThinItalic from "./Lato/Lato-ThinItalic.ttf"
|
||||
import BoldItalic from "./Lato/Lato-BoldItalic.ttf"
|
||||
import ExtraLight from "./Lato/Lato-ExtraLight.ttf"
|
||||
import Medium from "./Lato/Lato-Medium.ttf"
|
||||
import Thin from "./Lato/Lato-Thin.ttf"
|
||||
import Bold from "./Lato/Lato-Bold.ttf"
|
||||
import Italic from "./Lato/Lato-Italic.ttf"
|
||||
import Regular from "./Lato/Lato-Regular.ttf"
|
||||
import ExtraBoldItalic from "./Lato/Lato-ExtraBoldItalic.ttf"
|
||||
import LightItalic from "./Lato/Lato-LightItalic.ttf"
|
||||
import SemiBoldItalic from "./Lato/Lato-SemiBoldItalic.ttf"
|
||||
|
||||
export const Latos = [
|
||||
{name: BlackItalic, format: "ttf"},
|
||||
{name: ExtraBold, format: "ttf"},
|
||||
{name: Light, format: "ttf"},
|
||||
{name: SemiBold, format: "ttf"},
|
||||
{name: Black, format: "ttf"},
|
||||
{name: ExtraLightItalic, format: "ttf"},
|
||||
{name: MediumItalic, format: "ttf"},
|
||||
{name: ThinItalic, format: "ttf"},
|
||||
{name: BoldItalic, format: "ttf"},
|
||||
{name: ExtraLight, format: "ttf"},
|
||||
{name: Medium, format: "ttf"},
|
||||
{name: Thin, format: "ttf"},
|
||||
{name: Bold, format: "ttf"},
|
||||
{name: Italic, format: "ttf"},
|
||||
{name: Regular, format: "ttf"},
|
||||
{name: ExtraBoldItalic, format: "ttf"},
|
||||
{name: LightItalic, format: "ttf"},
|
||||
{name: SemiBoldItalic, format: "ttf"}
|
||||
]
|
@ -17,6 +17,7 @@ export function useQuestionFlowControl() {
|
||||
const setCurrentQuizStep = useQuizViewStore(state => state.setCurrentQuizStep);
|
||||
|
||||
const currentQuestion = sortedQuestions.find(question => question.id === currentQuestionId) ?? sortedQuestions[0];
|
||||
console.log("currentQuestion", currentQuestion)
|
||||
|
||||
const linearQuestionIndex = currentQuestion && sortedQuestions.every(({ content }) => content.rule.parentId !== "root") // null when branching enabled
|
||||
? sortedQuestions.indexOf(currentQuestion)
|
||||
@ -149,6 +150,26 @@ export function useQuestionFlowControl() {
|
||||
if (!nextQuestion) throw new Error("Next question not found");
|
||||
|
||||
if (nextQuestion.type === "result") return showResult();
|
||||
//засчитываем переход с вопроса дальше
|
||||
|
||||
//@ts-ignore
|
||||
let YM = window?.ym;
|
||||
//@ts-ignore
|
||||
let VP = window?._tmr;
|
||||
if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) {
|
||||
YM(
|
||||
settings.cfg.yandexMetricNumber,
|
||||
"reachGoal",
|
||||
`penaquiz-step{${currentQuestion.id}}`
|
||||
);
|
||||
};
|
||||
if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) {
|
||||
VP.push({
|
||||
type: "reachGoal",
|
||||
id: settings.cfg.vkMetricNumber,
|
||||
goal: `penaquiz-step{${currentQuestion.id}}`
|
||||
});
|
||||
};
|
||||
|
||||
setCurrentQuestionId(nextQuestion.id);
|
||||
}, [nextQuestion, showResult]);
|
||||
|
30
lib/utils/hooks/useVKMetrics.ts
Normal file
30
lib/utils/hooks/useVKMetrics.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const useVKMetrics = (vkMetricNumber: number | undefined) => {
|
||||
useEffect(() => {
|
||||
if (
|
||||
vkMetricNumber &&
|
||||
typeof vkMetricNumber === "number" &&
|
||||
!Number.isNaN(vkMetricNumber)
|
||||
) {
|
||||
const script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.innerHTML = `
|
||||
var _tmr = window._tmr || (window._tmr = []);
|
||||
_tmr.push({id: "${vkMetricNumber}", type: "pageView", start: (new Date()).getTime()});
|
||||
(function (d, w, id) {
|
||||
if (d.getElementById(id)) return;
|
||||
var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id;
|
||||
ts.src = "https://top-fwz1.mail.ru/js/code.js";
|
||||
var f = function () {var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s);};
|
||||
if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); }
|
||||
})(document, window, "tmr-code");
|
||||
`;
|
||||
document.body.appendChild(script);
|
||||
|
||||
const noscript = document.createElement("noscript");
|
||||
noscript.innerHTML = `<div><img src="https://top-fwz1.mail.ru/counter?id=${vkMetricNumber};js=na" style="position:absolute;left:-9999px;" alt="Top.Mail.Ru" /></div>`;
|
||||
document.body.appendChild(noscript);
|
||||
}
|
||||
}, [vkMetricNumber]);
|
||||
};
|
61
lib/utils/hooks/useVkMetricGoals.ts
Normal file
61
lib/utils/hooks/useVkMetricGoals.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type VkMetric = {
|
||||
type: "reachGoal";
|
||||
id: number;
|
||||
goal: string;
|
||||
};
|
||||
|
||||
type ExtendedWindow = Window & { _tmp?: VkMetric[] };
|
||||
|
||||
type Messenger =
|
||||
| "telegram"
|
||||
| "viber"
|
||||
| "whatsapp"
|
||||
| "vkontakte"
|
||||
| "messenger"
|
||||
| "skype"
|
||||
| "instagram";
|
||||
|
||||
const sendMetrics = (vkPixelId: number | undefined, goal: string) => {
|
||||
if (vkPixelId) {
|
||||
(window as ExtendedWindow)._tmp?.push({
|
||||
type: "reachGoal",
|
||||
id: vkPixelId,
|
||||
goal,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const useVkMetricGoals = (vkPixelId: number | undefined) => {
|
||||
const [vkId, setVkId] = useState<number | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (vkPixelId) {
|
||||
setVkId(vkPixelId);
|
||||
}
|
||||
}, [vkPixelId]);
|
||||
|
||||
return {
|
||||
// Посетитель открыл квиз
|
||||
quizOpened: () => sendMetrics(vkId, "penaquiz-start"),
|
||||
// Посетитель нажал на кнопку стартовой страницы
|
||||
firstPageOpened: () => sendMetrics(vkId, "penaquiz-startquiz"),
|
||||
// Посетитель кликнул по номеру телефона на стартовой странице
|
||||
phoneNumberOpened: () => sendMetrics(vkId, "penaquiz-phone"),
|
||||
// Посетитель кликнул по email на стартовой странице
|
||||
emailOpened: () => sendMetrics(vkId, "penaquiz-email"),
|
||||
// Посетитель увидел определенный результат (id - айдишник вопроса с типом result)
|
||||
resultShown: (resultId: string) => sendMetrics(vkId, `penaquiz-result-${resultId}`),
|
||||
// Посетитель дошёл до формы контактов
|
||||
contactsFormOpened: () => sendMetrics(vkId, "penaquiz-form"),
|
||||
// Посетитель заполнил форму контактов
|
||||
contactsFormFilled: () => sendMetrics(vkId, "penaquiz-contacts"),
|
||||
// Посетитель отправил заявку с мессенджером
|
||||
messengerRequestSended: (messenger: Messenger) =>
|
||||
sendMetrics(vkId, `marquiz-messengers-${messenger}`),
|
||||
// Посетитель прошёл вопрос
|
||||
questionPassed: (questionId: string) =>
|
||||
sendMetrics(vkId, `marquiz-step${questionId}`),
|
||||
};
|
||||
};
|
@ -1,29 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const useYandexMetrica = (yandexMetricNumber: number | undefined) => {
|
||||
useEffect(() => {
|
||||
if (yandexMetricNumber) {
|
||||
const script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.innerHTML = `
|
||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
|
||||
ym(${yandexMetricNumber}, "init", {
|
||||
clickmap:true,
|
||||
trackLinks:true,
|
||||
accurateTrackBounce:true,
|
||||
webvisor:true
|
||||
});
|
||||
`;
|
||||
document.body.appendChild(script);
|
||||
|
||||
const noscript = document.createElement("noscript");
|
||||
noscript.innerHTML = `<div><img src="https://mc.yandex.ru/watch/${yandexMetricNumber}" style="position:absolute; left:-9999px;" alt="" /></div>`;
|
||||
document.body.appendChild(noscript);
|
||||
}
|
||||
}, [yandexMetricNumber]);
|
||||
};
|
34
lib/utils/hooks/useYandexMetrics.ts
Normal file
34
lib/utils/hooks/useYandexMetrics.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const useYandexMetrics = (yandexMetricNumber: number | undefined) => {
|
||||
useEffect(() => {
|
||||
if (
|
||||
yandexMetricNumber &&
|
||||
typeof yandexMetricNumber === "number" &&
|
||||
!Number.isNaN(yandexMetricNumber)
|
||||
) {
|
||||
const script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.innerHTML = `
|
||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
|
||||
ym(${yandexMetricNumber}, "init", {
|
||||
clickmap:true,
|
||||
trackLinks:true,
|
||||
accurateTrackBounce:true,
|
||||
webvisor:true
|
||||
});
|
||||
`;
|
||||
|
||||
document.body.appendChild(script);
|
||||
|
||||
const noscript = document.createElement("noscript");
|
||||
noscript.innerHTML = `<div><img src="https://mc.yandex.ru/watch/${yandexMetricNumber}" style="position:absolute; left:-9999px;" alt="" /></div>`;
|
||||
document.body.appendChild(noscript);
|
||||
}
|
||||
}, [yandexMetricNumber]);
|
||||
};
|
85
lib/utils/phoneMasksByCountry.tsx
Normal file
85
lib/utils/phoneMasksByCountry.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
|
||||
type PhoneMasksByCountry = {
|
||||
[countryCode: string]: [string, string];
|
||||
};
|
||||
|
||||
export const phoneMasksByCountry:PhoneMasksByCountry = {
|
||||
// СНГ
|
||||
'RU': ['Russia +7', '+{7} (000) 000-00-00'], // Россия
|
||||
'UA': ['Ukraine +380', '+{380} (00) 000-00-00'], // Украина
|
||||
'BY': ['Belarus +375', '+{375} (00) 000-00-00'], // Беларусь
|
||||
'KZ': ['Kazakhstan +7', '+{7} (000) 000-00-00'], // Казахстан
|
||||
'UZ': ['Uzbekistan +998', '+{998} (00) 000-00-00'], // Узбекистан
|
||||
'TJ': ['Tajikistan +992', '+{992} (00) 000-00-00'], // Таджикистан
|
||||
'KG': ['Kyrgyzstan +996', '+{996} (000) 00-00-00'], // Кыргызстан
|
||||
'TM': ['Turkmenistan +993', '+{993} (00) 00-00-00'], // Туркменистан
|
||||
'AZ': ['Azerbaijan +994', '+{994} (00) 000-00-00'], // Азербайджан
|
||||
'AM': ['Armenia +374', '+{374} (00) 000-000'], // Армения
|
||||
'GE': ['Georgia +995', '+{995} (000) 00-00-00'], // Грузия
|
||||
|
||||
// Европа
|
||||
'DE': ['Germany +49', '+{49} 0000 0000000'], // Германия
|
||||
'FR': ['France +33', '+{33} 0 00 00 00 00'], // Франция
|
||||
'IT': ['Italy +39', '+{39} 000 000 0000'], // Италия
|
||||
'ES': ['Spain +34', '+{34} 000 00 00 00'], // Испания
|
||||
'GB': ['United Kingdom +44', '+{44} 0000 000000'], // Великобритания
|
||||
'PL': ['Poland +48', '+{48} 000 000 000'], // Польша
|
||||
'NL': ['Netherlands +31', '+{31} 00 000 0000'], // Нидерланды
|
||||
'BE': ['Belgium +32', '+{32} 00 00 00 00'], // Бельгия
|
||||
'CH': ['Switzerland +41', '+{41} 00 000 00 00'], // Швейцария
|
||||
'AT': ['Austria +43', '+{43} 000 000 0000'], // Австрия
|
||||
'DK': ['Denmark +45', '+{45} 00 00 00 00'], // Дания
|
||||
'SE': ['Sweden +46', '+{46} 00 000 00 00'], // Швеция
|
||||
'NO': ['Norway +47', '+{47} 000 00 000'], // Норвегия
|
||||
'FI': ['Finland +358', '+{358} 00 000 0000'], // Финляндия
|
||||
'CZ': ['Czech Republic +420', '+{420} 000 000 000'], // Чехия
|
||||
'SK': ['Slovakia +421', '+{421} 000 000 000'], // Словакия
|
||||
'HU': ['Hungary +36', '+{36} 00 000 0000'], // Венгрия
|
||||
'RO': ['Romania +40', '+{40} 000 000 000'], // Румыния
|
||||
'BG': ['Bulgaria +359', '+{359} 00 000 000'], // Болгария
|
||||
'GR': ['Greece +30', '+{30} 000 000 0000'], // Греция
|
||||
'PT': ['Portugal +351', '+{351} 000 000 000'], // Португалия
|
||||
'IE': ['Ireland +353', '+{353} 00 000 0000'], // Ирландия
|
||||
|
||||
// Азия
|
||||
'CN': ['China +86', '+{86} 000 0000 0000'], // Китай
|
||||
'JP': ['Japan +81', '+{81} 000-000-0000'], // Япония
|
||||
'IN': ['India +91', '+{91} 00000 00000'], // Индия
|
||||
'KR': ['South Korea +82', '+{82} 00-0000-0000'], // Южная Корея
|
||||
'ID': ['Indonesia +62', '+{62} 0000 0000 0000'], // Индонезия
|
||||
'TR': ['Turkey +90', '+{90} 000 000 00 00'], // Турция
|
||||
'IL': ['Israel +972', '+{972} 00 000-0000'], // Израиль
|
||||
'SA': ['Saudi Arabia +966', '+{966} 00 000 0000'], // Саудовская Аравия
|
||||
'AE': ['United Arab Emirates +971', '+{971} 00 000 0000'], // ОАЭ
|
||||
'TH': ['Thailand +66', '+{66} 00 000 0000'], // Таиланд
|
||||
'VN': ['Vietnam +84', '+{84} 000 000 000'], // Вьетнам
|
||||
'MY': ['Malaysia +60', '+{60} 00-000 0000'], // Малайзия
|
||||
'PH': ['Philippines +63', '+{63} 000 000 0000'], // Филиппины
|
||||
|
||||
// Северная Америка
|
||||
'US': ['United States +1', '+{1} (000) 000-0000'], // США
|
||||
'CA': ['Canada +1', '+{1} (000) 000-0000'], // Канада
|
||||
'MX': ['Mexico +52', '+{52} 000 000 0000'], // Мексика
|
||||
|
||||
// Южная Америка
|
||||
'BR': ['Brazil +55', '+{55} (00) 0000-0000'], // Бразилия
|
||||
'AR': ['Argentina +54', '+{54} 000 000-0000'], // Аргентина
|
||||
'CO': ['Colombia +57', '+{57} 000 000 0000'], // Колумбия
|
||||
'PE': ['Peru +51', '+{51} 000 000 000'], // Перу
|
||||
'CL': ['Chile +56', '+{56} 00 000 0000'], // Чили
|
||||
'EC': ['Ecuador +593', '+{593} 00 000 0000'], // Эквадор
|
||||
'VE': ['Venezuela +58', '+{58} 000 000 0000'], // Венесуэла
|
||||
|
||||
// Африка
|
||||
'EG': ['Egypt +20', '+{20} 000 000 0000'], // Египет
|
||||
'NG': ['Nigeria +234', '+{234} 000 0000 0000'], // Нигерия
|
||||
'ZA': ['South Africa +27', '+{27} 000 000 0000'], // Южная Африка
|
||||
'MA': ['Morocco +212', '+{212} 00 00 00 00'], // Марокко
|
||||
'DZ': ['Algeria +213', '+{213} 00 00 00 00'], // Алжир
|
||||
'KE': ['Kenya +254', '+{254} 000 000 000'], // Кения
|
||||
'ET': ['Ethiopia +251', '+{251} 00 000 0000'], // Эфиопия
|
||||
|
||||
// Австралия и Океания
|
||||
'AU': ['Australia +61', '+{61} 0000 000 000'], // Австралия
|
||||
'NZ': ['New Zealand +64', '+{64} 00 000 0000'], // Новая Зеландия
|
||||
};
|
130
lib/utils/themes/fontFace.ts
Normal file
130
lib/utils/themes/fontFace.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { domain } from "../defineDomain";
|
||||
|
||||
|
||||
export const fontFaces = `
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Thin.ttf);
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-ThinItalic.ttf);
|
||||
font-weight: 100;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-ExtraLight.ttf);
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-ExtraLightItalic.ttf);
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Light.ttf);
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-LightItalic.ttf);
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Regular.ttf);
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Italic.ttf);
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Medium.ttf);
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-MediumItalic.ttf);
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-SemiBold.ttf);
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-SemiBoldItalic.ttf);
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Bold.ttf);
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-BoldItalic.ttf);
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-ExtraBold.ttf);
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-ExtraBoldItalic.ttf);
|
||||
font-weight: 800;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-Black.ttf);
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url(${domain}/fonts/Lato-BlackItalic.ttf);
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
}
|
||||
`;
|
@ -1,4 +1,5 @@
|
||||
import {createTheme} from "@mui/material";
|
||||
import {fontFaces} from "./fontFace";
|
||||
|
||||
declare module '@mui/material/Button' {
|
||||
interface ButtonPropsVariantOverrides {
|
||||
@ -18,6 +19,14 @@ const theme = createTheme({
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: fontFaces,
|
||||
},
|
||||
MuiScopedCssBaseline: {
|
||||
styleOverrides: {
|
||||
root: fontFaces,
|
||||
}
|
||||
},
|
||||
MuiTypography: {
|
||||
defaultProps: {
|
||||
variantMapping: {
|
||||
@ -29,7 +38,8 @@ const theme = createTheme({
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
variant: 'contained' },
|
||||
variant: 'contained'
|
||||
},
|
||||
style: {
|
||||
backgroundColor: "#7E2AEA",
|
||||
padding: '13px 20px',
|
||||
@ -44,7 +54,8 @@ const theme = createTheme({
|
||||
},
|
||||
{
|
||||
props: {
|
||||
variant: 'outlined' },
|
||||
variant: 'outlined'
|
||||
},
|
||||
style: {
|
||||
backgroundColor: "#F2F3F7",
|
||||
padding: '10px 20px',
|
||||
@ -89,12 +100,10 @@ const theme = createTheme({
|
||||
fontWeight: 500,
|
||||
},
|
||||
fontFamily: [
|
||||
'"Lato"',
|
||||
"Twemoji Country Flags",
|
||||
"Rubik",
|
||||
"-apple-system",
|
||||
"BlinkMacSystemFont",
|
||||
'"Segoe UI"',
|
||||
'"Helvetica Neue"',
|
||||
"Arial",
|
||||
"sans-serif",
|
||||
'"Apple Color Emoji"',
|
||||
|
@ -89,6 +89,9 @@
|
||||
"country-flag-emoji-polyfill": "^0.1.8",
|
||||
"current-device": "^0.10.2",
|
||||
"hex-rgb": "^5.0.0",
|
||||
"mobile-detect": "^1.4.5"
|
||||
"mobile-detect": "^1.4.5",
|
||||
"mui-tel-input": "^5.1.2",
|
||||
"react-imask": "^7.6.0",
|
||||
"react-phone-number-input": "^3.4.1"
|
||||
}
|
||||
}
|
||||
|
228
pub.js
228
pub.js
File diff suppressed because one or more lines are too long
BIN
public/fonts/Lato-Black.ttf
Normal file
BIN
public/fonts/Lato-Black.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-BlackItalic.ttf
Normal file
BIN
public/fonts/Lato-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-Bold.ttf
Normal file
BIN
public/fonts/Lato-Bold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-BoldItalic.ttf
Normal file
BIN
public/fonts/Lato-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-ExtraBold.ttf
Normal file
BIN
public/fonts/Lato-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-ExtraBoldItalic.ttf
Normal file
BIN
public/fonts/Lato-ExtraBoldItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-ExtraLight.ttf
Normal file
BIN
public/fonts/Lato-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-ExtraLightItalic.ttf
Normal file
BIN
public/fonts/Lato-ExtraLightItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-Italic.ttf
Normal file
BIN
public/fonts/Lato-Italic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-Light.ttf
Normal file
BIN
public/fonts/Lato-Light.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-LightItalic.ttf
Normal file
BIN
public/fonts/Lato-LightItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-Medium.ttf
Normal file
BIN
public/fonts/Lato-Medium.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-MediumItalic.ttf
Normal file
BIN
public/fonts/Lato-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-Regular.ttf
Normal file
BIN
public/fonts/Lato-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Lato-SemiBold.ttf
Normal file
BIN
public/fonts/Lato-SemiBold.ttf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user