Files
rclone_gui/webgui/web/assets/js/views/remotes.js
T
2026-06-20 13:54:22 +08:00

97 lines
3.1 KiB
JavaScript

// views/remotes.js — connector-tile grid of configured remotes with CRUD.
import { post } from "../rc.js?v=job-layout-1";
import { escapeHtml, getState, setState, toast } from "../state.js?v=job-layout-1";
import { t } from "../i18n.js?v=job-layout-1";
export async function renderRemotes() {
const app = document.getElementById("app");
if (!app) return;
app.innerHTML = `
<div class="section-head">
<div>
<h2>${t("remotes.title")}</h2>
<p class="subtitle">${t("remotes.subtitle")}</p>
</div>
<a class="btn btn-primary btn-sm" href="#/configure/new">${t("remotes.new_remote")}</a>
</div>
<div id="remote-grid" class="connector-grid">
<p class="empty" style="grid-column:1/-1">${t("remotes.loading")}</p>
</div>
`;
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 = `
<div class="empty" style="grid-column:1/-1">
<h3>${t("remotes.empty_title")}</h3>
<p>${t("remotes.empty_body")}</p>
</div>
`;
return;
}
grid.innerHTML = remotes
.map(
(r) => `
<a class="connector-tile tile-with-actions" href="#/browse/${encodeURIComponent(r.name)}">
<span class="tile-name">${escapeHtml(r.name)}</span>
<span class="badge">${escapeHtml(r.type)}</span>
<div class="tile-actions">
<button type="button" title="${t("browser.rename")}" data-edit="${escapeHtml(r.name)}">✎</button>
<button type="button" title="${t("browser.delete")}" class="danger" data-delete="${escapeHtml(r.name)}">🗑</button>
</div>
</a>
`,
)
.join("");
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(t("remotes.delete_confirm", name))) return;
try {
await post("config/delete", { name });
toast(t("remotes.deleted", name), "success");
await renderRemotes();
} catch (err) {
toast(`${t("remotes.delete_failed")}: ${err.message}`, "error");
}
});
});
} catch (e) {
grid.innerHTML = `
<div class="empty" style="grid-column:1/-1">
<h3>${t("error.couldnt_reach")}</h3>
<p>${escapeHtml(e.message)}</p>
</div>
`;
toast(`${t("remotes.load_failed")}: ${e.message}`, "error");
}
}