This commit is contained in:
1 parent
94f7ed8411
commit
acb6b25657
5 files changed
+71
-2
No files matched your search
@@ -24,6 +24,7 @@
|
||||
}
|
||||
.card:hover { border-color: var(--border-hi); box-shadow: var(--shadow-md); }
|
||||
.card.resizing { outline: 2px dashed var(--accent); }
|
||||
#ups-cards.resize-disabled .card { resize: none; }
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
|
||||
@@ -136,6 +136,7 @@ try {
|
||||
} catch(_) {}
|
||||
// Set defaults
|
||||
uiCfg.allow_resize = uiCfg.allow_resize !== false;
|
||||
if (uiCfgEl) uiCfgEl.classList.toggle('resize-disabled', uiCfg.allow_resize === false);
|
||||
// Human-friendly metric labels for dropdown & headers
|
||||
const METRIC_LABELS = {
|
||||
'LOADPCT': 'UPS Load %',
|
||||
@@ -155,8 +156,12 @@ const lastUpdateTs = {};
|
||||
const UPS_STALE_SECONDS = 120; // mark offline after 2 minutes inactivity
|
||||
// Freeform tile layout enhancements
|
||||
const SNAP_GRID = 20; // pixel grid for snapping
|
||||
const SNAP_ORIGIN = 10; // default tile layout starts at 10px
|
||||
let selectedTiles = new Set();
|
||||
function snap(v) { return Math.round(v / SNAP_GRID) * SNAP_GRID; }
|
||||
function snap(v) {
|
||||
if (v <= SNAP_ORIGIN) return SNAP_ORIGIN;
|
||||
return SNAP_ORIGIN + Math.round((v - SNAP_ORIGIN) / SNAP_GRID) * SNAP_GRID;
|
||||
}
|
||||
function clearSelection() { selectedTiles.forEach(t => t.classList.remove('selected')); selectedTiles.clear(); }
|
||||
function toggleTileSelection(tile, additive) {
|
||||
if (!additive) clearSelection();
|
||||
@@ -164,6 +169,20 @@ function toggleTileSelection(tile, additive) {
|
||||
else { tile.classList.add('selected'); selectedTiles.add(tile); }
|
||||
}
|
||||
|
||||
function applyCardFeatureVisibility(card) {
|
||||
const runtimeBadge = card.querySelector('[data-badge="runtime"]');
|
||||
const wattsBadge = card.querySelector('[data-badge="watts"]');
|
||||
const headroomBadge = card.querySelector('[data-badge="headroom"]');
|
||||
if (runtimeBadge) runtimeBadge.hidden = uiCfg.show_runtime === false;
|
||||
if (wattsBadge) wattsBadge.hidden = uiCfg.show_watts === false;
|
||||
if (headroomBadge) headroomBadge.hidden = uiCfg.show_headroom === false;
|
||||
}
|
||||
|
||||
function clearEventFooter(name) {
|
||||
const footer = getCard(name)?.querySelector('[data-ups-footer]');
|
||||
if (footer) footer.textContent = '';
|
||||
}
|
||||
|
||||
// --- Redis-backed tile layout persistence ---
|
||||
async function loadServerTileConfig(name) {
|
||||
try {
|
||||
@@ -418,6 +437,7 @@ function ensureUpsCard(name) {
|
||||
if (!card.style.width && savedCardSizes[name]) {
|
||||
restoreCardSize(name);
|
||||
}
|
||||
applyCardFeatureVisibility(card);
|
||||
return card;
|
||||
}
|
||||
const container = document.getElementById('ups-cards');
|
||||
@@ -459,6 +479,7 @@ function ensureUpsCard(name) {
|
||||
attachResizeBehavior(div, handle);
|
||||
}
|
||||
container.appendChild(div);
|
||||
applyCardFeatureVisibility(div);
|
||||
|
||||
// Observe card for resize events to update footer position
|
||||
if (typeof ro !== 'undefined') {
|
||||
@@ -1029,7 +1050,11 @@ evtSource.onmessage = (e) => {
|
||||
const conn = card.querySelector('.ups-connection');
|
||||
if (conn) conn.textContent = `${meta.host}:${meta.port}`;
|
||||
// Update event footer asynchronously (lightweight)
|
||||
if (uiCfg.show_events === false) {
|
||||
clearEventFooter(meta.name);
|
||||
} else {
|
||||
updateEventFooter(meta.name);
|
||||
}
|
||||
});
|
||||
|
||||
Object.entries(snapshots).forEach(([name, snap]) => {
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
show_events: f.show_events.checked,
|
||||
show_energy: f.show_energy.checked,
|
||||
color_badges: f.color_badges.checked,
|
||||
show_runtime: f.show_runtime.checked,
|
||||
show_watts: f.show_watts.checked,
|
||||
show_headroom: f.show_headroom.checked,
|
||||
allow_resize: f.allow_resize.checked,
|
||||
enable_transfer_burst_alert: f.enable_transfer_burst_alert.checked,
|
||||
enable_voltage_deviation_alert: f.enable_voltage_deviation_alert.checked,
|
||||
energy_cost_per_kwh: Number.parseFloat(normalizeEnergyRate(f.energy_cost_per_kwh.value)),
|
||||
|
||||
@@ -30,6 +30,34 @@
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" name="color_badges"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-title">Show runtime badge</span>
|
||||
<span class="toggle-help">Display estimated runtime on each UPS card</span>
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" name="show_runtime"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-title">Show watts badge</span>
|
||||
<span class="toggle-help">Display current load in watts on each UPS card</span>
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" name="show_watts"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-title">Show headroom badge</span>
|
||||
<span class="toggle-help">Display remaining load capacity on each UPS card</span>
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" name="show_headroom"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-title">Allow resizing</span>
|
||||
<span class="toggle-help">Enable card and tile resize handles on the dashboard</span>
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" name="allow_resize"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-title">Transfer-burst alerts</span>
|
||||
|
||||
@@ -127,12 +127,23 @@ def test_ui_config_default(authed_client):
|
||||
def test_ui_config_update(authed_client):
|
||||
r = authed_client.put(
|
||||
"/api/config/ui",
|
||||
json={"show_energy": True, "energy_cost_per_kwh": 0.15},
|
||||
json={
|
||||
"show_energy": True,
|
||||
"energy_cost_per_kwh": 0.15,
|
||||
"show_runtime": False,
|
||||
"show_watts": False,
|
||||
"show_headroom": False,
|
||||
"allow_resize": False,
|
||||
},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["ui"]["show_energy"] is True
|
||||
assert body["ui"]["energy_cost_per_kwh"] == 0.15
|
||||
assert body["ui"]["show_runtime"] is False
|
||||
assert body["ui"]["show_watts"] is False
|
||||
assert body["ui"]["show_headroom"] is False
|
||||
assert body["ui"]["allow_resize"] is False
|
||||
|
||||
|
||||
def test_ui_config_energy_cost_keeps_four_decimal_rate(authed_client):
|
||||
|
||||
Reference in new issue
Block a user