added comp_systems and modular instances frontend

This commit is contained in:
2026-05-06 14:46:16 +03:00
parent 45c157b910
commit 26623dc2c1
44 changed files with 7739 additions and 899 deletions

View File

@@ -36,27 +36,48 @@ function ExperimentsListCard(props: {
<Text mb="sm" size="md">
Команда: {props.team?.team_name}
</Text>
<Pill className="ExperimentPill">
<Text size="md">Статус: {props.experiment.experiment_status}</Text>
</Pill>
<div style={{ display: "flex", gap: "10px" }}>
<Text size="md">Статус: </Text>
<Pill style={{ backgroundColor: "var(--mantine-color-yellow-5)" }}>
<Text size="md">{props.experiment.experiment_status}</Text>
</Pill>
</div>
</div>
<div className="ExperimentSectionWithLine">
<SimpleGrid cols={2} verticalSpacing="0px">
<Text>Задачи:</Text>
{experiment_tasks.map((task: TaskData<any>) => {
return (
<Text
size="md"
style={{
textWrap: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
}}
>
{task.name}
</Text>
);
})}
{experiment_tasks.map(
(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
task: TaskData<any>,
) => {
return (
<Pill
size="md"
style={{
backgroundColor: "transparent",
border: "2px solid black",
alignContent: "center",
alignItems: "center",
justifyContent: "center",
justifyItems: "center",
}}
>
<Text
size="md"
style={{
textWrap: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
}}
>
{task.name}
</Text>
</Pill>
);
},
)}
{experiment_tasks.length == 0 ? (
<Pill size="md" className="ExperimentPill2">
Нет Задач
@@ -70,10 +91,14 @@ function ExperimentsListCard(props: {
<div className="TopRightContainer">
<div className="dateContainer">
<Text size="sm">
{props.experiment.date_created.toLocaleDateString("ru")}{" "}
{new Date(
props.experiment.date_created + "Z",
).toLocaleDateString("ru")}{" "}
</Text>
<Text size="sm">
{props.experiment.date_created.toLocaleTimeString("ru")}
{new Date(
props.experiment.date_created + "Z",
).toLocaleTimeString("ru")}
</Text>
</div>
<UnstyledButton

View File

@@ -0,0 +1,56 @@
.MachinesListCard {
height: 85px;
padding: 12px;
}
.MachineSection {
border-right: 1px solid var(--mantine-color-contrast-filled);
padding-right: 10px;
}
.MachinePill {
background-color: var(--mantine-color-contrast-filled);
color: var(--mantine-color-primary-filled);
}
.MachinePill2 {
border: 2px solid var(--mantine-color-accent-filled);
background-color: transparent;
color: var(--mantine-color-accent-filled);
* {
width: 100%;
text-align: center;
}
}
.membersPills {
display: flex;
flex-direction: row;
gap: 10px;
}
.dateContainer {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: right;
gap: 5px;
.p {
text-wrap: nowrap;
}
}
.TopRightContainer {
justify-content: right;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 15px;
justify-items: center;
}
.RightMachineSection {
text-align: right;
padding-right: 10px;
display: flex;
flex-direction: column;
}

View File

@@ -0,0 +1,193 @@
import { Card, Pill, Text, UnstyledButton } from "@mantine/core";
import "./MachinesListCard.css";
import { useNavigate } from "react-router";
import { IconCancel, IconCheck, IconTrash } from "@tabler/icons-react";
import type { MouseEvent } from "react";
import { notifications } from "@mantine/notifications";
import { useDeviceStore } from "Stores/DeviceStore";
import { deleteComputationalSystem } from "Api/QuantumBackend/MachineManagment";
import type { SystemWithTeams } from "Types/Machine/Machine";
const colors = {
ONLINE: "var(--mantine-color-green-7)",
OFFLINE: "var(--mantine-color-red-7)",
BUSY: "var(--mantine-color-yellow-5)",
};
function MachinesListCard(props: SystemWithTeams) {
const navigate = useNavigate();
const { setTotalDevices, totalDevices, removeDevice } = useDeviceStore();
const handleDelete = () => {
// TODO: fix delete
deleteComputationalSystem({ system_id: props.system.id })
.then(() => {
removeDevice(props.system.id);
setTotalDevices(totalDevices - 1);
notifications.show({
radius: "md",
title: "Устройство удалено успешно",
message: "",
icon: <IconCheck />,
style: { paddingLeft: "5px" },
});
})
.catch(() => {
notifications.show({
radius: "md",
color: "red",
title: "Не удалось удалить устройство",
message: "",
icon: <IconCancel />,
style: { paddingLeft: "5px" },
});
});
};
return (
<Card
className="MachinesListCard"
onClick={() => {
navigate(props.system.id.toString());
}}
orientation="horizontal"
>
<Card.Section inheritPadding pl="sm" pr="xl">
<Text mb="sm" size="md" style={{ textDecorationLine: "underline" }}>
Устройство: {props.system.system_name}
</Text>
<div style={{ display: "flex" }}>
<Text mb="sm" size="md">
Статус:
</Text>
<Pill
//className="MachinePill"
style={{
marginLeft: "10px",
backgroundColor: colors[props.system.status || "ONLINE"],
}}
>
<Text size="md">{props.system.status}</Text>
</Pill>
</div>
</Card.Section>
<Card.Section
withBorder
inheritPadding
px="xs"
style={{ textAlign: "right" }}
>
<Text mb="sm" size="md">
Дата обновления статуса:
</Text>
<div style={{ display: "flex", gap: "10px", justifyContent: "right" }}>
<Text size="sm">
{new Date(props.system.created_at + "Z").toLocaleDateString(
"ru",
)}{" "}
</Text>
<Text size="sm">
{new Date(props.system.created_at + "Z").toLocaleTimeString("ru")}
</Text>
</div>
</Card.Section>
<Card.Section
inheritPadding
px="xs"
withBorder
style={{
flexGrow: "1",
gap: "15px",
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
}}
>
<Text>Команды с доступом:</Text>
<div
className="membersPills"
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
}}
>
{props.teams.map((team) => {
return (
<Pill className="MachinePill" style={{ marginLeft: "10px" }}>
<Text
size="md"
style={{
textWrap: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
}}
>
{team.team.team_name}
</Text>
</Pill>
);
})}
{props.teams.length == 0 && (
<Pill className="MachinePill2" style={{ marginLeft: "10px" }}>
<Text
size="md"
style={{
textWrap: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
}}
>
Команд нет
</Text>
</Pill>
)}
</div>
</Card.Section>
<Card.Section
inheritPadding
px="md"
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
}}
>
<div className="TopRightContainer">
<div className="dateContainer">
<Text size="sm">
{new Date(props.system.created_at + "Z").toLocaleDateString(
"ru",
)}{" "}
</Text>
<Text size="sm">
{new Date(props.system.created_at + "Z").toLocaleTimeString("ru")}
</Text>
</div>
<UnstyledButton
onClick={(e: MouseEvent) => {
e.stopPropagation();
handleDelete();
}}
style={{ cursor: "pointer" }}
>
<IconTrash size={20} />
</UnstyledButton>
</div>
<div className="TopRightContainer">
<div className="dateContainer">
<Text size="sm">Максимальное количество кубит:</Text>
<Text size="sm" style={{ fontWeight: "bold" }}>
{props.system.max_qubits}
</Text>
</div>
<Text c="dimmed" size="sm" style={{ alignSelf: "flex-end" }}>
#{props.system.id}
</Text>
</div>
</Card.Section>
</Card>
);
}
export default MachinesListCard;

View File

@@ -0,0 +1,136 @@
import { Card, Pill, Text, UnstyledButton } from "@mantine/core";
import { useNavigate } from "react-router";
import { IconCancel, IconCheck, IconTrash } from "@tabler/icons-react";
import type { MouseEvent } from "react";
import { notifications } from "@mantine/notifications";
import { useDeviceStore } from "Stores/DeviceStore";
import { removeSystemFromTeam } from "Api/QuantumBackend/MachineManagment";
import type { SystemWithTeams } from "Types/Machine/Machine";
function TeamMachinesListCard(props: SystemWithTeams) {
const navigate = useNavigate();
const { setTotalDevices, totalDevices, removeDevice } = useDeviceStore();
const handleLeave = () => {
// TODO: fix delete
if (props.system) {
removeSystemFromTeam({
team_id: props.teams[0].team.team_id,
system_id: props.system.id,
})
.then(() => {
removeDevice(props.teams[0].team.team_id);
setTotalDevices(totalDevices - 1);
notifications.show({
radius: "md",
title: "Устройство удалено успешно",
message: "",
icon: <IconCheck />,
style: { paddingLeft: "5px" },
});
})
.catch(() => {
notifications.show({
radius: "md",
color: "red",
title: "Не удалось удалить устройство",
message: "",
icon: <IconCancel />,
style: { paddingLeft: "5px" },
});
});
}
};
return (
<Card
onClick={() => {
navigate(props.system.id.toString());
}}
orientation="horizontal"
>
<Card.Section
inheritPadding
withBorder
px="xs"
style={{ width: "250px" }}
>
<Text mb="sm" size="md" style={{ textDecorationLine: "underline" }}>
Устройство: {props.system.system_name}
</Text>
</Card.Section>
<Card.Section
inheritPadding
withBorder
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
flexGrow: "1",
}}
px="xs"
>
<div style={{ display: "flex" }}>
<Text mb="sm" size="md">
Статус:
</Text>
<Pill className="MachinePill" style={{ marginLeft: "10px" }}>
<Text size="md">{props.system.status}</Text>
</Pill>
</div>
<div style={{ display: "flex", gap: "15px" }}>
<Text mb="sm" size="md">
Дата обновления статуса:
</Text>
<Text size="md">
{new Date(props.system.created_at + "Z").toLocaleDateString(
"ru",
)}{" "}
</Text>
<Text size="md">
{new Date(props.system.created_at + "Z").toLocaleTimeString("ru")}
</Text>
</div>
</Card.Section>
<Card.Section
inheritPadding
px="xs"
withBorder
style={{
gap: "15px",
display: "flex",
flexDirection: "row",
}}
>
<Text size="md">Максимальное количество кубит:</Text>
<Text size="md" style={{ fontWeight: "bold" }}>
{props.system.max_qubits}
</Text>
</Card.Section>
<Card.Section
inheritPadding
px="md"
style={{
width: "50px",
}}
>
<UnstyledButton
onClick={(e: MouseEvent) => {
e.stopPropagation();
handleLeave();
}}
style={{ cursor: "pointer" }}
>
<IconTrash size={20} />
</UnstyledButton>
</Card.Section>
<Card.Section inheritPadding px="md">
<Text c="dimmed" size="md" style={{ width: "20px" }}>
#{props.system.id}
</Text>
</Card.Section>
</Card>
);
}
export default TeamMachinesListCard;

View File

@@ -0,0 +1,116 @@
import { Card, Text, UnstyledButton } from "@mantine/core";
import { IconCancel, IconCheck, IconTrash } from "@tabler/icons-react";
import type { MouseEvent } from "react";
import { notifications } from "@mantine/notifications";
import type { SystemTeamData, SystemWithTeams } from "Types/Machine/Machine";
import { removeSystemFromTeam } from "Api/QuantumBackend/MachineManagment";
import { useDeviceStore } from "Stores/DeviceStore";
function TeamInMachineListCard(props: {
team: SystemTeamData;
system: SystemWithTeams;
}) {
const { updateDevice } = useDeviceStore();
const handleRemovePerm = () => {
// TODO: fix delete
removeSystemFromTeam({
team_id: props.team.team.team_id,
system_id: props.system.system.id,
})
.then(() => {
updateDevice(props.system.system.id, {
teams: props.system.teams.filter(
(team) => team.team.team_id != props.team.team.team_id,
),
});
notifications.show({
radius: "md",
title: "Команда удалена успешно",
message: "",
icon: <IconCheck />,
style: { paddingLeft: "5px" },
});
})
.catch(() => {
notifications.show({
radius: "md",
color: "red",
title: "Не удалось удалить команду",
message: "",
icon: <IconCancel />,
style: { paddingLeft: "5px" },
});
});
};
return (
<Card orientation="horizontal">
<Card.Section
withBorder
inheritPadding
px="xs"
style={{
width: "430px",
display: "flex",
flexDirection: "row",
gap: "15px",
}}
>
<Text size="md" style={{ textDecorationLine: "underline" }}>
Команда: {props.team.team.team_name}
</Text>
<Text c="dimmed" size="md">
#{props.team.team.team_id}
</Text>
</Card.Section>
<Card.Section
inheritPadding
px="xs"
withBorder
style={{
flexGrow: "1",
gap: "15px",
display: "flex",
flexDirection: "row",
}}
>
<Text>Количество кубит: {props.team.num_qubits}</Text>
</Card.Section>
<Card.Section
inheritPadding
px="md"
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
}}
>
<div className="TopRightContainer">
<div className="dateContainer">
<Text size="sm">
{new Date(props.team.created_at + "Z").toLocaleDateString(
"ru",
)}{" "}
</Text>
<Text size="sm">
{new Date(props.team.created_at + "Z").toLocaleTimeString("ru")}
</Text>
</div>
<UnstyledButton
onClick={(e: MouseEvent) => {
e.stopPropagation();
handleRemovePerm();
}}
style={{ cursor: "pointer" }}
>
<IconTrash size={20} />
</UnstyledButton>
</div>
</Card.Section>
</Card>
);
}
export default TeamInMachineListCard;

View File

@@ -1,6 +1,3 @@
.TeamMembers {
margin-top: 10px;
gap: 10px;
display: flex;
flex-direction: column;
.TeamMemberCard {
min-height: 100px;
}

View File

@@ -30,7 +30,13 @@ function TeamMemberCard(props: { cur_team: Team; member: TeamMember }) {
);
return (
<Card padding="sm" withBorder orientation="horizontal">
<Card
padding="sm"
withBorder
orientation="horizontal"
className="TeamMemberCard"
key={props.member.user.keycloak_id}
>
<Card.Section
inheritPadding
px="xs"
@@ -168,7 +174,6 @@ function TeamMemberCard(props: { cur_team: Team; member: TeamMember }) {
placeholder="Выберите разрешения"
searchable
data={props.cur_team.creator.permissions}
classNames={{ input: "MantineInput" }}
onChange={(value) => {
set_selected_permissions(value);
}}
@@ -227,10 +232,10 @@ function TeamMemberCard(props: { cur_team: Team; member: TeamMember }) {
}}
>
<Text size="sm" style={{ textAlign: "right" }}>
{new Date(props.member.joined_at).toLocaleDateString("ru")}
{new Date(props.member.joined_at + "Z").toLocaleDateString("ru")}
</Text>
<Text size="sm" style={{ textAlign: "right" }}>
{new Date(props.member.joined_at).toLocaleTimeString("ru")}
{new Date(props.member.joined_at + "Z").toLocaleTimeString("ru")}
</Text>
</div>
</Stack>

View File

@@ -90,17 +90,19 @@ function TeamsListCard(props: Team) {
<Text mb="sm" size="md" style={{ textDecorationLine: "underline" }}>
Команда: {props.name}
</Text>
<Text mb="sm" size="md">
Владелец:
<div style={{ display: "flex" }}>
<Text mb="sm" size="md">
Владелец:
</Text>
<Pill className="TeamPill" style={{ marginLeft: "10px" }}>
<Text size="md">{props.creator.user.username}</Text>
</Pill>
</Text>
</div>
<div className="membersPills">
<Text>Члены:</Text>
{props.members.map((team: TeamMember) => {
return (
<Pill className="TeamPill" style={{ marginLeft: "10px" }}>
<Pill className="TeamPill">
<Text
size="md"
style={{
@@ -170,10 +172,10 @@ function TeamsListCard(props: Team) {
<div className="TopRightContainer">
<div className="dateContainer">
<Text size="sm">
{new Date(props.created_at).toLocaleDateString()}{" "}
{new Date(props.created_at + "Z").toLocaleDateString()}{" "}
</Text>
<Text size="sm">
{new Date(props.created_at).toLocaleTimeString()}
{new Date(props.created_at + "Z").toLocaleTimeString()}
</Text>
</div>
{props.creator.user.keycloak_id == profile?.id && (