Merge branch 'skeletons' into input-limit

This commit is contained in:
Nastya 2024-02-15 11:02:56 +03:00
commit c0e7396455
7 changed files with 336 additions and 184 deletions

@ -41,6 +41,7 @@ export default function OptionsAndPicture({
setOpenBranchingPage,
}: Props) {
const [switchState, setSwitchState] = useState("setting");
const [pictureUploding, setPictureUploading] = useState<boolean>(false);
const [selectedVariantId, setSelectedVariantId] = useState<string | null>(
null,
);
@ -66,6 +67,8 @@ export default function OptionsAndPicture({
const handleImageUpload = async (file: File) => {
if (!selectedVariantId) return;
setPictureUploading(true);
const url = await uploadQuestionImage(
question.id,
quizQid,
@ -84,6 +87,8 @@ export default function OptionsAndPicture({
);
closeImageUploadModal();
openCropModal(file, url);
setPictureUploading(false);
};
function handleCropModalSaveClick(imageBlob: Blob) {
@ -111,6 +116,7 @@ export default function OptionsAndPicture({
{!isMobile && (
<AddOrEditImageButton
imageSrc={variant.extendedText}
uploading={pictureUploding}
onImageClick={() => {
setSelectedVariantId(variant.id);
if (variant.extendedText) {
@ -136,6 +142,7 @@ export default function OptionsAndPicture({
{isMobile && (
<AddOrEditImageButton
imageSrc={variant.extendedText}
uploading={pictureUploding}
onImageClick={() => {
setSelectedVariantId(variant.id);
if (variant.extendedText) {

@ -1,4 +1,11 @@
import { Box, Link, Typography, useMediaQuery, useTheme } from "@mui/material";
import {
Box,
Link,
Typography,
Skeleton,
useMediaQuery,
useTheme,
} from "@mui/material";
import {
addQuestionVariant,
uploadQuestionImage,
@ -30,6 +37,7 @@ export default function OptionsPicture({
const theme = useTheme();
const onClickAddAnAnswer = useAddAnswer();
const quizQid = useCurrentQuiz()?.qid;
const [pictureUploding, setPictureUploading] = useState<boolean>(false);
const [selectedVariantId, setSelectedVariantId] = useState<string | null>(
null,
);
@ -53,6 +61,8 @@ export default function OptionsPicture({
const handleImageUpload = async (file: File) => {
if (!selectedVariantId) return;
setPictureUploading(true);
const url = await uploadQuestionImage(
question.id,
quizQid,
@ -69,8 +79,11 @@ export default function OptionsPicture({
variant.originalImageUrl = url;
},
);
closeImageUploadModal();
openCropModal(file, url);
setPictureUploading(false);
};
function handleCropModalSaveClick(imageBlob: Blob) {
@ -98,6 +111,7 @@ export default function OptionsPicture({
{!isMobile && (
<AddOrEditImageButton
imageSrc={variant.extendedText}
uploading={pictureUploding}
onImageClick={() => {
setSelectedVariantId(variant.id);
if (variant.extendedText) {
@ -123,6 +137,7 @@ export default function OptionsPicture({
{isMobile && (
<AddOrEditImageButton
imageSrc={variant.extendedText}
uploading={pictureUploding}
onImageClick={() => {
setSelectedVariantId(variant.id);
if (variant.extendedText) {

@ -1,5 +1,6 @@
import { useState } from "react";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { Box, ButtonBase, Typography, useTheme } from "@mui/material";
import { Box, Skeleton, Typography, useTheme } from "@mui/material";
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
@ -12,6 +13,7 @@ type UploadImageProps = {
};
export default function UploadImage({ question }: UploadImageProps) {
const [pictureUploding, setPictureUploading] = useState<boolean>(false);
const theme = useTheme();
const quiz = useCurrentQuiz();
@ -29,29 +31,51 @@ export default function UploadImage({ question }: UploadImageProps) {
>
Загрузить изображение
</Typography>
<DropZone
text={"5 MB максимум"}
{pictureUploding ? (
<Skeleton variant="rounded" sx={{ height: "120px", width: "300px" }} />
) : (
<DropZone
text={"5 MB максимум"}
heightImg={"110px"}
sx={{ maxWidth: "300px", width: "100%" }}
imageUrl={question.content.back}
originalImageUrl={question.content.originalBack}
onImageUploadClick={(file) => {
uploadQuestionImage(question.id, quiz.qid, file, (question, url) => {
question.content.back = url;
question.content.originalBack = url;
});
}}
onDeleteClick={() => {
updateQuestion(question.id, (question) => {
question.content.back = null;
});
}}
onImageSaveClick={(file) => {
uploadQuestionImage(question.id, quiz.qid, file, (question, url) => {
question.content.back = url;
});
}}
/>
sx={{ maxWidth: "300px", width: "100%" }}
imageUrl={question.content.back}
originalImageUrl={question.content.originalBack}
onImageUploadClick={async (file) => {
setPictureUploading(true);
await uploadQuestionImage(
question.id,
quiz.qid,
file,
(question, url) => {
question.content.back = url;
question.content.originalBack = url;
},
);
setPictureUploading(false);
}}
onDeleteClick={() => {
updateQuestion(question.id, (question) => {
question.content.back = null;
});
}}
onImageSaveClick={async (file) => {
setPictureUploading(true);
await uploadQuestionImage(
question.id,
quiz.qid,
file,
(question, url) => {
question.content.back = url;
},
);
setPictureUploading(false);
}}
/>
)}
</Box>
);
}

@ -258,7 +258,7 @@ function TariffPage() {
component="h2"
mb="20px"
>
Вы подтверждаете платёж в сумму {openModal.price}
Вы подтверждаете платёж в сумму {openModal.price}
</Typography>
<Button variant="contained" onClick={() => tryBuy(openModal)}>
купить

@ -22,6 +22,7 @@ import {
Select,
Tooltip,
Typography,
Skeleton,
useMediaQuery,
useTheme,
} from "@mui/material";
@ -72,6 +73,9 @@ export default function StartPageSettings() {
const quiz = useCurrentQuiz();
const [formState, setFormState] = useState<"design" | "content">("design");
const [mobileVersion, setMobileVersion] = useState(false);
const [faviconUploding, setFaviconUploading] = useState<boolean>(false);
const [backgroundUploding, setBackgroundUploading] = useState<boolean>(false);
const [logoUploding, setLogoUploading] = useState<boolean>(false);
if (!quiz) return null;
@ -81,14 +85,20 @@ export default function StartPageSettings() {
const designType = quiz?.config?.startpageType;
const favIconDropZoneElement = (
const favIconDropZoneElement = faviconUploding ? (
<Skeleton sx={{ width: "48px", height: "48px", transform: "none" }} />
) : (
<FaviconDropZone
imageUrl={quiz.config.startpage.favIcon}
onImageUploadClick={async (file) => {
setFaviconUploading(true);
const resizedImage = await resizeFavIcon(file);
uploadQuizImage(quiz.id, resizedImage, (quiz, url) => {
await uploadQuizImage(quiz.id, resizedImage, (quiz, url) => {
quiz.config.startpage.favIcon = url;
});
setFaviconUploading(false);
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
@ -327,30 +337,47 @@ export default function StartPageSettings() {
>
Изображение
</Typography>
<DropZone
value={"5 MB максимум"}
sx={{ maxWidth: "300px" }}
imageUrl={quiz.config.startpage.background.desktop}
originalImageUrl={
quiz.config.startpage.background.originalDesktop
}
onImageUploadClick={(file) => {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.background.desktop = url;
quiz.config.startpage.background.originalDesktop = url;
});
}}
onImageSaveClick={(file) => {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.background.desktop = url;
});
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
quiz.config.startpage.background.desktop = null;
});
}}
/>
{backgroundUploding ? (
<Skeleton
sx={{
width: "300px",
height: "120px",
transform: "none",
}}
/>
) : (
<DropZone
text={"5 MB максимум"}
sx={{ maxWidth: "300px" }}
imageUrl={quiz.config.startpage.background.desktop}
originalImageUrl={
quiz.config.startpage.background.originalDesktop
}
onImageUploadClick={async (file) => {
setBackgroundUploading(true);
await uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.background.desktop = url;
quiz.config.startpage.background.originalDesktop =
url;
});
setBackgroundUploading(false);
}}
onImageSaveClick={async (file) => {
setBackgroundUploading(true);
await uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.background.desktop = url;
});
setBackgroundUploading(false);
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
quiz.config.startpage.background.desktop = null;
});
}}
/>
)}
</Box>
<ModalSizeImage />
@ -383,47 +410,68 @@ export default function StartPageSettings() {
</Tooltip>
)}
</Box>
<ButtonBase
component="label"
sx={{
justifyContent: "center",
height: "48px",
width: "48px",
display: "flex",
alignItems: "center",
my: "20px",
}}
>
<input
onChange={(event) => {
const file = event.target.files?.[0];
if (file) {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.background.video = url;
});
// setVideo(URL.createObjectURL(file));
}
}}
hidden
accept=".mp4"
multiple
type="file"
/>
<UploadBox
icon={<UploadIcon />}
{backgroundUploding ? (
<Skeleton
sx={{
height: "48px",
width: "48px",
height: "48px",
transform: "none",
margin: "20px 0",
}}
/>
</ButtonBase>
{quiz.config.startpage.background.video ? (
<video
src={quiz.config.startpage.background.video}
width="400"
controls
/>
) : null}
) : (
<>
<ButtonBase
component="label"
sx={{
justifyContent: "center",
height: "48px",
width: "48px",
display: "flex",
alignItems: "center",
my: "20px",
}}
>
<input
onChange={async (event) => {
setBackgroundUploading(true);
const file = event.target.files?.[0];
if (file) {
await uploadQuizImage(
quiz.id,
file,
(quiz, url) => {
quiz.config.startpage.background.video = url;
},
);
// setVideo(URL.createObjectURL(file));
}
setBackgroundUploading(false);
}}
hidden
accept=".mp4"
multiple
type="file"
/>
<UploadBox
icon={<UploadIcon />}
sx={{
height: "48px",
width: "48px",
}}
/>
</ButtonBase>
{quiz.config.startpage.background.video && (
<video
src={quiz.config.startpage.background.video}
width="400"
controls
/>
)}
</>
)}
</>
)}
{designType !== "centered" && (
@ -497,28 +545,44 @@ export default function StartPageSettings() {
>
Логотип
</Typography>
<DropZone
value={"5 MB максимум"}
sx={{ maxWidth: "300px" }}
imageUrl={quiz.config.startpage.logo}
originalImageUrl={quiz.config.startpage.originalLogo}
onImageUploadClick={(file) => {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
quiz.config.startpage.originalLogo = url;
});
}}
onImageSaveClick={(file) => {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
});
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
quiz.config.startpage.logo = null;
});
}}
/>
{logoUploding ? (
<Skeleton
sx={{
width: "300px",
height: "120px",
transform: "none",
}}
/>
) : (
<DropZone
text={"5 MB максимум"}
sx={{ maxWidth: "300px" }}
imageUrl={quiz.config.startpage.logo}
originalImageUrl={quiz.config.startpage.originalLogo}
onImageUploadClick={async (file) => {
setLogoUploading(true);
await uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
quiz.config.startpage.originalLogo = url;
});
setLogoUploading(false);
}}
onImageSaveClick={async (file) => {
setLogoUploading(true);
await uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
});
setLogoUploading(false);
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
quiz.config.startpage.logo = null;
});
}}
/>
)}
</Box>
<Typography
@ -564,28 +628,44 @@ export default function StartPageSettings() {
>
Логотип
</Typography>
<DropZone
value={"5 MB максимум"}
sx={{ maxWidth: "300px" }}
imageUrl={quiz.config.startpage.logo}
originalImageUrl={quiz.config.startpage.originalLogo}
onImageUploadClick={(file) => {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
quiz.config.startpage.originalLogo = url;
});
}}
onImageSaveClick={(file) => {
uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
});
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
quiz.config.startpage.logo = null;
});
}}
/>
{logoUploding ? (
<Skeleton
sx={{
width: "300px",
height: "120px",
transform: "none",
}}
/>
) : (
<DropZone
text={"5 MB максимум"}
sx={{ maxWidth: "300px" }}
imageUrl={quiz.config.startpage.logo}
originalImageUrl={quiz.config.startpage.originalLogo}
onImageUploadClick={async (file) => {
setLogoUploading(true);
await uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
quiz.config.startpage.originalLogo = url;
});
setLogoUploading(false);
}}
onImageSaveClick={async (file) => {
setLogoUploading(true);
await uploadQuizImage(quiz.id, file, (quiz, url) => {
quiz.config.startpage.logo = url;
});
setLogoUploading(false);
}}
onDeleteClick={() => {
updateQuiz(quiz.id, (quiz) => {
quiz.config.startpage.logo = null;
});
}}
/>
)}
</Box>
<Typography

@ -1,12 +1,15 @@
import { Box, Button, Skeleton, useTheme, useMediaQuery } from "@mui/material";
import Plus from "@icons/questionsPage/plus";
import { Box, Button, SxProps, Theme } from "@mui/material";
import Image from "../assets/icons/questionsPage/image";
import type { SxProps, Theme } from "@mui/material";
interface Props {
sx?: SxProps<Theme>;
imageSrc?: string;
onImageClick?: () => void;
onPlusClick?: () => void;
uploading: boolean;
}
export default function AddOrEditImageButton({
@ -14,59 +17,76 @@ export default function AddOrEditImageButton({
onPlusClick,
sx,
imageSrc,
uploading = false,
}: Props) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790));
return (
<Box
sx={{
display: "flex",
height: "40px",
minWidth: "60px",
width: "60px",
borderRadius: "3px",
overflow: "hidden",
...sx,
}}
>
<Button
onClick={onImageClick}
sx={{
p: 0,
minWidth: "40px",
flexGrow: 1,
backgroundColor: "#EEE4FC",
}}
>
{imageSrc ? (
<img
src={imageSrc}
alt=""
style={{
width: "100%",
height: "100%",
objectFit: "scale-down",
display: "block",
}}
/>
) : (
<Image
<>
{uploading ? (
<Skeleton
variant="rounded"
sx={{
height: "40px",
width: isMobile ? "auto" : "80px",
margin: isMobile ? "8px" : "0 10px 0 8px",
}}
/>
) : (
<Box
sx={{
display: "flex",
height: "40px",
minWidth: "60px",
width: "60px",
borderRadius: "3px",
overflow: "hidden",
...sx,
}}
>
<Button
onClick={onImageClick}
sx={{
height: "100%",
width: "100%",
p: 0,
minWidth: "40px",
flexGrow: 1,
backgroundColor: "#EEE4FC",
}}
/>
)}
</Button>
<Button
onClick={onPlusClick}
data-cy="add-image-button"
sx={{
p: 0,
minWidth: "20px",
width: "20px",
}}
>
<Plus />
</Button>
</Box>
>
{imageSrc ? (
<img
src={imageSrc}
alt=""
style={{
width: "100%",
height: "100%",
objectFit: "scale-down",
display: "block",
}}
/>
) : (
<Image
sx={{
height: "100%",
width: "100%",
}}
/>
)}
</Button>
<Button
onClick={onPlusClick}
data-cy="add-image-button"
sx={{
p: 0,
minWidth: "20px",
width: "20px",
}}
>
<Plus />
</Button>
</Box>
)}
</>
);
}

@ -1,4 +1,4 @@
import { FC } from "react";
import { useState, FC } from "react";
import {
Box,
Button,
@ -25,6 +25,7 @@ interface Iprops {
}
export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData }) => {
const [pictureUploding, setPictureUploading] = useState<boolean>(false);
const quizQid = useCurrentQuiz()?.qid;
const theme = useTheme();
const {
@ -38,6 +39,8 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData }) => {
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] =
useDisclosure();
async function handleImageUpload(file: File) {
setPictureUploading(true);
const url = await uploadQuestionImage(
resultData.id,
quizQid,
@ -49,6 +52,8 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData }) => {
);
closeImageUploadModal();
openCropModal(file, url);
setPictureUploading(false);
}
function handleCropModalSaveClick(imageBlob: Blob) {
@ -142,6 +147,7 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData }) => {
>
<AddOrEditImageButton
imageSrc={resultData.content.back}
uploading={pictureUploding}
onImageClick={() => {
if (resultData.content.back) {
return openCropModal(