Skip to content

Commit

Permalink
✨ Add scheduled maintenance events
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Feb 24, 2021
1 parent ce9fa58 commit ca13c83
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 36 deletions.
10 changes: 10 additions & 0 deletions i18n.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,13 @@ errorTitle: An error occurred
errorIntro: An error occurred in trying to get the latest status details.
errorText: You can try again in a few moments.
errorHome: Go to the homepage
pastScheduledMaintenance: Past Scheduled Maintenance
scheduledMaintenance: Scheduled Maintenance
scheduledMaintenanceSummaryStarted: Started at $DATE for $DURATION minutes
scheduledMaintenanceSummaryStarts: Starts at $DATE for $DURATION minutes
startedAt: Started at
startsAt: Starts at
duration: Duration
durationMin: $DURATION minutes
incidentCompleted: Completed
incidentScheduled: Scheduled
100 changes: 100 additions & 0 deletions src/components/ActiveScheduled.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<script>
import Loading from "../components/Loading.svelte";
import { onMount } from "svelte";
import config from "../data/config.json";
import { cachedResponse, createOctokit, handleError } from "../utils/createOctokit";
let loading = true;
const octokit = createOctokit();
const owner = config.owner;
const repo = config.repo;
let incidents = [];
onMount(async () => {
try {
incidents = (
await cachedResponse(`scheduled-current-${owner}-${repo}`, () =>
octokit.issues.listForRepo({
owner,
repo,
state: "open",
filter: "all",
sort: "created",
direction: "desc",
labels: "maintenance",
})
)
).data;
incidents = incidents.map((incident, index) => {
incident.showHeading =
index === 0 ||
new Date(incidents[index - 1].created_at).toLocaleDateString() !==
new Date(incident.created_at).toLocaleDateString();
incident.metadata = {};
if (incident.body.includes("<!--")) {
const summary = incident.body.split("<!--")[1].split("-->")[0];
const lines = summary
.split("\n")
.filter((i) => i.trim())
.filter((i) => i.includes(":"));
lines.forEach((i) => {
incident.metadata[i.split(/:(.+)/)[0].trim()] = i.split(/:(.+)/)[1].trim();
});
}
return incident;
});
} catch (error) {
handleError(error);
}
loading = false;
});
</script>

{#if !incidents.length && !loading}
<article class="up">✅ &nbsp; {config.i18n.allSystemsOperational}</article>
{/if}

<section>
{#if loading}
<Loading />
{:else if incidents.length}
<h2>{config.i18n.scheduledMaintenance}</h2>
{#each incidents as incident}
<article class="degraded degraded-active link">
<div class="f">
<div>
<h4>{incident.title.replace("🛑", "").replace("⚠️", "").trim()}</h4>
{#if incident.metadata.start && incident.metadata.end}
<div>
{(new Date(incident.metadata.start).getTime() < new Date().getTime()
? config.i18n.scheduledMaintenanceSummaryStarted
: config.i18n.scheduledMaintenanceSummaryStarts
)
.replace(/\$DATE/g, new Date(incident.metadata.start).toLocaleString())
.replace(
/\$DURATION/g,
Math.floor(
(new Date(incident.metadata.end).getTime() -
new Date(incident.metadata.start).getTime()) /
60000
)
)}
</div>
{/if}
</div>
<div class="f r">
<a href={`${config.path}/incident/${incident.number}`}>
{config.i18n.incidentReport.replace(/\$NUMBER/g, incident.number)}
</a>
</div>
</div>
</article>
{/each}
{/if}
</section>

<style>
section {
margin-bottom: 2rem;
}
</style>
78 changes: 58 additions & 20 deletions src/components/Incident.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@
})
)
).data;
incident.metadata = {};
if (incident.body.includes("<!--")) {
const summary = incident.body.split("<!--")[1].split("-->")[0];
const lines = summary
.split("\n")
.filter((i) => i.trim())
.filter((i) => i.includes(":"));
lines.forEach((i) => {
incident.metadata[i.split(/:(.+)/)[0].trim()] = i.split(/:(.+)/)[1].trim();
});
}
} catch (error) {
handleError(error);
}
Expand All @@ -51,23 +62,8 @@
});
</script>

<style>
footer {
margin-top: 2rem;
}
p {
margin-top: 0;
}
h2 {
line-height: 1;
}
.r {
text-align: right;
}
</style>

<svelte:head>
<title>{config.i18n.incidentTitle.replace('$NUMBER', number)}</title>
<title>{config.i18n.incidentTitle.replace("$NUMBER", number)}</title>
</svelte:head>

<h2>
Expand All @@ -76,7 +72,13 @@
{:else}
{incident.title}
<span class={`tag ${incident.state}`}>
{incident.state === 'closed' ? config.i18n.incidentFixed : config.i18n.incidentOngoing}
{incident.state === "closed"
? incident.metadata.start
? config.i18n.incidentCompleted
: config.i18n.incidentFixed
: incident.metadata.start
? config.i18n.incidentScheduled
: config.i18n.incidentOngoing}
</span>
{/if}
</h2>
Expand All @@ -87,9 +89,30 @@
{:else}
<div class="f">
<dl>
<dt>{config.i18n.incidentOpenedAt}</dt>
<dd>{new Date(incident.created_at).toLocaleString()}</dd>
{#if incident.closed_at}
{#if incident.metadata.start}
<dt>
{new Date(incident.metadata.start).getTime() < new Date().getTime()
? config.i18n.startedAt
: config.i18n.startsAt}
</dt>
<dd>{new Date(incident.metadata.start).toLocaleString()}</dd>
{:else}
<dt>{config.i18n.incidentOpenedAt}</dt>
<dd>{new Date(incident.created_at).toLocaleString()}</dd>
{/if}
{#if incident.metadata.start && incident.metadata.end}
<dt>{config.i18n.duration}</dt>
<dd>
{config.i18n.durationMin.replace(
/\$DURATION/g,
Math.floor(
(new Date(incident.metadata.end).getTime() -
new Date(incident.metadata.start).getTime()) /
60000
)
)}
</dd>
{:else if incident.closed_at}
<dt>{config.i18n.incidentClosedAt}</dt>
<dd>{new Date(incident.closed_at).toLocaleString()}</dd>
{/if}
Expand Down Expand Up @@ -126,3 +149,18 @@
</section>

<footer><a href={config.path}>{config.i18n.incidentBack}</a></footer>

<style>
footer {
margin-top: 2rem;
}
p {
margin-top: 0;
}
h2 {
line-height: 1;
}
.r {
text-align: right;
}
</style>
72 changes: 72 additions & 0 deletions src/components/Scheduled.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<script>
import Loading from "../components/Loading.svelte";
import { onMount } from "svelte";
import config from "../data/config.json";
import { cachedResponse, createOctokit, handleError } from "../utils/createOctokit";
let loading = true;
const octokit = createOctokit();
const owner = config.owner;
const repo = config.repo;
let incidents = [];
onMount(async () => {
try {
incidents = (
await cachedResponse(`maintenance-issues-${owner}-${repo}`, () =>
octokit.issues.listForRepo({
owner,
repo,
state: "closed",
filter: "all",
sort: "created",
direction: "desc",
labels: "maintenance",
})
)
).data;
} catch (error) {
handleError(error);
}
incidents = incidents.map((incident, index) => {
incident.showHeading =
index === 0 ||
new Date(incidents[index - 1].created_at).toLocaleDateString() !==
new Date(incident.created_at).toLocaleDateString();
return incident;
});
loading = false;
});
</script>

<section>
{#if loading}
<Loading />
{:else if incidents.length}
<h2>{config.i18n.pastScheduledMaintenance}</h2>
{#each incidents as incident}
{#if incident.showHeading}
<h3>{new Date(incident.created_at).toLocaleDateString()}</h3>
{/if}
<article class="link degraded">
<div class="f">
<div>
<h4>{incident.title.replace("🛑", "").replace("⚠️", "").trim()}</h4>
<div>Completed</div>
</div>
<div class="f r">
<a href={`${config.path}/incident/${incident.number}`}>
{config.i18n.incidentReport.replace(/\$NUMBER/g, incident.number)}
</a>
</div>
</div>
</article>
{/each}
{/if}
</section>

<style>
h2 {
margin-top: 2rem;
}
</style>
36 changes: 20 additions & 16 deletions src/routes/index.svelte
Original file line number Diff line number Diff line change
@@ -1,44 +1,48 @@
<script>
import snarkdown from "snarkdown";
import ActiveIncidents from "../components/ActiveIncidents.svelte";
import LiveStatus from "../components/LiveStatus.svelte";
import ActiveScheduled from "../components/ActiveScheduled.svelte";
import Incidents from "../components/Incidents.svelte";
import LiveStatus from "../components/LiveStatus.svelte";
import Scheduled from "../components/Scheduled.svelte";
import config from "../data/config.json";
import snarkdown from "snarkdown";
let title = "Status";
try {
title = config["status-website"].name;
} catch (error) {}
</script>

<style>
p.lead {
font-size: 110%;
}
header {
margin-bottom: 2rem;
}
</style>

<svelte:head>
<title>{title}</title>
</svelte:head>

<header>
{#if config['status-website']}
{#if config['status-website'].introTitle}
{#if config["status-website"]}
{#if config["status-website"].introTitle}
<h1>
{@html snarkdown(config['status-website'].introTitle)}
{@html snarkdown(config["status-website"].introTitle)}
</h1>
{/if}
{#if config['status-website'].introMessage}
{#if config["status-website"].introMessage}
<p class="lead">
{@html snarkdown(config['status-website'].introMessage)}
{@html snarkdown(config["status-website"].introMessage)}
</p>
{/if}
{/if}
</header>

<ActiveIncidents />
<ActiveScheduled />
<LiveStatus />
<Scheduled />
<Incidents />

<style>
p.lead {
font-size: 110%;
}
header {
margin-bottom: 2rem;
}
</style>

0 comments on commit ca13c83

Please sign in to comment.