let selectedNode = { type: "group", path: "/" }; // type: "group" | "entry" let currentKind = "simple"; const treeDiv = document.getElementById("tree"); const currentPathEl = document.getElementById("current-path"); const editorSimple = document.getElementById("editor-simple"); const editorComplex = document.getElementById("editor-complex"); const kindSelect = document.getElementById("entry-kind"); function showEditor(kind) { editorSimple.style.display = (kind === "simple") ? "block" : "none"; editorComplex.style.display = (kind === "complex") ? "block" : "none"; } kindSelect.addEventListener("change", () => { currentKind = kindSelect.value; showEditor(currentKind); }); async function api(path, opts = {}) { const resp = await fetch(path, opts); if (!resp.ok) { const txt = await resp.text(); throw new Error("API error: " + txt); } if (resp.headers.get("Content-Type")?.includes("application/json")) { return resp.json(); } return null; } function renderTree(node) { const ul = document.createElement("ul"); const mkEntryLi = (e) => { const li = document.createElement("li"); li.textContent = "🔑 " + e.title; li.className = "node"; li.onclick = () => { selectedNode = { type: "entry", path: e.path, kind: e.kind }; currentPathEl.textContent = e.path; kindSelect.value = e.kind; showEditor(e.kind); if (e.kind === "simple") { document.getElementById("s-key").value = e.title; document.getElementById("s-value").value = ""; } else { document.getElementById("c-title").value = e.title; document.getElementById("c-username").value = ""; document.getElementById("c-password").value = ""; document.getElementById("c-url").value = ""; document.getElementById("c-notes").value = ""; } }; return li; }; const mkGroupLi = (g) => { const li = document.createElement("li"); const header = document.createElement("div"); header.textContent = "📂 " + (g.name || "/"); header.className = "node"; header.onclick = () => { selectedNode = { type: "group", path: g.path }; currentPathEl.textContent = g.path; }; li.appendChild(header); const inner = renderTreeChildren(g); li.appendChild(inner); return li; }; function renderTreeChildren(node) { const wrap = document.createElement("ul"); node.groups.forEach(sg => wrap.appendChild(mkGroupLi(sg))); node.entries.forEach(e => wrap.appendChild(mkEntryLi(e))); return wrap; } ul.appendChild(mkGroupLi(node)); treeDiv.innerHTML = ""; treeDiv.appendChild(ul); } async function refreshTree() { const data = await api("/api/v1/tree"); renderTree(data); } // Toolbar lewej kolumny document.getElementById("btn-refresh").onclick = refreshTree; document.getElementById("btn-add-group").onclick = async () => { const name = prompt("Nazwa nowej grupy:"); if (!name) return; await api("/api/v1/group", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ name, parent_path: selectedNode.type === "group" ? selectedNode.path : "/" }) }); await refreshTree(); }; document.getElementById("btn-add-entry").onclick = async () => { const parent_path = selectedNode.type === "group" ? selectedNode.path : "/"; const kind = kindSelect.value; if (kind === "simple") { const key = prompt("Klucz (title):"); if (!key) return; const value = prompt("Wartość (password):"); if (value === null) return; await api("/api/v1/entry", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ kind, parent_path, data: { key, value } }) }); } else { const title = prompt("Tytuł:"); if (!title) return; await api("/api/v1/entry", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ kind, parent_path, data: { title, username:"", password:"", url:"", notes:"" } }) }); } await refreshTree(); }; // Edytory document.getElementById("s-save").onclick = async () => { if (selectedNode.type !== "entry") { alert("Wybierz wpis"); return; } const data = { key: document.getElementById("s-key").value, value: document.getElementById("s-value").value }; await api("/api/v1/entry", { method: "PATCH", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ path: selectedNode.path, kind: "simple", data }) }); await refreshTree(); }; document.getElementById("s-delete").onclick = async () => { if (selectedNode.type !== "entry") return; if (!confirm("Usunąć wpis?")) return; await api("/api/v1/entry?path=" + encodeURIComponent(selectedNode.path), { method: "DELETE" }); await refreshTree(); }; document.getElementById("c-save").onclick = async () => { if (selectedNode.type !== "entry") { alert("Wybierz wpis"); return; } const data = { title: document.getElementById("c-title").value, username: document.getElementById("c-username").value, password: document.getElementById("c-password").value, url: document.getElementById("c-url").value, notes: document.getElementById("c-notes").value }; await api("/api/v1/entry", { method: "PATCH", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ path: selectedNode.path, kind: "complex", data }) }); await refreshTree(); }; document.getElementById("c-delete").onclick = async () => { if (selectedNode.type !== "entry") return; if (!confirm("Usunąć wpis?")) return; await api("/api/v1/entry?path=" + encodeURIComponent(selectedNode.path), { method: "DELETE" }); await refreshTree(); }; document.getElementById("btn-move-entry").onclick = async () => { if (selectedNode.type !== "entry") { alert("Wybierz wpis"); return; } const target = document.getElementById("target-group").value || "/"; await api("/api/v1/entry/move", { method: "PATCH", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ path: selectedNode.path, target_group_path: target }) }); await refreshTree(); }; document.getElementById("btn-save-all").onclick = async () => { await api("/api/v1/save", { method: "POST" }); alert("Zapisano do bazy"); }; // Start showEditor(currentKind); refreshTree().catch(err => console.error(err));