Thên button edit scenarios ở ngoài (góc trái trên)
This commit is contained in:
parent
237a514ae1
commit
ef57da0154
|
|
@ -505,6 +505,8 @@ function App() {
|
|||
setIsOpenAddStation={setIsOpenAddStation}
|
||||
setStationEdit={setStationEdit}
|
||||
tabsData={stations}
|
||||
scenarios={scenarios}
|
||||
setScenarios={setScenarios}
|
||||
panels={stations.map((station) => (
|
||||
<Tabs.Panel
|
||||
className={classes.content}
|
||||
|
|
|
|||
|
|
@ -1,379 +1,335 @@
|
|||
import {
|
||||
ActionIcon,
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
Group,
|
||||
Menu,
|
||||
Tabs,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
DndContext,
|
||||
useSensor,
|
||||
useSensors,
|
||||
PointerSensor,
|
||||
closestCenter,
|
||||
type DragEndEvent,
|
||||
} from "@dnd-kit/core";
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
horizontalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { ActionIcon, Avatar, Box, Button, Flex, Group, Menu, Tabs, Text, Tooltip, UnstyledButton } from "@mantine/core";
|
||||
import { DndContext, useSensor, useSensors, PointerSensor, closestCenter, type DragEndEvent } from "@dnd-kit/core";
|
||||
import { arrayMove, SortableContext, useSortable, horizontalListSortingStrategy } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { useEffect, useMemo, useState, type JSX } from "react";
|
||||
import {
|
||||
IconChevronRight,
|
||||
IconEdit,
|
||||
IconLogout,
|
||||
IconSettings,
|
||||
IconSettingsPlus,
|
||||
IconUsersGroup,
|
||||
} from "@tabler/icons-react";
|
||||
import { IconChevronRight, IconEdit, IconLogout, IconSettings, IconSettingsPlus, IconListDetails, IconUsersGroup } from "@tabler/icons-react";
|
||||
import classes from "./Component.module.css";
|
||||
import type { TStation, TUser } from "../untils/types";
|
||||
import type { IScenario, TStation, TUser } from "../untils/types";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import ModalHistory from "./ModalHistory";
|
||||
import ModalConfig from "./ModalConfig";
|
||||
import DrawerScenario from "./DrawerScenario";
|
||||
|
||||
interface DraggableTabsProps {
|
||||
tabsData: TStation[];
|
||||
panels: JSX.Element[];
|
||||
storageKey?: string;
|
||||
onChange: (activeTabId: string | null) => void;
|
||||
w?: string | number;
|
||||
isStationSettings?: boolean;
|
||||
socket: Socket | null;
|
||||
usersConnecting: TUser[];
|
||||
setIsEditStation: (value: React.SetStateAction<boolean>) => void;
|
||||
setIsOpenAddStation: (value: React.SetStateAction<boolean>) => void;
|
||||
setStationEdit: (value: React.SetStateAction<TStation | undefined>) => void;
|
||||
active: string;
|
||||
setActive: (value: React.SetStateAction<string>) => void;
|
||||
onSendCommand: (value: string) => void;
|
||||
tabsData: TStation[];
|
||||
panels: JSX.Element[];
|
||||
storageKey?: string;
|
||||
onChange: (activeTabId: string | null) => void;
|
||||
w?: string | number;
|
||||
isStationSettings?: boolean;
|
||||
socket: Socket | null;
|
||||
usersConnecting: TUser[];
|
||||
setIsEditStation: (value: React.SetStateAction<boolean>) => void;
|
||||
setIsOpenAddStation: (value: React.SetStateAction<boolean>) => void;
|
||||
setStationEdit: (value: React.SetStateAction<TStation | undefined>) => void;
|
||||
active: string;
|
||||
setActive: (value: React.SetStateAction<string>) => void;
|
||||
onSendCommand: (value: string) => void;
|
||||
scenarios: IScenario[];
|
||||
setScenarios: (value: React.SetStateAction<IScenario[]>) => void;
|
||||
}
|
||||
|
||||
function SortableTab({
|
||||
tab,
|
||||
active,
|
||||
onChange,
|
||||
tab,
|
||||
active,
|
||||
onChange,
|
||||
}: {
|
||||
tab: TStation;
|
||||
active: string | null;
|
||||
onChange: (id: string) => void;
|
||||
isStationSettings?: boolean;
|
||||
tab: TStation;
|
||||
active: string | null;
|
||||
onChange: (id: string) => void;
|
||||
isStationSettings?: boolean;
|
||||
}) {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||
useSortable({ id: tab.id.toString() });
|
||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: tab.id.toString() });
|
||||
|
||||
return (
|
||||
<Tabs.Tab
|
||||
className={classes.tab}
|
||||
ref={setNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
onPointerDown={(e) => {
|
||||
listeners?.onPointerDown?.(e);
|
||||
onChange(tab.id.toString());
|
||||
}}
|
||||
value={tab.id.toString()}
|
||||
style={{
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
cursor: "grab",
|
||||
userSelect: "none",
|
||||
backgroundColor: active === tab.id.toString() ? "#deffde" : "",
|
||||
}}
|
||||
color={active === tab.id.toString() ? "green" : ""}
|
||||
fw={600}
|
||||
fz="md"
|
||||
c="#747474"
|
||||
>
|
||||
<Box className={classes.stationName}>
|
||||
<Text fw={600} fz="md" className={classes.stationText}>
|
||||
{tab.name}
|
||||
</Text>
|
||||
</Box>
|
||||
</Tabs.Tab>
|
||||
);
|
||||
return (
|
||||
<Tabs.Tab
|
||||
className={classes.tab}
|
||||
ref={setNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
onPointerDown={(e) => {
|
||||
listeners?.onPointerDown?.(e);
|
||||
onChange(tab.id.toString());
|
||||
}}
|
||||
value={tab.id.toString()}
|
||||
style={{
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
cursor: "grab",
|
||||
userSelect: "none",
|
||||
backgroundColor: active === tab.id.toString() ? "#deffde" : "",
|
||||
}}
|
||||
color={active === tab.id.toString() ? "green" : ""}
|
||||
fw={600}
|
||||
fz="md"
|
||||
c="#747474">
|
||||
<Box className={classes.stationName}>
|
||||
<Text fw={600} fz="md" className={classes.stationText}>
|
||||
{tab.name}
|
||||
</Text>
|
||||
</Box>
|
||||
</Tabs.Tab>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DraggableTabs({
|
||||
tabsData,
|
||||
panels,
|
||||
storageKey = "draggable-tabs-order",
|
||||
onChange,
|
||||
w,
|
||||
isStationSettings = false,
|
||||
socket,
|
||||
usersConnecting,
|
||||
setIsEditStation,
|
||||
setIsOpenAddStation,
|
||||
setStationEdit,
|
||||
active,
|
||||
setActive,
|
||||
tabsData,
|
||||
panels,
|
||||
storageKey = "draggable-tabs-order",
|
||||
onChange,
|
||||
w,
|
||||
isStationSettings = false,
|
||||
socket,
|
||||
usersConnecting,
|
||||
setIsEditStation,
|
||||
setIsOpenAddStation,
|
||||
setStationEdit,
|
||||
active,
|
||||
setActive,
|
||||
scenarios,
|
||||
setScenarios,
|
||||
}: DraggableTabsProps) {
|
||||
const user = useMemo(() => {
|
||||
return localStorage.getItem("user") &&
|
||||
typeof localStorage.getItem("user") === "string"
|
||||
? JSON.parse(localStorage.getItem("user") || "")
|
||||
: null;
|
||||
}, []);
|
||||
const [tabs, setTabs] = useState<TStation[]>(tabsData);
|
||||
const [isChangeTab, setIsChangeTab] = useState<boolean>(false);
|
||||
const [isSetActive, setIsSetActive] = useState<boolean>(false);
|
||||
const [isHistoryModalOpen, setIsHistoryModalOpen] = useState<boolean>(false);
|
||||
const [openConfig, setOpenConfig] = useState<boolean>(false);
|
||||
const user = useMemo(() => {
|
||||
return localStorage.getItem("user") && typeof localStorage.getItem("user") === "string" ? JSON.parse(localStorage.getItem("user") || "") : null;
|
||||
}, []);
|
||||
const [tabs, setTabs] = useState<TStation[]>(tabsData);
|
||||
const [isChangeTab, setIsChangeTab] = useState<boolean>(false);
|
||||
const [isSetActive, setIsSetActive] = useState<boolean>(false);
|
||||
const [isHistoryModalOpen, setIsHistoryModalOpen] = useState<boolean>(false);
|
||||
const [openConfig, setOpenConfig] = useState<boolean>(false);
|
||||
const [openDrawerScenario, setOpenDrawerScenario] = useState<boolean>(false);
|
||||
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
// Load saved order from localStorage
|
||||
useEffect(() => {
|
||||
if (isChangeTab) {
|
||||
setTabs((pre) =>
|
||||
pre
|
||||
.map((t) => {
|
||||
const updatedTab = tabsData.find((td) => td.id === t.id);
|
||||
return updatedTab ? updatedTab : t;
|
||||
})
|
||||
.filter((t) => (tabsData.find((td) => td.id === t.id) ? true : false))
|
||||
);
|
||||
} else {
|
||||
const saved = localStorage.getItem(storageKey);
|
||||
let tabSelected =
|
||||
tabsData?.length > 0 ? tabsData[0]?.id.toString() : null;
|
||||
if (saved) {
|
||||
try {
|
||||
const order = JSON.parse(saved) as { id: string; index: number }[];
|
||||
// Load saved order from localStorage
|
||||
useEffect(() => {
|
||||
if (isChangeTab) {
|
||||
setTabs((pre) =>
|
||||
pre
|
||||
.map((t) => {
|
||||
const updatedTab = tabsData.find((td) => td.id === t.id);
|
||||
return updatedTab ? updatedTab : t;
|
||||
})
|
||||
.filter((t) => (tabsData.find((td) => td.id === t.id) ? true : false))
|
||||
);
|
||||
} else {
|
||||
const saved = localStorage.getItem(storageKey);
|
||||
let tabSelected = tabsData?.length > 0 ? tabsData[0]?.id.toString() : null;
|
||||
if (saved) {
|
||||
try {
|
||||
const order = JSON.parse(saved) as { id: string; index: number }[];
|
||||
|
||||
// Find the max index in saved order
|
||||
const maxIndex = Math.max(...order.map((o) => o.index), 0);
|
||||
// Find the max index in saved order
|
||||
const maxIndex = Math.max(...order.map((o) => o.index), 0);
|
||||
|
||||
const sorted = [...tabsData].sort((a, b) => {
|
||||
const aOrder = order.find(
|
||||
(o) => o.id.toString() === a.id.toString()
|
||||
)?.index;
|
||||
const bOrder = order.find(
|
||||
(o) => o.id.toString() === b.id.toString()
|
||||
)?.index;
|
||||
const sorted = [...tabsData].sort((a, b) => {
|
||||
const aOrder = order.find((o) => o.id.toString() === a.id.toString())?.index;
|
||||
const bOrder = order.find((o) => o.id.toString() === b.id.toString())?.index;
|
||||
|
||||
// If not found, assign index after all existing ones
|
||||
const aIndex = aOrder !== undefined ? aOrder : maxIndex + 1;
|
||||
const bIndex = bOrder !== undefined ? bOrder : maxIndex + 1;
|
||||
// If not found, assign index after all existing ones
|
||||
const aIndex = aOrder !== undefined ? aOrder : maxIndex + 1;
|
||||
const bIndex = bOrder !== undefined ? bOrder : maxIndex + 1;
|
||||
|
||||
return aIndex - bIndex;
|
||||
});
|
||||
return aIndex - bIndex;
|
||||
});
|
||||
|
||||
tabSelected = sorted?.length > 0 ? sorted[0]?.id.toString() : null;
|
||||
setTabs(sorted);
|
||||
} catch {
|
||||
setTabs(tabsData);
|
||||
}
|
||||
} else {
|
||||
setTabs(tabsData);
|
||||
}
|
||||
tabSelected = sorted?.length > 0 ? sorted[0]?.id.toString() : null;
|
||||
setTabs(sorted);
|
||||
} catch {
|
||||
setTabs(tabsData);
|
||||
}
|
||||
} else {
|
||||
setTabs(tabsData);
|
||||
}
|
||||
|
||||
if (!isSetActive && tabSelected) {
|
||||
setActive(tabSelected);
|
||||
setTimeout(() => {
|
||||
onChange(tabSelected);
|
||||
}, 100);
|
||||
setIsSetActive(true);
|
||||
}
|
||||
}
|
||||
}, [tabsData, storageKey]);
|
||||
if (!isSetActive && tabSelected) {
|
||||
setActive(tabSelected);
|
||||
setTimeout(() => {
|
||||
onChange(tabSelected);
|
||||
}, 100);
|
||||
setIsSetActive(true);
|
||||
}
|
||||
}
|
||||
}, [tabsData, storageKey]);
|
||||
|
||||
// Handle reorder
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active: dragActive, over } = event;
|
||||
if (dragActive.id !== over?.id && over?.id) {
|
||||
const oldIndex = tabs.findIndex(
|
||||
(t) => t.id.toString() === dragActive.id.toString()
|
||||
);
|
||||
const newIndex = tabs.findIndex(
|
||||
(t) => t.id.toString() === over?.id.toString()
|
||||
);
|
||||
const newTabs = arrayMove(tabs, oldIndex, newIndex);
|
||||
setTabs(newTabs);
|
||||
// Handle reorder
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active: dragActive, over } = event;
|
||||
if (dragActive.id !== over?.id && over?.id) {
|
||||
const oldIndex = tabs.findIndex((t) => t.id.toString() === dragActive.id.toString());
|
||||
const newIndex = tabs.findIndex((t) => t.id.toString() === over?.id.toString());
|
||||
const newTabs = arrayMove(tabs, oldIndex, newIndex);
|
||||
setTabs(newTabs);
|
||||
|
||||
const order = newTabs.map((t, i) => ({ id: t.id, index: i }));
|
||||
localStorage.setItem(storageKey, JSON.stringify(order));
|
||||
}
|
||||
};
|
||||
const order = newTabs.map((t, i) => ({ id: t.id, index: i }));
|
||||
localStorage.setItem(storageKey, JSON.stringify(order));
|
||||
}
|
||||
};
|
||||
|
||||
// Clean up
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setIsChangeTab(false);
|
||||
setIsSetActive(false);
|
||||
setTabs([]);
|
||||
setActive("0");
|
||||
};
|
||||
}, []);
|
||||
// Clean up
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setIsChangeTab(false);
|
||||
setIsSetActive(false);
|
||||
setTabs([]);
|
||||
setActive("0");
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<Tabs
|
||||
value={active}
|
||||
onChange={(val) => {
|
||||
setIsChangeTab(true);
|
||||
onChange(val);
|
||||
setActive(val || "0");
|
||||
}}
|
||||
w={w}
|
||||
>
|
||||
<Flex justify={"space-between"}>
|
||||
<Flex style={{ marginTop: "8px" }}>
|
||||
<ActionIcon
|
||||
title="Setting"
|
||||
variant="outline"
|
||||
onClick={() => setOpenConfig(true)}
|
||||
>
|
||||
<IconSettings />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
<Tabs.List className={classes.list}>
|
||||
<SortableContext
|
||||
items={tabs}
|
||||
strategy={horizontalListSortingStrategy}
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<SortableTab
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
active={active}
|
||||
onChange={(id) => {
|
||||
setIsChangeTab(true);
|
||||
onChange(id);
|
||||
setActive(id);
|
||||
}}
|
||||
isStationSettings={isStationSettings}
|
||||
/>
|
||||
))}
|
||||
</SortableContext>
|
||||
return (
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<Tabs
|
||||
value={active}
|
||||
onChange={(val) => {
|
||||
setIsChangeTab(true);
|
||||
onChange(val);
|
||||
setActive(val || "0");
|
||||
}}
|
||||
w={w}>
|
||||
<Flex justify={"space-between"}>
|
||||
<Flex style={{ marginTop: "8px" }} gap="xs" align="center">
|
||||
<ActionIcon title="Setting" variant="outline" onClick={() => setOpenConfig(true)}>
|
||||
<IconSettings />
|
||||
</ActionIcon>
|
||||
<Button
|
||||
color="yellow"
|
||||
variant="filled"
|
||||
size="xs"
|
||||
leftSection={<IconListDetails size={16} />}
|
||||
onClick={() => setOpenDrawerScenario(true)}>
|
||||
Scenario
|
||||
</Button>
|
||||
</Flex>
|
||||
<Tabs.List className={classes.list}>
|
||||
<SortableContext items={tabs} strategy={horizontalListSortingStrategy}>
|
||||
{tabs.map((tab) => (
|
||||
<SortableTab
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
active={active}
|
||||
onChange={(id) => {
|
||||
setIsChangeTab(true);
|
||||
onChange(id);
|
||||
setActive(id);
|
||||
}}
|
||||
isStationSettings={isStationSettings}
|
||||
/>
|
||||
))}
|
||||
</SortableContext>
|
||||
|
||||
<Flex gap={"md"} ms={"xs"} align={"center"}>
|
||||
{Number(active) ? (
|
||||
<ActionIcon
|
||||
title="Edit Station"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setStationEdit(
|
||||
tabsData.find((el) => el.id === Number(active))
|
||||
);
|
||||
setIsOpenAddStation(true);
|
||||
setIsEditStation(true);
|
||||
}}
|
||||
>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<ActionIcon
|
||||
title="Add Station"
|
||||
variant="outline"
|
||||
color="green"
|
||||
onClick={() => {
|
||||
setIsOpenAddStation(true);
|
||||
setIsEditStation(false);
|
||||
setStationEdit(undefined);
|
||||
}}
|
||||
>
|
||||
<IconSettingsPlus />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</Tabs.List>
|
||||
<Flex align={"center"}>
|
||||
<Button
|
||||
variant="outline"
|
||||
style={{ height: "26px", width: "80px", marginRight: "20px" }}
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setIsHistoryModalOpen(true);
|
||||
}}
|
||||
>
|
||||
History
|
||||
</Button>
|
||||
<Tooltip
|
||||
withArrow
|
||||
label={usersConnecting.map((el) => (
|
||||
<Text key={el.userId || el.id}>{el.userName}</Text>
|
||||
))}
|
||||
>
|
||||
<Avatar radius="xl" me={"sm"}>
|
||||
<IconUsersGroup color="green" />
|
||||
</Avatar>
|
||||
</Tooltip>
|
||||
<Menu withArrow>
|
||||
<Menu.Target>
|
||||
<UnstyledButton
|
||||
style={{
|
||||
padding: "var(--mantine-spacing-md)",
|
||||
color: "var(--mantine-color-text)",
|
||||
borderRadius: "var(--mantine-radius-sm)",
|
||||
}}
|
||||
>
|
||||
<Group>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={500}>
|
||||
{user?.userName || user?.user_name || ""}
|
||||
</Text>
|
||||
<Flex gap={"md"} ms={"xs"} align={"center"}>
|
||||
{Number(active) ? (
|
||||
<ActionIcon
|
||||
title="Edit Station"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setStationEdit(tabsData.find((el) => el.id === Number(active)));
|
||||
setIsOpenAddStation(true);
|
||||
setIsEditStation(true);
|
||||
}}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<ActionIcon
|
||||
title="Add Station"
|
||||
variant="outline"
|
||||
color="green"
|
||||
onClick={() => {
|
||||
setIsOpenAddStation(true);
|
||||
setIsEditStation(false);
|
||||
setStationEdit(undefined);
|
||||
}}>
|
||||
<IconSettingsPlus />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</Tabs.List>
|
||||
<Flex align={"center"}>
|
||||
<Button
|
||||
variant="outline"
|
||||
style={{ height: "26px", width: "80px", marginRight: "20px" }}
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setIsHistoryModalOpen(true);
|
||||
}}>
|
||||
History
|
||||
</Button>
|
||||
<Tooltip
|
||||
withArrow
|
||||
label={usersConnecting.map((el) => (
|
||||
<Text key={el.userId || el.id}>{el.userName}</Text>
|
||||
))}>
|
||||
<Avatar radius="xl" me={"sm"}>
|
||||
<IconUsersGroup color="green" />
|
||||
</Avatar>
|
||||
</Tooltip>
|
||||
<Menu withArrow>
|
||||
<Menu.Target>
|
||||
<UnstyledButton
|
||||
style={{
|
||||
padding: "var(--mantine-spacing-md)",
|
||||
color: "var(--mantine-color-text)",
|
||||
borderRadius: "var(--mantine-radius-sm)",
|
||||
}}>
|
||||
<Group>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={500}>
|
||||
{user?.userName || user?.user_name || ""}
|
||||
</Text>
|
||||
|
||||
<Text c="dimmed" size="xs">
|
||||
{user?.email}
|
||||
</Text>
|
||||
</div>
|
||||
<Text c="dimmed" size="xs">
|
||||
{user?.email}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<IconChevronRight size={16} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
style={{ width: "150px" }}
|
||||
onClick={() => {
|
||||
localStorage.removeItem("user");
|
||||
window.location.href = "/";
|
||||
socket?.disconnect();
|
||||
}}
|
||||
color="red"
|
||||
leftSection={<IconLogout size={16} stroke={1.5} />}
|
||||
>
|
||||
Logout
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<IconChevronRight size={16} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
style={{ width: "150px" }}
|
||||
onClick={() => {
|
||||
localStorage.removeItem("user");
|
||||
window.location.href = "/";
|
||||
socket?.disconnect();
|
||||
}}
|
||||
color="red"
|
||||
leftSection={<IconLogout size={16} stroke={1.5} />}>
|
||||
Logout
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{panels}
|
||||
</Tabs>
|
||||
{panels}
|
||||
</Tabs>
|
||||
|
||||
<ModalHistory
|
||||
opened={isHistoryModalOpen}
|
||||
onClose={() => setIsHistoryModalOpen(false)}
|
||||
socket={socket}
|
||||
stationIds={tabs.map((el) => el.id)}
|
||||
tabs={tabs}
|
||||
/>
|
||||
<ModalHistory
|
||||
opened={isHistoryModalOpen}
|
||||
onClose={() => setIsHistoryModalOpen(false)}
|
||||
socket={socket}
|
||||
stationIds={tabs.map((el) => el.id)}
|
||||
tabs={tabs}
|
||||
/>
|
||||
|
||||
<ModalConfig
|
||||
opened={openConfig}
|
||||
onClose={() => setOpenConfig(false)}
|
||||
onSave={() => {
|
||||
onChange(active);
|
||||
}}
|
||||
/>
|
||||
</DndContext>
|
||||
);
|
||||
<ModalConfig
|
||||
opened={openConfig}
|
||||
onClose={() => setOpenConfig(false)}
|
||||
onSave={() => {
|
||||
onChange(active);
|
||||
}}
|
||||
/>
|
||||
|
||||
<DrawerScenario
|
||||
scenarios={scenarios}
|
||||
setScenarios={setScenarios}
|
||||
externalOpened={openDrawerScenario}
|
||||
onExternalClose={() => setOpenDrawerScenario(false)}
|
||||
/>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue