Update positioning, remove IPs from cards, remove icons, update NOC content

This commit is contained in:
2026-05-22 00:42:21 -05:00
parent 13141443c6
commit 295effeba1
3 changed files with 52 additions and 85 deletions
+25 -28
View File
@@ -1,6 +1,6 @@
# Ced's NOC Live Network Operations Center # Ced's NOC Live Network Operations Center
> A custom-built, public-facing Network Operations Center dashboard running at [noc.chasedumphord.com](https://noc.chasedumphord.com) providing real-time visibility into Ced's HomeLab infrastructure, services, and RF edge systems. > A custom-built, public facing Network Operations Center dashboard running at [noc.chasedumphord.com](https://noc.chasedumphord.com) providing real time visibility into Ced's HomeLab infrastructure, services, and RF edge systems.
[![Live NOC](https://img.shields.io/badge/Live-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com) [![Live NOC](https://img.shields.io/badge/Live-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com)
[![Portfolio](https://img.shields.io/badge/Portfolio-chasedumphord.com-0F6E56?style=flat-square)](https://chasedumphord.com) [![Portfolio](https://img.shields.io/badge/Portfolio-chasedumphord.com-0F6E56?style=flat-square)](https://chasedumphord.com)
@@ -11,11 +11,9 @@
## What This Is ## What This Is
This is the public-facing front door to Ced's HomeLab infrastructure. This is the frontend layer of Ced's observability platform a custom-built status page that makes live infrastructure data publicly This is the public-facing front door to Ced's HomeLab infrastructure. This is the frontend layer of Ced's observability platform a custom built status page that makes live infrastructure data publicly accessible without requiring login. The backend engine ([ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack)) handles metrics collection via Prometheus and Grafana. This repo is what the public sees real-time service status, latency, and health across every layer of the homelab. No login required.
accessible without requiring login. The backend engine ([ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack)) handles metrics collection via Prometheus and Grafana. This repo is
what the public sees — real-time service status, latency, and health across every layer of the homelab. no login required.
It's not a template or a third-party uptime service. It's a purpose-built frontend that pulls live status data and presents it as a professional NOC dashboard. Every card on the page represents a real, running service with real latency and uptime data. It's not a template or a third party uptime service. It's a purpose built frontend that pulls live status data and presents it as a professional NOC dashboard. Every card on the page represents a real, running service with real latency and uptime data.
--- ---
@@ -23,7 +21,7 @@ It's not a template or a third-party uptime service. It's a purpose-built fronte
[![NOC Systems](./screenshots/noc-systems.png)](https://noc.chasedumphord.com) [![NOC Systems](./screenshots/noc-systems.png)](https://noc.chasedumphord.com)
The NOC displays real-time status for every layer of the infrastructure stack from the Proxmox hypervisor down to the APRS RF edge system. The NOC displays real time status for every layer of the infrastructure stack from the Proxmox hypervisor down to the APRS RF edge system.
--- ---
@@ -49,8 +47,8 @@ The NOC displays real-time status for every layer of the infrastructure stack
| Service | Role | Status Source | | Service | Role | Status Source |
|---------|------|--------------| |---------|------|--------------|
| APRS Home iGate | RF-to-internet gateway (KJ5JCO-10) | TCP probe via Blackbox | | APRS Home iGate | RF to internet gateway (KJ5JCO-10) | TCP probe via Blackbox |
| APRS-IS | External APRS internet service | TCP probe noam.aprs2.net:14580 | | APRS-IS | External APRS internet service | TCP probe noam.aprs2.net:14580 |
--- ---
@@ -89,7 +87,7 @@ graph LR
2. Prometheus collects and stores probe results 2. Prometheus collects and stores probe results
3. Python scripts generate status JSON from Prometheus data 3. Python scripts generate status JSON from Prometheus data
4. NOC frontend reads JSON and renders live status cards 4. NOC frontend reads JSON and renders live status cards
5. Public visitors see real-time infrastructure status no login needed 5. Public visitors see real time infrastructure status no login needed
--- ---
@@ -119,21 +117,20 @@ ceds-noc/
Each service card on the NOC displays: Each service card on the NOC displays:
- **Service name and role** what it is and what it does - **Service name and role** what it is and what it does
- **Category badge** HYPERVISOR, STORAGE, CLUSTER, PORTAL, etc. - **Category badge** HYPERVISOR, STORAGE, CLUSTER, PORTAL, etc.
- **Health badge** HEALTHY or DEGRADED based on probe results - **Health badge** HEALTHY or DEGRADED based on probe results
- **Internal IP and port** — where the service lives on the HomeLab VLAN - **Status** Online / Offline
- **Status** — Online / Offline - **Latency** real response time in milliseconds
- **Latency** — real response time in milliseconds - **Last check timestamp** when the probe last ran
- **Last check timestamp** — when the probe last ran
This gives anyone viewing the page the same operational picture that the internal Grafana dashboard provides without requiring access to the internal network. This gives anyone viewing the page the same operational picture that the internal Grafana dashboard provides without requiring access to the internal network.
--- ---
## Screenshots ## Screenshots
### Systems Overview Live Service Status ### Systems Overview Live Service Status
![NOC Systems](./screenshots/noc-systems.png) ![NOC Systems](./screenshots/noc-systems.png)
### Metrics Overview ### Metrics Overview
@@ -145,10 +142,10 @@ This gives anyone viewing the page the same operational picture that the interna
| Project | Role | | Project | Role |
|---------|------| |---------|------|
| [ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack) | Backend Prometheus, Grafana, Alertmanager, all exporters | | [ceds-observability-stack](https://github.com/ced4568/ceds-observability-stack) | Backend Prometheus, Grafana, Alertmanager, all exporters |
| [ceds-homelab](https://github.com/ced4568/ceds-homelab) | Infrastructure Proxmox cluster, TrueNAS, networking | | [ceds-homelab](https://github.com/ced4568/ceds-homelab) | Infrastructure Proxmox cluster, TrueNAS, networking |
| [ced-k3s-homelab](https://github.com/ced4568/ced-k3s-homelab) | Orchestration 12-node Raspberry Pi K3s cluster | | [ced-k3s-homelab](https://github.com/ced4568/ced-k3s-homelab) | Orchestration 12 node Raspberry Pi K3s cluster |
| [ceds-aprs-igate](https://github.com/ced4568/ceds-aprs-igate) | Edge Dual-node APRS RF-to-internet gateway | | [ceds-aprs-igate](https://github.com/ced4568/ceds-aprs-igate) | Edge Dual node APRS RF-to-internet gateway |
--- ---
@@ -156,11 +153,11 @@ This gives anyone viewing the page the same operational picture that the interna
- [x] Live service status cards with real probe data - [x] Live service status cards with real probe data
- [x] APRS RF edge system monitoring - [x] APRS RF edge system monitoring
- [x] Public access no login required - [x] Public access no login required
- [x] Custom badge and status JSON generation - [x] Custom badge and status JSON generation
- [ ] Alerting integration visual alerts on NOC page - [ ] Alerting integration visual alerts on NOC page
- [ ] Historical uptime percentage per service - [ ] Historical uptime percentage per service
- [ ] Mobile-optimized layout - [ ] Mobile optimized layout
- [ ] Auto-refresh with configurable interval - [ ] Auto-refresh with configurable interval
- [ ] Public Grafana dashboard embed - [ ] Public Grafana dashboard embed
@@ -169,9 +166,9 @@ This gives anyone viewing the page the same operational picture that the interna
## Author ## Author
**Chase Dumphord (Ced)** **Chase Dumphord (Ced)**
Digital Systems Engineer · GE Aerospace · Oxford, MS DevOps and Cloud Infrastructure Engineer · GE Aerospace · Oxford, MS
[![Portfolio](https://img.shields.io/badge/Portfolio-chasedumphord.com-0F6E56?style=flat-square)](https://chasedumphord.com) [![Portfolio](https://img.shields.io/badge/Portfolio-chasedumphord.com-0F6E56?style=flat-square)](https://chasedumphord.com)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-chase--dumphord-0A66C2?style=flat-square)](https://www.linkedin.com/in/chase-dumphord/) [![LinkedIn](https://img.shields.io/badge/LinkedIn-chase--dumphord-0A66C2?style=flat-square)](https://www.linkedin.com/in/chase-dumphord/)
[![GitHub](https://img.shields.io/badge/GitHub-ced4568-181717?style=flat-square)](https://github.com/ced4568) [![GitHub](https://img.shields.io/badge/GitHub-ced4568-181717?style=flat-square)](https://github.com/ced4568)
[![Live NOC](https://img.shields.io/badge/Live%20NOC-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com) [![Live NOC](https://img.shields.io/badge/Live%20NOC-noc.chasedumphord.com-1D9E75?style=flat-square)](https://noc.chasedumphord.com)
+16 -23
View File
@@ -17,12 +17,12 @@
<meta <meta
name="description" name="description"
content="Ced's NOC — live network operations center dashboard showing real-time infrastructure health, service status, and edge system monitoring." content="Ced's NOC — live network operations center dashboard showing real-time infrastructure health across 18 nodes and 28 services. Proxmox, K3s, TrueNAS, APRS edge systems."
/> />
<!-- Open Graph --> <!-- Open Graph -->
<meta property="og:title" content="Ced's NOC | Live Systems Dashboard" /> <meta property="og:title" content="Ced's NOC | Live Systems Dashboard" />
<meta property="og:description" content="Live infrastructure visibility — Proxmox, K3s, TrueNAS, APRS edge systems." /> <meta property="og:description" content="Live infrastructure visibility — 18 nodes, 28 services. Proxmox, K3s, TrueNAS, APRS edge systems. Always running." />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://noc.chasedumphord.com" /> <meta property="og:url" content="https://noc.chasedumphord.com" />
@@ -36,7 +36,7 @@
<meta name="robots" content="index, follow" /> <meta name="robots" content="index, follow" />
<meta name="author" content="Chase Dumphord" /> <meta name="author" content="Chase Dumphord" />
<!-- Fonts same as main site --> <!-- Fonts same as main site -->
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Syne:wght@700;800&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Syne:wght@700;800&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet" />
@@ -46,7 +46,6 @@
<!-- NOC-specific styles --> <!-- NOC-specific styles -->
<style> <style>
/* ── NOC page overrides & extras ── */
.noc-page-hero { .noc-page-hero {
padding: 3.5rem 0 2rem; padding: 3.5rem 0 2rem;
border-bottom: 1px solid var(--line); border-bottom: 1px solid var(--line);
@@ -90,12 +89,10 @@
.noc-page-meta a { color: var(--muted); } .noc-page-meta a { color: var(--muted); }
.noc-page-meta a:hover { color: var(--accent-2); } .noc-page-meta a:hover { color: var(--accent-2); }
/* Full-width NOC content area */
.noc-full { .noc-full {
padding: 2.5rem 0 4rem; padding: 2.5rem 0 4rem;
} }
/* Uptime summary bar */
.uptime-bar { .uptime-bar {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -131,7 +128,6 @@
background: rgba(255, 95, 86, 0.06); background: rgba(255, 95, 86, 0.06);
} }
/* Layer filter tabs */
.layer-tabs { .layer-tabs {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -159,14 +155,12 @@
background: rgba(94, 250, 204, 0.07); background: rgba(94, 250, 204, 0.07);
} }
/* Roadmap section on NOC page */
.noc-roadmap { .noc-roadmap {
margin-top: 3rem; margin-top: 3rem;
padding-top: 2.5rem; padding-top: 2.5rem;
border-top: 1px solid var(--line); border-top: 1px solid var(--line);
} }
/* Stack info footer on NOC page */
.noc-stack-bar { .noc-stack-bar {
margin-top: 2.5rem; margin-top: 2.5rem;
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
@@ -195,7 +189,6 @@
text-decoration: underline; text-decoration: underline;
} }
/* Grafana CTA button row */
.noc-cta-row { .noc-cta-row {
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;
@@ -213,7 +206,7 @@
<body> <body>
<!-- ═══════════════════════════════ NAV ═══════════════════════════════ --> <!-- NAV -->
<header class="site-header"> <header class="site-header">
<div class="container nav"> <div class="container nav">
<a href="https://chasedumphord.com" class="logo">Ced<span class="logo-cursor">_</span></a> <a href="https://chasedumphord.com" class="logo">Ced<span class="logo-cursor">_</span></a>
@@ -233,7 +226,7 @@
<main id="top"> <main id="top">
<!-- ═══════════════════════════════ NOC HERO ═══════════════════════════════ --> <!-- NOC HERO -->
<div class="noc-page-hero"> <div class="noc-page-hero">
<div class="container"> <div class="container">
<p class="eyebrow"> <p class="eyebrow">
@@ -246,9 +239,9 @@
</h1> </h1>
<p class="noc-page-lead"> <p class="noc-page-lead">
Real-time visibility into Ced's HomeLab infrastructure — Proxmox, K3s, Real-time visibility into Ced's HomeLab infrastructure across 18 nodes and 28 services
TrueNAS, APRS RF edge systems, and self-hosted services. Proxmox, K3s, TrueNAS, APRS RF edge systems, and self-hosted services.
Not simulated. Not a demo. Running hardware. Built from the ground up and running in production every day. The infrastructure on this page is live. Not a demo.
</p> </p>
<!-- Grafana CTA buttons --> <!-- Grafana CTA buttons -->
@@ -256,7 +249,7 @@
<a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5" <a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5"
target="_blank" rel="noopener noreferrer" target="_blank" rel="noopener noreferrer"
class="btn btn-primary"> class="btn btn-primary">
📊 View Live Grafana Dashboard View Live Grafana Dashboard
</a> </a>
<a href="https://github.com/ced4568/ceds-observability-stack" <a href="https://github.com/ced4568/ceds-observability-stack"
target="_blank" rel="noopener noreferrer" target="_blank" rel="noopener noreferrer"
@@ -273,7 +266,7 @@
</div> </div>
</div> </div>
<!-- ═══════════════════════════════ NOC DASHBOARD ═══════════════════════════════ --> <!-- NOC DASHBOARD -->
<div class="noc-full"> <div class="noc-full">
<div class="container"> <div class="container">
@@ -333,7 +326,7 @@
<!-- Fallback --> <!-- Fallback -->
<div id="noc-fallback" class="noc-fallback" style="display:none;"> <div id="noc-fallback" class="noc-fallback" style="display:none;">
<p class="noc-fallback-msg"> <p class="noc-fallback-msg">
📡 NOC feed unavailable from this context systems run locally.<br/> NOC feed unavailable from this context systems run locally.<br/>
Data updates when the local pipeline pushes a fresh noc-status.json. Data updates when the local pipeline pushes a fresh noc-status.json.
</p> </p>
</div> </div>
@@ -341,9 +334,9 @@
<!-- Stack info bar --> <!-- Stack info bar -->
<div class="noc-stack-bar"> <div class="noc-stack-bar">
<span><strong>Stack:</strong> Python health checks → JSON → JS dashboard</span> <span><strong>Stack:</strong> Python health checks → JSON → JS dashboard</span>
<span><strong>Live:</strong> Grafana + Prometheus <span><strong>Live:</strong> Grafana + Prometheus
<a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5" <a href="https://grafana.cedshomelab.com/dashboard/snapshot/PmEdg0ALfAVsmKN1Qgv7K11XP8h52ne5"
target="_blank" rel="noopener noreferrer">View Dashboard</a> target="_blank" rel="noopener noreferrer">View Dashboard</a>
</span> </span>
<span><strong>Source:</strong> <span><strong>Source:</strong>
<a href="https://github.com/ced4568/ceds-homelab" target="_blank" rel="noopener noreferrer"> <a href="https://github.com/ced4568/ceds-homelab" target="_blank" rel="noopener noreferrer">
@@ -379,7 +372,7 @@
<h3>Grafana + Prometheus</h3> <h3>Grafana + Prometheus</h3>
<p> <p>
Full observability stack for Proxmox, TrueNAS, K3s, Raspberry Pi, Full observability stack for Proxmox, TrueNAS, K3s, Raspberry Pi,
Uptime Kuma, and APRS edge systems with real-time metrics ingestion. Uptime Kuma, and APRS edge systems with real time metrics ingestion.
Public dashboard available. Public dashboard available.
</p> </p>
</div> </div>
@@ -391,7 +384,7 @@
</main> </main>
<!-- ═══════════════════════════════ FOOTER ═══════════════════════════════ --> <!-- FOOTER -->
<footer class="site-footer"> <footer class="site-footer">
<div class="container footer-content"> <div class="container footer-content">
<p>© <span data-year></span> Chase Dumphord — <span class="footer-mono">noc.chasedumphord.com</span></p> <p>© <span data-year></span> Chase Dumphord — <span class="footer-mono">noc.chasedumphord.com</span></p>
@@ -402,7 +395,7 @@
<!-- Shared script from main portfolio --> <!-- Shared script from main portfolio -->
<script src="https://chasedumphord.com/script.js"></script> <script src="https://chasedumphord.com/script.js"></script>
<!-- NOC page extras layer filter tabs + uptime pills --> <!-- NOC page extras layer filter tabs + uptime pills -->
<script> <script>
let allSystems = []; let allSystems = [];
+11 -34
View File
@@ -1,5 +1,5 @@
// ═══════════════════════════════════════════════ // ═══════════════════════════════════════════════
// Ced's Portfolio — script.js // Ced's NOC script.js
// NOC data loads from data/noc-status.json // NOC data loads from data/noc-status.json
// Refreshes every 30s. Graceful fallback on fail. // Refreshes every 30s. Graceful fallback on fail.
// ═══════════════════════════════════════════════ // ═══════════════════════════════════════════════
@@ -34,8 +34,6 @@ async function loadNocStatus() {
const fallback = document.getElementById("noc-fallback"); const fallback = document.getElementById("noc-fallback");
try { try {
// Cache-bust to always get fresh data
// Use absolute URL when on NOC subdomain, relative when on main portfolio
const nocDataUrl = window.location.hostname === 'noc.chasedumphord.com' const nocDataUrl = window.location.hostname === 'noc.chasedumphord.com'
? `https://chasedumphord.com/data/noc-status.json?t=${Date.now()}` ? `https://chasedumphord.com/data/noc-status.json?t=${Date.now()}`
: `data/noc-status.json?t=${Date.now()}`; : `data/noc-status.json?t=${Date.now()}`;
@@ -46,32 +44,25 @@ async function loadNocStatus() {
const data = await res.json(); const data = await res.json();
// Reset fail counter on success
failCount = 0; failCount = 0;
// ── Status bar ── if (dot) dot.className = "status-dot live";
if (dot) {
dot.className = "status-dot live";
}
if (statusText) { if (statusText) {
const d = new Date(data.lastUpdated); const d = new Date(data.lastUpdated);
const formatted = d.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }); const formatted = d.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
statusText.textContent = `${data.nocName}${data.status} — Last updated: ${formatted}`; statusText.textContent = `${data.nocName}${data.status} — Last updated: ${formatted}`;
} }
if (badge) {
badge.textContent = `Auto-refresh: 30s`;
}
// Hide fallback if it was showing if (badge) badge.textContent = `Auto-refresh: 30s`;
if (fallback) fallback.style.display = "none"; if (fallback) fallback.style.display = "none";
// ── Metrics ──
if (nodes) animateMetric(nodes, data.summary.nodes); if (nodes) animateMetric(nodes, data.summary.nodes);
if (services) animateMetric(services, data.summary.services); if (services) animateMetric(services, data.summary.services);
if (vlans) animateMetric(vlans, data.summary.vlans); if (vlans) animateMetric(vlans, data.summary.vlans);
if (edge) animateMetric(edge, data.summary.edgeSystems); if (edge) animateMetric(edge, data.summary.edgeSystems);
// ── System cards ──
if (grid) { if (grid) {
grid.innerHTML = ""; grid.innerHTML = "";
data.systems.forEach((system) => { data.systems.forEach((system) => {
@@ -95,7 +86,6 @@ async function loadNocStatus() {
</div> </div>
<h3>${system.name}</h3> <h3>${system.name}</h3>
<p>${system.type}</p> <p>${system.type}</p>
<code>${system.address}</code>
<div class="system-meta"> <div class="system-meta">
<small>Status: ${system.status}</small> <small>Status: ${system.status}</small>
<small>Latency: ${system.latency || "N/A"}</small> <small>Latency: ${system.latency || "N/A"}</small>
@@ -107,14 +97,12 @@ async function loadNocStatus() {
}); });
} }
// ── Alerts ──
if (alertList) renderAlerts(data.systems, alertList); if (alertList) renderAlerts(data.systems, alertList);
} catch (err) { } catch (err) {
failCount++; failCount++;
console.warn(`[NOC] Fetch failed (${failCount}/${MAX_FAILS}):`, err.message); console.warn(`[NOC] Fetch failed (${failCount}/${MAX_FAILS}):`, err.message);
// Update dot to error state
if (dot) dot.className = "status-dot error"; if (dot) dot.className = "status-dot error";
if (statusText) { if (statusText) {
@@ -125,27 +113,23 @@ async function loadNocStatus() {
if (badge) badge.textContent = ""; if (badge) badge.textContent = "";
// After MAX_FAILS attempts, show the fallback block
if (failCount >= MAX_FAILS && fallback) { if (failCount >= MAX_FAILS && fallback) {
fallback.style.display = "block"; fallback.style.display = "block";
} }
} }
} }
// Animate a number metric with a quick pop
function animateMetric(el, value) { function animateMetric(el, value) {
el.classList.remove("metric-pop"); el.classList.remove("metric-pop");
el.textContent = value; el.textContent = value;
void el.offsetWidth; // force reflow void el.offsetWidth;
el.classList.add("metric-pop"); el.classList.add("metric-pop");
} }
// ═══════════════════════════════════════════════ // ═══════════════════════════════════════════════
// GitHub Repos — fetches public repos for ced4568 // GitHub Repos
// Sorted by last pushed, capped at 6 cards
// ═══════════════════════════════════════════════ // ═══════════════════════════════════════════════
// Language → color map (subset of GitHub's palette)
const LANG_COLORS = { const LANG_COLORS = {
Python: "#3572A5", Python: "#3572A5",
JavaScript: "#f1e05a", JavaScript: "#f1e05a",
@@ -170,8 +154,6 @@ async function loadGithubRepos() {
if (!res.ok) throw new Error(`GitHub API ${res.status}`); if (!res.ok) throw new Error(`GitHub API ${res.status}`);
const repos = await res.json(); const repos = await res.json();
// Filter out forks for cleaner display
const ownRepos = repos.filter((r) => !r.fork).slice(0, 6); const ownRepos = repos.filter((r) => !r.fork).slice(0, 6);
if (ownRepos.length === 0) { if (ownRepos.length === 0) {
@@ -181,10 +163,7 @@ async function loadGithubRepos() {
container.innerHTML = ownRepos.map((repo) => { container.innerHTML = ownRepos.map((repo) => {
const langColor = LANG_COLORS[repo.language] || "var(--accent-2)"; const langColor = LANG_COLORS[repo.language] || "var(--accent-2)";
const desc = repo.description const desc = repo.description ? repo.description : "No description provided.";
? repo.description
: "No description provided.";
const updatedDate = new Date(repo.pushed_at).toLocaleDateString("en-US", { const updatedDate = new Date(repo.pushed_at).toLocaleDateString("en-US", {
month: "short", month: "short",
year: "numeric", year: "numeric",
@@ -203,9 +182,7 @@ async function loadGithubRepos() {
>${repo.name}</a> >${repo.name}</a>
<span class="repo-visibility">${repo.private ? "private" : "public"}</span> <span class="repo-visibility">${repo.private ? "private" : "public"}</span>
</div> </div>
<p class="repo-desc">${desc}</p> <p class="repo-desc">${desc}</p>
<div class="repo-meta"> <div class="repo-meta">
${repo.language ${repo.language
? `<span> ? `<span>
@@ -226,9 +203,9 @@ async function loadGithubRepos() {
container.innerHTML = ` container.innerHTML = `
<div class="repo-loading"> <div class="repo-loading">
<p class="noc-loading"> <p class="noc-loading">
Could not load repositories. Could not load repositories.
<a href="https://github.com/ced4568" target="_blank" rel="noopener noreferrer"> <a href="https://github.com/ced4568" target="_blank" rel="noopener noreferrer">
View on GitHub View on GitHub
</a> </a>
</p> </p>
</div> </div>
@@ -236,7 +213,7 @@ async function loadGithubRepos() {
} }
} }
} }
// Build the alert panel from system data
function renderAlerts(systems, alertList) { function renderAlerts(systems, alertList) {
const scored = systems const scored = systems
.map((sys) => { .map((sys) => {