Files
HellreigN/frontend/src/pages/templates.page.tsx
T
nikita 337e5891f3
ci-front / build (push) Successful in 2m18s
feat: update tamplates
2026-04-05 07:07:14 +03:00

181 lines
5.0 KiB
TypeScript

import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { FiEdit3 } from "react-icons/fi";
import { FaSpinner } from "react-icons/fa";
import { FilePicker } from "../modules/ide";
import { RunScriptModal } from "../modules/ide/components/RunScriptModal";
import type { FileNode } from "../modules/ide";
import { scriptsApi } from "../modules/ide/api/scripts.api";
const convertTreeToFileNode = (data: any[]): FileNode => {
const convertItem = (item: any): FileNode => {
const node: FileNode = {
id: item.id,
name: item.name,
type: item.type === "folder" ? "folder" : "file",
content: item.content || "",
path: item.name,
interpreter_id: item.interpreter_id,
};
if (item.type === "folder") {
node.children = [];
if (item.children && Array.isArray(item.children)) {
node.children = item.children.map((child: any) => {
const childNode = convertItem(child);
childNode.path = `${item.name}/${child.name}`;
return childNode;
});
}
}
return node;
};
return {
name: "templates",
type: "folder",
children: data.map((item) => convertItem(item)),
};
};
export const TemplatesPage = () => {
const navigate = useNavigate();
const [files, setFiles] = useState<FileNode | null>(null);
const [loading, setLoading] = useState(true);
const [runModal, setRunModal] = useState<{
scriptPath: string;
scriptId: number;
} | null>(null);
useEffect(() => {
scriptsApi
.getTree()
.then((data) => {
setFiles(convertTreeToFileNode(data));
})
.catch((e) => {
console.error("Failed to load tree:", e);
setFiles({ name: "templates", type: "folder", children: [] });
})
.finally(() => setLoading(false));
}, []);
const handleRun = (path: string, id?: number) => {
if (!id) {
console.warn("Script ID not found for:", path);
return;
}
setRunModal({ scriptPath: path, scriptId: id });
};
return (
<div
style={{
height: "100vh",
backgroundColor: "var(--bg-primary)",
display: "flex",
flexDirection: "column",
}}
>
{/* Header bar */}
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
padding: "12px 16px",
borderBottom: "1px solid var(--border)",
backgroundColor: "var(--card-bg)",
flexShrink: 0,
}}
>
{/* Open in Editor button */}
<button
onClick={() => navigate("/ide")}
style={{
display: "flex",
alignItems: "center",
gap: "8px",
padding: "6px 16px",
backgroundColor: "#0e639c",
border: "none",
borderRadius: "4px",
color: "#ffffff",
cursor: "pointer",
fontSize: "12px",
fontWeight: 500,
transition: "all 0.15s",
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = "#1177bb";
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = "#0e639c";
}}
>
<FiEdit3 size={14} />
Open Editor
</button>
</div>
{/* File Picker (terminal встроен внутрь) */}
<div style={{ flex: 1, overflow: "hidden" }}>
{loading ? (
<div
style={{
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<FaSpinner
size={24}
style={{
color: "var(--accent)",
animation: "spin 1s linear infinite",
}}
/>
</div>
) : files ? (
<FilePicker
files={files}
onRun={(path) => {
// Находим ID скрипта по пути
const findNodeById = (
node: FileNode,
p: string,
): FileNode | null => {
if (node.path === p) return node;
if (node.children) {
for (const child of node.children) {
const found = findNodeById(child, p);
if (found) return found;
}
}
return null;
};
const node = findNodeById(files, path);
if (node?.id) {
handleRun(path, node.id);
} else {
console.warn("Script ID not found for path:", path);
}
}}
/>
) : null}
</div>
{/* Run Script Modal */}
{runModal && (
<RunScriptModal
scriptPath={runModal.scriptPath}
scriptId={runModal.scriptId}
onClose={() => setRunModal(null)}
/>
)}
</div>
);
};