Soften updater stub messaging (no error)
This commit is contained in:
@@ -6,9 +6,14 @@ const headers = { "Content-Type": "application/json" };
|
||||
export async function api(path, opts = {}) {
|
||||
// When running `npm run dev` without the backend, allow mock JSON from /data/
|
||||
const isMock = import.meta?.env?.MODE === 'development' && path.startsWith('/api');
|
||||
const target = isMock
|
||||
? path.replace('/api/status', '/data/mock-status.json').replace('/api/updates/config', '/data/mock-updates.json')
|
||||
: path;
|
||||
|
||||
const mockMap = {
|
||||
'/api/status': '/data/mock-status.json',
|
||||
'/api/updates/config': '/data/mock-updates.json',
|
||||
'/api/update/status': '/data/mock-update-status.json',
|
||||
};
|
||||
|
||||
const target = isMock && mockMap[path] ? mockMap[path] : path;
|
||||
|
||||
const res = await fetch(target, { headers, ...opts });
|
||||
|
||||
@@ -38,6 +43,26 @@ export const saveUpdateConfig = (config) =>
|
||||
body: JSON.stringify(config),
|
||||
});
|
||||
|
||||
// Pi-Kit release updater endpoints
|
||||
export const getReleaseStatus = () => api("/api/update/status");
|
||||
export const checkRelease = () =>
|
||||
api("/api/update/check", {
|
||||
method: "POST",
|
||||
});
|
||||
export const applyRelease = () =>
|
||||
api("/api/update/apply", {
|
||||
method: "POST",
|
||||
});
|
||||
export const rollbackRelease = () =>
|
||||
api("/api/update/rollback", {
|
||||
method: "POST",
|
||||
});
|
||||
export const setReleaseAutoCheck = (enable) =>
|
||||
api("/api/update/auto", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ enable }),
|
||||
});
|
||||
|
||||
export const triggerReset = (confirm) =>
|
||||
api("/api/reset", {
|
||||
method: "POST",
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
// Entry point for the dashboard: wires UI events, pulls status, and initializes
|
||||
// feature modules (services, settings, stats).
|
||||
import { getStatus, triggerReset } from "./api.js";
|
||||
import {
|
||||
getStatus,
|
||||
triggerReset,
|
||||
getReleaseStatus,
|
||||
checkRelease,
|
||||
applyRelease,
|
||||
rollbackRelease,
|
||||
setReleaseAutoCheck,
|
||||
} from "./api.js";
|
||||
import { placeholderStatus, renderStats } from "./status.js";
|
||||
import { initServiceControls, renderServices } from "./services.js";
|
||||
import { initSettings } from "./settings.js";
|
||||
@@ -22,6 +30,7 @@ const updatesStatus = document.getElementById("updatesStatus");
|
||||
const updatesFlagTop = document.getElementById("updatesFlagTop");
|
||||
const updatesNoteTop = document.getElementById("updatesNoteTop");
|
||||
const tempFlagTop = document.getElementById("tempFlagTop");
|
||||
const releaseFlagTop = document.getElementById("releaseFlagTop");
|
||||
const refreshIntervalInput = document.getElementById("refreshIntervalInput");
|
||||
const refreshIntervalSave = document.getElementById("refreshIntervalSave");
|
||||
const refreshIntervalMsg = document.getElementById("refreshIntervalMsg");
|
||||
@@ -74,6 +83,18 @@ const menuClose = document.getElementById("menuClose");
|
||||
const advBtn = document.getElementById("advBtn");
|
||||
const advModal = document.getElementById("advModal");
|
||||
const advClose = document.getElementById("advClose");
|
||||
const releaseBtn = document.getElementById("releaseBtn");
|
||||
const releaseModal = document.getElementById("releaseModal");
|
||||
const releaseClose = document.getElementById("releaseClose");
|
||||
const releaseCurrent = document.getElementById("releaseCurrent");
|
||||
const releaseLatest = document.getElementById("releaseLatest");
|
||||
const releaseStatusMsg = document.getElementById("releaseStatusMsg");
|
||||
const releaseProgress = document.getElementById("releaseProgress");
|
||||
const releaseCheckBtn = document.getElementById("releaseCheckBtn");
|
||||
const releaseApplyBtn = document.getElementById("releaseApplyBtn");
|
||||
const releaseRollbackBtn = document.getElementById("releaseRollbackBtn");
|
||||
const releaseAutoCheck = document.getElementById("releaseAutoCheck");
|
||||
|
||||
const helpBtn = document.getElementById("helpBtn");
|
||||
const helpModal = document.getElementById("helpModal");
|
||||
const helpClose = document.getElementById("helpClose");
|
||||
@@ -228,6 +249,7 @@ function applyTooltips() {
|
||||
updatesFlagTop: "Auto updates status; configure in Settings → Automatic updates.",
|
||||
refreshFlagTop: "Auto-refresh interval; change in Settings → Refresh interval.",
|
||||
tempFlagTop: "CPU temperature status; see details in the hero stats below.",
|
||||
releaseFlagTop: "Pi-Kit release status; open Settings → Updates to install.",
|
||||
themeToggle: "Toggle light or dark theme",
|
||||
helpBtn: "Open quick help",
|
||||
advBtn: "Open settings",
|
||||
@@ -327,6 +349,8 @@ async function loadStatus() {
|
||||
setTimeout(loadStatus, 3000);
|
||||
}
|
||||
}
|
||||
// Pull Pi-Kit release status after core status
|
||||
loadReleaseStatus();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
renderStats(heroStats, placeholderStatus);
|
||||
@@ -361,6 +385,67 @@ function updatesFlagEl(enabled) {
|
||||
else if (enabled === false) updatesFlagTop.classList.add("chip-off");
|
||||
}
|
||||
|
||||
async function loadReleaseStatus() {
|
||||
if (!releaseFlagTop) return;
|
||||
setReleaseChip({ status: "checking" });
|
||||
try {
|
||||
const data = await getReleaseStatus();
|
||||
const {
|
||||
current_version = "n/a",
|
||||
latest_version = "n/a",
|
||||
status = "unknown",
|
||||
message = "",
|
||||
auto_check = false,
|
||||
progress = null,
|
||||
} = data || {};
|
||||
setReleaseChip(data);
|
||||
if (releaseCurrent) releaseCurrent.textContent = current_version;
|
||||
if (releaseLatest) releaseLatest.textContent = latest_version;
|
||||
if (releaseStatusMsg) {
|
||||
releaseStatusMsg.textContent =
|
||||
status === "update_available"
|
||||
? message || "Update available"
|
||||
: status === "up_to_date"
|
||||
? "Up to date"
|
||||
: message || status;
|
||||
releaseStatusMsg.classList.toggle("error", status === "error");
|
||||
}
|
||||
if (releaseAutoCheck) releaseAutoCheck.checked = !!auto_check;
|
||||
if (releaseProgress) {
|
||||
releaseProgress.textContent = progress ? progress : "";
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to load release status", e);
|
||||
setReleaseChip({ status: "error", message: "Failed to load" });
|
||||
if (releaseStatusMsg) {
|
||||
releaseStatusMsg.textContent = "Failed to load release status";
|
||||
releaseStatusMsg.classList.add("error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setReleaseChip(state) {
|
||||
if (!releaseFlagTop) return;
|
||||
releaseFlagTop.textContent = "Pi-Kit: n/a";
|
||||
releaseFlagTop.className = "status-chip quiet";
|
||||
if (!state) return;
|
||||
const { status, latest_version, current_version, message } = state;
|
||||
const label =
|
||||
status === "update_available"
|
||||
? `Update → ${latest_version || "new"}`
|
||||
: status === "up_to_date"
|
||||
? `Pi-Kit: ${current_version || "latest"}`
|
||||
: status === "checking"
|
||||
? "Checking…"
|
||||
: status === "error"
|
||||
? "Update error"
|
||||
: `Pi-Kit: ${current_version || "n/a"}`;
|
||||
releaseFlagTop.textContent = label;
|
||||
if (status === "update_available") releaseFlagTop.classList.add("chip-warm");
|
||||
if (status === "error") releaseFlagTop.classList.add("chip-off");
|
||||
releaseFlagTop.title = message || "Pi-Kit release status";
|
||||
}
|
||||
|
||||
function wireModals() {
|
||||
advBtn.onclick = () => advModal.classList.remove("hidden");
|
||||
advClose.onclick = () => advModal.classList.add("hidden");
|
||||
@@ -374,6 +459,65 @@ function wireModals() {
|
||||
addServiceModal?.addEventListener("click", (e) => {
|
||||
if (e.target === addServiceModal) addServiceModal.classList.add("hidden");
|
||||
});
|
||||
releaseBtn?.addEventListener("click", () => {
|
||||
releaseModal?.classList.remove("hidden");
|
||||
loadReleaseStatus();
|
||||
});
|
||||
releaseClose?.addEventListener("click", () => releaseModal?.classList.add("hidden"));
|
||||
releaseModal?.addEventListener("click", (e) => {
|
||||
if (e.target === releaseModal) releaseModal.classList.add("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
function wireReleaseControls() {
|
||||
releaseCheckBtn?.addEventListener("click", async () => {
|
||||
try {
|
||||
if (releaseProgress) releaseProgress.textContent = "Checking for updates…";
|
||||
await checkRelease();
|
||||
await loadReleaseStatus();
|
||||
showToast("Checked for updates", "success");
|
||||
} catch (e) {
|
||||
showToast(e.error || "Check failed", "error");
|
||||
} finally {
|
||||
if (releaseProgress) releaseProgress.textContent = "";
|
||||
}
|
||||
});
|
||||
|
||||
releaseApplyBtn?.addEventListener("click", async () => {
|
||||
try {
|
||||
if (releaseProgress) releaseProgress.textContent = "Downloading and installing…";
|
||||
await applyRelease();
|
||||
await loadReleaseStatus();
|
||||
showToast("Update started", "success");
|
||||
} catch (e) {
|
||||
showToast(e.error || "Update failed", "error");
|
||||
} finally {
|
||||
if (releaseProgress) releaseProgress.textContent = "";
|
||||
}
|
||||
});
|
||||
|
||||
releaseRollbackBtn?.addEventListener("click", async () => {
|
||||
try {
|
||||
if (releaseProgress) releaseProgress.textContent = "Rolling back…";
|
||||
await rollbackRelease();
|
||||
await loadReleaseStatus();
|
||||
showToast("Rollback complete", "success");
|
||||
} catch (e) {
|
||||
showToast(e.error || "Rollback failed", "error");
|
||||
} finally {
|
||||
if (releaseProgress) releaseProgress.textContent = "";
|
||||
}
|
||||
});
|
||||
|
||||
releaseAutoCheck?.addEventListener("change", async () => {
|
||||
try {
|
||||
await setReleaseAutoCheck(releaseAutoCheck.checked);
|
||||
showToast("Auto-check preference saved", "success");
|
||||
} catch (e) {
|
||||
showToast(e.error || "Failed to save preference", "error");
|
||||
releaseAutoCheck.checked = !releaseAutoCheck.checked;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showBusy(title = "Working…", text = "This may take a few seconds.") {
|
||||
@@ -449,6 +593,7 @@ if (typeof window !== "undefined") {
|
||||
function main() {
|
||||
applyTooltips();
|
||||
wireModals();
|
||||
wireReleaseControls();
|
||||
wireResetAndUpdates();
|
||||
wireAccordions();
|
||||
loadToastSettings();
|
||||
|
||||
10
pikit-web/data/mock-update-status.json
Normal file
10
pikit-web/data/mock-update-status.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"current_version": "0.1.0-dev",
|
||||
"latest_version": "0.1.1-mock",
|
||||
"last_check": "2025-12-10T22:00:00Z",
|
||||
"status": "update_available",
|
||||
"message": "New UI polish and bug fixes.",
|
||||
"auto_check": true,
|
||||
"in_progress": false,
|
||||
"progress": null
|
||||
}
|
||||
3
pikit-web/data/version.json
Normal file
3
pikit-web/data/version.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "0.1.0-dev"
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
<span id="updatesNoteTop" class="hint quiet"></span>
|
||||
<span id="refreshFlagTop" class="status-chip quiet">Refresh: 10s</span>
|
||||
<span id="tempFlagTop" class="status-chip quiet">Temp: OK</span>
|
||||
<span id="releaseFlagTop" class="status-chip quiet">Pi-Kit: n/a</span>
|
||||
</div>
|
||||
<div class="top-actions">
|
||||
<button
|
||||
@@ -32,6 +33,9 @@
|
||||
>
|
||||
<span id="themeToggleIcon" aria-hidden="true">🌙</span>
|
||||
</button>
|
||||
<button id="releaseBtn" class="ghost" title="Pi-Kit updates">
|
||||
Update
|
||||
</button>
|
||||
<button id="aboutBtn" class="ghost" title="About Pi-Kit">About</button>
|
||||
<button id="helpBtn" class="ghost" title="Open help">Help</button>
|
||||
<button id="advBtn" class="ghost" title="Open settings">
|
||||
@@ -83,6 +87,52 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="releaseModal" class="modal hidden">
|
||||
<div class="modal-card wide">
|
||||
<div class="panel-header sticky">
|
||||
<div>
|
||||
<p class="eyebrow">Updates</p>
|
||||
<h3>Pi-Kit releases</h3>
|
||||
<p class="hint">
|
||||
Check for a new Pi-Kit release, download it, and install from here.
|
||||
</p>
|
||||
</div>
|
||||
<button id="releaseClose" class="ghost icon-btn close-btn" title="Close updates panel">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="controls column">
|
||||
<div class="control-card">
|
||||
<div>
|
||||
<p class="hint quiet">Current version</p>
|
||||
<h3 id="releaseCurrent">n/a</h3>
|
||||
</div>
|
||||
<div>
|
||||
<p class="hint quiet">Latest available</p>
|
||||
<h3 id="releaseLatest">—</h3>
|
||||
<p id="releaseStatusMsg" class="hint status-msg"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-actions split-row">
|
||||
<button id="releaseCheckBtn" class="ghost" title="Check for a new release">
|
||||
Check
|
||||
</button>
|
||||
<button id="releaseApplyBtn" title="Download and install the latest release">
|
||||
Download & install
|
||||
</button>
|
||||
<button id="releaseRollbackBtn" class="ghost" title="Rollback to previous backup">
|
||||
Rollback
|
||||
</button>
|
||||
<label class="checkbox-row inline">
|
||||
<input type="checkbox" id="releaseAutoCheck" />
|
||||
<span>Auto-check daily</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="releaseProgress" class="hint status-msg"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="busyOverlay" class="overlay hidden">
|
||||
<div class="overlay-box">
|
||||
<h3 id="busyTitle">Working…</h3>
|
||||
|
||||
Reference in New Issue
Block a user