diff --git a/app/routes/api_ups.py b/app/routes/api_ups.py
index b7719a7..8f96acc 100644
--- a/app/routes/api_ups.py
+++ b/app/routes/api_ups.py
@@ -297,7 +297,10 @@ async def ups_debug(ups_name: str, user=Depends(require_session)):
async def get_ups_ui_tiles(ups_name: str, user=Depends(require_session)):
r = get_redis()
raw = r.get(f"ups:ui:tiles:{ups_name}")
- default = {"types": {}, "order": [], "hidden": [], "custom": [], "positions": {}}
+ default = {
+ "types": {}, "order": [], "hidden": [], "custom": [],
+ "positions": {}, "card_size": None,
+ }
if not raw:
return default
try:
@@ -308,6 +311,7 @@ async def get_ups_ui_tiles(ups_name: str, user=Depends(require_session)):
"hidden": data.get("hidden", []),
"custom": data.get("custom", []),
"positions": data.get("positions", {}),
+ "card_size": data.get("card_size"),
}
except Exception:
return default
@@ -322,6 +326,17 @@ async def save_ups_ui_tiles(
hidden = payload.get("hidden") if isinstance(payload.get("hidden"), list) else []
custom = payload.get("custom") if isinstance(payload.get("custom"), list) else []
positions = payload.get("positions") if isinstance(payload.get("positions"), dict) else {}
+ raw_card_size = payload.get("card_size")
+ card_size = None
+ if isinstance(raw_card_size, dict):
+ try:
+ w = int(raw_card_size.get("width") or 0)
+ h = int(raw_card_size.get("height") or 0)
+ # Clamp to sane bounds matching the frontend resize limits
+ if 200 <= w <= 2000 and 200 <= h <= 2000:
+ card_size = {"width": w, "height": h}
+ except (TypeError, ValueError):
+ card_size = None
norm_custom = []
for c in custom or []:
if not isinstance(c, dict):
@@ -342,6 +357,7 @@ async def save_ups_ui_tiles(
"hidden": hidden,
"custom": norm_custom,
"positions": positions,
+ "card_size": card_size,
"saved_ts": int(time.time()),
}
r = get_redis()
diff --git a/app/static/js/dashboard.js b/app/static/js/dashboard.js
index fa39201..b6254ce 100644
--- a/app/static/js/dashboard.js
+++ b/app/static/js/dashboard.js
@@ -163,8 +163,8 @@ async function loadServerTileConfig(name) {
saveToStorage(TILE_TYPE_KEY, savedTileTypes);
saveToStorage(CARD_SIZE_KEY, savedCardSizes);
// Clear server layout
- try {
- await fetch(`/api/ups/${name}/ui_tiles`, { method:'DELETE' });
+ try {
+ await window.apiFetch(`/api/ups/${name}/ui_tiles`, { method:'DELETE' });
debugLog(`Cleared server layout for ${name}`);
} catch(err) {
debugWarn(`Failed to clear server layout for ${name}:`, err);
@@ -324,10 +324,11 @@ async function saveServerTileConfig(name) {
id: c.id, metric: c.metric, chart: c.chart, source: c.source || 'live'
}));
const positions = savedTilePos[name] || {};
+ const card_size = savedCardSizes[name] || null;
try {
- await fetch(`/api/ups/${name}/ui_tiles`, {
- method: 'POST', headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ types, order, hidden: hiddenArr, custom, positions })
+ await window.apiFetch(`/api/ups/${name}/ui_tiles`, {
+ method: 'POST',
+ body: JSON.stringify({ types, order, hidden: hiddenArr, custom, positions, card_size })
});
} catch {}
}
@@ -369,6 +370,10 @@ function ensureUpsCard(name) {
card.appendChild(handle);
attachResizeBehavior(card, handle);
}
+ // Restore saved card size if not already applied inline
+ if (!card.style.width && savedCardSizes[name]) {
+ restoreCardSize(name);
+ }
return card;
}
const container = document.getElementById('ups-cards');
@@ -1261,7 +1266,7 @@ function persistTileOrder(name, grid) {
function persistTilePositions(name, grid) {
const positions = {};
let maxX = 0, maxY = 0;
-
+
grid.querySelectorAll('.tile').forEach(tile => {
const id = tile.dataset.tile;
if (id) {
@@ -1269,34 +1274,39 @@ function persistTilePositions(name, grid) {
const top = parseInt(tile.style.top || '0', 10);
const width = parseInt(tile.style.width || '0', 10) || tile.offsetWidth;
const height = parseInt(tile.style.height || '0', 10) || tile.offsetHeight;
-
+
positions[id] = { left, top, width, height };
-
- // Calculate required card size
+
+ // Track required card size (to grow card if tiles overflow)
maxX = Math.max(maxX, left + width);
maxY = Math.max(maxY, top + height);
}
});
-
+
savedTilePos[name] = positions;
try { localStorage.setItem(TILE_POS_KEY, JSON.stringify(savedTilePos)); } catch(_) {}
-
- // Auto-adjust card size based on tile positions
+
+ // Only grow the card if tiles overflow its current bounds. Never shrink
+ // the user's manually-sized card.
if (maxX > 0 && maxY > 0) {
- const cardWidth = Math.max(maxX + 20, 400); // 20px padding
- const cardHeight = Math.max(maxY + 70, 300); // 70px for header + padding
- persistCardSize(name, cardWidth, cardHeight);
-
- // Apply the size immediately
const card = document.getElementById(`card-${name}`);
if (card) {
- card.style.width = `${cardWidth}px`;
- card.style.height = `${cardHeight}px`;
- grid.style.width = `${cardWidth - 20}px`;
- grid.style.height = `${cardHeight - 70}px`;
+ const neededW = maxX + 20; // 20px right padding
+ const neededH = maxY + 70; // 70px for header + bottom padding
+ const currentW = card.offsetWidth;
+ const currentH = card.offsetHeight;
+ const newW = Math.max(currentW, neededW);
+ const newH = Math.max(currentH, neededH);
+ if (newW > currentW || newH > currentH) {
+ card.style.width = `${newW}px`;
+ card.style.height = `${newH}px`;
+ grid.style.width = `${newW - 20}px`;
+ grid.style.height = `${newH - 70}px`;
+ persistCardSize(name, newW, newH);
+ }
}
}
-
+
scheduleServerTileSave(name);
}
@@ -1476,6 +1486,10 @@ document.addEventListener('DOMContentLoaded', () => {
savedTileHidden[name] = hiddenSet;
savedCustomTiles[name] = cfg.custom || [];
savedTilePos[name] = cfg.positions || savedTilePos[name] || {};
+ if (cfg.card_size && cfg.card_size.width && cfg.card_size.height) {
+ savedCardSizes[name] = { width: cfg.card_size.width, height: cfg.card_size.height };
+ saveToStorage(CARD_SIZE_KEY, savedCardSizes);
+ }
try { localStorage.setItem(TILE_TYPE_KEY, JSON.stringify(savedTileTypes)); } catch{}
try { localStorage.setItem(TILE_ORDER_KEY, JSON.stringify(savedTileOrder)); } catch{}
try { localStorage.setItem(TILE_HIDDEN_KEY, JSON.stringify(savedTileHidden)); } catch{}
diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html
index 5c8b919..0135f80 100644
--- a/app/templates/dashboard.html
+++ b/app/templates/dashboard.html
@@ -50,5 +50,5 @@
{% endblock %}
{% block scripts %}
-
+
{% endblock %}