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

@@ -0,0 +1,82 @@
import React, { useRef, useEffect, useState, useMemo } from "react";
import { useUserPreferencesStore } from "Stores/PreferencesStore";
import type { TaskData } from "Types/Experiment/Experiment";
interface IframePluginProps {
pluginUrl: string;
mode: "list" | "editor";
taskData: TaskData<any>;
onUpdate?: (data: any) => void;
}
export const IframePlugin: React.FC<IframePluginProps> = ({
pluginUrl,
mode,
taskData,
onUpdate,
}) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const theme = useUserPreferencesStore();
const [isIframeReady, setIsIframeReady] = useState(false);
// Memoize the iframe component to prevent recreation when taskData changes
const memoizedIframe = useMemo(() => {
const url = `${pluginUrl}?mode=${mode}`;
return (
<iframe
ref={iframeRef}
src={url}
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
style={{
width: "100%",
display: "flex",
flexDirection: "column",
flexGrow: 1,
border: "none",
}}
title="Plugin"
/>
);
}, [pluginUrl, mode]); // Only recreate when pluginUrl or mode changes
// Send data to iframe when taskData or theme changes, or when iframe becomes ready
useEffect(() => {
if (!isIframeReady || !iframeRef.current?.contentWindow) return;
const message = {
type: "plugin-data",
data: {
taskData,
theme: theme.theme,
},
};
iframeRef.current.contentWindow.postMessage(message, "*");
}, [taskData, theme.theme, isIframeReady]);
// Listen for messages from iframe
useEffect(() => {
const handler = (event: MessageEvent) => {
// Handle iframe ready signal
if (event.data.type === "plugin-ready") {
setIsIframeReady(true);
return;
}
// Handle plugin updates
if (event.data.type === "plugin-update" && onUpdate) {
onUpdate(event.data.data);
}
};
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, [onUpdate]);
// Reset ready state when URL changes (new plugin or mode)
useEffect(() => {
setIsIframeReady(false);
}, [pluginUrl, mode]);
return memoizedIframe;
};

View File

@@ -0,0 +1,110 @@
import keycloak from "Api/Keycloak/Keycloak";
import axios from "axios";
import type {
ComputationalSystemCreateRequest,
ComputationalSystemCreateResponse,
ComputationalSystemEditRequest,
ComputationalSystemListResponse,
SystemWithTeams,
SystemStatusResponse,
GiveSystemToTeamRequest,
RemoveSystemFromTeamRequest,
ComputationalSystemDeleteRequest,
} from "Types/Machine/Machine";
const api = axios.create({
baseURL: `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/machine`,
headers: { "Content-Type": "application/json" },
});
// Add auth token to requests
api.interceptors.request.use((config) => {
const token = keycloak.token;
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 1. Create or get computational system
export const createOrGetComputationalSystem = async (
data: ComputationalSystemCreateRequest,
): Promise<ComputationalSystemCreateResponse> => {
const response = await api.post<ComputationalSystemCreateResponse>("", data);
return response.data;
};
// 2. Edit computational system (owner only)
export const editComputationalSystem = async (
data: ComputationalSystemEditRequest,
): Promise<ComputationalSystemEditRequest> => {
const response = await api.put<ComputationalSystemEditRequest>("", data);
return response.data;
};
// 3. Get my systems (owned by current user)
export const getMySystems = async ({
page_num = 1,
page_size = undefined,
}): Promise<ComputationalSystemListResponse> => {
const response = await api.get<ComputationalSystemListResponse>("", {
params: { page_num, page_size },
});
return response.data;
};
// 4. Get team systems (systems shared with a team)
export const getTeamSystems = async (
team_id: number,
page_num: number = 1,
page_size: number = 10,
): Promise<ComputationalSystemListResponse> => {
const response = await api.get<ComputationalSystemListResponse>("/team", {
params: { team_id, page_num, page_size },
});
return response.data;
};
// 5. Get computational system by ID
export const getComputationalSystemById = async (
system_id: number,
): Promise<SystemWithTeams> => {
const response = await api.get<SystemWithTeams>("/system", {
params: { system_id },
});
return response.data;
};
// 6. Get system status
export const getSystemStatus = async (
system_id: number,
): Promise<SystemStatusResponse> => {
const response = await api.get<SystemStatusResponse>("/status", {
params: { system_id },
});
return response.data;
};
// 7. Give system to team (share access)
export const giveSystemToTeam = async (
data: GiveSystemToTeamRequest,
): Promise<GiveSystemToTeamRequest> => {
const response = await api.put<GiveSystemToTeamRequest>("/team", data);
return response.data;
};
// 8. Remove system from team (revoke access)
export const removeSystemFromTeam = async (
data: RemoveSystemFromTeamRequest,
): Promise<{ message: string }> => {
const response = await api.delete<{ message: string }>("/team", { data });
return response.data;
};
// 9. Delete computational system (owner only)
export const deleteComputationalSystem = async (
data: ComputationalSystemDeleteRequest,
): Promise<{ message: string }> => {
const response = await api.delete<{ message: string }>("", { data });
return response.data;
};

View File

@@ -1,48 +1,16 @@
import keycloak from "Api/Keycloak/Keycloak";
import axios from "axios";
import type { Team, TeamMember } from "Types/Team/Team";
export interface TeamCreateRequest {
name: string;
description?: string | null;
}
export interface TeamUpdateRequest {
team_id: number;
name: string;
description?: string | null;
}
export interface TeamCreateResponse {
team_id: number;
}
export interface TeamListRequest {
page_num: number;
page_size: number;
}
export interface TeamDeleteRequest {
team_id: number;
}
export interface MemberAddRequest {
team_id: number;
user_id: string;
permissions: string[];
}
export interface MemberDeleteRequest {
team_id: number;
user_id: string;
}
export interface TeamsListResponse {
teams: Team[];
cur_page: number;
total_teams: number;
page_size: number;
}
import type {
Team,
TeamMember,
TeamCreateRequest,
TeamCreateResponse,
TeamUpdateRequest,
TeamListRequest,
TeamsListResponse,
MemberAddRequest,
MemberDeleteRequest,
} from "Types/Team/Team";
const api = axios.create({
baseURL: `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/team`,
@@ -84,11 +52,13 @@ export const getTeams = async (
return response.data;
};
export const getShortTeamsList = async (): Promise<
{ team_id: number; team_name: string }[]
> => {
const response =
await api.get<{ team_id: number; team_name: string }[]>("short_list");
export const getShortTeamsList = async (
params: Partial<{ permission: string }>,
): Promise<{ team_id: number; team_name: string }[]> => {
const response = await api.get<{ team_id: number; team_name: string }[]>(
"short_list",
{ headers: {}, params },
);
return response.data;
};