Files
molecular_frontend/src/Components/Layout/Sidebar/Sidebar.tsx
DeOwl 4a2e3e665c
Some checks failed
Build and Deploy React Package / build-and-publish (push) Failing after 2m49s
fix build errors
2026-05-25 14:12:27 +03:00

211 lines
5.8 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
Avatar,
Divider,
Modal,
Stack,
Text,
Title,
UnstyledButton,
useMantineTheme,
} from "@mantine/core";
import "./Sidebar.css";
import {
IconDevicesPc,
IconFileDescription,
IconFlaskFilled,
IconLogout,
IconSettings2,
IconUsers,
type Icon,
type IconProps,
} from "@tabler/icons-react";
import {
Link,
useLocation,
useNavigate,
type NavigateFunction,
} from "react-router";
import { useEffect, useState, type ForwardRefExoticComponent } from "react";
import { routes } from "Routes/Routes";
import keycloak from "Api/Keycloak/Keycloak";
import { useAuthenticationStore } from "Stores/AuthenticationStore";
import CustomButton from "Components/CustomButton/CustomButton";
interface SubtleLinkButtonProps {
link: string;
Icon: ForwardRefExoticComponent<IconProps & React.RefAttributes<Icon>>;
text: string;
color: string;
selected?: boolean;
nav: NavigateFunction;
}
function SubtleLinkButton(props: SubtleLinkButtonProps) {
return (
<Link to={props.link} className="invisible_link">
<CustomButton
icon={<props.Icon color={props.color} size={20} />}
style="subtle"
color={props.selected ? props.color : "contrast"}
text={props.text}
textAlign="left"
onClick={() => {
props.nav(props.link);
}}
/>
</Link>
);
}
function Sidebar() {
const { profile, set_profile_picture_path, profile_picture_path } =
useAuthenticationStore();
const [open, set_open] = useState(false);
const theme = useMantineTheme();
const location = useLocation(); // get current URL
const navigate = useNavigate();
useEffect(() => {
if (!profile) return;
const fetchAvatar = async () => {
try {
const response = await fetch(
`${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user/serve/${profile.id}`,
{
headers: {
Authorization: `Bearer ${keycloak.token}`,
},
},
);
if (response.ok) {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
set_profile_picture_path(url);
}
} catch (error) {
console.error("Failed to load avatar:", error);
}
};
fetchAvatar();
}, [profile]);
return (
<div className="sidebar">
<Modal
opened={open}
onClose={() => set_open(false)}
title=<Title size="lg">Выйти?</Title>
centered
withCloseButton={false}
size="auto"
>
<div className="sidebar_bottom_bottom">
<CustomButton
text="Подтвердить"
onClick={() =>
keycloak.logout({
redirectUri: window.location.origin,
})
}
textSize="lg"
color="contrast"
></CustomButton>
<CustomButton
onClick={() => set_open(false)}
text="Отменить"
textSize="lg"
style="outline"
color="contrast"
></CustomButton>
</div>
</Modal>
<div className="sidebar_top">
<SubtleLinkButton
link={routes.ExperimentsPage.path}
Icon={IconFlaskFilled}
text="Эксперименты"
color={theme.colors.teal[7]}
selected={location.pathname.startsWith(routes.ExperimentsPage.path)}
nav={navigate}
/>
<SubtleLinkButton
link={routes.MachinesPage.path}
Icon={IconDevicesPc}
text="Вычислительные системы"
color={theme.colors.violet[7]}
selected={location.pathname.startsWith(routes.MachinesPage.path)}
nav={navigate}
/>
<SubtleLinkButton
link={routes.TeamsPage.path}
Icon={IconUsers}
text="Команды"
color={theme.colors.grape[7]}
selected={location.pathname.startsWith(routes.TeamsPage.path)}
nav={navigate}
/>
<SubtleLinkButton
link={routes.DocumentationPage.path}
Icon={IconFileDescription}
text="Документация"
color={theme.colors.blue[7]}
selected={location.pathname.startsWith(routes.DocumentationPage.path)}
nav={navigate}
/>
</div>
<div className="sidebar_bottom">
{keycloak.authenticated && (
<>
<div className="sidebar_bottom_top">
<Avatar radius="xl" src={profile_picture_path} />
<Stack className="userInfo">
<Text size="md">{profile?.username}</Text>
<Text size="sm" c="dimmed" truncate="end">
{profile?.email}
</Text>
</Stack>
</div>
<Divider
color="secondaryContrast"
style={{ width: "100%", height: "5px", margin: "5px" }}
/>
<div className="sidebar_bottom_bottom">
<CustomButton
style="subtle"
icon={<IconSettings2 size={26} />}
text="Настройки"
color="contrast"
textSize="lg"
onClick={() => {
navigate(routes.SettingsPage.path);
}}
/>
<CustomButton
style="subtle"
icon={<IconLogout size={26} />}
text="Выйти"
textSize="lg"
color="contrast"
onClick={() => {
set_open(true);
}}
/>
</div>
</>
)}
{!keycloak.authenticated && (
<UnstyledButton onClick={() => keycloak.login()}>
Войти / Зарегестрироваться
</UnstyledButton>
)}
</div>
</div>
);
}
export default Sidebar;