<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hypha Workspace Management</title>
    <!-- External Scripts -->
    <script src="../assets/hypha-rpc-websocket.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.24.7/babel.min.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" />
    <script src="https://cdn.auth0.com/js/auth0-spa-js/2.0/auth0-spa-js.production.js"></script>
    <script src="https://rawcdn.githack.com/nextapps-de/winbox/0.2.82/dist/winbox.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.7.7/axios.min.js"></script>
    <!-- Favicon and Icons -->
    <link rel="apple-touch-icon" sizes="180x180" href="./static/img/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="./static/img/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="./static/img/favicon-16x16.png">
    <!-- Stylesheets -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.3.0/github-markdown-light.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;800&display=swap">
    <!-- Meta Tags -->
    <meta name="msapplication-TileColor" content="#da532c">
    <meta name="theme-color" content="#ffffff">
    <style>
        /* Styles for the sidebar and main content */
        .sidebar {
            width: 250px;
            position: fixed;
            top: 0;
            left: 0;
            height: 100%;
            background-color: #1f2937;
            padding-top: 20px;
            transition: all 0.3s;
        }

        .sidebar.collapsed {
            width: 60px;
        }

        .sidebar .logo {
            padding-left: 10px;
            text-align: center;
            margin-bottom: 1rem;
        }

        .sidebar .menu-item {
            padding: 1rem;
            color: white;
            cursor: pointer;
            display: flex;
            align-items: center;
            transition: background-color 0.3s;
        }

        .sidebar .menu-item:hover {
            background-color: #374151;
        }

        .sidebar.collapsed .menu-item span {
            display: none;
        }

        .main-content {
            padding: 20px;
            transition: all 0.3s;
        }

        .main-content.collapsed {
            margin-left: 60px;
        }

        .file-item {
            position: relative;
        }
        .file-item-buttons {
            display: none;
            position: absolute;
            right: 0;
            top: 50%;
            transform: translateY(-50%);
        }
        .file-item:hover .file-item-buttons {
            display: flex;
        }
        .file-card:hover {
            background-color: #2d3748;
        }

        @media (max-width: 768px) {
            .sidebar {
                width: 60px;
            }

            .main-content {
                margin-left: 60px;
            }

            .sidebar .menu-item span {
                display: none;
            }
        }

        .grid {
            min-height: 30vh;
        }

        .service-card {
            background-color: #2d3748;
            border-radius: 0.5rem;
            padding: 1.5rem;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }

        .service-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15);
        }

        .modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .modal-content {
            background-color: #1f2937;
            border-radius: 0.5rem;
            padding: 2rem;
            width: 90%;
            max-width: 600px;
            box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3);
        }

        .modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 1rem;
        }

        .modal-body {
            max-height: 400px;
            overflow-y: auto;
        }
    </style>
</head>

<body class="bg-black text-white font-poppins">
    <div id="app"></div>
    <script type="module">
        import { HyphaCore } from "https://cdn.jsdelivr.net/npm/hypha-core@0.20.39/dist/hypha-core.mjs";

        const defaultService = {
            getServerConfig(context){
                let token;
                try{
                    token = document.cookie.split('; ').find(row => row.startsWith('access_token')).split('=')[1]
                }
                catch(e){
                    token = null;
                }
                // extrac workspace from url
                // for example: http://127.0.0.1:9527/ws-user-github%7C478667#services => ws-user-github|478667
                const workspace = decodeURIComponent(window.location.href.split(window.location.origin)[1].split('/')[1].split("#")[0].split('?')[0]);
                return {
                    server_url: window.location.origin,
                    workspace,
                    token,
                }
            }
        }
        window.defaultService = defaultService;
        const hyphaCore = new HyphaCore({base_url: "/hypha-core-app/", default_service: defaultService});
        hyphaCore.on("add_window", (config) => {
            const root = document.getElementById("hypha-window-container");
            // get the left and top position of the root element
            // then set it to the WinBox
            const rect = root.getBoundingClientRect();

            const wb = new WinBox(config.name || config.src.slice(0, 128), {
                root: root,
                y: rect.top + 10,
                x: rect.left + 10,
                background: "#448aff",
            });
            wb.body.innerHTML = `<iframe src="${config.src}" id="${config.window_id}" style="width: 100%; height: 100%; border: none;"></iframe>`;
        });
        const api = await hyphaCore.start();

        console.log("Hypha core started:", hyphaCore.url);
        async function loadPluginFromUrl(url) {
            const plugin = await api.loadPlugin({ src: url});
            await plugin.run({ config: {}, data: {} });
            console.log("Loaded and ran plugin from URL:", url);
        }
        window.loadPlugin = loadPluginFromUrl;

        window.loadCodeEditor = async function () {
            const response = await fetch("/ws/code_template.html");
            const pythonPluginCode = await response.text();
            const codeEditor = await api.loadPlugin({
                src: "https://if.imjoy.io",
            });
            await codeEditor.run({ config: {fold: [1]},  data: {code: pythonPluginCode} });
            console.log("Loaded and ran code editor plugin");
        };
    </script>

    <!-- Render the App component -->
    <script type="text/babel" data-presets="env,react" data-env-targets="defaults, safari >= 12">
        const Sidebar = ({ isCollapsed, onToggle, setActivePanel, enabledPanels }) => {
            return (
                <div className={`sidebar ${isCollapsed ? 'collapsed' : ''}`}>
                    <div className="logo">
                        <a href="/"><img src="/static/img/hypha-logo-black.svg" className={`h-10 invert ${isCollapsed ? 'hidden' : 'block'}`} alt="Hypha Logo" /></a>
                    </div>
                    <div className="menu-item" onClick={() => onToggle()}>
                        <i className="fas fa-bars"></i>
                        <span className="ml-2">Menu</span>
                    </div>
                    <hr className="border-gray-700"></hr>
                    {enabledPanels.includes('dashboard') && (
                    <div className="menu-item" onClick={() => setActivePanel('dashboard')}>
                        <i className="fas fa-cogs"></i>
                        <span className="ml-2">Dashboard</span>
                    </div>
                    )}
                    {enabledPanels.includes('apps') && (
                    <div className="menu-item" onClick={() => setActivePanel('apps')}>
                        <i className="fas fa-th-large"></i>
                        <span className="ml-2">Apps</span>
                    </div>
                    )}
                    {enabledPanels.includes('files') && (
                    <div className="menu-item" onClick={() => setActivePanel('files')}>
                        <i className="fas fa-file-alt"></i>
                        <span className="ml-2">Files</span>
                    </div>
                    )}
                    {enabledPanels.includes('artifacts') && (
                    <div className="menu-item" onClick={() => setActivePanel('artifacts')}>
                        <i className="fas fa-file-alt"></i>
                        <span className="ml-2">Artifacts</span>
                    </div>
                    )}
                    {enabledPanels.includes('development') && (
                    <div className="menu-item" onClick={() => setActivePanel('development')}>
                        <i className="fas fa-code"></i>
                        <span className="ml-2">Development</span>
                    </div>
                    )}
                </div>
            );
        };

        const ClientCard = ({ client }) => {
            return (
                <div className="flex flex-col items-start p-6 bg-gray-800 rounded-lg shadow-lg mb-6 w-full">
                    <div className="flex items-center mb-4">
                        <i className="fas fa-users fa-3x text-white mr-4"></i>
                    </div>
                    <div className="text-gray-400 whitespace-pre-wrap break-words w-full">
                        <p className="mb-4">
                            <strong>Client ID:</strong> {client.id.split('/')[1]}
                        </p>
                        <p className="mb-4">
                            {client.user && (<strong>User:</strong>)} {client.user && client.user.id}
                        </p>
                        <p className="mb-4">
                            {client.user && (<strong>Email:</strong>)} {client.user && client.user.email}
                        </p>
                    </div>
                </div>
            );
        };        

        const ServiceCard = ({ service, onShowDetails, onBookmarkService }) => {
            return (
              <div className="relative flex flex-col items-start p-6 bg-gray-800 rounded-lg shadow-lg mb-6 w-full">
                {/* Bookmark icon in the upper right corner */}
                <button
                  class="absolute top-2 right-2 text-white"
                  onClick={() => onBookmarkService(service.id)}
                >
                  <i className="fas fa-bookmark"></i>
                </button>
          
                {/* Service information */}
                <div className="flex items-center mb-4">
                  <i className="fas fa-cog fa-3x text-white mr-4"></i>
                  <div>
                    <h3 className="text-xl font-bold whitespace-pre-wrap break-words">
                      {service.id.split(":")[1]}
                    </h3>
                    <p className="text-gray-400">Type: {service.type}</p>
                  </div>
                </div>
          
                <div className="text-gray-400 whitespace-pre-wrap break-words w-full">
                  <p className="mb-4">
                    <strong>ID:</strong> {service.id}
                  </p>
                  <p className="mb-4">
                    <strong>Visibility:</strong>
                    {service.config.visibility === 'public' ? (
                      <i className="fas fa-globe m-1 text-blue-500"></i>
                    ) : (
                      <i className="fas fa-lock m-1 text-red-500"></i>
                    )}
                    {service.config.visibility}
                  </p>
                  <p className="mb-4">{service.description}</p>
                </div>
          
                <button
                  className="mt-auto bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                  onClick={() => onShowDetails(service)}
                >
                  <i className="fas fa-info-circle mr-2"></i> View Details
                </button>
              </div>
            );
          };          
        

        const AppCard = ({ app }) => {
            return (
                <div className="flex flex-col items-center p-6 bg-gray-800 rounded-lg shadow-lg mb-6">
                    <i className={`fas fa-th-large fa-3x text-white mb-4`}></i>
                    <div className="text-center">
                        <h3 className="text-xl font-bold">{app.name}</h3>
                        <p className="text-gray-400">{app.description}</p>
                    </div>
                </div>
            );
        };

        const FileCard = ({
            file,
            onDownload,
            onDelete,
            onToggle,
            isExpanded,
            children,
            fullPath,
        }) => {
            const isDirectory = file.type === 'directory';
            return (
                <div className="file-card flex flex-col justify-between items-start p-3 bg-gray-700 shadow-md">
                    <div className="file-item flex items-center w-full justify-between">
                        <div className="flex items-center">
                            {isDirectory && (
                                <button className="mr-2" onClick={() => onToggle(fullPath)}>
                                    {isExpanded ? '-' : '+'}
                                </button>
                            )}
                            <i className={`fas ${isDirectory ? 'fa-folder' : 'fa-file-alt'} mr-2`}></i>
                            {file.name}
                        </div>
                        <div className="file-item-buttons">
                            {!isDirectory && (
                                <>
                                <button
                                    className="bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-full transition duration-300"
                                    onClick={() => onDownload(fullPath)}
                                >
                                    <i className="fas fa-download"></i>
                                </button>
                                <button
                                    className="bg-red-600 hover:bg-red-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 ml-2"
                                    onClick={() => onDelete(fullPath, file.type)}
                                >
                                    <i className="fas fa-trash"></i>
                                </button>
                                </>
                            )}
                            
                        </div>
                    </div>
                    {isExpanded && <div className="w-full mt-3 ml-3">{children}</div>}
                </div>
            );
        };        
        
        const FileTree = ({
            files,
            onDownload,
            onDelete,
            listFiles,
            expandedDirs,
            setExpandedDirs,
            parentPath = "",
        }) => {
            const handleToggle = async (fullPath) => {
                const isExpanded = !!expandedDirs[fullPath];
                if (isExpanded) {
                    setExpandedDirs({ ...expandedDirs, [fullPath]: null });
                } else {
                    const children = await listFiles(fullPath);
                    setExpandedDirs({ ...expandedDirs, [fullPath]: children });
                }
            };
        
            return (
                <div>
                    {files.map((file) => {
                        const fullPath = parentPath ? `${parentPath}/${file.name}` : file.name;
                        return (
                            <FileCard
                                key={`${fullPath}-${file.type}`} // Ensure key is unique
                                file={file}
                                onDownload={onDownload}
                                onDelete={onDelete}
                                isExpanded={!!expandedDirs[fullPath]}
                                onToggle={handleToggle}
                                fullPath={fullPath}
                            >
                                {expandedDirs[fullPath] && (
                                    <FileTree
                                        files={expandedDirs[fullPath]}
                                        onDownload={onDownload}
                                        onDelete={onDelete}
                                        listFiles={listFiles}
                                        expandedDirs={expandedDirs}
                                        setExpandedDirs={setExpandedDirs}
                                        parentPath={fullPath}
                                    />
                                )}
                            </FileCard>
                        );
                    })}
                </div>
            );
        };                    

        const Modal = ({ serviceInfo, onClose }) => {
            return (
                <div className="modal">
                    <div className="modal-content">
                        <div className="modal-header">
                            <h2 className="text-2xl font-bold">Service Details</h2>
                            <button onClick={onClose} className="text-white text-2xl">&times;</button>
                        </div>
                        <div className="modal-body">
                            <pre className="whitespace-pre-wrap text-gray-200">
                                {JSON.stringify(serviceInfo, null, 2)}
                            </pre>
                        </div>
                    </div>
                </div>
            );
        };

        const TokenPanel = ({ workspaceId, onGenerateToken }) => {
            const [workspace, setWorkspace] = React.useState(workspaceId);
            const [expiryTime, setExpiryTime] = React.useState(3600);
            const [generatedToken, setGeneratedToken] = React.useState('');
            const [showSnackBar, setShowSnackBar] = React.useState(false);

            const handleGenerateToken = async () => {
                try {
                    const token = await onGenerateToken(workspace, expiryTime);
                    setGeneratedToken(token);
                } catch (error) {
                    console.error("Failed to generate token:", error);
                    alert("Failed to generate token. Please try again later.");
                }
            };
        
            const handleCopyToken = () => {
                navigator.clipboard.writeText(generatedToken);
                setShowSnackBar(true);
                setTimeout(() => setShowSnackBar(false), 2000); // Snack bar disappears after 2 seconds
            };
        
            return (
                <div className="main-content">
                    <div className="mt-4">
                        <label htmlFor="workspace" className="block text-sm font-medium text-gray-300">
                            Workspace Name
                        </label>
                        <input
                            type="text"
                            id="workspace"
                            name="workspace"
                            className="text-black mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md"
                            value={workspace}
                            onChange={(e) => setWorkspace(e.target.value)}
                        />
                    </div>
                    <div className="mt-4">
                        <label htmlFor="expiry-time" className="block text-sm font-medium text-gray-300">
                            Expiry Time (seconds)
                        </label>
                        <input
                            type="number"
                            id="expiry-time"
                            name="expiry-time"
                            className="text-black mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md"
                            placeholder="3600"
                            value={expiryTime}
                            onChange={(e) => setExpiryTime(e.target.value)}
                        />
                    </div>
                    <div className="mt-4">
                        <button
                            className="bg-blue-500 text-white py-2 px-4 rounded"
                            onClick={handleGenerateToken}
                        >
                            Generate Token
                        </button>
                    </div>
                    {generatedToken && (
                        <div className="mt-4">
                            <h3 className="text-lg font-bold">Generated Token:</h3>
                            <pre className="text-black bg-gray-200 p-4 rounded break-words whitespace-pre-wrap max-h-48 overflow-y-auto">
                                {generatedToken}
                            </pre>
                            <button 
                                className="bg-gray-500 text-white py-1 px-4 rounded mt-2" 
                                onClick={handleCopyToken}
                            >
                                Copy Token
                            </button>
                        </div>
                    )}
                    {showSnackBar && (
                        <div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-green-500 text-white py-2 px-4 rounded shadow-md">
                            Token copied to clipboard!
                        </div>
                    )}
                </div>
            );
        };
        const ArtifactCard = ({
            artifact,
            onToggle,
            isExpanded,
            children,
            fullPath,
            onMoreInfo,
            onDelete,
        }) => {
            const isCollection = artifact.type === "collection";
        
            return (
                <div className="artifact-card flex flex-col justify-between items-start p-3 bg-gray-700 shadow-md">
                    <div className="artifact-item flex items-center w-full justify-between">
                        <div className="flex items-center p-6 bg-gray-800 rounded-lg shadow-lg mb-6 w-full">
                            {isCollection && (
                                <button className="mr-2" onClick={() => onToggle(fullPath)}>
                                    {isExpanded ? "-" : "+"}
                                </button>
                            )}
                            <i
                                className={`fas ${
                                    isCollection ? "fa-folder" : "fa-file-alt"
                                } mr-2`}
                            ></i>
                            <div className="w-full overflow-hidden">
                                <h4 className="text-xl font-bold truncate">{artifact.manifest?.name}</h4>
                                <p className="text-gray-400 truncate">{artifact.manifest?.description}</p>
                                <p className="text-gray-400">Type: {artifact.type}</p>
                                <code className="text-gray-400 whitespace-pre-wrap break-words w-full break-all">
                                    ID: {artifact.id}
                                </code>
                                <div className="flex justify-between w-full">
                                    {/* More Info Button */}
                                    <div>
                                        <button
                                            className="bg-blue-500 hover:bg-blue-400 text-white font-bold py-1 px-2 rounded"
                                            onClick={() => onMoreInfo(artifact)}
                                        >
                                            More Info
                                        </button>
                                    </div>
                                    {/* Delete Button */}
                                    <div>
                                        <button
                                            className="bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2 rounded"
                                            onClick={() => onDelete(artifact.id)}
                                        >
                                            Delete
                                        </button>
                                    </div>
                                </div>                                
                            </div>
                        </div>
                        
                    </div>
                    {isExpanded && <div className="w-full mt-3 ml-3">{children}</div>}
                </div>
            );
        };
        
        const ArtifactTree = ({
            artifacts,
            onToggle,
            expandedArtifacts,
            setExpandedArtifacts,
            parentPath = "",
            listArtifacts,
            onMoreInfo,
            onDelete,
        }) => {
            const handleToggle = async (fullPath) => {
                const isExpanded = !!expandedArtifacts[fullPath];
                if (isExpanded) {
                    setExpandedArtifacts({ ...expandedArtifacts, [fullPath]: null });
                } else {
                    const children = await listArtifacts(fullPath);
                    setExpandedArtifacts({ ...expandedArtifacts, [fullPath]: children });
                }
            };
        
            return (
                <div>
                    {artifacts.map((artifact) => {
                        const fullPath = parentPath
                            ? `${parentPath}/${artifact.id}`
                            : artifact.id;
                        return (
                            <ArtifactCard
                                key={`${fullPath}-${artifact.type}`}
                                artifact={artifact}
                                onToggle={handleToggle}
                                isExpanded={!!expandedArtifacts[fullPath]}
                                fullPath={fullPath}
                                onMoreInfo={onMoreInfo}
                                onDelete={(id) => onDelete(id, parentPath || null)} // Pass parent ID to onDelete
                            >
                                {expandedArtifacts[fullPath] && (
                                    <ArtifactTree
                                        artifacts={expandedArtifacts[fullPath]}
                                        onToggle={onToggle}
                                        expandedArtifacts={expandedArtifacts}
                                        setExpandedArtifacts={setExpandedArtifacts}
                                        parentPath={fullPath}
                                        listArtifacts={listArtifacts}
                                        onMoreInfo={onMoreInfo}
                                        onDelete={onDelete}
                                    />
                                )}
                            </ArtifactCard>
                        );
                    })}
                </div>
            );
        };
        
        const ArtifactInfoModal = ({ artifact, onClose, listFiles, am }) => {
            const [files, setFiles] = React.useState(null);
            const [loading, setLoading] = React.useState(false);
            const [uploading, setUploading] = React.useState(false);
        
            const handleListFiles = async () => {
                try {
                    setLoading(true); // Show loading state
                    const fileList = await listFiles(artifact.id, ""); // Fetch files at the root of the artifact
                    setFiles(fileList);
                } catch (error) {
                    console.error("Failed to list files:", error);
                } finally {
                    setLoading(false); // Hide loading state
                }
            };
        
            const handleDownloadFile = async (filePath) => {
                try {
                    const url = await am.getFile(artifact.id, filePath); // Get the file URL
                    window.open(url, "_blank"); // Open the URL in a new tab
                } catch (error) {
                    console.error("Failed to download file:", error);
                }
            };
        
            const handleDeleteFile = async (filePath) => {
                try {
                    await am.removeFile(artifact.id, filePath); // Delete the file
                    setFiles(await listFiles(artifact.id, "")); // Refresh the file list
                } catch (error) {
                    console.error("Failed to delete file:", error);
                }
            };
        
            const handleUploadFile = async (event) => {
                const file = event.target.files[0];
                if (!file) return;
        
                const filePath = file.name; // Set the file path to the file's name
                try {
                    setUploading(true);
                    const uploadUrl = await am.putFile(artifact.id, filePath); // Get the upload URL
                    await axios.put(uploadUrl, file, {
                        headers: { "Content-Type": file.type || "application/octet-stream" },
                    });
                    setFiles(await listFiles(artifact.id, "")); // Refresh the file list
                } catch (error) {
                    console.error("Failed to upload file:", error);
                } finally {
                    setUploading(false);
                }
            };
        
            return (
                <div className="modal fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
                    <div className="modal-content bg-gray-800 rounded-lg shadow-lg p-6 w-full max-w-2xl">
                        <div className="modal-header flex justify-between items-center border-b border-gray-700 pb-4">
                            <h2 className="text-2xl font-bold text-white">Artifact Details</h2>
                            <button
                                onClick={onClose}
                                className="text-white text-2xl hover:text-red-500 transition"
                            >
                                &times;
                            </button>
                        </div>
                        <div className="modal-body mt-4 text-gray-300">
                            <p>
                                <strong>Name:</strong> {artifact.manifest?.name || "N/A"}
                            </p>
                            <p>
                                <strong>Description:</strong>{" "}
                                {artifact.manifest?.description || "N/A"}
                            </p>
                            <p>
                                <strong>Type:</strong> {artifact.type}
                            </p>
                            <p>
                                <strong>ID:</strong>{" "}
                                <code className="bg-gray-700 text-gray-300 px-2 py-1 rounded">
                                    {artifact.id}
                                </code>
                            </p>
                            <p>
                                <strong>Created At:</strong>{" "}
                                {new Date(artifact.created_at * 1000).toLocaleString()}
                            </p>
                            <p>
                                <strong>File Count:</strong> {artifact.file_count}
                            </p>
                            {artifact.file_count > 0 && (
                                <button
                                    className="hover:bg-blue-500 text-white font-bold py-2 px-4 rounded mt-4 flex items-center space-x-2 transition"
                                    onClick={handleListFiles}
                                    disabled={loading}
                                >
                                    <i className="fas fa-folder-open"></i>
                                    <span>{loading ? "Loading..." : "List Files"}</span>
                                </button>
                            )}
                            {files && files.length > 0 ? (
                                <div className="mt-4">
                                    <h3 className="text-lg font-bold">Files:</h3>
                                    <ul className="list-disc list-inside text-gray-300 mt-2">
                                        {files.map((file) => (
                                            <li key={file.name} className="break-all flex items-center justify-between">
                                                <span><i className={`fas ${file.type==='directory' ? 'fa-folder' : 'fa-file-alt'} mr-2`}></i>{file.name}</span>
                                                {file.type === "file" && (
                                                    <div className="flex space-x-2">
                                                        <button
                                                            className="bg-blue-600 hover:bg-blue-500 text-white py-1 px-3 rounded text-sm transition"
                                                            onClick={() => handleDownloadFile(file.name)}
                                                        >
                                                            Download
                                                        </button>
                                                        {artifact.staging !== null && (
                                                            <button
                                                                className="bg-red-600 hover:bg-red-500 text-white py-1 px-3 rounded text-sm transition"
                                                                onClick={() => handleDeleteFile(file.name)}
                                                            >
                                                                Delete
                                                            </button>
                                                        )}
                                                    </div>
                                                )}

                                            </li>
                                        ))}
                                    </ul>
                                </div>
                            ) : (
                                files !== null && <p className="text-gray-400 mt-2">No files available.</p>
                            )}
                            {artifact.staging !== null && (
                                <div className="mt-4">
                                    <label
                                        htmlFor="upload-file"
                                        className="bg-green-600 hover:bg-green-500 text-white font-bold py-2 px-4 rounded cursor-pointer transition"
                                    >
                                        {uploading ? "Uploading..." : "Upload File"}
                                    </label>
                                    <input
                                        id="upload-file"
                                        type="file"
                                        className="hidden"
                                        onChange={handleUploadFile}
                                        disabled={uploading}
                                    />
                                </div>
                            )}
                            <br></br>
                            <p>
                                <strong>Artifact Info:</strong>
                                <pre>{JSON.stringify(artifact, null, 2)}</pre>
                            </p>
                        </div>
                        <div className="modal-footer mt-6 flex justify-end border-t border-gray-700 pt-4">
                            <button
                                onClick={onClose}
                                className="bg-gray-600 hover:bg-gray-500 text-white font-bold py-2 px-4 rounded transition"
                            >
                                Close
                            </button>
                        </div>
                    </div>
                </div>
            );
        };                  
        
        const ArtifactsPanel = ({ am }) => {
            const [artifacts, setArtifacts] = React.useState([]);
            const [expandedArtifacts, setExpandedArtifacts] = React.useState({});
            const [selectedArtifact, setSelectedArtifact] = React.useState(null);
        
            React.useEffect(() => {
                const fetchTopLevelArtifacts = async () => {
                    if (am) {
                        const topLevelArtifacts = await am.list(); // Fetch top-level artifacts
                        setArtifacts(topLevelArtifacts);
                    }
                };
                fetchTopLevelArtifacts();
            }, [am]);
        
            const listArtifacts = async (parentId) => {
                try {
                    const children = await am.list(parentId);
                    return children;
                } catch (error) {
                    console.error("Failed to list artifacts:", error);
                    return [];
                }
            };
        
            const listFiles = async (artifactId, dir) => {
                try {
                    return await am.listFiles(artifactId, dir);
                } catch (error) {
                    console.error("Failed to list files:", error);
                    return [];
                }
            };
        
            const handleDelete = async (artifactId, parentId = null) => {
                event.preventDefault();
                if (confirm(`Are you sure you want to delete the artifact with ID: ${artifactId}?`)) {
                    try {
                        await am.delete(artifactId, {delete_files: true, recursive: true, _rkwargs: true}); // Delete the artifact
            
                        if (parentId) {
                            // If the artifact is a child, refresh the parent's children
                            const updatedChildren = await am.list(parentId);
                            setExpandedArtifacts((prevState) => ({
                                ...prevState,
                                [parentId]: updatedChildren,
                            }));
                        } else {
                            // If the artifact is top-level, refresh the top-level artifacts
                            setArtifacts(await am.list());
                        }
                    } catch (error) {
                        alert(`Failed to delete artifact with ID: ${artifactId}. Error: ${error}`);
                    }
                }
            };            
        
            return (
                <div className="main-content">
                    {artifacts.length === 0 ? (
                        <p>No artifacts available.</p>
                    ) : (
                        <ArtifactTree
                            artifacts={artifacts}
                            expandedArtifacts={expandedArtifacts}
                            setExpandedArtifacts={setExpandedArtifacts}
                            listArtifacts={listArtifacts}
                            onMoreInfo={setSelectedArtifact}
                            onDelete={handleDelete}
                        />
                    )}
                    {selectedArtifact && (
                        <ArtifactInfoModal
                            artifact={selectedArtifact}
                            onClose={() => setSelectedArtifact(null)}
                            listFiles={listFiles}
                            am={am}
                        />
                    )}
                </div>
            );
        };
        

        const WorkspaceCard = ({ workspace, onInfoClick, onEditClick, onBookmarkClick }) => {
            return (
                <div className="flex flex-col items-start p-6 bg-gray-800 rounded-lg shadow-lg mb-6 w-full relative">
                    <button
                        className="absolute top-2 right-2 text-white"
                        onClick={() => onBookmarkClick(workspace.id)}
                    >
                        <i className="fas fa-bookmark"></i>
                    </button>
                    <h3 className="text-xl font-bold">{workspace.id}</h3>
                    <p className="text-gray-400">{workspace.description}</p>
                        <div className="mt-4 flex justify-between w-full">
                        <div className="flex space-x-4">
                            <button
                                className="bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                                onClick={() => onInfoClick(workspace.id)}
                            >
                                <i className="fas fa-info-circle mr-2"></i> Info
                            </button>
                            <a
                                href={`/${workspace.id}`}
                                className="bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                            >
                                <i className="fas fa-arrow-right mr-2"></i> Open
                            </a>
                        </div>
                        <button
                            className="bg-yellow-600 hover:bg-yellow-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                            onClick={() => onEditClick(workspace)}
                        >
                            <i className="fas fa-edit mr-2"></i> Edit
                        </button>
                    </div>
                </div>
            );
        };        
        
        const WorkspaceInfoModal = ({ workspaceInfo, onClose }) => {
            return (
                <div className="modal">
                    <div className="modal-content">
                        <div className="modal-header">
                            <h2 className="text-2xl font-bold">{workspaceInfo.name}</h2>
                            <button onClick={onClose} className="text-white text-2xl">&times;</button>
                        </div>
                        <div className="modal-body">
                            <p><strong>Description:</strong> {workspaceInfo.description || "N/A"}</p>
                            <p><strong>Persistent:</strong> {workspaceInfo.persistent ? "Yes" : "No"}</p>
                            <p><strong>Read-Only:</strong> {workspaceInfo.read_only ? "Yes" : "No"}</p>
                            <p><strong>Owners:</strong> {workspaceInfo.owners.length > 0 ? workspaceInfo.owners.join(", ") : "None"}</p>
                            <p><strong>Applications:</strong> {workspaceInfo.applications ? Object.keys(workspaceInfo.applications).join(", ") : "None"}</p>
                            <p><strong>Service Types:</strong> {workspaceInfo.service_types ? Object.keys(workspaceInfo.service_types).join(", ") : "None"}</p>
                            <p><strong>Docs:</strong> {workspaceInfo.docs || "None"}</p>
                            <p><strong>Config:</strong> <pre>{JSON.stringify(workspaceInfo.config, null, 2)}</pre></p>
                        </div>
                    </div>
                </div>
            );
        };
        const CreateWorkspaceModal = ({
            onCreateWorkspace,
            onDeleteWorkspace,
            onClose,
            workspace = {},
            isEditing = false,
        }) => {
            const [id, setId] = React.useState(workspace.id || "");
            const [name, setName] = React.useState(workspace.id || "");
            const [description, setDescription] = React.useState(
                workspace.description || ""
            );
            const [persistent, setPersistent] = React.useState(
                workspace.persistent ?? true
            );
            const [readOnly, setReadOnly] = React.useState(
                workspace.read_only ?? false
            );
            const [overwrite, setOverwrite] = React.useState(true);
            const [owners, setOwners] = React.useState(
                workspace.owners ? workspace.owners.join(", ") : ""
            );
            const handleDelete = async (event) => {
                event.preventDefault();
                if (confirm("Are you sure you want to delete this workspace?")) {
                    await onDeleteWorkspace(workspace.id);
                    onClose();
                }
            };
            const handleSubmit = async (event) => {
                event.preventDefault();
                const ownersArray = owners.split(",").map((owner) => owner.trim());
                await onCreateWorkspace(
                    { id, name, description, owners: ownersArray, persistent, read_only: readOnly },
                    overwrite
                );
                onClose();
            };
        
            return (
                <div className="modal">
                    <div className="modal-content">
                        <div className="modal-header">
                            <h2 className="text-2xl font-bold">
                                {isEditing ? "Edit Workspace" : "Create Workspace"}
                            </h2>
                            <button
                                onClick={onClose}
                                className="text-white text-2xl"
                            >
                                &times;
                            </button>
                        </div>
                        <div className="modal-body">
                            <form onSubmit={handleSubmit}>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">ID
                                        <p className="text-gray-500">A workspace id must contain at least one hyphen (e.g. my-workspace), in lowercase letters, numbers and hyphens only.</p>
                                    </label>
                                    <input
                                        type="text"
                                        value={id}
                                        onChange={(e) => setId(e.target.value)}
                                        className="text-black mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md"
                                        required
                                    />
                                </div>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">Name
                                        <p className="text-gray-500">A human-readable name for the workspace.</p>
                                    </label>
                                    <input
                                        type="text"
                                        value={name}
                                        onChange={(e) => setName(e.target.value)}
                                        className="text-black mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md"
                                        required
                                    />
                                </div>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">Description
                                        <p className="text-gray-500">A brief description of the workspace, this will be displayed in the workspace list.</p>
                                    </label>
                                    <input
                                        type="text"
                                        value={description}
                                        onChange={(e) => setDescription(e.target.value)}
                                        className="text-black mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md"
                                        required
                                    />
                                </div>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">Owners (comma-separated)
                                        <p className="text-gray-500">A list of owners who can manage the workspace, it must be a comma-separated list of user ids or email addresses.</p>
                                    </label>
                                    <input
                                        type="text"
                                        value={owners}
                                        onChange={(e) => setOwners(e.target.value)}
                                        className="text-black mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md"
                                    />
                                </div>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">Persistent
                                        <p className="text-gray-500">If checked, the workspace will be persistent and will not be deleted after a period of inactivity.</p>
                                    </label>
                                    <input
                                        type="checkbox"
                                        checked={persistent}
                                        onChange={(e) => setPersistent(e.target.checked)}
                                        className="ml-2"
                                    />
                                </div>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">Read-Only</label>
                                    <input
                                        type="checkbox"
                                        checked={readOnly}
                                        onChange={(e) => setReadOnly(e.target.checked)}
                                        className="ml-2"
                                    />
                                </div>
                                <div className="mb-4">
                                    <label className="block text-sm font-medium text-gray-200">Overwrite
                                        <p className="text-gray-500">If checked, the workspace will be overwritten if it already exists.</p>
                                    </label>
                                    <input
                                        type="checkbox"
                                        checked={overwrite}
                                        onChange={(e) => setOverwrite(e.target.checked)}
                                        className="ml-2"
                                    />
                                </div>

                                {isEditing && <button
                                    className="bg-red-600 mr-3 text-white font-bold py-2 px-4 rounded"
                                    onClick={(evt) => handleDelete(evt)}
                                >
                                    Delete Workspace
                                </button>}
                                
                                <button
                                    type="submit"
                                    className="bg-blue-600 text-white font-bold py-2 px-4 rounded"
                                >
                                    {isEditing ? "Save Workspace" : "Create Workspace"}
                                </button>
                            </form>
                        </div>
                    </div>
                </div>
            );
        };
        
        const MainContent = ({
            isCollapsed,
            enabledPanels,
            ws,
            s3,
            am,
            workspaceId,
            panel,
            clients,
            services,
            apps,
            files,
            onShowDetails,
            onInstallApp,
        }) => {
        
            const [workspaces, setWorkspaces] = React.useState([]);
            const [showCreateModal, setShowCreateModal] = React.useState(false);
            const [selectedWorkspaceInfo, setSelectedWorkspaceInfo] = React.useState(null);
            const [editingWorkspace, setEditingWorkspace] = React.useState(null);
            const [currentWorkspaceInfo, setCurrentWorkspaceInfo] = React.useState(null);
            const [bookmarkedItems, setBookmarkedItems] = React.useState([]);
            const [uploadProgress, setUploadProgress] = React.useState(0);
            const [fileList, setFileList] = React.useState(files);
        
            // Centralized expandedDirs state
            const [expandedDirs, setExpandedDirs] = React.useState({});
        
            React.useEffect(() => {
                setFileList(files); // Keep file list updated
            }, [files]);
        
            React.useEffect(() => {
                const fetchWorkspaces = async () => {
                    if (panel === 'development' && ws) {
                        const workspacesList = await ws.listWorkspaces();
                        console.log('Workspaces:', workspacesList);
                        setWorkspaces(workspacesList);
                    }
                    if (panel === 'dashboard' && ws) {
                        try {
                            const workspaceInfo = await ws.getWorkspaceInfo(workspaceId);
                            console.log('Workspace Info:', workspaceInfo);
                            setCurrentWorkspaceInfo(workspaceInfo);
                        } catch (error) {
                            console.error('Failed to fetch workspace info:', error);
                        }
                    }
                };
                fetchWorkspaces();
            }, [panel, ws]);
        
            // Update bookmarks when currentWorkspaceInfo changes
            React.useEffect(() => {
                // Check for bookmarks in config
                if (currentWorkspaceInfo && currentWorkspaceInfo.config && currentWorkspaceInfo.config.bookmarks) {
                    setBookmarkedItems(currentWorkspaceInfo.config.bookmarks);
                } else {
                    setBookmarkedItems([]);
                }
            }, [currentWorkspaceInfo]);
        
            const handleDeleteWorkspace = async (workspaceId) => {
                try {
                    await ws.deleteWorkspace(workspaceId);
                    setWorkspaces(await ws.listWorkspaces());
                    setSelectedWorkspaceInfo(null);
                } catch (error) {
                    alert(`Failed to delete workspace: ${error.message}`);
                }
            };
        
            const handleCreateWorkspace = async (workspaceData, overwrite) => {
                try {
                    await ws.createWorkspace({ ...workspaceData }, overwrite);
                    setWorkspaces(await ws.listWorkspaces());
                } catch (error) {
                    alert(`Failed to create workspace: ${error.message}`);
                    throw error;
                }
            };
        
            const handleInfoClick = async (workspaceId) => {
                try {
                    const workspaceInfo = await ws.getWorkspaceInfo(workspaceId);
                    setSelectedWorkspaceInfo(workspaceInfo);
                } catch (error) {
                    alert(`Failed to get workspace info: ${error.message}`);
                }
            };
        
            const handleEditWorkspace = (workspace) => {
                setEditingWorkspace(workspace); // Open the create modal for editing
            };
        
            const handleSaveEditedWorkspace = async (workspaceData, overwrite) => {
                try {
                    await ws.createWorkspace({ ...workspaceData }, overwrite);
                    setWorkspaces(await ws.listWorkspaces());
                    setEditingWorkspace(null); // Close the modal after editing
                } catch (error) {
                    alert(`Failed to update workspace: ${error.message}`);
                }
            };
        
            const handleBookmarkWorkspace = async (newWorkspaceId) => {
                try {
                    await ws.bookmark({
                        bookmark_type: 'workspace',
                        id: newWorkspaceId
                    });
                    // Refresh the workspace info
                    const workspaceInfo = await ws.getWorkspaceInfo(workspaceId);
                    setCurrentWorkspaceInfo(workspaceInfo);
                    alert('Workspace bookmarked successfully!');
                } catch (error) {
                    alert(`Failed to bookmark workspace: ${error.message}`);
                }
            };
        
            const handleBookmarkService = async (serviceId) => {
                try {
                    await ws.bookmark({
                        bookmark_type: 'service',
                        id: serviceId
                    });
                    // Refresh the workspace info
                    const workspaceInfo = await ws.getWorkspaceInfo(workspaceId);
                    setCurrentWorkspaceInfo(workspaceInfo);
                    alert('Service bookmarked successfully!');
                } catch (error) {
                    alert(`Failed to bookmark service: ${error.message}`);
                }
            };
        
            const handleUnbookmarkItem = async (bookmark_type, id) => {
                try {
                    await ws.unbookmark({
                        bookmark_type,
                        id
                    });
                    // Refresh the workspace info
                    const workspaceInfo = await ws.getWorkspaceInfo(workspaceId);
                    setCurrentWorkspaceInfo(workspaceInfo);
                } catch (error) {
                    alert(`Failed to unbookmark workspace: ${error.message}`);
                }
            };
        
            const handleFileDrop = async (e) => {
                e.preventDefault();
                const droppedFiles = e.dataTransfer.files;
                for (const file of droppedFiles) {
                    await handleFileUpload(file);
                }
            };
        
            const handleFileUpload = async (file) => {
                try {
                    const presignedUrl = await s3.generatePresignedUrl(file.name, "put_object");
        
                    const response = await axios.put(presignedUrl, file, {
                        headers: {
                            "Content-Type": file.type || "application/octet-stream",
                        },
                        onUploadProgress: (progressEvent) => {
                            if (!progressEvent.total) return;
                            const progress = (progressEvent.loaded / progressEvent.total) * 100;
                            setUploadProgress(progress);
                        },
                    });
        
                    if (response.status === 200) {
                        console.log(`File ${file.name} uploaded successfully`);
        
                        // Re-fetch the updated file list and update the state
                        const updatedFiles = await listFiles(); // Fetch the updated files after upload
                        setFileList(updatedFiles); // Update the fileList state to trigger a re-render
                    } else {
                        console.error("File upload failed", response.statusText);
                    }
                    setUploadProgress(0); // Reset progress after upload
                } catch (error) {
                    console.error("File upload failed", error);
                }
            };
        
            const handleFileDownload = async (filePath) => {
                try {
                    const presignedUrl = await s3.generatePresignedUrl(filePath, "get_object");
                    window.open(presignedUrl, '_blank'); // Trigger file download
                } catch (error) {
                    console.error("Failed to generate download link", error);
                }
            };
        
            const handleFileDelete = async (fullPath, type) => {
                try {
                    let answer;
                    if (type === "directory") {
                        answer = confirm(
                            `Are you sure you want to delete the directory ${fullPath} and all its contents?`
                        );
                    } else {
                        answer = confirm(`Are you sure you want to delete the file ${fullPath}?`);
                    }
                    if (!answer) return;
            
                    // Delete the file or directory
                    if (type === "directory") {
                        await s3.deleteDirectory(fullPath + "/");
                    } else {
                        await s3.deleteFile(fullPath);
                    }
            
                    // Determine the parent directory
                    const parentPath = fullPath.substring(0, fullPath.lastIndexOf("/")) || ""; // Root if no slash
            
                    if (parentPath) {
                        // Refresh the parent directory in expandedDirs
                        const updatedFiles = await listFiles(parentPath);
                        setExpandedDirs((prevState) => ({
                            ...prevState,
                            [parentPath]: updatedFiles,
                        }));
                    } else {
                        // Refresh the top-level file list
                        const updatedFiles = await listFiles("");
                        setFileList(updatedFiles);
                    }
                } catch (error) {
                    console.error("Failed to delete file", error);
                }
            };            
        
            const listFiles = async (path = '') => {
                try {
                    const files = await s3.listFiles(path); // Ensure this calls the server to get updated data
                    return files;
                } catch (error) {
                    console.error("Failed to list files", error);
                    return [];
                }
            };
        
            return (
                <div
                    className={`main-content transition-all duration-300 ${isCollapsed ? 'ml-16' : 'ml-64'}`}
                    onDrop={handleFileDrop}
                    onDragOver={(e) => e.preventDefault()} // Prevent default to allow drop
                >
                    {panel === 'dashboard' && (
                        <>
                            <h1 className="text-4xl font-bold capitalize">Dashboard</h1>
                            {/* Header Section for Workspace Info */}
                            {currentWorkspaceInfo && (
                                <div className="relative p-6 bg-gray-800 rounded-lg shadow-lg m-10">
                                    {/* Bookmark Icon in the Upper Right Corner */}
                                    <button
                                        className="absolute top-4 right-4 text-yellow-400 hover:text-yellow-500 transition duration-300"
                                        onClick={() => handleBookmarkWorkspace(currentWorkspaceInfo.id)}
                                    >
                                        <i className="fas fa-bookmark fa-2x"></i>
                                    </button>
                                
                                    {/* Header Section */}
                                    <div className="flex items-center mb-6">
                                        <i className="fas fa-folder-open fa-2x text-blue-400 mr-4"></i>
                                        <h2 className="text-3xl font-bold text-white">Workspace: {currentWorkspaceInfo.name}</h2>
                                    </div>
                                
                                    {/* Main Info Section */}
                                    <div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-2">
                                        <div>
                                            <p className="text-lg text-gray-400 mb-4 flex items-center">
                                                <i className="fas fa-tag mr-2 text-blue-400"></i>
                                                <strong>ID: </strong> {currentWorkspaceInfo.id}
                                            </p>
                                            <p className="text-lg text-gray-400 mb-4 flex items-center">
                                                <i className="fas fa-tag mr-2 text-blue-400"></i>
                                                <strong>Name: </strong> {currentWorkspaceInfo.name}
                                            </p>
                                            <p className="text-lg text-gray-400 mb-4 flex items-center">
                                                <i className="fas fa-shield-alt mr-2 text-green-400"></i>
                                                <strong>Persistent: </strong> {currentWorkspaceInfo.persistent ? 'Yes' : 'No'}
                                            </p>
                                            <p className="text-lg text-gray-400 mb-4 flex items-center">
                                                <i className="fas fa-lock mr-2 text-red-400"></i>
                                                <strong>Read-Only: </strong> {currentWorkspaceInfo.read_only ? 'Yes' : 'No'}
                                            </p>
                                        </div>
                                        <div>
                                            <div className="flex items-center text-lg text-gray-400 mb-4">
                                                <i className="fas fa-users mr-2 text-yellow-400"></i>
                                                <strong>Owners: </strong>
                                                <div className="flex flex-wrap ml-2">
                                                    {currentWorkspaceInfo.owners ? (
                                                        currentWorkspaceInfo.owners.map((owner, idx) => (
                                                            <span key={idx} className="bg-gray-600 text-white rounded-full px-3 py-1 mr-2 mb-2 text-sm">
                                                                {owner}
                                                            </span>
                                                        ))
                                                    ) : (
                                                        <span>No owners</span>
                                                    )}
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <div className="text-gray-300 p-2 rounded-lg mb-3">
                                        <p className="text-md">
                                            <strong>Description: </strong> {currentWorkspaceInfo.description || 'No description available'}
                                        </p>
                                    </div>
                                
                                    {/* Info Box */}
                                    <div className="bg-gray-700 text-gray-300 p-4 rounded-lg mb-6">
                                        <p className="text-md mb-4">
                                            <i className="fas fa-info-circle text-blue-400 mr-2"></i>
                                            A Hypha workspace is an isolated virtual space for connected users, computational clients, and AI agents, similar to an online meeting room. To connect to this workspace, you need to 
                                            <a href="#development" className="text-blue-400 underline ml-1">generate an access token</a>.
                                        </p>
                                    </div>
                                </div>
                                
                                
                            )}
                            {bookmarkedItems.length > 0 && (
                                <>
                                  <hr className="my-8 border-gray-700"></hr>
                                  <div className="flex items-center mb-6">
                                    {/* Icon for bookmarks */}
                                    <i className="fas fa-bookmark fa-2x text-yellow-400 mr-3"></i>
                                    <h2 className="text-2xl font-bold">Bookmarks</h2>
                                  </div>
                                  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-8 mt-6">
                                    {bookmarkedItems.map((item, idx) => (
                                      <div
                                        key={idx}
                                        className="p-6 bg-gray-800 rounded-lg shadow-lg relative hover:shadow-xl transition-shadow duration-300"
                                      >
                                        {/* Icon based on type */}
                                        <div className="flex items-center mb-4">
                                          {item.bookmark_type === "workspace" ? (
                                            <i className="fas fa-folder fa-2x text-blue-400 mr-3"></i>
                                          ) : (
                                            <i className="fas fa-cogs fa-2x text-green-400 mr-3"></i>
                                          )}
                                          <p className="text-white whitespace-pre-wrap break-words">{item.name}</p>
                                        </div>
                                        <p className="text-gray-400 whitespace-pre-wrap break-words">ID: <code>{item.id}</code></p>
                                        {/* Description */}
                                        <p className="text-gray-400 mb-4">{item.description || "No description available"}</p>
                              
                                        {/* Type label */}
                                        <span
                                          className={`text-sm font-semibold ${
                                            item.bookmark_type === "workspace" ? "text-blue-300" : "text-green-300"
                                          } mb-4`}
                                        >
                                          {item.bookmark_type.charAt(0).toUpperCase() + item.bookmark_type.slice(1)}
                                        </span>
                              
                                        {/* Button to go to the workspace (only for workspace type) */}
                                        {item.bookmark_type === "workspace" && (
                                          <a
                                            href={`/${item.id}`}
                                            className="absolute bottom-4 right-4 bg-blue-500 hover:bg-blue-400 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                                          >
                                            <i className="fas fa-arrow-right mr-2"></i> Go to Workspace
                                          </a>
                                        )}
                              
                                        {/* Delete Button */}
                                        <button
                                            onClick={() => handleUnbookmarkItem(item.bookmark_type, item.id)}
                                            className="absolute top-4 right-4 text-gray-400 hover:text-red-500 transition duration-300"
                                            >
                                            <i className="fas fa-times fa-2x"></i>
                                            </button>
                                      </div>
                                    ))}
                                  </div>
                                </>
                            )}
                            <hr className="my-8 border-gray-700"></hr>
                            <div className="flex items-center mb-6">
                                <i className="fas fa-users fa-2x text-yellow-400 mr-3"></i>
                                <h2 className="text-2xl font-bold">Clients</h2>
                            </div>
                            <p className="text-xl text-gray-400 mt-4">
                                Clients connected to workspace: <code>{workspaceId}</code>
                            </p>
                            <div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-2 gap-8 mt-6">
                                {clients.map(client => (
                                    <ClientCard key={client.id} client={client} />
                                ))}
                            </div>
                            <hr className="my-8 border-gray-700"></hr>
                            <div className="flex items-center mb-6">
                                <i className="fas fa-cogs fa-2x text-green-400 mr-3"></i>
                                <h2 className="text-2xl font-bold">Services</h2>
                            </div>
                            <p className="text-xl text-gray-400 mt-4">
                                Services available in workspace: <code>{workspaceId}</code>
                            </p>
                            <div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-2 gap-8 mt-6">
                                {services.map(service => (
                                    service.type !== "built-in" && (
                                        <ServiceCard 
                                            key={service.id} 
                                            service={service} 
                                            onBookmarkService={handleBookmarkService} 
                                            onShowDetails={onShowDetails} 
                                        />
                                    )
                                ))}
                            </div>
                        </>
                    )}
        
                    {panel === 'apps' && (
                        <>
                            <h1 className="text-4xl font-bold capitalize">Applications</h1>
        
                            <p className="text-xl text-gray-400 mt-4">
                                Applications installed in workspace: <code>{workspaceId}</code>
                            </p>
                            <button
                                className="mt-4 bg-green-600 hover:bg-green-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                                onClick={() => onInstallApp()}
                            >
                                <i className="fas fa-plus mr-2"></i> Install App
                            </button>
                            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-6">
                                {apps.map(app => (
                                    <AppCard key={app.id} app={app} />
                                ))}
                            </div>
                        </>
                    )}
        
                    {panel === 'files' && (
                        <>
                            <h1 className="text-4xl font-bold capitalize">Files</h1>
                            <p className="text-xl text-gray-400 mt-4">
                                Files in workspace: <code>{workspaceId}</code>
                            </p>
                            <div className="p-4 border-dashed border-4 border-gray-600 rounded mt-6">
                                <p className="text-center text-gray-500">
                                    Drag and drop files here to upload
                                </p>
                                {!!uploadProgress && (
                                    <div className="w-full bg-gray-600 h-4 rounded relative flex items-center justify-center">
                                        <div
                                            className="bg-blue-500 h-4 rounded"
                                            style={{ width: `${uploadProgress}%` }}
                                        ></div>
                                        <span className="absolute text-white text-sm">
                                            {Math.round(uploadProgress, 1)}%
                                        </span>
                                    </div>
                                )}
                            </div>
                            <div className="mt-6">
                                {fileList.length === 0 ? (
                                    <p>No files available.</p>
                                ) : (
                                    <FileTree
                                        files={fileList}
                                        onDelete={handleFileDelete}
                                        onDownload={handleFileDownload}
                                        listFiles={listFiles}
                                        // Pass the centralized expandedDirs and setExpandedDirs
                                        expandedDirs={expandedDirs}
                                        setExpandedDirs={setExpandedDirs}
                                    />
                                )}
                            </div>
                        </>
                    )}
        
                    {panel === 'development' && (
                        <>
                            <h1 className="text-4xl font-bold capitalize">Development</h1>

                            {enabledPanels.includes('apps') && (
                                <>
                                <hr className="my-8 border-gray-700"></hr>
                                <h2 className="text-2xl font-bold mt-5">
                                    Apps <i className="fas fa-th-large text-xl text-gray-400"></i>
                                </h2>
                                <p className="text-xl text-gray-400 mt-4">Develop Apps for Hypha</p>
                                <button
                                    onClick={() => window.loadCodeEditor()}
                                    className="mt-4 bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                                >
                                    <i className="fas fa-code mr-2"></i> New App
                                </button>
                                <div
                                    style={{ display: 'block', width: '100%', height: '100%', zIndex: 1000 }}
                                    id="hypha-window-container"
                                ></div>
                                </>
                            )}

                            <hr className="my-8 border-gray-700"></hr>
        
                            <h2 className="text-2xl font-bold mt-5">
                                Workspaces <i className="fas fa-folder-open text-xl text-gray-400"></i>
                            </h2>
                            <p className="text-xl text-gray-400 mt-4">
                                Workspaces accessible to you
                            </p>
                            <div className="flex justify-between items-center mt-6">
                                <button
                                    className="bg-green-600 hover:bg-green-500 text-white font-bold py-2 px-4 rounded-full transition duration-300 flex items-center"
                                    onClick={() => setShowCreateModal(true)}
                                >
                                    <i className="fas fa-plus mr-2"></i> Create Workspace
                                </button>
                            </div>
                            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-8 mt-6">
                                {workspaces.map((workspace) => (
                                    <WorkspaceCard
                                        key={workspace.id}
                                        workspace={workspace}
                                        onInfoClick={handleInfoClick}
                                        onEditClick={handleEditWorkspace}
                                        onBookmarkClick={handleBookmarkWorkspace}
                                    />
                                ))}
                            </div>

                            <hr className="my-8 border-gray-700"></hr>

                            <h2 className="text-2xl font-bold mt-5">
                                Connection Tokens <i className="fas fa-key text-xl text-gray-400"></i>
                            </h2>
                            <TokenPanel
                                workspaceId={workspaceId}
                                onGenerateToken={async (workspace, expiryTime) => {
                                    const token = await ws.generateToken({ expires_in: expiryTime, workspace });
                                    return token;
                                }}
                            />
                        </>
                    )}
                    { panel === 'artifacts' && (
                        <>
                            <h1 className="text-4xl font-bold capitalize">Artifacts</h1>
                            <p className="text-xl text-gray-400 mt-4">
                                Artifacts in workspace: <code>{workspaceId}</code>
                            </p>
                            <div className="mt-6">
                                <ArtifactsPanel am={am} />
                            </div>
                        </>
                    )}
        
                    {showCreateModal && (
                        <CreateWorkspaceModal
                            onDeleteWorkspace={handleDeleteWorkspace}
                            onCreateWorkspace={handleCreateWorkspace}
                            onClose={() => setShowCreateModal(false)}
                        />
                    )}
        
                    {editingWorkspace && (
                        <CreateWorkspaceModal
                            workspace={editingWorkspace} // Prefill with existing workspace data
                            onDeleteWorkspace={handleDeleteWorkspace}
                            onCreateWorkspace={handleSaveEditedWorkspace} // Save edited data
                            onClose={() => setEditingWorkspace(null)}
                            isEditing={true} // Indicator to show that it's editing mode
                        />
                    )}
        
                    {selectedWorkspaceInfo && (
                        <WorkspaceInfoModal
                            workspaceInfo={selectedWorkspaceInfo}
                            onClose={() => setSelectedWorkspaceInfo(null)}
                        />
                    )}
                    <footer className="bg-gray-900 text-gray-400 py-6 mt-16">
                        <div className="container mx-auto text-center">
                            <p>Hypha is an open-source platform released under the MIT license.</p>
                            <p>
                                Visit our <a href="https://github.com/amun-ai/hypha" target="_blank" className="text-gray-300 hover:text-white">GitHub repository</a> for more information.
                            </p>
                        </div>
                    </footer>
                </div>
            );
        };

        const config = window.defaultService.getServerConfig();

        const getActivePanelFromHash = () => {
            const hash = window.location.hash.slice(1); // Remove the "#" from the hash
            return hash || 'dashboard'; // Default to 'dashboard' panel if no hash
        };

        const App = () => {
            const [isCollapsed, setIsCollapsed] = React.useState(false);
            const [enabledPanels, setEnabledPanels] = React.useState(['dashboard', 'development']);
            const [activePanel, setActivePanel] = React.useState(getActivePanelFromHash);
            const [clients, setClients] = React.useState([]);
            const [services, setServices] = React.useState([]);
            const [apps, setApps] = React.useState([]);
            const [files, setFiles] = React.useState([]);
            const [ws, setWs] = React.useState(null);
            const [s3, setS3] = React.useState(null);
            const [am, setAM] = React.useState(null);
            const [selectedService, setSelectedService] = React.useState(null);
        
            React.useEffect(() => {
                // Connect to the Hypha server
                const connectToServer = async () => {
                    try{
                        const remoteServer = await hyphaWebsocketClient.connectToServer(config);
                        window.remoteServer = remoteServer;
                        const newPanels = [];
                        try{
                            await remoteServer.getService('public/server-apps', { case_conversion: 'camel' });
                            newPanels.push('apps');
                        }
                        catch(e){
                            console.log("Server apps service not available");
                        }
                        try {
                            const s3 = await remoteServer.getService('public/s3-storage', { case_conversion: 'camel' });
                            newPanels.push('files');
                            setS3(s3);
                        }
                        catch(e){
                            console.log("S3 storage service not available");
                        }
                        try {
                            // get artifact service
                            const am = await remoteServer.getService('public/artifact-manager', { case_conversion: 'camel' });
                            newPanels.push('artifacts');
                            setAM(am);
                        }
                        catch(e){
                            console.log("Artifacts service not available");
                        }
                        setEnabledPanels([...enabledPanels, ...newPanels]);
                        setWs(remoteServer);
                    }
                    catch(e){
                        alert(`Failed to connect to the server: ${e.message}`);
                        window.location.href = '/';
                    }
                };
                connectToServer();
            }, []);
        
            React.useEffect(() => {
                // Fetch initial workspace data based on active panel
                const fetchWorkspaceData = async () => {
                    if (!ws) {
                        return;
                    }
                    if (activePanel === 'dashboard') {
                        setClients(await ws.listClients());
                        setServices(await ws.listServices(config.workspace));
                    } else if (activePanel === 'apps') {
                        try {
                            const controller = await ws.getService('public/server-apps', { case_conversion: 'camel' });
                            const apps = await controller.listApps();
                            setApps(apps);
                        } catch (e) {
                            alert(`Failed to list apps: ${e.message}`);
                        }
                    } else if (activePanel === 'files') {
                        try {
                            const s3 = await ws.getService('public/s3-storage', { case_conversion: 'camel' });
                            const files = await s3.listFiles();
                            setFiles(files);
                        } catch (e) {
                            alert(`Failed to list files: ${e.message}`);
                        }
                    } else if (activePanel === 'artifacts') {
                        try {
                            const am = await ws.getService('public/artifact-manager', { case_conversion: 'camel' });
                            setAM(am);
                        } catch (e) {
                            alert(`Failed to list artifacts: ${e.message}`);
                        }
                    }
                };
                fetchWorkspaceData();
                document.title = `${activePanel.charAt(0).toUpperCase() + activePanel.slice(1)} - Hypha Workspace Management`;
            }, [activePanel, ws]);
        
            const onShowDetails = (service) => {
                setSelectedService(service);
            };
        
            const onInstallApp = async () => {
                const url = prompt(
                    'Enter the URL of the app to install',
                    'https://raw.githubusercontent.com/amun-ai/hypha/main/tests/testWebWorkerPlugin.imjoy.html'
                );
                try {
                    if (!url) return;
                    const controller = await ws.getService('public/server-apps', { case_conversion: 'camel' });
                    const appInfo = await controller.install(url, { overwrite: true, _rkwargs: true});
                    console.log('App installed:', appInfo);
                    setApps(await controller.listApps());
                } catch (e) {
                    alert(`Failed to install app: ${e.message}`);
                }
            };
        
            const closeModal = () => {
                setSelectedService(null);
            };

            React.useEffect(() => {
                const updateHash = () => {
                    window.location.hash = activePanel; // Update the URL hash when the active panel changes
                };
                updateHash();
            }, [activePanel]);

            React.useEffect(() => {
                const handleHashChange = () => {
                    setActivePanel(getActivePanelFromHash()); // Set the active panel based on the URL hash when it changes
                };

                window.addEventListener('hashchange', handleHashChange);
                return () => window.removeEventListener('hashchange', handleHashChange); // Cleanup event listener
            }, []);

            return (
                <div>
                    <Sidebar isCollapsed={isCollapsed} enabledPanels={enabledPanels} onToggle={() => setIsCollapsed(!isCollapsed)} setActivePanel={setActivePanel} />
                    <MainContent
                        isCollapsed={isCollapsed}
                        enabledPanels={enabledPanels}
                        ws={ws}
                        s3={s3}
                        am={am}
                        workspaceId={config.workspace}
                        panel={activePanel}
                        clients={clients}
                        services={services}
                        apps={apps}
                        files={files}
                        onShowDetails={onShowDetails}
                        onInstallApp={onInstallApp}
                    />
                    {selectedService && <Modal serviceInfo={selectedService} onClose={() => setSelectedService(null)} />}
                </div>
            );
        };
        
        ReactDOM.render(<App />, document.getElementById('app'));
        
    </script>
</body>

</html>
