feat: Refactor old code with delete modal and icons for buttons

This commit is contained in:
2025-06-04 20:19:06 +03:00
parent 078f051e8a
commit 89488d9921
27 changed files with 2070 additions and 476 deletions

View File

@ -0,0 +1,33 @@
import { Button } from "@mui/material";
export const DeleteModal = ({
onDelete,
onCancel,
open,
}: {
onDelete: () => void;
onCancel: () => void;
open: boolean;
}) => {
return (
<div
className={`fixed top-0 left-0 w-screen h-screen flex justify-center items-center z-10000 bg-black/30 transition-all duration-300 ${
open ? "block" : "hidden"
}`}
>
<div className="bg-white p-4 w-100 rounded-lg flex flex-col gap-4 items-center">
<p className="text-black w-100 text-center">
Вы уверены, что хотите удалить этот элемент?
</p>
<div className="flex gap-4 justify-center">
<Button variant="contained" color="error" onClick={onDelete}>
Да
</Button>
<Button variant="outlined" onClick={onCancel}>
Нет
</Button>
</div>
</div>
</div>
);
};

View File

@ -1,6 +1,6 @@
import React, { useRef, useState, DragEvent, useEffect } from "react";
import { Paper, Box, Typography, Button, Tooltip } from "@mui/material";
import { X, Info } from "lucide-react"; // Assuming lucide-react for icons
import { X, Info, MousePointer } from "lucide-react"; // Assuming lucide-react for icons
import { editSightStore } from "@shared";
interface ImageUploadCardProps {
@ -159,6 +159,7 @@ export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
<Button
variant="contained"
color="primary"
startIcon={<MousePointer color="white" size={18} />}
onClick={(e) => {
e.stopPropagation(); // Prevent `handleZoneClick` from firing
onSelectFileClick(); // This button might trigger a different modal

View File

@ -0,0 +1,21 @@
import { Button } from "@mui/material";
export const LeaveAgree = ({ blocker }: { blocker: any }) => {
return (
<div className="fixed top-0 left-0 w-screen h-screen flex justify-center items-center z-10000 bg-black/30">
<div className="bg-white p-4 w-100 rounded-lg flex flex-col gap-4 items-center">
<p className="text-black w-100 text-center">
При выходе со страницы, несохраненные данные будут потеряны.
</p>
<div className="flex gap-4 justify-center">
<Button variant="contained" onClick={() => blocker.proceed()}>
Да
</Button>
<Button variant="outlined" onClick={() => blocker.reset()}>
Нет
</Button>
</div>
</div>
</div>
);
};

View File

@ -69,7 +69,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 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 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 ${
isDragging ? "bg-blue-100 border-blue-400" : ""
}`}
onDrop={handleDrop}

View File

@ -17,13 +17,11 @@ export function MediaViewer({
return (
<Box
sx={{
width: "80%",
width: "100%",
height: "100%",
maxWidth: "600px",
display: "flex",
flexGrow: 1,
justifyContent: "center",
margin: "0 auto",
}}
className={className}
>
@ -34,10 +32,9 @@ export function MediaViewer({
}/download?token=${token}`}
alt={media?.filename}
style={{
maxWidth: "100%",
height: "auto",
objectFit: "contain",
borderRadius: 8,
width: "100%",
height: "100%",
objectFit: "cover",
}}
/>
)}
@ -48,9 +45,7 @@ export function MediaViewer({
media?.id
}/download?token=${token}`}
style={{
margin: "auto 0",
height: "fit-content",
width: "fit-content",
height: "100%",
objectFit: "contain",
borderRadius: 30,
}}
@ -66,7 +61,7 @@ export function MediaViewer({
}/download?token=${token}`}
alt={media?.filename}
style={{
maxWidth: "100%",
width: "100%",
height: "100%",
objectFit: "contain",
borderRadius: 8,
@ -80,7 +75,7 @@ export function MediaViewer({
}/download?token=${token}`}
alt={media?.filename}
style={{
maxWidth: "100%",
width: "100%",
height: "100%",
objectFit: "contain",
borderRadius: 8,

View File

@ -18,8 +18,10 @@ import {
SightCommonInfo,
createSightStore,
UploadMediaDialog,
MEDIA_TYPE_VALUES,
} from "@shared";
import { ImageUploadCard, LanguageSwitcher } from "@widgets";
import { Save } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
@ -331,6 +333,7 @@ export const CreateInformationTab = observer(
<Button
variant="contained"
color="success"
startIcon={<Save color="white" size={18} />}
onClick={async () => {
await createSight(language);
toast.success("Достопримечательность создана");
@ -369,6 +372,13 @@ export const CreateInformationTab = observer(
onSelectMedia={(media) => {
handleMediaSelect(media, activeMenuType ?? "thumbnail");
}}
mediaType={
activeMenuType
? MEDIA_TYPE_VALUES[
activeMenuType as keyof typeof MEDIA_TYPE_VALUES
]
: undefined
}
/>
<PreviewMediaDialog

View File

@ -16,8 +16,16 @@ import {
ReactMarkdownComponent,
ReactMarkdownEditor,
MediaViewer,
DeleteModal,
} from "@widgets";
import { Trash2, ImagePlus } from "lucide-react";
import {
Trash2,
ImagePlus,
Unlink,
MousePointer,
Plus,
Save,
} from "lucide-react";
import { useState, useCallback } from "react";
import { observer } from "mobx-react-lite";
import { toast } from "react-toastify";
@ -47,7 +55,7 @@ export const CreateLeftTab = observer(
useState(false);
const [isSelectMediaDialogOpen, setIsSelectMediaDialogOpen] =
useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
// const handleMediaSelected = useCallback(() => {
// // При выборе медиа, обновляем данные для ТЕКУЩЕГО ЯЗЫКА
// // сохраняя текущие heading и body.
@ -123,6 +131,7 @@ export const CreateLeftTab = observer(
color="primary"
size="small"
style={{ transition: "0" }}
startIcon={<Unlink size={18} />}
onClick={() => {
unlinkLeftArticle();
toast.success("Статья откреплена");
@ -136,10 +145,7 @@ export const CreateLeftTab = observer(
style={{ transition: "0" }}
startIcon={<Trash2 size={18} />}
size="small"
onClick={() => {
deleteLeftArticle(sight.left_article);
toast.success("Статья откреплена");
}}
onClick={() => setIsDeleteModalOpen(true)}
>
Удалить
</Button>
@ -150,6 +156,7 @@ export const CreateLeftTab = observer(
variant="contained"
color="primary"
size="small"
startIcon={<MousePointer color="white" size={18} />}
onClick={() => setIsSelectArticleDialogOpen(true)}
>
Выбрать статью
@ -158,6 +165,7 @@ export const CreateLeftTab = observer(
variant="contained"
color="primary"
size="small"
startIcon={<Plus color="white" size={18} />}
style={{ transition: "0" }}
onClick={createLeftArticle}
>
@ -301,6 +309,7 @@ export const CreateLeftTab = observer(
display: "flex",
flexDirection: "column",
gap: 1.5,
maxWidth: "320px",
}}
>
<Paper
@ -405,10 +414,11 @@ export const CreateLeftTab = observer(
<Button
variant="contained"
color="success"
startIcon={<Save color="white" size={18} />}
onClick={async () => {
try {
await createSight(language);
toast.success("Странца создана");
toast.success("Страница создана");
} catch (error) {
console.error(error);
}
@ -445,6 +455,14 @@ export const CreateLeftTab = observer(
onClose={handleCloseArticleDialog}
onSelectArticle={handleArticleSelect}
/>
<DeleteModal
open={isDeleteModalOpen}
onDelete={() => {
deleteLeftArticle(sight.left_article);
toast.success("Статья откреплена");
}}
onCancel={() => setIsDeleteModalOpen(false)}
/>
</TabPanel>
);
}

View File

@ -14,7 +14,8 @@ import {
SelectArticleModal,
TabPanel,
SelectMediaDialog, // Import
UploadMediaDialog, // Import
UploadMediaDialog,
Media, // Import
} from "@shared";
import {
LanguageSwitcher,
@ -22,12 +23,14 @@ import {
MediaAreaForSight, // Import
ReactMarkdownComponent,
ReactMarkdownEditor,
DeleteModal,
} from "@widgets";
import { ImagePlus, Plus, X } from "lucide-react"; // Import X
import { ImagePlus, Plus, Save, Trash2, Unlink, X } from "lucide-react"; // Import X
import { observer } from "mobx-react-lite";
import { useState, useEffect } from "react"; // Added useEffect
import { MediaViewer } from "../../MediaViewer/index";
import { toast } from "react-toastify";
import { authInstance } from "@shared";
type MediaItemShared = {
// Define if not already available from @shared
@ -65,14 +68,27 @@ export const CreateRightTab = observer(
null
);
const [type, setType] = useState<"article" | "media">("media");
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isSelectMediaDialogOpen, setIsSelectMediaDialogOpen] =
useState(false);
const [mediaTarget, setMediaTarget] = useState<
"sightPreview" | "rightArticle" | null
>(null);
const [previewMedia, setPreviewMedia] = useState<Media | null>(null);
// Reset activeArticleIndex if language changes and index is out of bounds
useEffect(() => {
if (sight.preview_media) {
const fetchMedia = async () => {
const response = await authInstance.get(
`/media/${sight.preview_media}`
);
setPreviewMedia(response.data);
};
fetchMedia();
}
}, [sight.preview_media]);
useEffect(() => {
if (
activeArticleIndex !== null &&
@ -168,6 +184,7 @@ export const CreateRightTab = observer(
const handleMediaSelectedFromDialog = async (media: MediaItemShared) => {
setIsSelectMediaDialogOpen(false);
if (mediaTarget === "sightPreview") {
await linkPreviewMedia(media.id);
} else if (mediaTarget === "rightArticle" && currentRightArticle) {
@ -176,6 +193,11 @@ export const CreateRightTab = observer(
setMediaTarget(null);
};
const handleUnlinkPreviewMedia = async () => {
await unlinkPreviewMedia();
setPreviewMedia(null);
};
const handleMediaUploaded = async (media: MediaItemShared) => {
// After UploadMediaDialog finishes
setUploadMediaOpen(false);
@ -273,12 +295,13 @@ export const CreateRightTab = observer(
{/* Main content area: Article Editor or Sight Media Preview */}
{type === "article" && currentRightArticle ? (
<Box className="w-[80%] border border-gray-300 rounded-2xl p-3 flex flex-col gap-2 overflow-hidden">
<Box className="w-[80%] border border-gray-300 p-3 flex flex-col gap-2 overflow-hidden">
<Box className="flex justify-end gap-2 mb-1 flex-shrink-0">
<Button
variant="outlined"
color="warning"
color="primary"
size="small"
startIcon={<Unlink color="white" size={18} />}
onClick={() => {
if (currentRightArticle) {
unlinkRightAritcle(currentRightArticle.id); // Corrected function name
@ -293,22 +316,9 @@ export const CreateRightTab = observer(
variant="contained"
color="error"
size="small"
startIcon={<Trash2 size={18} />}
onClick={async () => {
if (
currentRightArticle &&
window.confirm(
`Удалить статью "${currentRightArticle.heading}" окончательно?`
)
) {
try {
await deleteRightArticle(currentRightArticle.id);
setActiveArticleIndex(null);
setType("media");
toast.success("Статья удалена");
} catch {
toast.error("Не удалось удалить статью");
}
}
setIsDeleteModalOpen(true);
}}
>
Удалить
@ -373,28 +383,30 @@ export const CreateRightTab = observer(
) : type === "media" ? (
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3 relative flex justify-center items-center">
{type === "media" && (
<Box className="w-[80%] border border-gray-300 rounded-2xl relative">
{sight.preview_media && (
<Box className="w-[80%] border border-gray-300 rounded-2xl relative flex items-center justify-center">
{previewMedia && (
<>
<Box className="absolute top-4 right-4">
<Box className="absolute top-4 right-4 z-10">
<button
className="w-10 h-10 flex items-center justify-center"
onClick={unlinkPreviewMedia}
className="w-10 h-10 flex items-center justify-center z-10"
onClick={handleUnlinkPreviewMedia}
>
<X size={20} color="red" />
</button>
</Box>
<MediaViewer
media={{
id: sight.preview_media || "",
media_type: 1,
filename: sight.preview_media || "",
}}
/>
<Box sx={{}}>
<MediaViewer
media={{
id: previewMedia.id || "",
media_type: previewMedia.media_type,
filename: previewMedia.filename || "",
}}
/>
</Box>
</>
)}
{!sight.preview_media && (
{!previewMedia && (
<MediaAreaForSight
onFinishUpload={(mediaId) => {
linkPreviewMedia(mediaId);
@ -505,33 +517,6 @@ export const CreateRightTab = observer(
</Box>
</Paper>
)}
{/* Optional: Preview for sight.preview_media when type === "media" */}
{type === "media" && sight.preview_media && (
<Paper
className="flex-1 flex flex-col rounded-2xl"
elevation={2}
sx={{ height: "75vh", overflow: "hidden" }}
>
<Box
sx={{
width: "100%",
height: "100%",
background: "#877361",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<MediaViewer
media={{
id: sight.preview_media,
filename: sight.preview_media,
media_type: 1,
}}
/>
</Box>
</Paper>
)}
</Box>
</Box>
@ -539,17 +524,15 @@ export const CreateRightTab = observer(
<Box
sx={{
position: "absolute",
bottom: 0,
bottom: "-20px",
left: 0, // ensure it spans from left
right: 0,
padding: 2,
backgroundColor: "background.paper",
borderTop: "1px solid", // Add a subtle top border
borderColor: "divider", // Use theme's divider color
width: "100%",
display: "flex",
justifyContent: "flex-end",
boxShadow: "0 -2px 5px rgba(0,0,0,0.1)", // Optional shadow
}}
>
<Button
@ -557,8 +540,9 @@ export const CreateRightTab = observer(
color="success"
onClick={handleSave}
size="large"
startIcon={<Save color="white" size={18} />}
>
Сохранить достопримечательность
Сохранить
</Button>
</Box>
</Box>
@ -588,6 +572,20 @@ export const CreateRightTab = observer(
}}
onSelectMedia={handleMediaSelectedFromDialog}
/>
<DeleteModal
open={isDeleteModalOpen}
onDelete={async () => {
try {
await deleteRightArticle(currentRightArticle?.id || 0);
setActiveArticleIndex(null);
setType("media");
toast.success("Статья удалена");
} catch {
toast.error("Не удалось удалить статью");
}
}}
onCancel={() => setIsDeleteModalOpen(false)}
/>
</TabPanel>
);
}

View File

@ -18,8 +18,10 @@ import {
SightLanguageInfo,
SightCommonInfo,
UploadMediaDialog,
MEDIA_TYPE_VALUES,
} from "@shared";
import { ImageUploadCard, LanguageSwitcher } from "@widgets";
import { Save } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
@ -335,6 +337,7 @@ export const InformationTab = observer(
<Button
variant="contained"
color="success"
startIcon={<Save color="white" size={18} />}
onClick={async () => {
await updateSight();
toast.success("Достопримечательность сохранена");
@ -371,6 +374,13 @@ export const InformationTab = observer(
setActiveMenuType(null);
}}
onSelectMedia={handleMediaSelect}
mediaType={
activeMenuType
? MEDIA_TYPE_VALUES[
activeMenuType as keyof typeof MEDIA_TYPE_VALUES
]
: undefined
}
/>
<UploadMediaDialog

View File

@ -8,6 +8,7 @@ import {
editSightStore,
SelectArticleModal,
UploadMediaDialog,
Language,
} from "@shared";
import {
LanguageSwitcher,
@ -15,8 +16,16 @@ import {
ReactMarkdownEditor,
MediaArea,
MediaViewer,
DeleteModal,
} from "@widgets";
import { Trash2, ImagePlus } from "lucide-react";
import {
Trash2,
ImagePlus,
Unlink,
Plus,
MousePointer,
Save,
} from "lucide-react";
import { useState, useCallback } from "react";
import { observer } from "mobx-react-lite";
import { toast } from "react-toastify";
@ -40,12 +49,18 @@ export const LeftWidgetTab = observer(
const { language } = languageStore;
const data = sight[language];
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isSelectMediaDialogOpen, setIsSelectMediaDialogOpen] =
useState(false);
const [isSelectArticleDialogOpen, setIsSelectArticleDialogOpen] =
useState(false);
const handleDeleteLeftArticle = useCallback(() => {
deleteLeftArticle(sight.common.left_article);
setIsDeleteModalOpen(false);
}, [deleteLeftArticle, sight.common.left_article]);
const handleMediaSelected = useCallback(
async (media: {
id: string;
@ -130,6 +145,7 @@ export const LeftWidgetTab = observer(
color="primary"
size="small"
style={{ transition: "0" }}
startIcon={<Unlink size={18} />}
onClick={() => {
unlinkLeftArticle();
toast.success("Статья откреплена");
@ -144,8 +160,7 @@ export const LeftWidgetTab = observer(
startIcon={<Trash2 size={18} />}
size="small"
onClick={() => {
deleteLeftArticle(sight.common.left_article);
toast.success("Статья откреплена");
setIsDeleteModalOpen(true);
}}
>
Удалить
@ -157,6 +172,7 @@ export const LeftWidgetTab = observer(
variant="contained"
color="primary"
size="small"
startIcon={<MousePointer color="white" size={18} />}
onClick={() => setIsSelectArticleDialogOpen(true)}
>
Выбрать статью
@ -166,6 +182,7 @@ export const LeftWidgetTab = observer(
color="primary"
size="small"
style={{ transition: "0" }}
startIcon={<Plus color="white" size={18} />}
onClick={() => {
createLeftArticle();
toast.success("Статья создана");
@ -234,7 +251,8 @@ export const LeftWidgetTab = observer(
flex: 1,
display: "flex",
flexDirection: "column",
gap: 1.5,
maxWidth: "320px",
gap: 0.5,
}}
>
<Paper
@ -242,10 +260,9 @@ export const LeftWidgetTab = observer(
sx={{
width: "100%",
minWidth: 320,
maxWidth: 400,
height: "auto",
minHeight: 500,
backgroundColor: "#877361",
background:
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
overflowY: "auto",
padding: 0,
display: "flex",
@ -255,11 +272,10 @@ export const LeftWidgetTab = observer(
<Box
sx={{
width: "100%",
height: 200,
backgroundColor: "grey.300",
height: 175,
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "3px",
}}
>
{data.left.media.length > 0 ? (
@ -277,24 +293,50 @@ export const LeftWidgetTab = observer(
<Box
sx={{
backgroundColor: "#877361",
background:
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
color: "white",
padding: 1.5,
margin: "5px 0px 5px 0px",
display: "flex",
flexDirection: "column",
gap: 1,
padding: 1,
}}
>
<Typography
variant="h5"
component="h2"
sx={{ wordBreak: "break-word" }}
sx={{
wordBreak: "break-word",
fontSize: "24px",
fontWeight: 700,
lineHeight: "120%",
}}
>
{data?.left?.heading || "Название информации"}
</Typography>
<Typography
variant="h6"
component="h2"
sx={{
wordBreak: "break-word",
fontSize: "18px",
lineHeight: "120%",
}}
>
{sight[language as Language].address}
</Typography>
</Box>
{data?.left?.body && (
<Box
sx={{
padding: 2,
padding: 1,
maxHeight: "300px",
overflowY: "scroll",
background:
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
flexGrow: 1,
}}
>
@ -310,6 +352,7 @@ export const LeftWidgetTab = observer(
<Button
variant="contained"
color="success"
startIcon={<Save color="white" size={18} />}
onClick={async () => {
await updateSight();
toast.success("Достопримечательность сохранена");
@ -339,6 +382,11 @@ export const LeftWidgetTab = observer(
onClose={handleCloseArticleDialog}
onSelectArticle={handleSelectArticle}
/>
<DeleteModal
open={isDeleteModalOpen}
onDelete={handleDeleteLeftArticle}
onCancel={() => setIsDeleteModalOpen(false)}
/>
</>
);
}

View File

@ -23,7 +23,7 @@ import {
ReactMarkdownComponent,
ReactMarkdownEditor,
} from "@widgets";
import { ImagePlus, Plus, X } from "lucide-react";
import { ImagePlus, Plus, Save, Trash2, Unlink, X } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
import { toast } from "react-toastify";
@ -49,6 +49,19 @@ export const RightWidgetTab = observer(
createNewRightArticle,
} = editSightStore;
const [previewMedia, setPreviewMedia] = useState<any | null>(null);
useEffect(() => {
if (sight.common.preview_media) {
setPreviewMedia(sight.common.preview_media);
}
}, [sight.common.preview_media]);
const handleUnlinkPreviewMedia = () => {
unlinkPreviewMedia();
setPreviewMedia(null);
};
const [uploadMediaOpen, setUploadMediaOpen] = useState(false);
const { language } = languageStore;
const [type, setType] = useState<"article" | "media">("media");
@ -194,6 +207,7 @@ export const RightWidgetTab = observer(
<Button
variant="contained"
color="primary"
startIcon={<Unlink color="white" size={18} />}
onClick={() => {
unlinkRightArticle(
sight[language].right[activeArticleIndex].id
@ -205,7 +219,8 @@ export const RightWidgetTab = observer(
</Button>
<Button
variant="contained"
color="success"
color="error"
startIcon={<Trash2 size={18} />}
onClick={() => {
deleteRightArticle(
sight[language].right[activeArticleIndex].id
@ -285,31 +300,65 @@ export const RightWidgetTab = observer(
<Box className="w-[80%] border border-gray-300 rounded-2xl relative">
{sight.common.preview_media && (
<>
<Box className="absolute top-4 right-4">
<button
className="w-10 h-10 flex items-center justify-center"
onClick={unlinkPreviewMedia}
>
<X size={20} color="red" />
</button>
</Box>
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3 relative flex justify-center items-center">
{type === "media" && (
<Box className="w-[80%] border border-gray-300 rounded-2xl relative flex items-center justify-center">
{previewMedia && (
<>
<Box className="absolute top-4 right-4 z-10">
<button
className="w-10 h-10 flex items-center justify-center z-10"
onClick={handleUnlinkPreviewMedia}
>
<X size={20} color="red" />
</button>
</Box>
<MediaViewer
media={{
id: sight.common.preview_media || "",
media_type: 1,
filename: sight.common.preview_media || "",
}}
/>
<Box sx={{}}>
<MediaViewer
media={{
id: previewMedia.id || "",
media_type: previewMedia.media_type,
filename: previewMedia.filename || "",
}}
/>
</Box>
</>
)}
{!previewMedia && (
<MediaAreaForSight
onFinishUpload={(mediaId) => {
linkPreviewMedia(mediaId);
}}
onFilesDrop={() => {}}
/>
)}
</Box>
)}
</Box>
</>
)}
{!sight.common.preview_media && (
<MediaAreaForSight
onFinishUpload={(mediaId) => {
linkPreviewMedia(mediaId);
}}
onFilesDrop={() => {}}
/>
<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);
}}
onFilesDrop={() => {}}
/>
</Box>
</Box>
)}
</Box>
)}
@ -423,8 +472,13 @@ export const RightWidgetTab = observer(
justifyContent: "flex-end",
}}
>
<Button variant="contained" color="success" onClick={handleSave}>
Сохранить изменения
<Button
variant="contained"
startIcon={<Save color="white" size={18} />}
color="success"
onClick={handleSave}
>
Сохранить
</Button>
</Box>
</Box>

View File

@ -6,10 +6,10 @@ import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import { authInstance, cityStore, languageStore, sightsStore } from "@shared";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import { Button } from "@mui/material";
import { LanguageSwitcher } from "@widgets";
import { DeleteModal, LanguageSwitcher } from "@widgets";
import { Pencil, Trash2 } from "lucide-react";
import { useNavigate } from "react-router-dom";
@ -29,6 +29,8 @@ export const SightsTable = observer(() => {
const { language } = languageStore;
const { sights, getSights } = sightsStore;
const { cities, getCities } = cityStore;
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [rowId, setRowId] = useState<number | null>(null);
useEffect(() => {
const fetchData = async () => {
@ -92,7 +94,10 @@ export const SightsTable = observer(() => {
</button>
<button
className="rounded-md px-3 py-1.5 transition-transform transform hover:scale-105"
onClick={() => handleDelete(row?.id)}
onClick={() => {
setIsDeleteModalOpen(true);
setRowId(row?.id);
}}
>
<Trash2 size={18} className="text-red-500" />
</button>
@ -103,6 +108,17 @@ export const SightsTable = observer(() => {
</TableBody>
</Table>
</TableContainer>
<DeleteModal
open={isDeleteModalOpen}
onDelete={async () => {
if (rowId) {
await handleDelete(rowId);
}
setIsDeleteModalOpen(false);
setRowId(null);
}}
onCancel={() => setIsDeleteModalOpen(false)}
/>
</>
);
});

View File

@ -12,3 +12,5 @@ export * from "./MediaArea";
export * from "./ModelViewer3D";
export * from "./MediaAreaForSight";
export * from "./ImageUploadCard";
export * from "./LeaveAgree";
export * from "./DeleteModal";