feat: Improving page loading

This commit is contained in:
2025-11-20 20:17:52 +03:00
parent 6f32c6e671
commit 85c71563c1
17 changed files with 545 additions and 273 deletions

View File

@@ -1,9 +1,9 @@
import { useNavigate, useParams } from "react-router-dom";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { Box } from "@mui/material";
import { PreviewLeftWidget } from "./PreviewLeftWidget";
import { PreviewRightWidget } from "./PreviewRightWidget";
import { articlesStore, languageStore } from "@shared";
import { articlesStore, languageStore, LoadingSpinner } from "@shared";
import { ArrowLeft } from "lucide-react";
export const ArticlePreviewPage = () => {
@@ -11,18 +11,41 @@ export const ArticlePreviewPage = () => {
const { id } = useParams();
const { getArticle, getArticleMedia, getArticlePreview } = articlesStore;
const { language } = languageStore;
const [isLoadingData, setIsLoadingData] = useState(true);
useEffect(() => {
const fetchData = async () => {
if (id) {
setIsLoadingData(true);
try {
await getArticle(Number(id), language);
await getArticleMedia(Number(id));
await getArticlePreview(Number(id));
} finally {
setIsLoadingData(false);
}
} else {
setIsLoadingData(false);
}
};
fetchData();
}, [id, language]);
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных статьи..." />
</Box>
);
}
return (
<>
<div className="flex items-center gap-4 mb-10">

View File

@@ -6,13 +6,20 @@ import {
MenuItem,
FormControl,
InputLabel,
Box,
} from "@mui/material";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Save } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import { carrierStore, cityStore, mediaStore, languageStore } from "@shared";
import {
carrierStore,
cityStore,
mediaStore,
languageStore,
LoadingSpinner,
} from "@shared";
import { useState, useEffect } from "react";
import { ImageUploadCard, LanguageSwitcher, DeleteModal } from "@widgets";
import {
@@ -28,6 +35,7 @@ export const CarrierEditPage = observer(() => {
const { language } = languageStore;
const [isLoading, setIsLoading] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(true);
const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false);
const [isUploadMediaOpen, setIsUploadMediaOpen] = useState(false);
const [isPreviewMediaOpen, setIsPreviewMediaOpen] = useState(false);
@@ -39,6 +47,12 @@ export const CarrierEditPage = observer(() => {
useEffect(() => {
(async () => {
if (!id) {
setIsLoadingData(false);
return;
}
setIsLoadingData(true);
try {
await cityStore.getCities("ru");
await cityStore.getCities("en");
await cityStore.getCities("zh");
@@ -71,7 +85,10 @@ export const CarrierEditPage = observer(() => {
);
}
mediaStore.getMedia();
await mediaStore.getMedia();
} finally {
setIsLoadingData(false);
}
})();
languageStore.setLanguage("ru");
@@ -110,6 +127,21 @@ export const CarrierEditPage = observer(() => {
? mediaStore.media.find((m) => m.id === editCarrierData.logo)
: null;
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных перевозчика..." />
</Box>
);
}
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<LanguageSwitcher />

View File

@@ -6,6 +6,7 @@ import {
MenuItem,
FormControl,
InputLabel,
Box,
} from "@mui/material";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Save } from "lucide-react";
@@ -18,6 +19,7 @@ import {
languageStore,
mediaStore,
CashedCities,
LoadingSpinner,
} from "@shared";
import { useEffect, useState } from "react";
import { LanguageSwitcher, ImageUploadCard } from "@widgets";
@@ -30,6 +32,7 @@ import {
export const CityEditPage = observer(() => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(true);
const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false);
const [isUploadMediaOpen, setIsUploadMediaOpen] = useState(false);
const [isPreviewMediaOpen, setIsPreviewMediaOpen] = useState(false);
@@ -62,6 +65,8 @@ export const CityEditPage = observer(() => {
useEffect(() => {
(async () => {
if (id) {
setIsLoadingData(true);
try {
await getCountries("ru");
const ruData = await getCity(id as string, "ru");
@@ -75,6 +80,11 @@ export const CityEditPage = observer(() => {
await getOneMedia(ruData.arms as string);
await getMedia();
} finally {
setIsLoadingData(false);
}
} else {
setIsLoadingData(false);
}
})();
}, [id]);
@@ -97,6 +107,21 @@ export const CityEditPage = observer(() => {
? mediaStore.media.find((m) => m.id === editCityData.arms)
: null;
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных города..." />
</Box>
);
}
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<LanguageSwitcher />

View File

@@ -1,16 +1,17 @@
import { Button, Paper, TextField } from "@mui/material";
import { Button, Paper, TextField, Box } from "@mui/material";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Save } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import { countryStore, languageStore } from "@shared";
import { countryStore, languageStore, LoadingSpinner } from "@shared";
import { useEffect, useState } from "react";
import { LanguageSwitcher } from "@widgets";
export const CountryEditPage = observer(() => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(true);
const { language } = languageStore;
const { id } = useParams();
const { editCountryData, editCountry, getCountry, setEditCountryData } =
@@ -35,6 +36,8 @@ export const CountryEditPage = observer(() => {
useEffect(() => {
(async () => {
if (id) {
setIsLoadingData(true);
try {
const ruData = await getCountry(id as string, "ru");
const enData = await getCountry(id as string, "en");
const zhData = await getCountry(id as string, "zh");
@@ -42,10 +45,30 @@ export const CountryEditPage = observer(() => {
setEditCountryData(ruData.name, "ru");
setEditCountryData(enData.name, "en");
setEditCountryData(zhData.name, "zh");
} finally {
setIsLoadingData(false);
}
} else {
setIsLoadingData(false);
}
})();
}, [id]);
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных страны..." />
</Box>
);
}
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<LanguageSwitcher />

View File

@@ -3,7 +3,12 @@ import { InformationTab, LeaveAgree, RightWidgetTab } from "@widgets";
import { LeftWidgetTab } from "@widgets";
import { useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import { articlesStore, cityStore, editSightStore } from "@shared";
import {
articlesStore,
cityStore,
editSightStore,
LoadingSpinner,
} from "@shared";
import { useBlocker, useParams } from "react-router-dom";
function a11yProps(index: number) {
@@ -15,6 +20,7 @@ function a11yProps(index: number) {
export const EditSightPage = observer(() => {
const [value, setValue] = useState(0);
const [isLoadingData, setIsLoadingData] = useState(true);
const { sight, getSightInfo, needLeaveAgree } = editSightStore;
const { getArticles } = articlesStore;
@@ -33,6 +39,8 @@ export const EditSightPage = observer(() => {
useEffect(() => {
const fetchData = async () => {
if (id) {
setIsLoadingData(true);
try {
await getCities("ru");
await getSightInfo(+id, "ru");
await getSightInfo(+id, "en");
@@ -40,6 +48,11 @@ export const EditSightPage = observer(() => {
await getArticles("ru");
await getArticles("en");
await getArticles("zh");
} finally {
setIsLoadingData(false);
}
} else {
setIsLoadingData(false);
}
};
fetchData();
@@ -79,12 +92,25 @@ export const EditSightPage = observer(() => {
</Tabs>
</Box>
{sight.common.id !== 0 && (
{isLoadingData ? (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных достопримечательности..." />
</Box>
) : (
sight.common.id !== 0 && (
<div className="flex-1">
<InformationTab value={value} index={0} />
<LeftWidgetTab value={value} index={1} />
<RightWidgetTab value={value} index={2} />
</div>
)
)}
{blocker.state === "blocked" ? <LeaveAgree blocker={blocker} /> : null}

View File

@@ -21,6 +21,7 @@ import {
mediaStore,
MEDIA_TYPE_LABELS,
languageStore,
LoadingSpinner,
} from "@shared";
import { MediaViewer } from "@widgets";
@@ -138,8 +139,15 @@ export const MediaEditPage = observer(() => {
if (!media && id) {
return (
<Box className="flex justify-center items-center h-screen">
<CircularProgress />
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных медиа..." />
</Box>
);
}

View File

@@ -27,6 +27,7 @@ import {
ArticleSelectOrCreateDialog,
SelectMediaDialog,
UploadMediaDialog,
LoadingSpinner,
} from "@shared";
import { toast } from "react-toastify";
import { stationsStore } from "@shared";
@@ -37,6 +38,7 @@ export const RouteEditPage = observer(() => {
const { id } = useParams();
const { editRouteData, copyRouteAction } = routeStore;
const [isLoading, setIsLoading] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(true);
const [isSelectArticleDialogOpen, setIsSelectArticleDialogOpen] =
useState(false);
const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false);
@@ -48,18 +50,27 @@ export const RouteEditPage = observer(() => {
useEffect(() => {
const fetchData = async () => {
if (!id) {
setIsLoadingData(false);
return;
}
setIsLoadingData(true);
try {
const response = await routeStore.getRoute(Number(id));
routeStore.setEditRouteData(response);
languageStore.setLanguage("ru");
} finally {
setIsLoadingData(false);
}
};
fetchData();
}, []);
}, [id]);
useEffect(() => {
const fetchData = async () => {
carrierStore.getCarriers(language);
stationsStore.getStations();
articlesStore.getArticleList();
await carrierStore.getCarriers(language);
await stationsStore.getStations();
await articlesStore.getArticleList();
};
fetchData();
}, [id, language]);
@@ -233,6 +244,21 @@ export const RouteEditPage = observer(() => {
(article) => article.id === editRouteData.governor_appeal
);
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных маршрута..." />
</Box>
);
}
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<div className="flex items-center gap-4">

View File

@@ -6,13 +6,19 @@ import {
MenuItem,
FormControl,
InputLabel,
Box,
} from "@mui/material";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Save } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import { stationsStore, languageStore, cityStore } from "@shared";
import {
stationsStore,
languageStore,
cityStore,
LoadingSpinner,
} from "@shared";
import { useEffect, useState } from "react";
import { LanguageSwitcher } from "@widgets";
import { LinkedSights } from "../LinkedSights";
@@ -21,6 +27,7 @@ import { SaveWithoutCityAgree } from "@widgets";
export const StationEditPage = observer(() => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(true);
const { language } = languageStore;
const { id } = useParams();
const {
@@ -90,18 +97,41 @@ export const StationEditPage = observer(() => {
useEffect(() => {
const fetchAndSetStationData = async () => {
if (!id) return;
if (!id) {
setIsLoadingData(false);
return;
}
setIsLoadingData(true);
try {
const stationId = Number(id);
await getEditStation(stationId);
await getCities("ru");
await getCities("en");
await getCities("zh");
} finally {
setIsLoadingData(false);
}
};
fetchAndSetStationData();
}, [id]);
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных станции..." />
</Box>
);
}
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<LanguageSwitcher />

View File

@@ -1,9 +1,9 @@
import { Paper } from "@mui/material";
import { languageStore, stationsStore } from "@shared";
import { Paper, Box } from "@mui/material";
import { languageStore, stationsStore, LoadingSpinner } from "@shared";
import { LanguageSwitcher } from "@widgets";
import { observer } from "mobx-react-lite";
import { ArrowLeft } from "lucide-react";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { LinkedSights } from "../LinkedSights";
@@ -12,15 +12,38 @@ export const StationPreviewPage = observer(() => {
const { stationPreview, getStationPreview } = stationsStore;
const navigate = useNavigate();
const { language } = languageStore;
const [isLoadingData, setIsLoadingData] = useState(true);
useEffect(() => {
(async () => {
if (id) {
setIsLoadingData(true);
try {
await getStationPreview(Number(id));
} finally {
setIsLoadingData(false);
}
} else {
setIsLoadingData(false);
}
})();
}, [id, language]);
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных станции..." />
</Box>
);
}
return (
<Paper className="w-full p-3 py-5 flex flex-col gap-10">
<LanguageSwitcher />

View File

@@ -4,18 +4,20 @@ import {
Checkbox,
Paper,
TextField,
Box,
} from "@mui/material";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Save } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import { userStore, languageStore } from "@shared";
import { userStore, languageStore, LoadingSpinner } from "@shared";
import { useEffect, useState } from "react";
export const UserEditPage = observer(() => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(true);
const { id } = useParams();
const { editUserData, editUser, getUser, setEditUserData } = userStore;
@@ -41,6 +43,8 @@ export const UserEditPage = observer(() => {
useEffect(() => {
(async () => {
if (id) {
setIsLoadingData(true);
try {
const data = await getUser(Number(id));
setEditUserData(
@@ -49,10 +53,30 @@ export const UserEditPage = observer(() => {
data?.password || "",
data?.is_admin || false
);
} finally {
setIsLoadingData(false);
}
} else {
setIsLoadingData(false);
}
})();
}, [id]);
if (isLoadingData) {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "60vh",
}}
>
<LoadingSpinner message="Загрузка данных пользователя..." />
</Box>
);
}
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<div className="flex items-center gap-4">

View File

@@ -86,7 +86,11 @@ class EditSightStore {
}
hasLoadedCommon = false;
isLoading = false;
getSightInfo = async (id: number, language: Language) => {
this.isLoading = true;
try {
const response = await languageInstance(language).get(`/sight/${id}`);
const data = response.data;
@@ -108,6 +112,9 @@ class EditSightStore {
this.hasLoadedCommon = true;
}
});
} finally {
this.isLoading = false;
}
};
updateLeftInfo = (language: Language, heading: string, body: string) => {
@@ -168,6 +175,8 @@ class EditSightStore {
clearSightInfo = () => {
this.needLeaveAgree = false;
this.hasLoadedCommon = false;
this.isLoading = false;
this.sight = {
common: {
id: 0,

View File

@@ -3,3 +3,4 @@ export * from "./BackButton";
export * from "./Modal";
export * from "./CoordinatesInput";
export * from "./AnimatedCircleButton";
export * from "./LoadingSpinner";

View File

@@ -10,23 +10,25 @@ import { observer } from "mobx-react-lite";
import { useState, DragEvent, useRef } from "react";
import { toast } from "react-toastify";
export const MediaArea = observer(
({
articleId,
mediaIds,
deleteMedia,
onFilesDrop, // 👈 Проп для обработки загруженных файлов
setSelectMediaDialogOpen,
}: {
interface MediaAreaProps {
articleId: number;
mediaIds: { id: string; media_type: number; filename: string }[];
deleteMedia: (id: number, media_id: string) => void;
onFilesDrop?: (files: File[]) => void;
setSelectMediaDialogOpen: (open: boolean) => void;
}) => {
}
export const MediaArea = observer(
({
articleId,
mediaIds,
deleteMedia,
onFilesDrop,
setSelectMediaDialogOpen,
}: MediaAreaProps) => {
const [mediaModal, setMediaModal] = useState<boolean>(false);
const [mediaId, setMediaId] = useState<string>("");
const [isDragging, setIsDragging] = useState(false);
const [isDragging, setIsDragging] = useState<boolean>(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleMediaModal = (mediaId: string) => {
@@ -34,13 +36,11 @@ export const MediaArea = observer(
setMediaId(mediaId);
};
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
const processFiles = (files: File[]) => {
if (!files.length || !onFilesDrop) {
return;
}
const files = Array.from(e.dataTransfer.files);
if (files.length && onFilesDrop) {
const { validFiles, errors } = filterValidFiles(files);
if (errors.length > 0) {
@@ -50,7 +50,15 @@ export const MediaArea = observer(
if (validFiles.length > 0) {
onFilesDrop(validFiles);
}
}
};
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
const files = Array.from(e.dataTransfer.files);
processFiles(files);
};
const handleDragOver = (e: DragEvent<HTMLDivElement>) => {
@@ -68,19 +76,11 @@ export const MediaArea = observer(
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || []);
if (files.length && onFilesDrop) {
const { validFiles, errors } = filterValidFiles(files);
processFiles(files);
if (errors.length > 0) {
errors.forEach((error) => toast.error(error));
}
if (validFiles.length > 0) {
onFilesDrop(validFiles);
}
}
// Сбрасываем значение input, чтобы можно было выбрать тот же файл снова
if (event.target) {
event.target.value = "";
}
};
return (
@@ -96,7 +96,7 @@ export const MediaArea = observer(
<Box className="w-full flex flex-col items-center justify-center border rounded-md p-4">
<div className="w-full flex flex-col items-center justify-center">
<div
className={`w-full h-40 flex flex-col justify-center items-center text-gray-400 border-dashed border-2 rounded-md border-gray-400 p-4 cursor-pointer hover:bg-gray-50 ${
className={`w-full h-40 flex flex-col justify-center items-center text-gray-400 border-dashed border-2 rounded-md border-gray-400 p-4 cursor-pointer hover:bg-gray-50 transition-colors ${
isDragging ? "bg-blue-100 border-blue-400" : ""
}`}
onDrop={handleDrop}
@@ -105,9 +105,11 @@ export const MediaArea = observer(
onClick={handleClick}
>
<Upload size={32} className="mb-2" />
<span className="text-center">
Перетащите медиа файлы сюда или нажмите для выбора
</span>
</div>
<div>или</div>
<div className="my-2">или</div>
<Button
variant="contained"
color="primary"
@@ -117,12 +119,14 @@ export const MediaArea = observer(
</Button>
</div>
{mediaIds.length > 0 && (
<div className="w-full flex flex-start flex-wrap gap-2 mt-4 py-10">
{mediaIds.map((m) => (
<button
className="relative w-20 h-20"
key={m.id}
onClick={() => handleMediaModal(m.id)}
type="button"
>
<MediaViewer
media={{
@@ -133,17 +137,20 @@ export const MediaArea = observer(
height="40px"
/>
<button
className="absolute top-2 right-2"
className="absolute top-2 right-2 bg-white rounded-full p-1 shadow-md hover:shadow-lg transition-shadow"
onClick={(e) => {
e.stopPropagation();
deleteMedia(articleId, m.id);
}}
type="button"
aria-label="Удалить медиа"
>
<X size={16} color="red" />
</button>
</button>
))}
</div>
)}
</Box>
<PreviewMediaDialog

View File

@@ -11,52 +11,72 @@ import { observer } from "mobx-react-lite";
import { useState, DragEvent, useRef } from "react";
import { toast } from "react-toastify";
export const MediaAreaForSight = observer(
({
onFilesDrop, // 👈 Проп для обработки загруженных файлов
onFinishUpload,
contextObjectName,
contextType,
isArticle,
articleName,
}: {
onFilesDrop?: (files: File[]) => void;
onFinishUpload?: (mediaId: string) => void;
contextObjectName?: string;
contextType?:
type ContextType =
| "sight"
| "city"
| "carrier"
| "country"
| "vehicle"
| "station";
interface MediaAreaForSightProps {
onFilesDrop?: (files: File[]) => void;
onFinishUpload?: (mediaId: string) => void;
contextObjectName?: string;
contextType?: ContextType;
isArticle?: boolean;
articleName?: string;
}) => {
const [selectMediaDialogOpen, setSelectMediaDialogOpen] = useState(false);
const [uploadMediaDialogOpen, setUploadMediaDialogOpen] = useState(false);
const [isDragging, setIsDragging] = useState(false);
}
export const MediaAreaForSight = observer(
({
onFilesDrop,
onFinishUpload,
contextObjectName,
contextType,
isArticle,
articleName,
}: MediaAreaForSightProps) => {
const [selectMediaDialogOpen, setSelectMediaDialogOpen] =
useState<boolean>(false);
const [uploadMediaDialogOpen, setUploadMediaDialogOpen] =
useState<boolean>(false);
const [isDragging, setIsDragging] = useState<boolean>(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const { setFileToUpload } = editSightStore;
const processFiles = (files: File[]) => {
if (!files.length) {
return;
}
const { validFiles, errors } = filterValidFiles(files);
if (errors.length > 0) {
errors.forEach((error: string) => toast.error(error));
}
if (validFiles.length > 0) {
// Сохраняем первый файл для загрузки
setFileToUpload(validFiles[0]);
// Вызываем колбэк, если он передан
if (onFilesDrop) {
onFilesDrop(validFiles);
}
// Открываем диалог загрузки
setUploadMediaDialogOpen(true);
}
};
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
const files = Array.from(e.dataTransfer.files);
if (files.length) {
const { validFiles, errors } = filterValidFiles(files);
if (errors.length > 0) {
errors.forEach((error: string) => toast.error(error));
}
if (validFiles.length > 0 && onFilesDrop) {
setFileToUpload(validFiles[0]);
setUploadMediaDialogOpen(true);
}
}
processFiles(files);
};
const handleDragOver = (e: DragEvent<HTMLDivElement>) => {
@@ -74,22 +94,12 @@ export const MediaAreaForSight = observer(
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || []);
if (files.length) {
const { validFiles, errors } = filterValidFiles(files);
if (errors.length > 0) {
errors.forEach((error: string) => toast.error(error));
}
if (validFiles.length > 0 && onFilesDrop) {
setFileToUpload(validFiles[0]);
onFilesDrop(validFiles);
setUploadMediaDialogOpen(true);
}
}
processFiles(files);
// Сбрасываем значение input, чтобы можно было выбрать тот же файл снова
if (event.target) {
event.target.value = "";
}
};
return (
@@ -105,7 +115,7 @@ export const MediaAreaForSight = observer(
<Box className="w-full flex flex-col items-center justify-center border rounded-md p-4">
<div className="w-full flex flex-col items-center justify-center">
<div
className={`w-full h-40 flex text-center flex-col justify-center items-center text-gray-400 border-dashed border-2 rounded-md border-gray-400 p-4 cursor-pointer hover:bg-gray-50 ${
className={`w-full h-40 flex flex-col justify-center items-center text-gray-400 border-dashed border-2 rounded-md border-gray-400 p-4 cursor-pointer hover:bg-gray-50 transition-colors ${
isDragging ? "bg-blue-100 border-blue-400" : ""
}`}
onDrop={handleDrop}
@@ -114,9 +124,11 @@ export const MediaAreaForSight = observer(
onClick={handleClick}
>
<Upload size={32} className="mb-2" />
<span className="text-center">
Перетащите медиа файлы сюда или нажмите для выбора
</span>
</div>
<div>или</div>
<div className="my-2">или</div>
<Button
variant="contained"
color="primary"

View File

@@ -127,8 +127,8 @@ export function MediaViewer({
src={`${import.meta.env.VITE_KRBL_MEDIA}${
media?.id
}/download?token=${token}`}
width={width ? width : "500px"}
height={height ? height : "300px"}
width={fullWidth ? "100%" : width ? width : "500px"}
height={fullHeight ? "100%" : height ? height : "300px"}
/>
)}

View File

@@ -434,7 +434,6 @@ export const CreateRightTab = observer(
</Box>
) : type === "media" ? (
<Box className="w-[80%] border border-gray-300 rounded-2xl relative flex items-center justify-center">
{sight.preview_media && (
<>
{type === "media" && (
<Box className="w-[80%] h-full rounded-2xl relative flex items-center justify-center">
@@ -462,11 +461,19 @@ export const CreateRightTab = observer(
</Box>
</>
)}
</Box>
)}
</>
)}
{!previewMedia && (
<Box className="w-full h-full flex justify-center items-center">
<Box
sx={{
maxWidth: "500px",
maxHeight: "100%",
display: "flex",
flexGrow: 1,
margin: "0 auto",
justifyContent: "center",
}}
>
<MediaAreaForSight
onFinishUpload={(mediaId) => {
linkPreviewMedia(mediaId);
@@ -476,8 +483,13 @@ export const CreateRightTab = observer(
contextType="sight"
isArticle={false}
/>
</Box>
</Box>
)}
</Box>
)}
</>
</Box>
) : (
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3 flex justify-center items-center">
<Typography variant="h6" color="text.secondary">

View File

@@ -415,21 +415,12 @@ export const RightWidgetTab = observer(
media_type: previewMedia.media_type,
filename: previewMedia.filename || "",
}}
fullWidth
fullHeight
/>
</Box>
</>
)}
{!previewMedia && (
<MediaAreaForSight
onFinishUpload={(mediaId) => {
linkPreviewMedia(mediaId);
}}
onFilesDrop={() => {}}
contextObjectName={sight[language].name}
contextType="sight"
isArticle={false}
/>
)}
</Box>
)}
</>