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

@ -1,6 +1,11 @@
import { Box, Tab, Tabs } from "@mui/material";
import { Box, Button, Tab, Tabs } from "@mui/material";
import { articlesStore, cityStore, languageStore } from "@shared";
import { CreateInformationTab, CreateLeftTab, CreateRightTab } from "@widgets";
import {
CreateInformationTab,
CreateLeftTab,
CreateRightTab,
LeaveAgree,
} from "@widgets";
import { useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
@ -11,6 +16,8 @@ function a11yProps(index: number) {
};
}
import { useBlocker } from "react-router";
export const CreateSightPage = observer(() => {
const [value, setValue] = useState(0);
const { getCities } = cityStore;
@ -19,6 +26,11 @@ export const CreateSightPage = observer(() => {
setValue(newValue);
};
let blocker = useBlocker(
({ currentLocation, nextLocation }) =>
true && currentLocation.pathname !== nextLocation.pathname
);
useEffect(() => {
const fetchData = async () => {
await getCities();
@ -34,6 +46,7 @@ export const CreateSightPage = observer(() => {
display: "flex",
flexDirection: "column",
minHeight: "100vh",
z: 10,
}}
>
<Box
@ -66,6 +79,8 @@ export const CreateSightPage = observer(() => {
<CreateLeftTab value={value} index={1} />
<CreateRightTab value={value} index={2} />
</div>
{blocker.state === "blocked" ? <LeaveAgree blocker={blocker} /> : null}
</Box>
);
});

View File

@ -34,6 +34,7 @@ export const EditMediaPage = observer(() => {
const [mediaName, setMediaName] = useState(media?.media_name ?? "");
const [mediaFilename, setMediaFilename] = useState(media?.filename ?? "");
const [mediaType, setMediaType] = useState(media?.media_type ?? 1);
const [availableMediaTypes, setAvailableMediaTypes] = useState<number[]>([]);
useEffect(() => {
if (id) {
@ -48,6 +49,18 @@ export const EditMediaPage = observer(() => {
setMediaName(media.media_name);
setMediaFilename(media.filename);
setMediaType(media.media_type);
// Set available media types based on current file extension
const extension = media.filename.split(".").pop()?.toLowerCase();
if (extension) {
if (["glb", "gltf"].includes(extension)) {
setAvailableMediaTypes([6]); // 3D model
} else if (["jpg", "jpeg", "png", "gif"].includes(extension)) {
setAvailableMediaTypes([1, 3, 4, 5]); // Photo, Icon, Watermark, Panorama
} else if (["mp4", "webm", "mov"].includes(extension)) {
setAvailableMediaTypes([2]); // Video
}
}
}
}, [media]);
@ -76,8 +89,25 @@ export const EditMediaPage = observer(() => {
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
if (files && files.length > 0) {
setNewFile(files[0]);
setMediaFilename(files[0].name);
const file = files[0];
setNewFile(file);
setMediaFilename(file.name);
// Determine media type based on file extension
const extension = file.name.split(".").pop()?.toLowerCase();
if (extension) {
if (["glb", "gltf"].includes(extension)) {
setAvailableMediaTypes([6]); // 3D model
setMediaType(6);
} else if (["jpg", "jpeg", "png", "gif"].includes(extension)) {
setAvailableMediaTypes([1, 3, 4, 5]); // Photo, Icon, Watermark, Panorama
setMediaType(1); // Default to Photo
} else if (["mp4", "webm", "mov"].includes(extension)) {
setAvailableMediaTypes([2]); // Video
setMediaType(2);
}
}
setUploadDialogOpen(true); // Open dialog on file selection
}
};
@ -175,11 +205,21 @@ export const EditMediaPage = observer(() => {
onChange={(e) => setMediaType(Number(e.target.value))}
disabled={isLoading}
>
{Object.entries(MEDIA_TYPE_LABELS).map(([type, label]) => (
<MenuItem key={type} value={Number(type)}>
{label}
</MenuItem>
))}
{availableMediaTypes.length > 0
? availableMediaTypes.map((type) => (
<MenuItem key={type} value={type}>
{
MEDIA_TYPE_LABELS[
type as keyof typeof MEDIA_TYPE_LABELS
]
}
</MenuItem>
))
: Object.entries(MEDIA_TYPE_LABELS).map(([type, label]) => (
<MenuItem key={type} value={Number(type)}>
{label}
</MenuItem>
))}
</Select>
</FormControl>

View File

@ -1,5 +1,5 @@
import { Box, Tab, Tabs } from "@mui/material";
import { InformationTab, RightWidgetTab } from "@widgets";
import { InformationTab, LeaveAgree, RightWidgetTab } from "@widgets";
import { LeftWidgetTab } from "@widgets";
import { useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
@ -9,7 +9,7 @@ import {
editSightStore,
languageStore,
} from "@shared";
import { useParams } from "react-router-dom";
import { useBlocker, useParams } from "react-router-dom";
function a11yProps(index: number) {
return {
@ -26,6 +26,11 @@ export const EditSightPage = observer(() => {
const { id } = useParams();
const { getCities } = cityStore;
let blocker = useBlocker(
({ currentLocation, nextLocation }) =>
true && currentLocation.pathname !== nextLocation.pathname
);
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
@ -82,6 +87,8 @@ export const EditSightPage = observer(() => {
<RightWidgetTab value={value} index={2} />
</div>
)}
{blocker.state === "blocked" ? <LeaveAgree blocker={blocker} /> : null}
</Box>
);
});

View File

@ -28,8 +28,8 @@ import { FeatureLike } from "ol/Feature";
// --- CONFIGURATION ---
export const mapConfig = {
center: [37.6173, 55.7558] as [number, number],
zoom: 10,
center: [30.311, 59.94] as [number, number],
zoom: 13,
};
// --- SVG ICONS ---
@ -1128,7 +1128,7 @@ const MapControls: React.FC<MapControlsProps> = ({
const controls = [
{
mode: "edit",
title: "Редакт.",
title: "Редактировать",
longTitle: "Редактирование",
icon: <EditIcon />,
action: () => mapService.activateEditMode(),
@ -1156,7 +1156,7 @@ const MapControls: React.FC<MapControlsProps> = ({
},
];
return (
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-20 flex flex-wrap justify-center p-2 bg-white/90 backdrop-blur-sm rounded-lg shadow-xl space-x-1 sm:space-x-2">
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-20 flex flex-nowrap justify-center p-2 bg-white/90 backdrop-blur-sm rounded-lg shadow-xl space-x-1 sm:space-x-2">
{controls.map((c) => (
<button
key={c.mode}

View File

@ -2,10 +2,11 @@ import { TableBody } from "@mui/material";
import { TableRow, TableCell } from "@mui/material";
import { Table, TableHead } from "@mui/material";
import { mediaStore, MEDIA_TYPE_LABELS } from "@shared";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import { Eye, Pencil, Trash2 } from "lucide-react";
import { useNavigate } from "react-router-dom";
import { DeleteModal } from "@widgets";
const rows = (media: any[]) => {
return media.map((row) => ({
@ -24,7 +25,8 @@ export const MediaListPage = observer(() => {
}, []);
const currentRows = rows(media);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [rowId, setRowId] = useState<number | null>(null);
return (
<>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
@ -53,7 +55,12 @@ export const MediaListPage = observer(() => {
<Pencil size={20} className="text-blue-500" />
</button>
<button onClick={() => deleteMedia(row.id)}>
<button
onClick={() => {
setIsDeleteModalOpen(true);
setRowId(row.id);
}}
>
<Trash2 size={20} className="text-red-500" />
</button>
</div>
@ -62,6 +69,20 @@ export const MediaListPage = observer(() => {
))}
</TableBody>
</Table>
<DeleteModal
open={isDeleteModalOpen}
onDelete={async () => {
if (rowId) {
await deleteMedia(rowId.toString());
}
setIsDeleteModalOpen(false);
setRowId(null);
}}
onCancel={() => {
setIsDeleteModalOpen(false);
setRowId(null);
}}
/>
</>
);
});