v1.0
- added task rendering instead of molecule - fied a lot of errors - changes experiment page to show generic experiment info
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
import axios, { AxiosError } from "axios";
|
||||
import type { ConvertSchema } from "Types/ApiCalls/ConvertBackendCallsTypes";
|
||||
|
||||
export async function ConvertMoleculeToStandart(
|
||||
data: ConvertSchema,
|
||||
): Promise<string | AxiosError> {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
import.meta.env.VITE_MOLECULAR_BACKEND_URL + "/convert",
|
||||
{
|
||||
text: data.inputText,
|
||||
format: data.inputFormat,
|
||||
convert_3d: data.make_3d,
|
||||
add_hydrogen: data.add_h,
|
||||
optimize_geometry: data.optimize,
|
||||
},
|
||||
);
|
||||
|
||||
// Response from the FastAPI JSONResponse
|
||||
return response.data.molfile;
|
||||
} catch (error) {
|
||||
//Error handling
|
||||
if (axios.isAxiosError(error)) {
|
||||
// Do something with the axios error...
|
||||
return error;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function GetInFormats(): Promise<
|
||||
{ [key: string]: string } | AxiosError
|
||||
> {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
import.meta.env.VITE_MOLECULAR_BACKEND_URL + "/informats",
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
//Error handling
|
||||
if (axios.isAxiosError(error)) {
|
||||
// Do something with the axios error...
|
||||
return error;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,8 +61,6 @@ export const updatePasswordWithRedirect = async (
|
||||
redirectUri:
|
||||
successRedirectUrl ||
|
||||
window.location.origin +
|
||||
"/" +
|
||||
import.meta.env.VITE_BASE_PATH +
|
||||
"/" +
|
||||
routes.SettingsPage.path +
|
||||
"?password_updated=true",
|
||||
@@ -85,7 +83,6 @@ export const SendEmailVerification = async () => {
|
||||
action: "VERIFY_EMAIL",
|
||||
redirectUri:
|
||||
window.location.origin +
|
||||
import.meta.env.VITE_BASE_PATH +
|
||||
"/" +
|
||||
routes.SettingsPage.path +
|
||||
"?email_sent=true",
|
||||
|
||||
@@ -1,58 +1,59 @@
|
||||
import React, { useRef, useEffect, useState, useMemo } from "react";
|
||||
import { LoadingOverlay } from "@mantine/core";
|
||||
import React, { useRef, useEffect, useState } 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;
|
||||
index: number;
|
||||
plugin: string;
|
||||
mode: "List" | "Editor";
|
||||
taskData: string;
|
||||
simProgress: string;
|
||||
qubits_needed: number;
|
||||
onUpdate?: (data: string, qubits_needed: number) => void;
|
||||
}
|
||||
|
||||
export const IframePlugin: React.FC<IframePluginProps> = ({
|
||||
pluginUrl,
|
||||
index,
|
||||
plugin,
|
||||
mode,
|
||||
taskData,
|
||||
simProgress,
|
||||
qubits_needed,
|
||||
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
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
// 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,
|
||||
taskData: taskData,
|
||||
simProgress: simProgress,
|
||||
qubits_needed: qubits_needed,
|
||||
theme: theme.theme,
|
||||
mode: mode,
|
||||
},
|
||||
};
|
||||
|
||||
iframeRef.current.contentWindow.postMessage(message, "*");
|
||||
}, [taskData, theme.theme, isIframeReady]);
|
||||
setIsVisible(true);
|
||||
}, [taskData, theme.theme, isIframeReady, mode, simProgress]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isVisible) {
|
||||
if (!isIframeReady || !iframeRef.current?.contentWindow) return;
|
||||
const message = {
|
||||
type: "update",
|
||||
};
|
||||
|
||||
iframeRef.current.contentWindow.postMessage(message, "*");
|
||||
}
|
||||
}, [index]);
|
||||
|
||||
// Listen for messages from iframe
|
||||
useEffect(() => {
|
||||
@@ -65,7 +66,8 @@ export const IframePlugin: React.FC<IframePluginProps> = ({
|
||||
|
||||
// Handle plugin updates
|
||||
if (event.data.type === "plugin-update" && onUpdate) {
|
||||
onUpdate(event.data.data);
|
||||
console.log(event.data);
|
||||
onUpdate(event.data.data, event.data.qubits_needed);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,10 +75,33 @@ export const IframePlugin: React.FC<IframePluginProps> = ({
|
||||
return () => window.removeEventListener("message", handler);
|
||||
}, [onUpdate]);
|
||||
|
||||
// Reset ready state when URL changes (new plugin or mode)
|
||||
useEffect(() => {
|
||||
setIsIframeReady(false);
|
||||
}, [pluginUrl, mode]);
|
||||
|
||||
return memoizedIframe;
|
||||
return (
|
||||
<>
|
||||
<LoadingOverlay visible={!isIframeReady} />
|
||||
<div
|
||||
style={{
|
||||
visibility: isVisible ? "visible" : "hidden",
|
||||
flexGrow: "1",
|
||||
maxHeight: "100%",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
srcDoc={plugin}
|
||||
sandbox="allow-scripts allow-popups allow-forms"
|
||||
style={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
border: "none",
|
||||
pointerEvents: mode == "List" ? "none" : "initial",
|
||||
}}
|
||||
name={`Ifame#${index}`}
|
||||
id={`Ifame#${index}`}
|
||||
></iframe>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
186
src/Api/QuantumBackend/ExperimentsManagment.tsx
Normal file
186
src/Api/QuantumBackend/ExperimentsManagment.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import keycloak from "Api/Keycloak/Keycloak";
|
||||
import axios from "axios";
|
||||
import type {
|
||||
CreateExperimentRequest,
|
||||
ExperimentData,
|
||||
ExperimentListResponse,
|
||||
CreateInstanceRequest,
|
||||
SimpleInstanceData,
|
||||
UpdateInstanceRequest,
|
||||
InstanceListResponse,
|
||||
InstanceData,
|
||||
StartExperimentRequest,
|
||||
UpdateExperimentRequest,
|
||||
CreateExperimentTypeResponse,
|
||||
ExperimentTypeList,
|
||||
} from "Types/Experiment/Experiment";
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/experiment`,
|
||||
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;
|
||||
});
|
||||
|
||||
// ============= EXPERIMENT TYPE ENDPOINTS =============
|
||||
|
||||
// 1. Get all experiment types
|
||||
export const getExperimentTypes = async (): Promise<ExperimentTypeList[]> => {
|
||||
const response = await api.get<ExperimentTypeList[]>("/types");
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 2. Create experiment type (admin only, with file uploads)
|
||||
export const createExperimentType = async (
|
||||
name: string,
|
||||
fileFrontend: File,
|
||||
fileCompSystem: File,
|
||||
description?: string,
|
||||
): Promise<CreateExperimentTypeResponse> => {
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
formData.append("file_frontend", fileFrontend);
|
||||
formData.append("file_comp_system", fileCompSystem);
|
||||
if (description) {
|
||||
formData.append("description", description);
|
||||
}
|
||||
|
||||
const response = await api.post<CreateExperimentTypeResponse>(
|
||||
"/types",
|
||||
formData,
|
||||
{
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 3. Get frontend HTML file for experiment type
|
||||
export const getFrontendFile = async (
|
||||
experiment_type_id: number,
|
||||
): Promise<string> => {
|
||||
const response = await api.get<string>("/types/frontend", {
|
||||
params: { experiment_type_id },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// ============= EXPERIMENT ENDPOINTS =============
|
||||
|
||||
// 5. Create a new experiment
|
||||
export const createExperiment = async (
|
||||
data: CreateExperimentRequest,
|
||||
): Promise<ExperimentData> => {
|
||||
const response = await api.post<ExperimentData>("", data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 6. Get single experiment by ID
|
||||
export const getExperimentById = async (
|
||||
experiment_id: number,
|
||||
): Promise<ExperimentData> => {
|
||||
const response = await api.get<ExperimentData>("", {
|
||||
params: { experiment_id },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 7. Update experiment
|
||||
export const updateExperiment = async (
|
||||
data: UpdateExperimentRequest,
|
||||
): Promise<ExperimentData> => {
|
||||
const response = await api.put<ExperimentData>("", data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 8. Get user's experiments (with pagination)
|
||||
export const getUserExperiments = async ({
|
||||
page_num = 1,
|
||||
page_size = 10,
|
||||
}: {
|
||||
page_num?: number;
|
||||
page_size?: number;
|
||||
}): Promise<ExperimentListResponse> => {
|
||||
const response = await api.get<ExperimentListResponse>("/user", {
|
||||
params: { page_num, page_size },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 9. Delete experiment
|
||||
export const deleteExperiment = async (
|
||||
experiment_id: number,
|
||||
): Promise<{ message: string }> => {
|
||||
const response = await api.delete<{ message: string }>("", {
|
||||
params: { experiment_id },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// ============= INSTANCE ENDPOINTS =============
|
||||
|
||||
// 10. Create instance for an experiment
|
||||
export const createInstance = async (
|
||||
data: CreateInstanceRequest,
|
||||
): Promise<SimpleInstanceData> => {
|
||||
const response = await api.post<SimpleInstanceData>("/instance", data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 11. Update instance
|
||||
export const updateInstance = async (
|
||||
data: UpdateInstanceRequest,
|
||||
): Promise<SimpleInstanceData> => {
|
||||
const response = await api.put<SimpleInstanceData>("/instance", data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 12. Get all instances of an experiment (with pagination)
|
||||
export const getExperimentInstances = async (
|
||||
experiment_id: number,
|
||||
page_num: number = 1,
|
||||
page_size: number = 6,
|
||||
): Promise<InstanceListResponse> => {
|
||||
page_num = Math.max(page_num, 1);
|
||||
const response = await api.get<InstanceListResponse>("/instance", {
|
||||
params: { experiment_id, page_num, page_size },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 13. Get single instance by ID
|
||||
export const getInstanceById = async (
|
||||
instance_id: number,
|
||||
): Promise<InstanceData> => {
|
||||
const response = await api.get<InstanceData>("/instance/id", {
|
||||
params: { instance_id },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 14. Delete instance
|
||||
export const deleteInstance = async (
|
||||
instance_id: number,
|
||||
): Promise<{ message: string }> => {
|
||||
const response = await api.delete<{ message: string }>("/instance", {
|
||||
params: { instance_id },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// ============= SIMULATION ENDPOINTS =============
|
||||
|
||||
// 15. Start experiment (run simulation)
|
||||
export const startExperiment = async (
|
||||
data: StartExperimentRequest,
|
||||
): Promise<200> => {
|
||||
const response = await api.post<200>("/start", data);
|
||||
return response.data;
|
||||
};
|
||||
@@ -25,25 +25,38 @@ export const GetCurrentUserInfo = async (): Promise<UserData | undefined> => {
|
||||
}
|
||||
};
|
||||
|
||||
// In Api/QuantumBackend/UserManagement.ts
|
||||
export const UpdateCurrentUserInfo = async (
|
||||
profile_picture_path: string,
|
||||
): Promise<UserData | undefined> => {
|
||||
const response = await axios.put(
|
||||
`${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user`,
|
||||
{ profile_picture_path: profile_picture_path },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${keycloak.token}`,
|
||||
file: File,
|
||||
): Promise<{ profile_picture_path: string } | undefined> => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user/upload`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${keycloak.token}`,
|
||||
},
|
||||
body: formData,
|
||||
},
|
||||
withCredentials: true, // Important: allows credentials in CORS
|
||||
},
|
||||
);
|
||||
if (response.status === 200) {
|
||||
return response.data;
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to upload profile picture");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error("Error uploading profile picture:", error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Keep your existing GetCurrentUserInfo function
|
||||
|
||||
export const GetUserByEmail = async (
|
||||
email: string,
|
||||
): Promise<UserData | undefined> => {
|
||||
|
||||
Reference in New Issue
Block a user