Add queue export/import functionality to web UI
This commit is contained in:
@@ -22,6 +22,22 @@
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ order }),
|
||||
}),
|
||||
exportQueue: () =>
|
||||
fetch("/api/pqueue/export", { method: "GET" }).then((r) => r.json()),
|
||||
importQueue: (fileOrJson) => {
|
||||
try {
|
||||
if (fileOrJson instanceof File || fileOrJson instanceof Blob) {
|
||||
const form = new FormData();
|
||||
form.append("file", fileOrJson, fileOrJson.name || "queue.json");
|
||||
return fetch("/api/pqueue/import", { method: "POST", body: form }).then((r) => r.json());
|
||||
}
|
||||
} catch (err) { /* fall through to JSON */ }
|
||||
return fetch("/api/pqueue/import", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(fileOrJson),
|
||||
}).then((r) => r.json());
|
||||
},
|
||||
setPriority: (prompt_id, priority) =>
|
||||
fetch("/api/pqueue/priority", {
|
||||
method: "PATCH",
|
||||
|
||||
@@ -52,6 +52,69 @@
|
||||
} catch (err) { /* noop */ }
|
||||
},
|
||||
|
||||
async exportQueueToFile() {
|
||||
refreshRefs();
|
||||
try {
|
||||
const data = await API.exportQueue();
|
||||
const text = JSON.stringify(data, null, 2);
|
||||
const blob = new Blob([text], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
const dt = new Date();
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
const fname = `pqueue-${dt.getFullYear()}${pad(dt.getMonth()+1)}${pad(dt.getDate())}-${pad(dt.getHours())}${pad(dt.getMinutes())}${pad(dt.getSeconds())}.json`;
|
||||
a.download = fname;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
setStatusMessage("Queue exported");
|
||||
} catch (err) {
|
||||
console.error("pqueue: export failed", err);
|
||||
state.error = err?.message || "Failed to export queue";
|
||||
UI.updateToolbarStatus();
|
||||
}
|
||||
},
|
||||
|
||||
openImportDialog() {
|
||||
refreshRefs();
|
||||
try {
|
||||
let input = document.getElementById('pqueue-import-input');
|
||||
if (!input) {
|
||||
input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'application/json,.json';
|
||||
input.id = 'pqueue-import-input';
|
||||
input.style.display = 'none';
|
||||
document.body.appendChild(input);
|
||||
input.addEventListener('change', Events.handleImportSelected, { once: false });
|
||||
}
|
||||
input.value = '';
|
||||
input.click();
|
||||
} catch (err) { /* noop */ }
|
||||
},
|
||||
|
||||
async handleImportSelected(ev) {
|
||||
refreshRefs();
|
||||
try {
|
||||
const input = ev.currentTarget || ev.target;
|
||||
const file = input?.files?.[0];
|
||||
if (!file) return;
|
||||
const result = await API.importQueue(file);
|
||||
const cnt = Number(result?.count || (Array.isArray(result?.imported) ? result.imported.length : 0));
|
||||
setStatusMessage(`Imported ${cnt} item${cnt === 1 ? '' : 's'}`);
|
||||
await refresh({ force: true });
|
||||
// Clear selection after import
|
||||
state.selectedPending.clear();
|
||||
UI.updateSelectionUI();
|
||||
} catch (err) {
|
||||
console.error("pqueue: import failed", err);
|
||||
state.error = err?.message || "Failed to import queue";
|
||||
UI.updateToolbarStatus();
|
||||
}
|
||||
},
|
||||
|
||||
async syncLatestForAsc() {
|
||||
refreshRefs();
|
||||
try {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
const API = (window.PQueue && window.PQueue.API) || window.API;
|
||||
const UI = (window.PQueue && window.PQueue.UI) || (window.PQueue = (window.PQueue || {}), window.PQueue.UI = {}, window.PQueue.UI);
|
||||
const Format = (window.PQueue && window.PQueue.Format) || window.Format;
|
||||
const Events = (window.PQueue && window.PQueue.Events) || window.Events;
|
||||
const setStatusMessage = (window.PQueue && window.PQueue.setStatusMessage) || window.setStatusMessage;
|
||||
|
||||
// Metrics card removed; metrics now live within the toolbar summary.
|
||||
@@ -79,6 +80,22 @@
|
||||
state.dom.pendingCount = footerCount;
|
||||
state.dom.pendingUpdated = footerUpdated;
|
||||
|
||||
const exportBtn = UI.button({
|
||||
id: "pqueue-export",
|
||||
icon: "ti ti-download",
|
||||
variant: "ghost",
|
||||
subtle: true,
|
||||
title: "Export queue",
|
||||
onClick: () => { try { (Events && Events.exportQueueToFile) ? Events.exportQueueToFile() : null; } catch (err) {} },
|
||||
});
|
||||
const importBtn = UI.button({
|
||||
id: "pqueue-import",
|
||||
icon: "ti ti-upload",
|
||||
variant: "ghost",
|
||||
subtle: true,
|
||||
title: "Import queue",
|
||||
onClick: () => { try { (Events && Events.openImportDialog) ? Events.openImportDialog() : null; } catch (err) {} },
|
||||
});
|
||||
const deleteSelected = UI.button({
|
||||
id: "pqueue-delete-selected",
|
||||
icon: "ti ti-trash",
|
||||
@@ -91,7 +108,7 @@
|
||||
|
||||
const footer = UI.el("div", { class: "pqueue-table__footer" }, [
|
||||
UI.el("div", { class: "pqueue-table__footer-left" }, [footerCount, footerUpdated]),
|
||||
UI.el("div", { class: "pqueue-table__footer-right" }, [deleteSelected]),
|
||||
UI.el("div", { class: "pqueue-table__footer-right" }, [exportBtn, importBtn, deleteSelected]),
|
||||
]);
|
||||
|
||||
// Build collapsible card (mirrors metrics behavior)
|
||||
|
||||
Reference in New Issue
Block a user