131 lines
8.5 KiB
Python
131 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
|
"""V124 - Bulk clear + Pinned section + Recently updated badge"""
|
|
path = "/var/www/html/all-ia-hub.html"
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
c = f.read()
|
|
|
|
if "V124-ENRICH" in c:
|
|
print("ALREADY")
|
|
exit(0)
|
|
|
|
# 1. Add "Clear pins" button in controls row (next to sort)
|
|
old_controls_end = ' <span id="dash-count" style="font-size:10px;color:var(--mu)"></span>\n </div>'
|
|
|
|
new_controls_end = ''' <span id="dash-count" style="font-size:10px;color:var(--mu)"></span>
|
|
<!-- V124-ENRICH: bulk clear pins button -->
|
|
<button id="dash-clear-pins" class="mode" onclick="__dashClearAllPins()" style="display:none;font-size:10px" title="Supprimer tous les pins">★ Clear pins</button>
|
|
</div>
|
|
<!-- V124-ENRICH: separate pinned section -->
|
|
<div id="dash-pinned-section" style="display:none;margin-bottom:20px">
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;padding-bottom:6px;border-bottom:1px solid rgba(251,191,36,0.25)">
|
|
<span style="color:#fbbf24;font-size:14px">★ Favorites</span>
|
|
<span id="dash-pinned-count" style="font-size:10px;color:var(--mu)"></span>
|
|
</div>
|
|
<div id="dash-pinned-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px"></div>
|
|
</div>'''
|
|
|
|
if old_controls_end in c:
|
|
c = c.replace(old_controls_end, new_controls_end, 1)
|
|
|
|
# 2. Rewrite renderDashGrid: split into pinned + rest grid
|
|
old_full_render = """function renderDashGrid(items, cat){
|
|
/* V117-HTTP-BADGES + V119-SEARCH + V123-PINS */
|
|
const box = document.getElementById('dash-grid');
|
|
if (!box) return;
|
|
const search = (document.getElementById('dash-search')?.value || '').toLowerCase().trim();
|
|
const sort = document.getElementById('dash-sort')?.value || 'name';
|
|
const pinned = __dashPins();
|
|
let filtered = cat === 'all' ? items : items.filter(x => x.category === cat);"""
|
|
|
|
new_full_render = """function renderDashGrid(items, cat){
|
|
/* V117-HTTP-BADGES + V119-SEARCH + V123-PINS + V124-ENRICH */
|
|
const box = document.getElementById('dash-grid');
|
|
const pinnedBox = document.getElementById('dash-pinned-grid');
|
|
const pinnedSection = document.getElementById('dash-pinned-section');
|
|
const pinnedCountEl = document.getElementById('dash-pinned-count');
|
|
const clearBtn = document.getElementById('dash-clear-pins');
|
|
if (!box) return;
|
|
const search = (document.getElementById('dash-search')?.value || '').toLowerCase().trim();
|
|
const sort = document.getElementById('dash-sort')?.value || 'name';
|
|
const pinned = __dashPins();
|
|
let filtered = cat === 'all' ? items : items.filter(x => x.category === cat);"""
|
|
|
|
if old_full_render in c:
|
|
c = c.replace(old_full_render, new_full_render, 1)
|
|
|
|
# 3. Replace the grid-writing section: split pinned/rest + add recently-updated badge
|
|
# Find end of sort block and start of tile render
|
|
old_sort_and_render_tail = """ // V123-PINS: pinned tiles first, preserving existing sort within each group
|
|
filtered = filtered.slice().sort((a,b) => {
|
|
const ap = pinned.has(a.name) ? 0 : 1;
|
|
const bp = pinned.has(b.name) ? 0 : 1;
|
|
return ap - bp;
|
|
});
|
|
const countEl = document.getElementById('dash-count');
|
|
if (countEl) countEl.textContent = filtered.length + ' / ' + items.length + ' tuiles' + (pinned.size ? ' (' + pinned.size + ' pin' + (pinned.size>1?'s':'') + ')' : '');"""
|
|
|
|
new_sort_and_render_tail = """ // V124-ENRICH: split into pinned + rest; no more merge-sort with pin priority
|
|
const pinnedItems = filtered.filter(x => pinned.has(x.name));
|
|
const restItems = filtered.filter(x => !pinned.has(x.name));
|
|
const countEl = document.getElementById('dash-count');
|
|
if (countEl) countEl.textContent = filtered.length + ' / ' + items.length + ' tuiles' + (pinned.size ? ' (' + pinned.size + ' pin' + (pinned.size>1?'s':'') + ')' : '');
|
|
// Show/hide pinned section + clear button
|
|
if (pinnedSection) pinnedSection.style.display = pinnedItems.length ? 'block' : 'none';
|
|
if (clearBtn) clearBtn.style.display = pinned.size ? 'inline-block' : 'none';
|
|
if (pinnedCountEl) pinnedCountEl.textContent = pinnedItems.length + (pinnedItems.length === 1 ? ' dashboard' : ' dashboards');"""
|
|
|
|
if old_sort_and_render_tail in c:
|
|
c = c.replace(old_sort_and_render_tail, new_sort_and_render_tail, 1)
|
|
|
|
# 4. Replace grid innerHTML block with a renderer helper + two grids
|
|
old_grid_write = """ box.innerHTML = filtered.map(e =>
|
|
'<a href=\"'+e.url+'\" target=\"_blank\" class=\"dash-tile' + (pinned.has(e.name) ? ' pinned' : '') + '\" style=\"text-decoration:none;color:inherit;display:block;padding:10px;background:linear-gradient(135deg,'+e.color+'22,'+e.color+'11);border:1px solid ' + (pinned.has(e.name) ? '#fbbf24' : e.color+'55') + ';border-radius:6px;transition:transform 0.15s ease,border-color 0.15s ease,box-shadow 0.15s ease\" onmouseover=\"this.style.transform=\\'translateY(-2px)\\';this.style.borderColor=\\''+e.color+'\\';this.style.boxShadow=\\'0 4px 12px '+e.color+'33\\'\" onmouseout=\"this.style.transform=\\'\\';this.style.borderColor=\\''+e.color+'55\\';this.style.boxShadow=\\'\\'\">' +
|
|
'<div style=\"font-size:18px;margin-bottom:4px\">'+e.icon+' <span style=\"font-size:9px;color:'+e.color+';text-transform:uppercase\">'+e.category+'</span>'+statusBadge(e.http_status)+'</div>' +
|
|
'<div style=\"font-size:11px;font-weight:bold;line-height:1.3;margin-bottom:4px\">'+e.display+'</div>' +
|
|
'<div style=\"font-size:9px;color:var(--mu);display:flex;justify-content:space-between;align-items:center\"><span>'+e.name+' - '+e.size_kb+'KB</span>' +
|
|
'<button onclick=\"event.preventDefault();event.stopPropagation();__dashTogglePin(\\''+e.name+'\\')\" style=\"background:transparent;border:0;color:'+(pinned.has(e.name)?\"#fbbf24\":\"#555\")+';cursor:pointer;font-size:14px;padding:0 4px\" title=\"'+(pinned.has(e.name)?'Unpin':'Pin')+'\">★</button>' +
|
|
'</div>' +
|
|
'</a>'
|
|
).join('');"""
|
|
|
|
new_grid_write = """ // V124-ENRICH: helper + recently-updated badge + split render
|
|
function recentBadge(mtime){
|
|
if(!mtime) return '';
|
|
const age = Date.now() - new Date(mtime).getTime();
|
|
if(age < 24*3600*1000) return '<span title=\"Updated <24h\" style=\"color:#10b981;font-size:9px;margin-left:4px\">✨ new</span>';
|
|
return '';
|
|
}
|
|
function tileHtml(e){
|
|
return '<a href=\"'+e.url+'\" target=\"_blank\" class=\"dash-tile' + (pinned.has(e.name) ? ' pinned' : '') + '\" style=\"text-decoration:none;color:inherit;display:block;padding:10px;background:linear-gradient(135deg,'+e.color+'22,'+e.color+'11);border:1px solid ' + (pinned.has(e.name) ? '#fbbf24' : e.color+'55') + ';border-radius:6px;transition:transform 0.15s ease,border-color 0.15s ease,box-shadow 0.15s ease\" onmouseover=\"this.style.transform=\\'translateY(-2px)\\';this.style.borderColor=\\''+e.color+'\\';this.style.boxShadow=\\'0 4px 12px '+e.color+'33\\'\" onmouseout=\"this.style.transform=\\'\\';this.style.borderColor=\\'' + (pinned.has(e.name) ? '#fbbf24' : e.color+'55') + '\\';this.style.boxShadow=\\'\\'\">' +
|
|
'<div style=\"font-size:18px;margin-bottom:4px\">'+e.icon+' <span style=\"font-size:9px;color:'+e.color+';text-transform:uppercase\">'+e.category+'</span>'+statusBadge(e.http_status)+recentBadge(e.mtime)+'</div>' +
|
|
'<div style=\"font-size:11px;font-weight:bold;line-height:1.3;margin-bottom:4px\">'+e.display+'</div>' +
|
|
'<div style=\"font-size:9px;color:var(--mu);display:flex;justify-content:space-between;align-items:center\"><span>'+e.name+' - '+e.size_kb+'KB</span>' +
|
|
'<button onclick=\"event.preventDefault();event.stopPropagation();__dashTogglePin(\\''+e.name+'\\')\" style=\"background:transparent;border:0;color:'+(pinned.has(e.name)?\"#fbbf24\":\"#555\")+';cursor:pointer;font-size:14px;padding:0 4px\" title=\"'+(pinned.has(e.name)?'Unpin':'Pin')+'\">★</button>' +
|
|
'</div>' +
|
|
'</a>';
|
|
}
|
|
if (pinnedBox) pinnedBox.innerHTML = pinnedItems.map(tileHtml).join('');
|
|
box.innerHTML = restItems.map(tileHtml).join('');"""
|
|
|
|
if old_grid_write in c:
|
|
c = c.replace(old_grid_write, new_grid_write, 1)
|
|
|
|
# 5. Add __dashClearAllPins function
|
|
js_clear_anchor = "function __dashTogglePin(name){"
|
|
js_clear_new = """function __dashClearAllPins(){
|
|
if (!confirm('Clear all pins?')) return;
|
|
__dashSetPins(new Set());
|
|
if (__dashData) {
|
|
const activeCat = document.querySelector('.dash-filter.on')?.getAttribute('data-cat') || 'all';
|
|
renderDashGrid(__dashData.dashboards, activeCat);
|
|
}
|
|
}
|
|
function __dashTogglePin(name){"""
|
|
|
|
if js_clear_anchor in c:
|
|
c = c.replace(js_clear_anchor, js_clear_new, 1)
|
|
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
f.write(c)
|
|
print(f"PATCHED size={len(c)}")
|