feat(ui): wire Export button — POST /sync/export + toast
Export button is no longer disabled. On click it POSTs to the export endpoint and shows a toast next to the button: ✓ Exported · open in mxdrw (with viewer URL) ✗ Export failed — <detail>
This commit is contained in:
@@ -22,9 +22,8 @@
|
||||
<div class="topbar-spacer"></div>
|
||||
<button type="button" id="btn-apply-template" class="btn">Apply template…</button>
|
||||
<button type="button" id="btn-solve" class="btn btn-primary">Solve</button>
|
||||
<button type="button" id="btn-export" class="btn" disabled title="Slice 8">
|
||||
Export
|
||||
</button>
|
||||
<button type="button" id="btn-export" class="btn">Export</button>
|
||||
<span id="toast" class="toast" hidden></span>
|
||||
</header>
|
||||
|
||||
<main class="layout">
|
||||
|
||||
@@ -128,6 +128,7 @@ const solveProject = (pid, preview) => api("POST", `/projects/${pid}/solve${pre
|
||||
const portsAndResolve = (pid, devId, body) => api("POST", `/projects/${pid}/devices/${devId}/ports-and-resolve`, body);
|
||||
const listSetupTemplates = () => api("GET", `/setup-templates`);
|
||||
const applyTemplate = (pid, body) => api("POST", `/projects/${pid}/apply-template`, body);
|
||||
const syncExport = (pid) => api("POST", `/projects/${pid}/sync/export`, {});
|
||||
|
||||
// ---------- DOM helpers ---------- //
|
||||
|
||||
@@ -2112,6 +2113,40 @@ function renderTemplatePreview(preview, templateIDStr) {
|
||||
`;
|
||||
}
|
||||
|
||||
// ---------- export flow ---------- //
|
||||
|
||||
let toastTimer = null;
|
||||
|
||||
function showToast(kind, html, holdMs = 5000) {
|
||||
const t = $("#toast");
|
||||
t.className = "toast " + (kind || "");
|
||||
t.innerHTML = html;
|
||||
setHidden(t, false);
|
||||
if (toastTimer) clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(() => { setHidden(t, true); t.innerHTML = ""; }, holdMs);
|
||||
}
|
||||
|
||||
async function exportCurrentProject() {
|
||||
if (!state.active) { alert("Pick a project first"); return; }
|
||||
const btn = $("#btn-export");
|
||||
btn.disabled = true;
|
||||
showToast("", "Exporting…", 30000);
|
||||
try {
|
||||
const res = await syncExport(state.active.id);
|
||||
const url = res.url ?? "(no url)";
|
||||
const count = res.element_count ?? 0;
|
||||
showToast("ok",
|
||||
`Exported ${count} elements → <a href="${escapeHtml(url)}" target="_blank" rel="noopener">${escapeHtml(url)}</a>`,
|
||||
8000);
|
||||
} catch (e) {
|
||||
// Surface mxdrw unreachability or the upstream error verbatim.
|
||||
const detail = typeof e.details === "object" ? JSON.stringify(e.details) : (e.details ?? "");
|
||||
showToast("error", `Export failed: ${escapeHtml(e.message)}${detail ? ` (${escapeHtml(String(detail))})` : ""}`, 12000);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- boot ---------- //
|
||||
|
||||
async function boot() {
|
||||
@@ -2133,6 +2168,7 @@ async function boot() {
|
||||
});
|
||||
$("#btn-solve").addEventListener("click", openSolveModal);
|
||||
$("#btn-apply-template").addEventListener("click", openApplyTemplateModal);
|
||||
$("#btn-export").addEventListener("click", exportCurrentProject);
|
||||
|
||||
$("#project-select").addEventListener("change", (e) => {
|
||||
const v = /** @type {HTMLSelectElement} */ (e.target).value;
|
||||
|
||||
@@ -236,6 +236,24 @@ body {
|
||||
filter: drop-shadow(0 0 4px var(--accent));
|
||||
}
|
||||
|
||||
/* Header toast — slice 8 export feedback */
|
||||
.toast {
|
||||
display: inline-block;
|
||||
margin-left: 12px;
|
||||
font-size: 13px;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius);
|
||||
background: var(--surface-2);
|
||||
color: var(--text);
|
||||
max-width: 420px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.toast.ok { background: #e8f5e9; color: #1b5e20; }
|
||||
.toast.error { background: #fdecea; color: #911313; }
|
||||
.toast a { color: inherit; text-decoration: underline; }
|
||||
|
||||
/* IO markers — diamonds. Power-by-convention, so the default fill is
|
||||
the Power cable_type colour (#e03131). Rotated 45° rect is the
|
||||
easiest way to draw a diamond that still hit-tests at the rotated
|
||||
|
||||
Reference in New Issue
Block a user