Implement full system directory browser modal for temp directories
- Add GET /api/system/browse-dir and POST /api/system/create-dir endpoints - Add Folder Browser Modal overlay in index.html - Add browse buttons to both job-specific and default setting temp dir inputs - Add JS helpers and event handlers for directory list loading, parent navigation, directory selection, and new folder creation
This commit is contained in:
+214
@@ -2171,6 +2171,220 @@ async function initializeApp() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wire up folder browser modals
|
||||
const browseJobTempBtn = document.getElementById("browse-job-temp-btn");
|
||||
const browseDefaultTempBtn = document.getElementById("browse-default-temp-btn");
|
||||
const folderModalCloseBtn = document.getElementById("folder-modal-close-btn");
|
||||
const folderCancelBtn = document.getElementById("folder-cancel-btn");
|
||||
const folderSelectBtn = document.getElementById("folder-select-btn");
|
||||
const folderUpBtn = document.getElementById("folder-up-btn");
|
||||
const createFolderBtn = document.getElementById("create-folder-btn");
|
||||
const newFolderNameInput = document.getElementById("new-folder-name");
|
||||
|
||||
if (browseJobTempBtn) {
|
||||
browseJobTempBtn.addEventListener("click", () => {
|
||||
openFolderBrowser(document.getElementById("temp-dir-input"));
|
||||
});
|
||||
}
|
||||
|
||||
if (browseDefaultTempBtn) {
|
||||
browseDefaultTempBtn.addEventListener("click", () => {
|
||||
openFolderBrowser(document.getElementById("default-temp-dir-input"));
|
||||
});
|
||||
}
|
||||
|
||||
if (folderModalCloseBtn) {
|
||||
folderModalCloseBtn.addEventListener("click", closeFolderBrowser);
|
||||
}
|
||||
|
||||
if (folderCancelBtn) {
|
||||
folderCancelBtn.addEventListener("click", closeFolderBrowser);
|
||||
}
|
||||
|
||||
// Close modal when clicking outside card
|
||||
const folderModal = document.getElementById("folder-modal");
|
||||
if (folderModal) {
|
||||
folderModal.addEventListener("click", (e) => {
|
||||
if (e.target === folderModal) {
|
||||
closeFolderBrowser();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (folderSelectBtn) {
|
||||
folderSelectBtn.addEventListener("click", () => {
|
||||
if (activeTargetInput && currentBrowsePath) {
|
||||
activeTargetInput.value = currentBrowsePath;
|
||||
activeTargetInput.dispatchEvent(new Event("change"));
|
||||
activeTargetInput.dispatchEvent(new Event("input"));
|
||||
}
|
||||
closeFolderBrowser();
|
||||
});
|
||||
}
|
||||
|
||||
if (folderUpBtn) {
|
||||
folderUpBtn.addEventListener("click", () => {
|
||||
if (currentBrowsePath) {
|
||||
const parts = currentBrowsePath.split("/");
|
||||
if (parts.length > 1) {
|
||||
parts.pop();
|
||||
const parent = parts.join("/") || "/";
|
||||
loadFolderDir(parent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (createFolderBtn) {
|
||||
createFolderBtn.addEventListener("click", createFolder);
|
||||
}
|
||||
|
||||
if (newFolderNameInput) {
|
||||
newFolderNameInput.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
createFolder();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------
|
||||
Folder Browser Modal & API Helpers
|
||||
---------------------------------------------------- */
|
||||
let activeTargetInput = null;
|
||||
let currentBrowsePath = "";
|
||||
|
||||
function openFolderBrowser(targetInput) {
|
||||
activeTargetInput = targetInput;
|
||||
const modal = document.getElementById("folder-modal");
|
||||
if (!modal) return;
|
||||
|
||||
const statusDiv = document.getElementById("folder-modal-status");
|
||||
if (statusDiv) statusDiv.textContent = "";
|
||||
|
||||
let initialPath = targetInput ? targetInput.value.trim() : "";
|
||||
|
||||
modal.style.display = "flex";
|
||||
document.body.style.overflow = "hidden";
|
||||
|
||||
loadFolderDir(initialPath);
|
||||
}
|
||||
|
||||
function closeFolderBrowser() {
|
||||
const modal = document.getElementById("folder-modal");
|
||||
if (modal) {
|
||||
modal.style.display = "none";
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
activeTargetInput = null;
|
||||
}
|
||||
|
||||
async function loadFolderDir(path) {
|
||||
const listContainer = document.getElementById("folder-list");
|
||||
const currentPathInput = document.getElementById("folder-current-path");
|
||||
const statusDiv = document.getElementById("folder-modal-status");
|
||||
if (!listContainer) return;
|
||||
|
||||
listContainer.innerHTML = '<div style="text-align: center; padding: 2rem; color: var(--text-muted);"><i class="fa-solid fa-spinner fa-spin"></i> Loading...</div>';
|
||||
|
||||
try {
|
||||
const url = `/api/system/browse-dir?path=${encodeURIComponent(path)}`;
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.detail || "Failed to load directory.");
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
currentBrowsePath = data.current_path;
|
||||
if (currentPathInput) currentPathInput.value = currentBrowsePath;
|
||||
|
||||
listContainer.innerHTML = "";
|
||||
const entries = data.entries || [];
|
||||
|
||||
if (entries.length === 0) {
|
||||
listContainer.innerHTML = '<div style="text-align: center; padding: 2rem; color: var(--text-muted);">Empty directory</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
entries.forEach(entry => {
|
||||
const row = document.createElement("div");
|
||||
row.style.display = "flex";
|
||||
row.style.alignItems = "center";
|
||||
row.style.gap = "0.75rem";
|
||||
row.style.padding = "0.6rem 0.85rem";
|
||||
row.style.borderRadius = "6px";
|
||||
row.style.cursor = "pointer";
|
||||
row.style.transition = "background 0.2s";
|
||||
|
||||
row.addEventListener("mouseenter", () => {
|
||||
row.style.background = "rgba(255, 255, 255, 0.05)";
|
||||
});
|
||||
row.addEventListener("mouseleave", () => {
|
||||
row.style.background = "";
|
||||
});
|
||||
|
||||
const icon = document.createElement("i");
|
||||
if (entry.name === "..") {
|
||||
icon.className = "fa-solid fa-arrow-turn-up";
|
||||
icon.style.color = "var(--text-muted)";
|
||||
icon.style.transform = "rotate(-90deg)";
|
||||
} else {
|
||||
icon.className = "fa-solid fa-folder";
|
||||
icon.style.color = "var(--primary)";
|
||||
}
|
||||
row.appendChild(icon);
|
||||
|
||||
const label = document.createElement("span");
|
||||
label.textContent = entry.name;
|
||||
label.style.fontSize = "0.9rem";
|
||||
label.style.fontFamily = "monospace";
|
||||
row.appendChild(label);
|
||||
|
||||
row.addEventListener("click", () => {
|
||||
loadFolderDir(entry.path);
|
||||
});
|
||||
|
||||
listContainer.appendChild(row);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
listContainer.innerHTML = `<div style="text-align: center; padding: 2rem; color: var(--danger);"><i class="fa-solid fa-triangle-exclamation"></i> Error: ${err.message}</div>`;
|
||||
if (statusDiv) statusDiv.textContent = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function createFolder() {
|
||||
const nameInput = document.getElementById("new-folder-name");
|
||||
const statusDiv = document.getElementById("folder-modal-status");
|
||||
if (!nameInput || !nameInput.value.trim() || !currentBrowsePath) return;
|
||||
|
||||
const folderName = nameInput.value.trim();
|
||||
if (statusDiv) statusDiv.textContent = "";
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/system/create-dir", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
parent_path: currentBrowsePath,
|
||||
name: folderName
|
||||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
nameInput.value = "";
|
||||
const data = await res.json();
|
||||
loadFolderDir(data.path);
|
||||
} else {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
if (statusDiv) statusDiv.textContent = err.detail || "Failed to create folder.";
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (statusDiv) statusDiv.textContent = "Network error creating folder.";
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTempInfo() {
|
||||
|
||||
Reference in New Issue
Block a user