#14 Перепись редактирования и создания маршрута #16
@@ -59,7 +59,6 @@ export const RouteCreatePage = observer(() => {
|
|||||||
articlesStore.getArticleList();
|
articlesStore.getArticleList();
|
||||||
}, [language]);
|
}, [language]);
|
||||||
|
|
||||||
// Фильтруем перевозчиков только из выбранного города
|
|
||||||
const filteredCarriers = useMemo(() => {
|
const filteredCarriers = useMemo(() => {
|
||||||
const carriers =
|
const carriers =
|
||||||
carrierStore.carriers[language as keyof typeof carrierStore.carriers]
|
carrierStore.carriers[language as keyof typeof carrierStore.carriers]
|
||||||
@@ -281,7 +280,6 @@ export const RouteCreatePage = observer(() => {
|
|||||||
const lines = routeCoords.split("\n");
|
const lines = routeCoords.split("\n");
|
||||||
const lastLine = lines[lines.length - 1];
|
const lastLine = lines[lines.length - 1];
|
||||||
|
|
||||||
// Если мы на последней строке и она не пустая
|
|
||||||
if (lastLine && lastLine.trim()) {
|
if (lastLine && lastLine.trim()) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const newValue = routeCoords + "\n";
|
const newValue = routeCoords + "\n";
|
||||||
@@ -348,7 +346,6 @@ export const RouteCreatePage = observer(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Видео-превью как на странице редактирования */}
|
|
||||||
<VideoPreviewCard
|
<VideoPreviewCard
|
||||||
title="Видеозаставка"
|
title="Видеозаставка"
|
||||||
videoId={videoPreview}
|
videoId={videoPreview}
|
||||||
@@ -420,23 +417,17 @@ export const RouteCreatePage = observer(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Модальное окно выбора или создания статьи */}
|
|
||||||
<ArticleSelectOrCreateDialog
|
<ArticleSelectOrCreateDialog
|
||||||
open={isSelectArticleDialogOpen}
|
open={isSelectArticleDialogOpen}
|
||||||
onClose={() => setIsSelectArticleDialogOpen(false)}
|
onClose={() => setIsSelectArticleDialogOpen(false)}
|
||||||
onSelectArticle={handleArticleSelect}
|
onSelectArticle={handleArticleSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Модальное окно выбора видео */}
|
|
||||||
<SelectMediaDialog
|
<SelectMediaDialog
|
||||||
open={isSelectVideoDialogOpen}
|
open={isSelectVideoDialogOpen}
|
||||||
onClose={() => setIsSelectVideoDialogOpen(false)}
|
onClose={() => setIsSelectVideoDialogOpen(false)}
|
||||||
onSelectMedia={handleVideoSelect}
|
onSelectMedia={handleVideoSelect}
|
||||||
mediaType={2}
|
mediaType={2}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Модальное окно предпросмотра видео */}
|
|
||||||
{videoPreview && videoPreview !== "" && (
|
{videoPreview && videoPreview !== "" && (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={isVideoPreviewOpen}
|
open={isVideoPreviewOpen}
|
||||||
@@ -463,8 +454,6 @@ export const RouteCreatePage = observer(() => {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Модальное окно загрузки видео */}
|
|
||||||
<UploadMediaDialog
|
<UploadMediaDialog
|
||||||
open={isUploadVideoDialogOpen}
|
open={isUploadVideoDialogOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export const RouteEditPage = observer(() => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
// Устанавливаем русский язык при загрузке страницы
|
|
||||||
const response = await routeStore.getRoute(Number(id));
|
const response = await routeStore.getRoute(Number(id));
|
||||||
routeStore.setEditRouteData(response);
|
routeStore.setEditRouteData(response);
|
||||||
languageStore.setLanguage("ru");
|
languageStore.setLanguage("ru");
|
||||||
@@ -273,7 +272,6 @@ export const RouteEditPage = observer(() => {
|
|||||||
const lines = coordinates.split("\n");
|
const lines = coordinates.split("\n");
|
||||||
const lastLine = lines[lines.length - 1];
|
const lastLine = lines[lines.length - 1];
|
||||||
|
|
||||||
// Если мы на последней строке и она не пустая
|
|
||||||
if (lastLine && lastLine.trim()) {
|
if (lastLine && lastLine.trim()) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const newValue = coordinates + "\n";
|
const newValue = coordinates + "\n";
|
||||||
@@ -414,8 +412,6 @@ export const RouteEditPage = observer(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Правая часть - Видео (30%) */}
|
|
||||||
|
|
||||||
<VideoPreviewCard
|
<VideoPreviewCard
|
||||||
title="Видеозаставка"
|
title="Видеозаставка"
|
||||||
videoId={editRouteData.video_preview}
|
videoId={editRouteData.video_preview}
|
||||||
@@ -466,23 +462,17 @@ export const RouteEditPage = observer(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Модальное окно выбора или создания статьи */}
|
|
||||||
<ArticleSelectOrCreateDialog
|
<ArticleSelectOrCreateDialog
|
||||||
open={isSelectArticleDialogOpen}
|
open={isSelectArticleDialogOpen}
|
||||||
onClose={() => setIsSelectArticleDialogOpen(false)}
|
onClose={() => setIsSelectArticleDialogOpen(false)}
|
||||||
onSelectArticle={handleArticleSelect}
|
onSelectArticle={handleArticleSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Модальное окно выбора видео */}
|
|
||||||
<SelectMediaDialog
|
<SelectMediaDialog
|
||||||
open={isSelectVideoDialogOpen}
|
open={isSelectVideoDialogOpen}
|
||||||
onClose={() => setIsSelectVideoDialogOpen(false)}
|
onClose={() => setIsSelectVideoDialogOpen(false)}
|
||||||
onSelectMedia={handleVideoSelect}
|
onSelectMedia={handleVideoSelect}
|
||||||
mediaType={2}
|
mediaType={2}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Модальное окно предпросмотра видео */}
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={isVideoPreviewOpen}
|
open={isVideoPreviewOpen}
|
||||||
onClose={() => setIsVideoPreviewOpen(false)}
|
onClose={() => setIsVideoPreviewOpen(false)}
|
||||||
@@ -507,8 +497,6 @@ export const RouteEditPage = observer(() => {
|
|||||||
<Button onClick={() => setIsVideoPreviewOpen(false)}>Закрыть</Button>
|
<Button onClick={() => setIsVideoPreviewOpen(false)}>Закрыть</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{/* Модальное окно загрузки видео */}
|
|
||||||
<UploadMediaDialog
|
<UploadMediaDialog
|
||||||
open={isUploadVideoDialogOpen}
|
open={isUploadVideoDialogOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
|||||||
@@ -116,9 +116,7 @@ export const RouteListPage = observer(() => {
|
|||||||
<button onClick={() => navigate(`/route-preview/${params.row.id}`)}>
|
<button onClick={() => navigate(`/route-preview/${params.row.id}`)}>
|
||||||
<Map size={20} className="text-purple-500" />
|
<Map size={20} className="text-purple-500" />
|
||||||
</button>
|
</button>
|
||||||
{/* <button onClick={() => navigate(`/route/${params.row.id}`)}>
|
|
||||||
<Eye size={20} className="text-green-500" />
|
|
||||||
</button> */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsDeleteModalOpen(true);
|
setIsDeleteModalOpen(true);
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
// Режим редактирования: если выбрана статья для редактирования
|
|
||||||
const [selectedArticleId, setSelectedArticleId] = useState<number | null>(
|
const [selectedArticleId, setSelectedArticleId] = useState<number | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
@@ -68,7 +67,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
}[]
|
}[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
// Состояния для создания новой статьи
|
|
||||||
const [newArticleData, setNewArticleData] = useState({
|
const [newArticleData, setNewArticleData] = useState({
|
||||||
ru: { heading: "", body: "" },
|
ru: { heading: "", body: "" },
|
||||||
en: { heading: "", body: "" },
|
en: { heading: "", body: "" },
|
||||||
@@ -90,7 +88,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [fileToUpload, setFileToUpload] = useState<File | null>(null);
|
const [fileToUpload, setFileToUpload] = useState<File | null>(null);
|
||||||
|
|
||||||
// Определяем, редактируем ли мы существующую статью или создаем новую
|
|
||||||
const currentArticleId = selectedArticleId || tempArticleId;
|
const currentArticleId = selectedArticleId || tempArticleId;
|
||||||
const currentArticleData = selectedArticleId
|
const currentArticleData = selectedArticleId
|
||||||
? editedArticleData
|
? editedArticleData
|
||||||
@@ -98,15 +95,12 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
const currentMedia = selectedArticleId
|
const currentMedia = selectedArticleId
|
||||||
? editedArticleMedia
|
? editedArticleMedia
|
||||||
: createdArticleMedia;
|
: createdArticleMedia;
|
||||||
// Режим редактирования: если выбрана статья или мы на вкладке создания
|
|
||||||
const isEditMode =
|
const isEditMode =
|
||||||
selectedArticleId !== null || tempArticleId !== null || tabValue === 1;
|
selectedArticleId !== null || tempArticleId !== null || tabValue === 1;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
// Начинаем всегда с русского в модалке
|
|
||||||
setModalLanguage("ru");
|
setModalLanguage("ru");
|
||||||
// Загружаем списки статей для всех языков локально для модалки
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
getArticles("ru"),
|
getArticles("ru"),
|
||||||
@@ -135,14 +129,12 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
}
|
}
|
||||||
}, [open, getArticles]);
|
}, [open, getArticles]);
|
||||||
|
|
||||||
// При закрытии модалки возвращаем глобальный язык в 'ru'
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
languageStore.setLanguage("ru");
|
languageStore.setLanguage("ru");
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
// Загрузка статьи для редактирования
|
|
||||||
const loadArticleForEdit = async (articleId: number) => {
|
const loadArticleForEdit = async (articleId: number) => {
|
||||||
try {
|
try {
|
||||||
const [ruArticle, enArticle, zhArticle, mediaResponse] =
|
const [ruArticle, enArticle, zhArticle, mediaResponse] =
|
||||||
@@ -208,7 +200,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Обновляем данные статьи
|
|
||||||
await loadArticleForEdit(currentArticleId);
|
await loadArticleForEdit(currentArticleId);
|
||||||
|
|
||||||
toast.success("Статья успешно сохранена");
|
toast.success("Статья успешно сохранена");
|
||||||
@@ -259,7 +250,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
const { id } = response.data;
|
const { id } = response.data;
|
||||||
setTempArticleId(id);
|
setTempArticleId(id);
|
||||||
|
|
||||||
// Обновляем кэш статей сразу после создания
|
|
||||||
const ruHeading = newArticleData.ru.heading || "Новый заголовок (RU)";
|
const ruHeading = newArticleData.ru.heading || "Новый заголовок (RU)";
|
||||||
const enHeading = newArticleData.en.heading || "New Heading (EN)";
|
const enHeading = newArticleData.en.heading || "New Heading (EN)";
|
||||||
const zhHeading = newArticleData.zh.heading || "Новый заголовок (ZH)";
|
const zhHeading = newArticleData.zh.heading || "Новый заголовок (ZH)";
|
||||||
@@ -268,7 +258,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
const zhBody = newArticleData.zh.body || "Новый текст (ZH)";
|
const zhBody = newArticleData.zh.body || "Новый текст (ZH)";
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
// articleList используется в списке выбора — обновляем его
|
|
||||||
articlesStore.articleList.ru.data.unshift({
|
articlesStore.articleList.ru.data.unshift({
|
||||||
id,
|
id,
|
||||||
heading: ruHeading,
|
heading: ruHeading,
|
||||||
@@ -287,13 +276,9 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
body: zhBody,
|
body: zhBody,
|
||||||
service_name: zhHeading,
|
service_name: zhHeading,
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
// помечаем как загруженные, чтобы не перезатирать свежие данные
|
|
||||||
articlesStore.articleList.ru.loaded = true;
|
articlesStore.articleList.ru.loaded = true;
|
||||||
articlesStore.articleList.en.loaded = true;
|
articlesStore.articleList.en.loaded = true;
|
||||||
articlesStore.articleList.zh.loaded = true;
|
articlesStore.articleList.zh.loaded = true;
|
||||||
|
|
||||||
// Также поддержим общий список articles, если он где-то используется
|
|
||||||
if (articlesStore.articles) {
|
if (articlesStore.articles) {
|
||||||
articlesStore.articles.ru.unshift({
|
articlesStore.articles.ru.unshift({
|
||||||
id,
|
id,
|
||||||
@@ -332,7 +317,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загружаем медиа статьи
|
|
||||||
const mediaResponse = await authInstance.get(`/article/${id}/media`);
|
const mediaResponse = await authInstance.get(`/article/${id}/media`);
|
||||||
if (mediaResponse.data && mediaResponse.data.length > 0) {
|
if (mediaResponse.data && mediaResponse.data.length > 0) {
|
||||||
setCreatedArticleMedia(
|
setCreatedArticleMedia(
|
||||||
@@ -350,7 +334,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
|
|
||||||
toast.success("Статья успешно создана");
|
toast.success("Статья успешно создана");
|
||||||
|
|
||||||
// Сразу выбираем созданную статью как активную и закрываем диалог
|
|
||||||
onSelectArticle(id);
|
onSelectArticle(id);
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -532,7 +515,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
});
|
});
|
||||||
setCreatedArticleMedia([]);
|
setCreatedArticleMedia([]);
|
||||||
}
|
}
|
||||||
// При нажатии «Назад» переключаем язык модалки и глобальный язык в RU
|
|
||||||
setModalLanguage("ru");
|
setModalLanguage("ru");
|
||||||
languageStore.setLanguage("ru");
|
languageStore.setLanguage("ru");
|
||||||
};
|
};
|
||||||
@@ -541,7 +523,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
article?.service_name?.toLowerCase().includes(searchQuery.toLowerCase())
|
article?.service_name?.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ховер-предпросмотр для списка статей
|
|
||||||
const [hoveredArticleId, setHoveredArticleId] = useState<string | null>(
|
const [hoveredArticleId, setHoveredArticleId] = useState<string | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
@@ -579,7 +560,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
getArticleMedia,
|
getArticleMedia,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Данные для предпросмотра
|
|
||||||
const previewData = {
|
const previewData = {
|
||||||
heading: currentArticleData[modalLanguage].heading || "",
|
heading: currentArticleData[modalLanguage].heading || "",
|
||||||
body: currentArticleData[modalLanguage].body || "",
|
body: currentArticleData[modalLanguage].body || "",
|
||||||
@@ -594,7 +574,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// Данные предпросмотра для режима выбора (по ховеру)
|
|
||||||
const selectionPreviewHeading =
|
const selectionPreviewHeading =
|
||||||
(articlesStore.articleData as any)?.[modalLanguage]?.heading ||
|
(articlesStore.articleData as any)?.[modalLanguage]?.heading ||
|
||||||
(articlesStore.articleData as any)?.heading ||
|
(articlesStore.articleData as any)?.heading ||
|
||||||
@@ -632,7 +611,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
<Box
|
<Box
|
||||||
sx={{ display: "flex", gap: 2, flex: 1, overflow: "hidden" }}
|
sx={{ display: "flex", gap: 2, flex: 1, overflow: "hidden" }}
|
||||||
>
|
>
|
||||||
{/* Левая часть - список */}
|
|
||||||
<Paper className="w-[66%] flex flex-col">
|
<Paper className="w-[66%] flex flex-col">
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -699,7 +677,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Правая часть - предпросмотр */}
|
|
||||||
<Paper
|
<Paper
|
||||||
elevation={3}
|
elevation={3}
|
||||||
sx={{
|
sx={{
|
||||||
@@ -809,7 +786,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
// Режим редактирования или создания - в стиле LeftWidgetTab
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -890,7 +866,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* MediaArea - используем для всех случаев, с фиктивным articleId для создания */}
|
|
||||||
<MediaArea
|
<MediaArea
|
||||||
articleId={currentArticleId || 0}
|
articleId={currentArticleId || 0}
|
||||||
mediaIds={currentMedia}
|
mediaIds={currentMedia}
|
||||||
@@ -1017,7 +992,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
Отмена
|
Отмена
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Кнопка создания - только если мы на вкладке создания и статья еще не создана */}
|
|
||||||
{tabValue === 1 && !tempArticleId && !selectedArticleId && (
|
{tabValue === 1 && !tempArticleId && !selectedArticleId && (
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@@ -1028,7 +1002,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
{isCreating ? "Создание..." : "Создать статью"}
|
{isCreating ? "Создание..." : "Создать статью"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{/* Кнопки для редактирования - если статья создана или выбрана */}
|
|
||||||
{(tempArticleId || selectedArticleId) && (
|
{(tempArticleId || selectedArticleId) && (
|
||||||
<>
|
<>
|
||||||
{selectedArticleId && (
|
{selectedArticleId && (
|
||||||
@@ -1044,7 +1017,6 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Выбираем текущую и возвращаем язык в RU
|
|
||||||
handleSelectAndClose();
|
handleSelectAndClose();
|
||||||
languageStore.setLanguage("ru");
|
languageStore.setLanguage("ru");
|
||||||
}}
|
}}
|
||||||
@@ -1056,14 +1028,12 @@ export const ArticleSelectOrCreateDialog = observer(
|
|||||||
)}
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|
||||||
{/* Модальное окно выбора медиа */}
|
|
||||||
<SelectMediaDialog
|
<SelectMediaDialog
|
||||||
open={isSelectMediaDialogOpen}
|
open={isSelectMediaDialogOpen}
|
||||||
onClose={() => setIsSelectMediaDialogOpen(false)}
|
onClose={() => setIsSelectMediaDialogOpen(false)}
|
||||||
onSelectMedia={handleMediaSelect}
|
onSelectMedia={handleMediaSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Модальное окно загрузки медиа */}
|
|
||||||
<UploadMediaDialog
|
<UploadMediaDialog
|
||||||
open={isUploadMediaDialogOpen}
|
open={isUploadMediaDialogOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user