From 3fb3418e895b392c050be4f444632cf6b9099fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Fri, 30 Oct 2026 13:20:21 +0100 Subject: [PATCH] v2 --- dashboard/web/app.js | 4 ++-- dashboard/web/index.html | 10 +++++++++- dashboard/web/style.css | 6 +++--- tests/test_dashboard.py | 6 +++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/dashboard/web/app.js b/dashboard/web/app.js index ddd1a93..5e1e61c 100644 --- a/dashboard/web/app.js +++ b/dashboard/web/app.js @@ -16,8 +16,8 @@ function repoCard(title,url,links){return `

${esc(title)}

${esc(url)}
`} function renderRepositories(){const d=state.data;if(!d)return;const u=d.repository_url,dist=encodeURIComponent(d.apt_distribution);$('repo-cards').innerHTML=repoCard('Debian / Ubuntu',`${u}/apt`,[['GPG public key',`${u}/repo-key.gpg`],['APT InRelease',`${u}/apt/dists/${dist}/InRelease`],['Build info',`${u}/DEBIAN-BUILD-INFO.txt`]])+repoCard('RHEL / AlmaLinux / Rocky Linux',`${u}/rpm/x86_64`,[['GPG public key',`${u}/repo-key.gpg`],['RPM repomd.xml',`${u}/rpm/x86_64/repodata/repomd.xml`],['Metadata signature',`${u}/rpm/x86_64/repodata/repomd.xml.asc`],['Build info',`${u}/RHEL-BUILD-INFO.txt`]])+repoCard('SUSE / openSUSE',`${u}/suse/x86_64`,[['GPG public key',`${u}/repo-key.gpg`],['RPM repomd.xml',`${u}/suse/x86_64/repodata/repomd.xml`],['Metadata signature',`${u}/suse/x86_64/repodata/repomd.xml.asc`],['Build info',`${u}/SUSE-BUILD-INFO.txt`]]);$('metadata').innerHTML=['debian','rhel','suse'].map(ch=>{const b=d.builds[ch]||{};return `

${esc(channelNames[ch])}

${[['Version',b.app_version],['Package release',b.package_release],['Build time',fmtDate(b.built_at)],['Commit',b.source_commit?b.source_commit.slice(0,12):'-'],['Packaging revision',b.packaging_revision]].map(([a,v])=>`
${esc(a)}${esc(v||'-')}
`).join('')}
`}).join('')} function renderAll(){renderOverview();renderPackages();renderInstall();renderRepositories()} - async function refresh(){const btn=$('refresh');btn.disabled=true;try{const r=await fetch('/api/v1/packages',{cache:'no-store'});if(!r.ok)throw new Error(`HTTP ${r.status}`);state.data=await r.json();renderAll();$('last-sync').textContent=`Last refresh: ${fmtDate(state.data.generated_at)}`;$('status-text').textContent=state.data.ready?'Repository ready':'Waiting for first build'}catch(e){$('status-text').textContent='Repository unavailable';console.error(e)}finally{btn.disabled=false}} - function setTheme(theme){document.documentElement.dataset.theme=theme;localStorage.setItem('rustbox-repo-theme',theme)} + async function refresh(){const btn=$('refresh'),icon=btn.querySelector('.refresh-icon');btn.disabled=true;icon?.classList.add('spinning');try{const r=await fetch('/api/v1/packages',{cache:'no-store'});if(!r.ok)throw new Error(`HTTP ${r.status}`);state.data=await r.json();renderAll();$('last-sync').textContent=`Last refresh: ${fmtDate(state.data.generated_at)}`;$('status-text').textContent=state.data.ready?'Repository ready':'Waiting for first build'}catch(e){$('status-text').textContent='Repository unavailable';console.error(e)}finally{btn.disabled=false;icon?.classList.remove('spinning')}} + function setTheme(theme){document.documentElement.dataset.theme=theme;const next=theme==='dark'?'light':'dark';const btn=$('theme-toggle');btn.setAttribute('aria-label',`Switch to ${next} theme`);btn.title=`Switch to ${next} theme`;localStorage.setItem('rustbox-repo-theme',theme)} document.querySelectorAll('[data-view]').forEach(b=>b.addEventListener('click',()=>navigate(b.dataset.view))); document.querySelectorAll('[data-go]').forEach(b=>b.addEventListener('click',()=>navigate(b.dataset.go))); document.querySelectorAll('[data-install]').forEach(b=>b.addEventListener('click',()=>{state.install=b.dataset.install;renderInstall()})); diff --git a/dashboard/web/index.html b/dashboard/web/index.html index 53219d9..2953e13 100644 --- a/dashboard/web/index.html +++ b/dashboard/web/index.html @@ -22,7 +22,15 @@

Overview

-

-
+
+ + +
diff --git a/dashboard/web/style.css b/dashboard/web/style.css index b14b67f..536255d 100644 --- a/dashboard/web/style.css +++ b/dashboard/web/style.css @@ -1,3 +1,3 @@ -:root{color-scheme:dark;--bg:#121513;--panel:#181c19;--panel2:#1e231f;--panel3:#242a25;--sidebar:#101310;--line:#313832;--line2:#485149;--text:#edf0ec;--muted:#9da79f;--subtle:#7c877f;--accent:#8fb9a4;--accent2:#a4c9b6;--good:#a7c892;--field:#121713;--hover:#202621;--shadow:rgba(0,0,0,.32);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} -html[data-theme="light"]{color-scheme:light;--bg:#f2f4f1;--panel:#fff;--panel2:#f7f8f6;--panel3:#edf1ec;--sidebar:#e9eee9;--line:#cbd2cb;--line2:#aeb8af;--text:#202622;--muted:#647068;--subtle:#7a847d;--accent:#507b67;--accent2:#416b58;--good:#587a49;--field:#fff;--hover:#e8ede8;--shadow:rgba(31,39,33,.16)} -*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font-size:14px}.shell{min-height:100vh;display:grid;grid-template-columns:228px minmax(0,1fr)}.sidebar{position:sticky;top:0;height:100vh;background:var(--sidebar);border-right:1px solid var(--line);padding:18px 12px;display:flex;flex-direction:column}.brand{display:flex;align-items:center;padding:7px 8px 24px}.brand strong,.brand span{display:block}.brand span{color:var(--muted);font-size:11px;margin-top:2px}.sidebar nav{display:grid;gap:3px}.nav{border:0;background:transparent;color:var(--muted);text-align:left;padding:10px 12px;border-radius:5px;cursor:pointer;font-weight:600}.nav:hover{background:var(--hover);color:var(--text)}.nav.active{background:var(--panel3);color:var(--text);box-shadow:inset 2px 0 var(--accent)}.sidebar-foot{margin-top:auto;border-top:1px solid var(--line);padding:15px 8px 2px;color:var(--muted);display:flex;align-items:center;gap:8px;font-size:12px}main{min-width:0}.topbar{height:72px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 24px;background:color-mix(in srgb,var(--bg) 84%,transparent);backdrop-filter:blur(10px);position:sticky;top:0;z-index:5}.topbar h1{font-size:18px;margin:0}.topbar p{margin:3px 0 0;color:var(--subtle);font-size:11px}.toolbar{display:flex;gap:7px}.toolbar button,.panel button,.install-tabs button{border:1px solid var(--line);background:var(--panel);color:var(--text);min-height:32px;padding:0 11px;border-radius:5px;cursor:pointer}.toolbar button:hover,.panel button:hover,.install-tabs button:hover{border-color:var(--line2);background:var(--hover)}.view{display:none;padding:24px;max-width:1320px;margin:0 auto}.view.active{display:block}.hero{display:grid;grid-template-columns:minmax(0,1fr) 240px;gap:18px;background:linear-gradient(135deg,var(--panel),var(--panel2));border:1px solid var(--line);border-radius:7px;padding:28px}.hero h2{font-size:30px;margin:7px 0 8px}.hero p{color:var(--muted);max-width:700px;margin:0;line-height:1.55}.eyebrow{color:var(--accent2);font-size:11px;font-weight:800;letter-spacing:.14em}.version-box{background:var(--field);border:1px solid var(--line);border-radius:6px;padding:18px;display:flex;flex-direction:column;justify-content:center}.version-box span{color:var(--subtle);font-size:10px;letter-spacing:.1em}.version-box strong{font-size:27px;margin:5px 0}.version-box small{color:var(--muted)}.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:12px 0}.cards article{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:17px}.cards span,.cards small{display:block;color:var(--muted)}.cards strong{display:block;font-size:22px;margin:5px 0}.panel{background:var(--panel);border:1px solid var(--line);border-radius:6px;margin-top:12px;overflow:hidden}.panel-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--line)}.panel-head h3{margin:0;font-size:14px}.panel-head p{margin:4px 0 0;color:var(--muted)}.package-row,.table-row{display:grid;grid-template-columns:minmax(220px,1fr) 150px 90px 110px 150px;gap:12px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line)}.package-row:last-child,.table-row:last-child{border-bottom:0}.package-row:hover,.table-row:hover{background:var(--hover)}.pkg-name{font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.muted{color:var(--muted)}.pill{display:inline-flex;align-items:center;border:1px solid var(--line2);border-radius:99px;padding:3px 7px;font-size:10px;color:var(--muted);width:max-content}.download{color:var(--accent2);text-decoration:none;font-weight:650}.filters{display:grid;grid-template-columns:minmax(240px,1fr) 190px 170px;gap:8px;padding:12px;border-bottom:1px solid var(--line)}input,select{width:100%;height:34px;background:var(--field);color:var(--text);border:1px solid var(--line);border-radius:5px;padding:0 10px;outline:none}input:focus,select:focus{border-color:var(--accent)}.install-tabs{display:flex;gap:7px;flex-wrap:wrap}.install-tabs button.active{background:var(--accent);border-color:var(--accent);color:#102018;font-weight:700}.command-panel pre{margin:0;background:#0c0f0d;color:#e8efe9;padding:18px;overflow:auto;font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace}.note{margin-top:12px;border:1px solid var(--line);background:var(--panel2);padding:14px;border-radius:6px;color:var(--muted)}code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.repo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.repo-card{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:16px}.repo-card h3{margin:0 0 8px}.repo-card>a{color:var(--accent2);word-break:break-all}.repo-links{display:grid;margin-top:12px;border-top:1px solid var(--line)}.repo-links a{padding:9px 0;color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line)}.metadata{display:grid;grid-template-columns:repeat(3,1fr)}.meta-block{padding:16px}.meta-row{display:flex;justify-content:space-between;gap:18px;padding:8px 0;border-bottom:1px solid var(--line)}.meta-row span{color:var(--muted)}.toast{position:fixed;right:18px;bottom:18px;background:var(--panel3);border:1px solid var(--line2);box-shadow:0 12px 35px var(--shadow);padding:10px 14px;border-radius:6px;opacity:0;transform:translateY(8px);pointer-events:none;transition:.18s}.toast.show{opacity:1;transform:none}.empty{padding:28px 16px;color:var(--muted);text-align:center}@media(max-width:860px){.shell{grid-template-columns:1fr}.sidebar{position:static;height:auto;border-right:0;border-bottom:1px solid var(--line);padding:10px}.brand{padding:4px 5px 10px}.sidebar nav{grid-template-columns:repeat(4,1fr)}.nav{text-align:center;padding:9px 5px;font-size:12px}.sidebar-foot{display:none}.topbar{top:0;height:62px;padding:0 14px}.view{padding:14px}.hero{grid-template-columns:1fr;padding:20px}.cards{grid-template-columns:1fr}.repo-grid,.metadata{grid-template-columns:1fr}.filters{grid-template-columns:1fr}.package-row,.table-row{grid-template-columns:minmax(0,1fr) 90px 72px}.package-row>*:nth-child(4),.package-row>*:nth-child(5),.table-row>*:nth-child(4),.table-row>*:nth-child(5){display:none}.hero h2{font-size:24px}} +:root{color-scheme:dark;--bg:#101724;--panel:#172133;--panel2:#1c2940;--panel3:#23344e;--sidebar:#0d1522;--line:#2c3c55;--line2:#425673;--text:#f1f5fa;--muted:#aab6c8;--subtle:#8291a8;--accent:#62c9bd;--accent2:#7dd8ce;--accent-ink:#0b2a27;--good:#8bd5a0;--field:#111b2b;--hover:#202f46;--code-bg:#0b1220;--code-text:#e7eef8;--shadow:rgba(3,8,18,.34);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} +html[data-theme="light"]{color-scheme:light;--bg:#f4f8fc;--panel:#fff;--panel2:#f7fafc;--panel3:#eaf2f8;--sidebar:#edf4f9;--line:#d6e1eb;--line2:#b5c6d5;--text:#243247;--muted:#66778d;--subtle:#8290a3;--accent:#2f8078;--accent2:#236f69;--accent-ink:#fff;--good:#43875c;--field:#fff;--hover:#e8f1f7;--code-bg:#172235;--code-text:#f1f5fa;--shadow:rgba(42,64,84,.14)} +*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font-size:14px}.shell{min-height:100vh;display:grid;grid-template-columns:228px minmax(0,1fr)}.sidebar{position:sticky;top:0;height:100vh;background:var(--sidebar);border-right:1px solid var(--line);padding:18px 12px;display:flex;flex-direction:column}.brand{display:flex;align-items:center;padding:7px 8px 24px}.brand strong,.brand span{display:block}.brand span{color:var(--muted);font-size:11px;margin-top:2px}.sidebar nav{display:grid;gap:3px}.nav{border:0;background:transparent;color:var(--muted);text-align:left;padding:10px 12px;border-radius:5px;cursor:pointer;font-weight:600}.nav:hover{background:var(--hover);color:var(--text)}.nav.active{background:var(--panel3);color:var(--text);box-shadow:inset 2px 0 var(--accent)}.sidebar-foot{margin-top:auto;border-top:1px solid var(--line);padding:15px 8px 2px;color:var(--muted);display:flex;align-items:center;gap:8px;font-size:12px}main{min-width:0}.topbar{height:72px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 24px;background:color-mix(in srgb,var(--bg) 84%,transparent);backdrop-filter:blur(10px);position:sticky;top:0;z-index:5}.topbar h1{font-size:18px;margin:0}.topbar p{margin:3px 0 0;color:var(--subtle);font-size:11px}.toolbar{display:flex;gap:7px}.toolbar button,.panel button,.install-tabs button{border:1px solid var(--line);background:var(--panel);color:var(--text);min-height:32px;padding:0 11px;border-radius:5px;cursor:pointer}.toolbar button:hover,.panel button:hover,.install-tabs button:hover{border-color:var(--line2);background:var(--hover)}.toolbar .icon-button{width:34px;height:34px;min-height:34px;padding:0;display:inline-flex;align-items:center;justify-content:center}.icon-button svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.theme-icon-moon{display:none}html[data-theme="light"] .theme-icon-sun{display:none}html[data-theme="light"] .theme-icon-moon{display:block}.refresh-icon.spinning{animation:repo-spin .65s linear infinite}@keyframes repo-spin{to{transform:rotate(360deg)}}.view{display:none;padding:24px;max-width:1320px;margin:0 auto}.view.active{display:block}.hero{display:grid;grid-template-columns:minmax(0,1fr) 240px;gap:18px;background:linear-gradient(135deg,var(--panel),color-mix(in srgb,var(--panel2) 86%,var(--accent) 14%));border:1px solid var(--line);border-radius:7px;padding:28px}.hero h2{font-size:30px;margin:7px 0 8px}.hero p{color:var(--muted);max-width:700px;margin:0;line-height:1.55}.eyebrow{color:var(--accent2);font-size:11px;font-weight:800;letter-spacing:.14em}.version-box{background:var(--field);border:1px solid var(--line);border-radius:6px;padding:18px;display:flex;flex-direction:column;justify-content:center}.version-box span{color:var(--subtle);font-size:10px;letter-spacing:.1em}.version-box strong{font-size:27px;margin:5px 0}.version-box small{color:var(--muted)}.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:12px 0}.cards article{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:17px}.cards span,.cards small{display:block;color:var(--muted)}.cards strong{display:block;font-size:22px;margin:5px 0}.panel{background:var(--panel);border:1px solid var(--line);border-radius:6px;margin-top:12px;overflow:hidden}.panel-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--line)}.panel-head h3{margin:0;font-size:14px}.panel-head p{margin:4px 0 0;color:var(--muted)}.package-row,.table-row{display:grid;grid-template-columns:minmax(220px,1fr) 150px 90px 110px 150px;gap:12px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line)}.package-row:last-child,.table-row:last-child{border-bottom:0}.package-row:hover,.table-row:hover{background:var(--hover)}.pkg-name{font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.muted{color:var(--muted)}.pill{display:inline-flex;align-items:center;border:1px solid var(--line2);border-radius:99px;padding:3px 7px;font-size:10px;color:var(--muted);width:max-content}.download{color:var(--accent2);text-decoration:none;font-weight:650}.filters{display:grid;grid-template-columns:minmax(240px,1fr) 190px 170px;gap:8px;padding:12px;border-bottom:1px solid var(--line)}input,select{width:100%;height:34px;background:var(--field);color:var(--text);border:1px solid var(--line);border-radius:5px;padding:0 10px;outline:none}input:focus,select:focus{border-color:var(--accent)}.install-tabs{display:flex;gap:7px;flex-wrap:wrap}.install-tabs button.active{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:700}.command-panel pre{margin:0;background:var(--code-bg);color:var(--code-text);padding:18px;overflow:auto;font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace}.note{margin-top:12px;border:1px solid var(--line);background:var(--panel2);padding:14px;border-radius:6px;color:var(--muted)}code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.repo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.repo-card{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:16px}.repo-card h3{margin:0 0 8px}.repo-card>a{color:var(--accent2);word-break:break-all}.repo-links{display:grid;margin-top:12px;border-top:1px solid var(--line)}.repo-links a{padding:9px 0;color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line)}.metadata{display:grid;grid-template-columns:repeat(3,1fr)}.meta-block{padding:16px}.meta-row{display:flex;justify-content:space-between;gap:18px;padding:8px 0;border-bottom:1px solid var(--line)}.meta-row span{color:var(--muted)}.toast{position:fixed;right:18px;bottom:18px;background:var(--panel3);border:1px solid var(--line2);box-shadow:0 12px 35px var(--shadow);padding:10px 14px;border-radius:6px;opacity:0;transform:translateY(8px);pointer-events:none;transition:.18s}.toast.show{opacity:1;transform:none}.empty{padding:28px 16px;color:var(--muted);text-align:center}@media(max-width:860px){.shell{grid-template-columns:1fr}.sidebar{position:static;height:auto;border-right:0;border-bottom:1px solid var(--line);padding:10px}.brand{padding:4px 5px 10px}.sidebar nav{grid-template-columns:repeat(4,1fr)}.nav{text-align:center;padding:9px 5px;font-size:12px}.sidebar-foot{display:none}.topbar{top:0;height:62px;padding:0 14px}.view{padding:14px}.hero{grid-template-columns:1fr;padding:20px}.cards{grid-template-columns:1fr}.repo-grid,.metadata{grid-template-columns:1fr}.filters{grid-template-columns:1fr}.package-row,.table-row{grid-template-columns:minmax(0,1fr) 90px 72px}.package-row>*:nth-child(4),.package-row>*:nth-child(5),.table-row>*:nth-child(4),.table-row>*:nth-child(5){display:none}.hero h2{font-size:24px}} diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index 9a070e6..22f3d7c 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -38,12 +38,16 @@ def test_catalog_uses_three_linux_distribution_families(): assert len(catalog["packages"]) == 3 -def test_dashboard_is_english_text_only(): +def test_dashboard_is_english_with_utility_svg_icons_only(): html = (ROOT / "dashboard/web/index.html").read_text() js = (ROOT / "dashboard/web/app.js").read_text() assert 'lang="en"' in html assert "brand-icon" not in html assert "favicon" not in html assert "status-dot" not in html + assert "theme-icon-sun" in html + assert "theme-icon-moon" in html + assert "refresh-icon" in html + assert html.count("