feat: Delete user
preview + snapshot
preview + coordinates update
This commit is contained in:
@ -19,9 +19,7 @@ import {
|
||||
VehicleListPage,
|
||||
ArticleListPage,
|
||||
CityPreviewPage,
|
||||
UserPreviewPage,
|
||||
CountryPreviewPage,
|
||||
SnapshotPreviewPage,
|
||||
VehiclePreviewPage,
|
||||
CarrierPreviewPage,
|
||||
SnapshotCreatePage,
|
||||
@ -132,12 +130,10 @@ const router = createBrowserRouter([
|
||||
|
||||
// User
|
||||
{ path: "user", element: <UserListPage /> },
|
||||
{ path: "user/:id", element: <UserPreviewPage /> },
|
||||
|
||||
// Snapshot
|
||||
{ path: "snapshot", element: <SnapshotListPage /> },
|
||||
{ path: "snapshot/create", element: <SnapshotCreatePage /> },
|
||||
{ path: "snapshot/:id", element: <SnapshotPreviewPage /> },
|
||||
|
||||
// Carrier
|
||||
{ path: "carrier", element: <CarrierListPage /> },
|
||||
|
@ -3,3 +3,7 @@
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mde-preview {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
@ -1135,7 +1135,7 @@ const MapControls: React.FC<MapControlsProps> = ({
|
||||
},
|
||||
{
|
||||
mode: "statistics",
|
||||
title: "Инфо",
|
||||
title: "Информация",
|
||||
longTitle: "Информация",
|
||||
icon: <StatsIcon />,
|
||||
action: () => mapService.activateStatisticsMode(),
|
||||
|
@ -53,9 +53,7 @@ export const SnapshotListPage = observer(() => {
|
||||
>
|
||||
<DatabaseBackup size={20} className="text-blue-500" />
|
||||
</button>
|
||||
<button onClick={() => navigate(`/snapshot/${params.row.id}`)}>
|
||||
<Eye size={20} className="text-green-500" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(true);
|
||||
|
@ -1,58 +0,0 @@
|
||||
import { Paper } from "@mui/material";
|
||||
import { snapshotStore } from "@shared";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
export const SnapshotPreviewPage = observer(() => {
|
||||
const { id } = useParams();
|
||||
const { getSnapshot, snapshot } = snapshotStore;
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await getSnapshot(id as string);
|
||||
})();
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<Paper className="w-full h-full p-3 flex flex-col gap-10">
|
||||
<div className="flex justify-between items-center">
|
||||
<button
|
||||
className="flex items-center gap-2"
|
||||
onClick={() => navigate(-1)}
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
Назад
|
||||
</button>
|
||||
{/* <div className="flex gap-2">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => navigate(`/snapshot/${id}/edit`)}
|
||||
startIcon={<Pencil size={20} />}
|
||||
>
|
||||
Редактировать
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => navigate(`/snapshot/${id}/delete`)}
|
||||
startIcon={<Trash2 size={20} />}
|
||||
>
|
||||
Удалить
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
{snapshot && (
|
||||
<div className="flex flex-col gap-10 w-full">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-bold">Название</h1>
|
||||
<p>{snapshot?.Name}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Paper>
|
||||
);
|
||||
});
|
@ -1,3 +1,3 @@
|
||||
export * from "./SnapshotListPage";
|
||||
export * from "./SnapshotPreviewPage";
|
||||
|
||||
export * from "./SnapshotCreatePage";
|
||||
|
@ -56,9 +56,6 @@ export const UserListPage = observer(() => {
|
||||
renderCell: (params: GridRenderCellParams) => {
|
||||
return (
|
||||
<div className="flex h-full gap-7 justify-center items-center">
|
||||
<button onClick={() => navigate(`/user/${params.row.id}`)}>
|
||||
<Eye size={20} className="text-green-500" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(true);
|
||||
|
@ -1,74 +0,0 @@
|
||||
import { Paper } from "@mui/material";
|
||||
import { userStore } from "@shared";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
export const UserPreviewPage = observer(() => {
|
||||
const { id } = useParams();
|
||||
const { getUser, user } = userStore;
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await getUser(Number(id));
|
||||
})();
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<Paper className="w-full h-full p-3 flex flex-col gap-10">
|
||||
<div className="flex justify-between items-center">
|
||||
<button
|
||||
className="flex items-center gap-2"
|
||||
onClick={() => navigate(-1)}
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
Назад
|
||||
</button>
|
||||
{/* <div className="flex gap-2">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => navigate(`/user/${id}/edit`)}
|
||||
startIcon={<Pencil size={20} />}
|
||||
>
|
||||
Редактировать
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => navigate(`/user/${id}/delete`)}
|
||||
startIcon={<Trash2 size={20} />}
|
||||
>
|
||||
Удалить
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
{user && (
|
||||
<div className="flex flex-col gap-10 w-full">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-bold">Название</h1>
|
||||
<p>{user?.name}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-bold">Email</h1>
|
||||
<p>{user?.email}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-lg font-bold">Роль</h1>
|
||||
<p
|
||||
className={
|
||||
user?.is_admin === true ? "text-green-500" : "text-red-500"
|
||||
}
|
||||
>
|
||||
{user?.is_admin ? "Администратор" : "Пользователь"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Paper>
|
||||
);
|
||||
});
|
@ -1,2 +1 @@
|
||||
export * from "./UserListPage";
|
||||
export * from "./UserPreviewPage";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useMemo } from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import SimpleMDE, { SimpleMDEReactProps } from "react-simplemde-editor";
|
||||
import SimpleMDE from "react-simplemde-editor";
|
||||
import "easymde/dist/easymde.min.css";
|
||||
|
||||
const StyledMarkdownEditor = styled("div")(({ theme }) => ({
|
||||
@ -19,7 +20,6 @@ const StyledMarkdownEditor = styled("div")(({ theme }) => ({
|
||||
"& .editor-statusbar": {
|
||||
display: "none",
|
||||
},
|
||||
// Стили для самого редактора
|
||||
"& .CodeMirror": {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
color: theme.palette.text.primary,
|
||||
@ -33,14 +33,12 @@ const StyledMarkdownEditor = styled("div")(({ theme }) => ({
|
||||
minHeight: "200px",
|
||||
maxHeight: "500px",
|
||||
},
|
||||
// Стили для текста в редакторе
|
||||
"& .CodeMirror-selected": {
|
||||
backgroundColor: `${theme.palette.action.selected} !important`,
|
||||
},
|
||||
"& .CodeMirror-cursor": {
|
||||
borderLeftColor: theme.palette.text.primary,
|
||||
},
|
||||
// Стили для markdown разметки
|
||||
"& .cm-header": {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
@ -57,13 +55,11 @@ const StyledMarkdownEditor = styled("div")(({ theme }) => ({
|
||||
"& .cm-formatting": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
|
||||
"& .CodeMirror .editor-preview-full": {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
color: theme.palette.text.primary,
|
||||
borderColor: theme.palette.divider,
|
||||
},
|
||||
|
||||
"& .EasyMDEContainer": {
|
||||
position: "relative",
|
||||
zIndex: 1000,
|
||||
@ -73,45 +69,53 @@ const StyledMarkdownEditor = styled("div")(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export const ReactMarkdownEditor = (props: SimpleMDEReactProps) => {
|
||||
if (props.options)
|
||||
props.options.toolbar = [
|
||||
"bold",
|
||||
"italic",
|
||||
"strikethrough",
|
||||
{
|
||||
name: "Underline",
|
||||
action: (editor: any) => {
|
||||
const cm = editor.codemirror;
|
||||
let output = "";
|
||||
const selectedText = cm.getSelection();
|
||||
const text = selectedText ?? "placeholder";
|
||||
|
||||
output = "<u>" + text + "</u>";
|
||||
cm.replaceSelection(output);
|
||||
export const ReactMarkdownEditor = ({
|
||||
options: incomingOptions,
|
||||
...restProps
|
||||
}: any) => {
|
||||
const mergedOptions = useMemo(() => {
|
||||
return {
|
||||
...incomingOptions,
|
||||
forceSync: true,
|
||||
spellChecker: false,
|
||||
toolbar: [
|
||||
"bold",
|
||||
"italic",
|
||||
"strikethrough",
|
||||
{
|
||||
name: "Underline",
|
||||
action: (editor: any) => {
|
||||
const cm = editor.codemirror;
|
||||
const selectedText = cm.getSelection();
|
||||
const text = selectedText || "placeholder";
|
||||
const output = `<u>${text}</u>`;
|
||||
cm.replaceSelection(output);
|
||||
},
|
||||
className: "fa fa-underline",
|
||||
title: "Underline (Ctrl/Cmd-Alt-U)",
|
||||
},
|
||||
className: "fa fa-underline", // Look for a suitable icon
|
||||
title: "Underline (Ctrl/Cmd-Alt-U)",
|
||||
},
|
||||
"heading",
|
||||
"quote",
|
||||
"unordered-list",
|
||||
"ordered-list",
|
||||
"link",
|
||||
"image",
|
||||
"code",
|
||||
"table",
|
||||
"horizontal-rule",
|
||||
"preview",
|
||||
"fullscreen",
|
||||
"guide",
|
||||
];
|
||||
"heading",
|
||||
"quote",
|
||||
"unordered-list",
|
||||
"ordered-list",
|
||||
"link",
|
||||
"image",
|
||||
"code",
|
||||
"table",
|
||||
"horizontal-rule",
|
||||
"preview",
|
||||
"fullscreen",
|
||||
"guide",
|
||||
],
|
||||
};
|
||||
}, []); // создаётся один раз
|
||||
|
||||
return (
|
||||
<StyledMarkdownEditor
|
||||
className="my-markdown-editor"
|
||||
sx={{ marginTop: 1.5, marginBottom: 3 }}
|
||||
>
|
||||
<SimpleMDE {...props} />
|
||||
<SimpleMDE options={mergedOptions} {...restProps} />
|
||||
</StyledMarkdownEditor>
|
||||
);
|
||||
};
|
||||
|
@ -40,7 +40,7 @@ export const CreateInformationTab = observer(
|
||||
const data = sight[language];
|
||||
|
||||
const [, setCity] = useState<number>(sight.city_id ?? 0);
|
||||
const [coordinates, setCoordinates] = useState<string>(`0 0`);
|
||||
const [coordinates, setCoordinates] = useState<string>(`0, 0`);
|
||||
|
||||
// Menu state for each media button
|
||||
const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
|
||||
@ -60,7 +60,7 @@ export const CreateInformationTab = observer(
|
||||
useEffect(() => {
|
||||
// Показывать только при инициализации (не менять при ошибках пользователя)
|
||||
if (sight.latitude !== 0 || sight.longitude !== 0) {
|
||||
setCoordinates(`${sight.latitude} ${sight.longitude}`);
|
||||
setCoordinates(`${sight.latitude}, ${sight.longitude}`);
|
||||
}
|
||||
// если координаты обнулились — оставить поле как есть
|
||||
}, [sight.latitude, sight.longitude]);
|
||||
|
@ -418,7 +418,7 @@ export const CreateRightTab = observer(
|
||||
<Box sx={{ minHeight: 200, flexGrow: 1 }}>
|
||||
<ReactMarkdownEditor
|
||||
value={currentRightArticle.body}
|
||||
onChange={(mdValue) =>
|
||||
onChange={(mdValue: any) =>
|
||||
activeArticleIndex !== null &&
|
||||
updateRightArticleInfo(
|
||||
activeArticleIndex,
|
||||
|
@ -42,7 +42,7 @@ export const InformationTab = observer(
|
||||
const { sight, updateSightInfo, updateSight } = editSightStore;
|
||||
|
||||
const [, setCity] = useState<number>(sight.common.city_id ?? 0);
|
||||
const [coordinates, setCoordinates] = useState<string>(`0 0`);
|
||||
const [coordinates, setCoordinates] = useState<string>(`0, 0`);
|
||||
|
||||
// Menu state for each media button
|
||||
const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
|
||||
@ -54,7 +54,7 @@ export const InformationTab = observer(
|
||||
useEffect(() => {
|
||||
// Показывать только при инициализации (не менять при ошибках пользователя)
|
||||
if (sight.common.latitude !== 0 || sight.common.longitude !== 0) {
|
||||
setCoordinates(`${sight.common.latitude} ${sight.common.longitude}`);
|
||||
setCoordinates(`${sight.common.latitude}, ${sight.common.longitude}`);
|
||||
}
|
||||
// если координаты обнулились — оставить поле как есть
|
||||
}, [sight.common.latitude, sight.common.longitude]);
|
||||
@ -178,10 +178,12 @@ export const InformationTab = observer(
|
||||
label="Координаты"
|
||||
value={coordinates}
|
||||
onChange={(e) => {
|
||||
const input = e.target.value;
|
||||
setCoordinates(input); // показываем как есть
|
||||
const newValue = e.target.value;
|
||||
setCoordinates(newValue); // сохраняем ввод пользователя как есть
|
||||
|
||||
const [latStr, lonStr] = input.split(/\s+/); // учитываем любые пробелы
|
||||
// Обрабатываем значение для сохранения
|
||||
const input = newValue.replace(/,/g, " ").trim();
|
||||
const [latStr, lonStr] = input.split(/\s+/);
|
||||
|
||||
const lat = parseFloat(latStr);
|
||||
const lon = parseFloat(lonStr);
|
||||
@ -212,7 +214,7 @@ export const InformationTab = observer(
|
||||
}}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Введите координаты в формате: широта долгота"
|
||||
placeholder="Введите координаты в формате: широта долгота (можно использовать запятые или пробелы)"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
Reference in New Issue
Block a user