// views/remotes.js — connector-tile grid of configured remotes with CRUD. import { post } from "../rc.js"; import { getState, setState, toast } from "../state.js"; export async function renderRemotes() { const app = document.getElementById("app"); if (!app) return; app.innerHTML = `

Remotes

Configured cloud storage providers. Click a tile to browse, hover for edit/delete.

New remote

Loading remotes…

`; const grid = document.getElementById("remote-grid"); try { const [listRes, dumpRes] = await Promise.all([ post("config/listremotes"), post("config/dump"), ]); const names = (listRes && listRes.remotes) || []; const dump = dumpRes || {}; const remotes = names.map((name) => ({ name, type: (dump[name] && dump[name].type) || "unknown", })); setState({ remotes }); if (remotes.length === 0) { grid.innerHTML = `

No remotes configured

Click New remote above to add one from this GUI.

`; return; } grid.innerHTML = remotes .map( (r) => ` ${escapeHtml(r.name)} ${escapeHtml(r.type)}
`, ) .join(""); // Wire action buttons grid.querySelectorAll("[data-edit]").forEach((btn) => { btn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); location.hash = `#/configure/edit/${encodeURIComponent(btn.dataset.edit)}`; }); }); grid.querySelectorAll("[data-delete]").forEach((btn) => { btn.addEventListener("click", async (e) => { e.preventDefault(); e.stopPropagation(); const name = btn.dataset.delete; if (!confirm(`Delete remote ${name}? This cannot be undone.`)) return; try { await post("config/delete", { name }); toast(`Deleted ${name}`, "success"); await renderRemotes(); } catch (err) { toast(`Delete failed: ${err.message}`, "error"); } }); }); } catch (e) { grid.innerHTML = `

Couldn’t reach rclone

${escapeHtml(e.message)}

`; toast(`Failed to load remotes: ${e.message}`, "error"); } } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }