From f9e196fd66f5f49bbe67c0038a722fac81fb7220 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Mon, 5 Jan 2026 13:31:46 -0600 Subject: [PATCH] checkpoint --- index.html | 544 ++++++++++++++++++++++++++++------------------------- 1 file changed, 292 insertions(+), 252 deletions(-) diff --git a/index.html b/index.html index 72cb7638..8eae23b5 100644 --- a/index.html +++ b/index.html @@ -183,53 +183,53 @@ cursor: pointer; margin-right: 5px; } - .path-btn:hover { - background-color: #1976d2; - } - .branch-move-btn { - background-color: #424242; - color: white; - border: none; - cursor: pointer; - margin-right: 3px; - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - font-weight: bold; - } - .branch-move-btn:hover { - background-color: #616161; - } - .branch-move-btn:disabled { - opacity: 0.3; - cursor: not-allowed; - } - .branch-entry { - display: flex; - align-items: center; - padding: 8px; - margin: 5px 0; - background-color: #2d2d2d; - border-radius: 4px; - cursor: grab; - transition: background-color 0.2s, box-shadow 0.2s; - } - .branch-entry:hover { - background-color: #3d3d3d; - box-shadow: 0 2px 8px rgba(0,0,0,0.3); - } - .branch-entry.dragging { - opacity: 0.5; - cursor: grabbing; - box-shadow: 0 4px 12px rgba(0,0,0,0.5); - } - .branch-entry.drag-over { - border-top: 2px solid #1565c0; - } - .mount-list-item { + .path-btn:hover { + background-color: #1976d2; + } + .branch-move-btn { + background-color: #424242; + color: white; + border: none; + cursor: pointer; + margin-right: 3px; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + font-weight: bold; + } + .branch-move-btn:hover { + background-color: #616161; + } + .branch-move-btn:disabled { + opacity: 0.3; + cursor: not-allowed; + } + .branch-entry { + display: flex; + align-items: center; + padding: 8px; + margin: 5px 0; + background-color: #2d2d2d; + border-radius: 4px; + cursor: grab; + transition: background-color 0.2s, box-shadow 0.2s; + } + .branch-entry:hover { + background-color: #3d3d3d; + box-shadow: 0 2px 8px rgba(0,0,0,0.3); + } + .branch-entry.dragging { + opacity: 0.5; + cursor: grabbing; + box-shadow: 0 4px 12px rgba(0,0,0,0.5); + } + .branch-entry.drag-over { + border-top: 2px solid #1565c0; + } + .mount-list-item { padding: 10px; cursor: pointer; border-bottom: 1px solid #444; @@ -241,6 +241,28 @@ h3 { color: #e0e0e0; } + .toast { + display: none; + position: fixed; + bottom: 50%; + right: 50%; + color: white; + padding: 12px 24px; + border-radius: 4px; + z-index: 1000; + opacity: 0; + transition: opacity 2s; + } + .toast.show_success { + display: block; + background-color: #2e7d32; + opacity: 1; + } + .toast.show_failure { + display: block; + background-color: red; + opacity: 1; + } @@ -273,6 +295,7 @@
+
- + function addBranchEntry(path = '', mode = 'RW', minfreespace = '') { + const container = document.getElementById('branches-list'); + const entry = document.createElement('div'); + entry.className = 'branch-entry'; + entry.draggable = true; + entry.id = 'branch-entry-' + branchEntryCounter++; + entry.addEventListener('dragstart', handleDragStart); + entry.addEventListener('dragend', handleDragEnd); + entry.addEventListener('dragover', handleDragOver); + entry.addEventListener('dragleave', handleDragLeave); + entry.addEventListener('drop', handleDrop); + const pathInput = document.createElement('input'); + pathInput.type = 'text'; + pathInput.className = 'branch-path'; + pathInput.placeholder = 'path'; + pathInput.value = path; + const pathBtn = document.createElement('button'); + pathBtn.className = 'path-btn'; + pathBtn.textContent = 'Path'; + pathBtn.onclick = () => openPathModal(pathInput); + const modeSelect = document.createElement('select'); + modeSelect.className = 'branch-mode'; + ['RW', 'NC', 'RO'].forEach(m => { + const opt = document.createElement('option'); + opt.value = m; + opt.textContent = m; + if (m === mode) opt.selected = true; + modeSelect.appendChild(opt); + }); + const minfreespaceInput = document.createElement('input'); + minfreespaceInput.type = 'text'; + minfreespaceInput.className = 'branch-minfreespace'; + minfreespaceInput.placeholder = 'minfreespace'; + minfreespaceInput.value = minfreespace; + const moveUpBtn = document.createElement('button'); + moveUpBtn.className = 'branch-move-btn branch-move-up'; + moveUpBtn.innerHTML = '▲'; + moveUpBtn.title = 'Move up'; + moveUpBtn.onclick = () => moveEntryUp(entry); + const moveDownBtn = document.createElement('button'); + moveDownBtn.className = 'branch-move-btn branch-move-down'; + moveDownBtn.innerHTML = '▼'; + moveDownBtn.title = 'Move down'; + moveDownBtn.onclick = () => moveEntryDown(entry); + const removeBtn = document.createElement('button'); + removeBtn.className = 'branch-remove'; + removeBtn.textContent = 'Remove'; + removeBtn.onclick = () => entry.remove(); + entry.appendChild(pathInput); + entry.appendChild(pathBtn); + entry.appendChild(modeSelect); + entry.appendChild(minfreespaceInput); + entry.appendChild(moveUpBtn); + entry.appendChild(moveDownBtn); + entry.appendChild(removeBtn); + container.appendChild(entry); + } + let draggedEntry = null; + function handleDragStart(e) { + draggedEntry = this; + this.classList.add('dragging'); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', this.id); + } + function handleDragEnd(e) { + this.classList.remove('dragging'); + document.querySelectorAll('.branch-entry').forEach(entry => { + entry.classList.remove('drag-over'); + }); + draggedEntry = null; + } + function handleDragOver(e) { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + if (this !== draggedEntry) { + this.classList.add('drag-over'); + } + } + function handleDragLeave(e) { + this.classList.remove('drag-over'); + } + function handleDrop(e) { + e.preventDefault(); + this.classList.remove('drag-over'); + if (this !== draggedEntry) { + const container = document.getElementById('branches-list'); + const allEntries = Array.from(container.querySelectorAll('.branch-entry')); + const draggedIndex = allEntries.indexOf(draggedEntry); + const targetIndex = allEntries.indexOf(this); + if (draggedIndex < targetIndex) { + container.insertBefore(draggedEntry, this.nextSibling); + } else { + container.insertBefore(draggedEntry, this); + } + } + } + function moveEntryUp(entry) { + const prev = entry.previousElementSibling; + if (prev) { + entry.parentNode.insertBefore(entry, prev); + } + } + function moveEntryDown(entry) { + const next = entry.nextElementSibling; + if (next) { + entry.parentNode.insertBefore(next, entry); + } + } + function openPathModal(targetInput) { + pendingPathInput = targetInput; + const modal = document.getElementById('pathModal'); + const mountList = document.getElementById('mount-list'); + mountList.innerHTML = ''; + fetch('/mounts') + .then(r => r.json()) + .then(mounts => { + mounts.forEach(m => { + const div = document.createElement('div'); + div.className = 'mount-list-item'; + div.innerHTML = '[' + m.type + '] ' + m.path; + div.onclick = () => { + if (pendingPathInput) { + pendingPathInput.value = m.path; + } + closePathModal(); + }; + mountList.appendChild(div); + }); + }) + .catch(err => { + console.error('Error loading mounts:', err); + mountList.innerHTML = '
Error loading mounts
'; + }); + modal.style.display = 'block'; + } + function closePathModal() { + document.getElementById('pathModal').style.display = 'none'; + pendingPathInput = null; + } + function submitBranches() { + const mount = document.getElementById('mount-select-branches').value; + const entries = document.querySelectorAll('.branch-entry'); + const branches = []; + entries.forEach(entry => { + const pathInput = entry.querySelector('.branch-path'); + const modeSelect = entry.querySelector('.branch-mode'); + const minfreespaceInput = entry.querySelector('.branch-minfreespace'); + if (pathInput && pathInput.value.trim()) { + let branchStr = pathInput.value.trim(); + if (modeSelect && modeSelect.value && modeSelect.value !== 'RW') { + branchStr += '=' + modeSelect.value; + } else { + branchStr += '=RW'; + } + if (minfreespaceInput && minfreespaceInput.value.trim()) { + branchStr += ',' + minfreespaceInput.value.trim(); + } + branches.push(branchStr); + } + }); + const branchesStr = branches.join(':'); + fetch(`/kvs/branches?mount=${encodeURIComponent(mount)}`, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(branchesStr) + }).then(response => { + if (!response.ok) { + response.text().then(body => { + alert('Status: ' + response.status + '\nBody: ' + body); + }); + } + }); + } + + function showToast(msg,success) { + const toast = document.getElementById('toast'); + toast.innerHTML = msg; + if(success) { + toast.classList.remove('show_failure'); + toast.classList.add('show_success'); + setTimeout(() => toast.classList.remove('show_success'), 3000); + } else { + toast.classList.remove('show_success'); + toast.classList.add('show_failure') + setTimeout(() => toast.classList.remove('show_failure'), 5000); + } + } + + window.onclick = function(event) { + const modal = document.getElementById('pathModal'); + if (event.target === modal) { + closePathModal(); + } + } + window.onload = () => { loadMounts(); }; + +