Skip to content

Commit 7e744e9

Browse files
authored
feat: redesign status page (#6)
* chore: update the design of it using cmd /design * feat: update the design and logo * chore: update the design of status page * chore: remove all the report
1 parent e335ce0 commit 7e744e9

13 files changed

Lines changed: 1167 additions & 195 deletions

404.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta content="width=device-width,initial-scale=1" name=viewport> <meta content=#333333 name=theme-color> <base href=/ > <link href=manifest.json rel=manifest crossorigin=use-credentials> <script>__SAPPER__={baseUrl:"",preloaded:[void 0]};if('serviceWorker' in navigator)navigator.serviceWorker.register('/service-worker.js');(function(){try{eval("async function x(){}");var main="/client/client.9b2e11fb.js"}catch(e){main="/client/legacy/client.532031ad.js"};var s=document.createElement("script");try{new Function("if(0)import('')")();s.src=main;s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="/client/shimport@2.0.5.js";s.setAttribute("data-main",main);}document.head.appendChild(s);}());</script> <link href=client/client-31a52aa5.css rel=stylesheet><link href=client/createOctokit-a85f76c7.css rel=stylesheet><link href=client/index-b0b6455a.css rel=stylesheet> <script>// If `prefers-color-scheme` is not supported, fall back to light mode.
1+
<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta content="width=device-width,initial-scale=1" name=viewport> <meta content=#000000 name=theme-color> <base href=/ > <link href=manifest.json rel=manifest crossorigin=use-credentials> <script>__SAPPER__={baseUrl:"",preloaded:[void 0]};if('serviceWorker' in navigator)navigator.serviceWorker.register('/service-worker.js');(function(){try{eval("async function x(){}");var main="/client/client.9b2e11fb.js"}catch(e){main="/client/legacy/client.532031ad.js"};var s=document.createElement("script");try{new Function("if(0)import('')")();s.src=main;s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="/client/shimport@2.0.5.js";s.setAttribute("data-main",main);}document.head.appendChild(s);}());</script> <link href=client/client-31a52aa5.css rel=stylesheet><link href=client/createOctokit-a85f76c7.css rel=stylesheet><link href=client/index-b0b6455a.css rel=stylesheet> <script>// If `prefers-color-scheme` is not supported, fall back to light mode.
22
// In this case, light.css will be downloaded with `highest` priority.
33
if (typeof window !== "undefined" && typeof document !== "undefined" && "matchMedia" in window && window.matchMedia('(prefers-color-scheme: dark)').media === 'not all') {
44
document.documentElement.style.display = 'none';
55
document.head.insertAdjacentHTML(
66
'beforeend',
77
'<link rel="stylesheet" href={`${config.path}/themes/light.css`} onload="document.documentElement.style.display = \'\'">',
88
);
9-
} </script> <link href=https://status.commandcode.ai/themes/light.css rel=stylesheet media=(prefers-color-scheme:light)> <link href=https://status.commandcode.ai/themes/dark.css rel=stylesheet media=(prefers-color-scheme:dark)><link href=https://status.commandcode.ai/global.css rel=stylesheet><link href=https://raw.githubusercontent.com/CommandCodeAI/commandcode-status/master/assets/favicon.svg rel=icon type=image/svg><link href=/logo-192.png rel=icon type=image/png> <link href=/client/client.9b2e11fb.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/client-31a52aa5.css rel=preload as=style><link href=/client/index.1e36db55.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/createOctokit.5dd4437e.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/inject_styles.803b7e80.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/createOctokit-a85f76c7.css rel=preload as=style><link href=/client/index-b0b6455a.css rel=preload as=style></head> <body> <div id=sapper> <nav class=svelte-a08hsz><div class="svelte-a08hsz container"><div><a class="svelte-a08hsz logo" href=https://status.commandcode.ai><img alt="" class=svelte-a08hsz src=https://raw.githubusercontent.com/CommandCodeAI/docs-images/refs/heads/main/design/logo/logo-symbol.svg> <div>Command Code Status</div></a></div> <ul class=svelte-a08hsz><li><a class=svelte-a08hsz href=https://commandcode.ai target=_self>Command Code</a> <li><a class=svelte-a08hsz href=/ target=_self>Status</a> <li><a class=svelte-a08hsz href=https://github.com/CommandCodeAI/commandcode-status/issues target=_self>Subscribe on GitHub</a> </li> </ul></div> </nav> <main class=container></main> <footer class=svelte-jbr799><p></p> </footer></div> <script> try {
9+
} </script> <link href=/themes/light.css rel=stylesheet media=(prefers-color-scheme:light)> <link href=/themes/dark.css rel=stylesheet media=(prefers-color-scheme:dark)><link href=/global.css rel=stylesheet><link href=https://raw.githubusercontent.com/CommandCodeAI/commandcode-status/master/assets/favicon.svg rel=icon type=image/svg><link href=/logo-192.png rel=icon type=image/png> <link href=/client/client.9b2e11fb.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/client-31a52aa5.css rel=preload as=style><link href=/client/index.1e36db55.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/createOctokit.5dd4437e.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/inject_styles.803b7e80.js rel=modulepreload as=script crossorigin=use-credentials><link href=/client/createOctokit-a85f76c7.css rel=preload as=style><link href=/client/index-b0b6455a.css rel=preload as=style></head> <body> <div id=sapper> <nav class=svelte-a08hsz><div class="svelte-a08hsz container"><div><a class="svelte-a08hsz logo" href=https://status.commandcode.ai><img alt="" class=svelte-a08hsz src=https://commandcode.ai/logov2/logo-bg-transparent-fg-dark.svg> <div>Status</div></a></div> <ul class=svelte-a08hsz><li><a class=svelte-a08hsz href=https://commandcode.ai target=_self>Command Code</a> <li><a class=svelte-a08hsz href=/ target=_self>Status</a> <li><a class=svelte-a08hsz href=https://github.com/CommandCodeAI/commandcode-status/issues target=_self>Subscribe on GitHub</a> </li> </ul></div> </nav> <main class=container></main> <footer class=svelte-jbr799><p></p> </footer></div> <script> try {
1010
if (typeof window !== "undefined" && window.navigator && navigator.serviceWorker) {
1111
navigator.serviceWorker.getRegistrations().then(function (registrations) {
1212
for (let registration of registrations) {

client/client.9b2e11fb.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/legacy/client.532031ad.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/nav-label-fix.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(function () {
2+
"use strict";
3+
4+
var LABEL = "Status";
5+
6+
function fix() {
7+
var el = document.querySelector(".logo div");
8+
if (el && el.textContent !== LABEL) el.textContent = LABEL;
9+
}
10+
11+
fix();
12+
document.addEventListener("DOMContentLoaded", fix);
13+
window.addEventListener("load", fix);
14+
15+
// The Sapper bundle re-renders the nav on hydration, overwriting
16+
// the static label — watch for that and correct it back.
17+
var target = document.querySelector(".logo");
18+
if (target && window.MutationObserver) {
19+
new MutationObserver(fix).observe(target, {
20+
childList: true,
21+
subtree: true,
22+
characterData: true,
23+
});
24+
}
25+
})();

client/system-status.js

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
(function () {
2+
"use strict";
3+
4+
var OWNER = "CommandCodeAI";
5+
var REPO = "commandcode-status";
6+
var RAW_BASE = "https://raw.githubusercontent.com/" + OWNER + "/" + REPO + "/master/history/";
7+
var ISSUES_URL = "https://api.github.com/repos/" + OWNER + "/" + REPO + "/issues?state=all&per_page=100";
8+
var WINDOW_DAYS = 90;
9+
var DAY_MS = 24 * 60 * 60 * 1000;
10+
var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
11+
12+
var root = document.getElementById("system-status-rows");
13+
var rangeLabel = document.getElementById("system-status-range");
14+
var prevBtn = document.querySelector('.status-nav-btn[data-dir="prev"]');
15+
var nextBtn = document.querySelector('.status-nav-btn[data-dir="next"]');
16+
if (!root) return;
17+
18+
function toDateOnly(d) {
19+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
20+
}
21+
22+
function addDays(d, n) {
23+
return new Date(d.getTime() + n * DAY_MS);
24+
}
25+
26+
function formatRange(start, end) {
27+
var s = MONTH_NAMES[start.getUTCMonth()] + " " + start.getUTCFullYear();
28+
var e = MONTH_NAMES[end.getUTCMonth()] + " " + end.getUTCFullYear();
29+
return s === e ? s : s + " – " + e;
30+
}
31+
32+
function fetchJson(url) {
33+
return fetch(url, { headers: { Accept: "application/vnd.github+json" } }).then(function (res) {
34+
if (!res.ok) throw new Error("Request failed: " + res.status);
35+
return res.json();
36+
});
37+
}
38+
39+
function parseFrontMatterDate(body, key) {
40+
var m = body && body.match(new RegExp(key + ":\\s*([^\\n]+)"));
41+
return m ? new Date(m[1].trim()) : null;
42+
}
43+
44+
// Turn incident/maintenance issues into day-level severity windows.
45+
function classifyIssue(issue) {
46+
var labels = (issue.labels || []).map(function (l) {
47+
return typeof l === "string" ? l : l.name;
48+
});
49+
var isStatus = labels.indexOf("status") !== -1;
50+
var isMaintenance = labels.indexOf("maintenance") !== -1;
51+
if (!isStatus && !isMaintenance) return null;
52+
53+
var body = issue.body || "";
54+
var start = parseFrontMatterDate(body, "start") || new Date(issue.created_at);
55+
var end = parseFrontMatterDate(body, "end") || (issue.state === "closed" ? new Date(issue.closed_at) : new Date());
56+
57+
var severity;
58+
if (/expectedDown/i.test(body)) severity = "down";
59+
else if (/expectedDegraded/i.test(body)) severity = "degraded";
60+
else if (/degrad|latency|slow/i.test(issue.title + " " + body)) severity = "degraded";
61+
else severity = "down";
62+
63+
// Maintenance windows are shown as degraded (amber), never down (red).
64+
if (isMaintenance && severity === "down") severity = "degraded";
65+
66+
return { start: toDateOnly(start), end: toDateOnly(end), severity: severity };
67+
}
68+
69+
function severityOnDay(day, events) {
70+
var rank = { up: 0, degraded: 1, down: 2 };
71+
var worst = "up";
72+
for (var i = 0; i < events.length; i++) {
73+
var ev = events[i];
74+
if (day >= ev.start && day <= ev.end && rank[ev.severity] > rank[worst]) {
75+
worst = ev.severity;
76+
}
77+
}
78+
return worst;
79+
}
80+
81+
function statusLabel(status) {
82+
if (status === "down") return "Down";
83+
if (status === "degraded") return "Degraded performance";
84+
return "Operational";
85+
}
86+
87+
function el(tag, className, text) {
88+
var node = document.createElement(tag);
89+
if (className) node.className = className;
90+
if (text != null) node.textContent = text;
91+
return node;
92+
}
93+
94+
function renderRow(site, events, siteStart, windowStart, windowEnd) {
95+
var row = el("article", "status-row " + site.status);
96+
97+
var head = el("div", "status-row-head");
98+
head.appendChild(el("span", "status-dot"));
99+
head.appendChild(el("span", "status-name", site.name));
100+
head.appendChild(el("span", "status-label", statusLabel(site.status)));
101+
102+
var bar = el("div", "status-bar");
103+
bar.setAttribute("role", "img");
104+
105+
var dataDays = 0;
106+
var downDays = 0;
107+
var cursor = windowStart;
108+
while (cursor <= windowEnd) {
109+
var cell = el("span", "status-cell");
110+
if (siteStart && cursor < siteStart) {
111+
cell.className += " no-data";
112+
cell.title = "No data";
113+
} else {
114+
var sev = severityOnDay(cursor, events);
115+
cell.className += " " + sev;
116+
var dateStr = cursor.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
117+
cell.title = dateStr + " — " + statusLabel(sev);
118+
dataDays++;
119+
if (sev === "down") downDays++;
120+
}
121+
bar.appendChild(cell);
122+
cursor = addDays(cursor, 1);
123+
}
124+
125+
var uptimePct = dataDays > 0 ? (((dataDays - downDays) / dataDays) * 100).toFixed(2) : "100.00";
126+
head.appendChild(el("span", "status-uptime", uptimePct + "% uptime"));
127+
128+
row.appendChild(head);
129+
row.appendChild(bar);
130+
return row;
131+
}
132+
133+
function init(sites, issuesBySlug, startBySlug) {
134+
var today = toDateOnly(new Date());
135+
var windowEnd = today;
136+
137+
function render() {
138+
var windowStart = addDays(windowEnd, -(WINDOW_DAYS - 1));
139+
root.innerHTML = "";
140+
sites.forEach(function (site) {
141+
var events = issuesBySlug[site.slug] || [];
142+
var siteStart = startBySlug[site.slug] || null;
143+
root.appendChild(renderRow(site, events, siteStart, windowStart, windowEnd));
144+
});
145+
rangeLabel.textContent = formatRange(windowStart, windowEnd);
146+
147+
var earliestStart = sites.reduce(function (min, site) {
148+
var s = startBySlug[site.slug];
149+
if (!s) return min;
150+
return !min || s < min ? s : min;
151+
}, null);
152+
prevBtn.disabled = !!earliestStart && windowStart <= earliestStart;
153+
nextBtn.disabled = windowEnd >= today;
154+
}
155+
156+
prevBtn.addEventListener("click", function () {
157+
windowEnd = addDays(windowEnd, -WINDOW_DAYS);
158+
render();
159+
});
160+
nextBtn.addEventListener("click", function () {
161+
windowEnd = addDays(windowEnd, WINDOW_DAYS);
162+
if (windowEnd > today) windowEnd = today;
163+
render();
164+
});
165+
166+
render();
167+
}
168+
169+
fetchJson(RAW_BASE + "summary.json")
170+
.then(function (sites) {
171+
var startBySlug = {};
172+
var startFetches = sites.map(function (site) {
173+
return fetch(RAW_BASE + site.slug + ".yml")
174+
.then(function (res) {
175+
return res.ok ? res.text() : "";
176+
})
177+
.then(function (text) {
178+
var m = text.match(/startTime:\s*([^\n]+)/);
179+
if (m) startBySlug[site.slug] = toDateOnly(new Date(m[1].trim()));
180+
})
181+
.catch(function () {});
182+
});
183+
184+
var issuesBySlug = {};
185+
var issuesFetch = fetchJson(ISSUES_URL)
186+
.then(function (issues) {
187+
var events = issues.map(classifyIssue).filter(Boolean);
188+
// Single-monitor setup: attribute all events to every site.
189+
sites.forEach(function (site) {
190+
issuesBySlug[site.slug] = events;
191+
});
192+
})
193+
.catch(function () {
194+
// Rate-limited or offline — fall back to an all-green bar.
195+
sites.forEach(function (site) {
196+
issuesBySlug[site.slug] = [];
197+
});
198+
});
199+
200+
return Promise.all(startFetches.concat([issuesFetch])).then(function () {
201+
init(sites, issuesBySlug, startBySlug);
202+
});
203+
})
204+
.catch(function () {
205+
root.innerHTML = "";
206+
root.appendChild(el("p", "status-error", "Status data is unavailable right now."));
207+
rangeLabel.textContent = "";
208+
prevBtn.disabled = true;
209+
nextBtn.disabled = true;
210+
});
211+
})();

0 commit comments

Comments
 (0)