Skip to content

Commit 8eda12d

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/vite-8.0.0
2 parents f5f41ff + b68ac3a commit 8eda12d

36 files changed

Lines changed: 1843 additions & 272 deletions

.idea/runConfigurations/Local.xml

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/status.iml

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

package-lock.json

Lines changed: 236 additions & 118 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@
2121
},
2222
"devDependencies": {
2323
"@tailwindcss/typography": "^0.5.19",
24-
"@tailwindcss/vite": "^4.2.1",
24+
"@tailwindcss/vite": "^4.2.2",
25+
"@types/markdown-it": "^14.1.2",
2526
"rollup-plugin-minify-template-literals": "^1.1.7",
2627
"typescript": "^5.9.3",
2728
"vite": "^8.0.0"
2829
},
2930
"dependencies": {
31+
"@vercel/speed-insights": "^2.0.0",
3032
"lit": "^3.3.2",
33+
"markdown-it": "^14.1.1",
3134
"navigo": "^8.11.1"
3235
}
3336
}

public/favicon.ico

4.15 KB
Binary file not shown.

src/EnumMappings.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { ServiceStatus } from "./models/ServiceStatus";
2+
import { NoticeStatus } from "./models/NoticeStatus";
3+
4+
export namespace EnumMappings {
5+
export const SERVICE_STATUS_STYLES: Record<
6+
ServiceStatus,
7+
{ color: string; bar: string; label: string; icon: string }
8+
> = {
9+
[ServiceStatus.OPERATIONAL]: {
10+
color: "fill-emerald-400",
11+
bar: "bg-emerald-500",
12+
label: "Operational",
13+
icon:
14+
`<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"></path>`,
15+
},
16+
[ServiceStatus.UNDER_MAINTENANCE]: {
17+
color: "fill-blue-400",
18+
bar: "bg-blue-400",
19+
label: "Under maintenance",
20+
icon:
21+
`<path d="M128 24a104 104 0 1 0 104 104A104.13 104.13 0 0 0 128 24m14.052 54.734a34.2 34.2 0 0 1 9.427 1.006 3.79 3.79 0 0 1 1.865 6.25l-17.76 19.265 2.682 12.485 12.484 2.677 19.266-17.782a3.79 3.79 0 0 1 6.25 1.865 34.4 34.4 0 0 1 1.02 8.333 34.122 34.122 0 0 1-47.833 31.282l-24.672 28.536a4 4 0 0 1-.187.203 15.168 15.168 0 0 1-21.448-21.453q.098-.095.203-.182l28.542-24.667a34.155 34.155 0 0 1 30.161-47.818" />`,
22+
},
23+
[ServiceStatus.DEGRADED_PERFORMANCE]: {
24+
color: "fill-amber-400",
25+
bar: "bg-amber-400",
26+
label: "Degraded performance",
27+
icon:
28+
`<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z"></path>`,
29+
},
30+
[ServiceStatus.PARTIAL_OUTAGE]: {
31+
color: "fill-orange-400",
32+
bar: "bg-orange-400",
33+
label: "Partial outage",
34+
icon:
35+
`<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z"></path>`,
36+
},
37+
[ServiceStatus.MAJOR_OUTAGE]: {
38+
color: "fill-red-400",
39+
bar: "bg-red-400",
40+
label: "Major outage",
41+
icon:
42+
`<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm37.66,130.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>`,
43+
},
44+
};
45+
46+
export const NOTICE_STATUS_NAMES: Record<NoticeStatus, string> = {
47+
[NoticeStatus.INCIDENT_IDENTIFIED]: "Identified",
48+
[NoticeStatus.INCIDENT_INVESTIGATING]: "Investigating",
49+
[NoticeStatus.INCIDENT_MONITORING]: "Monitoring",
50+
[NoticeStatus.INCIDENT_RESOLVED]: "Resolved",
51+
[NoticeStatus.MAINTENANCE_NOT_STARTED_YET]: "Planned",
52+
[NoticeStatus.MAINTENANCE_IN_PROGRESS]: "In progress",
53+
[NoticeStatus.MAINTENANCE_COMPLETED]: "Completed",
54+
};
55+
}

src/Time.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
export namespace Time {
2+
export class Duration {
3+
public readonly ms: number;
4+
5+
public constructor(ms: number) {
6+
this.ms = ms;
7+
}
8+
9+
private getParts() {
10+
const totalSeconds = Math.floor(this.ms / 1000);
11+
return {
12+
days: Math.floor(totalSeconds / 86400),
13+
hours: Math.floor((totalSeconds % 86400) / 3600),
14+
minutes: Math.floor((totalSeconds % 3600) / 60),
15+
};
16+
}
17+
18+
public toISOString() {
19+
const { days, hours, minutes } = this.getParts();
20+
21+
return "P" +
22+
(days ? `${days}D` : "") +
23+
(hours || minutes ? "T" : "") +
24+
(hours ? `${hours}H` : "") +
25+
(minutes ? `${minutes}M` : "");
26+
}
27+
28+
public toString() {
29+
const { days, hours, minutes } = this.getParts();
30+
31+
const parts = [
32+
days && `${days} ${days === 1 ? "day" : "days"}`,
33+
hours && `${hours} ${hours === 1 ? "hour" : "hours"}`,
34+
minutes && `${minutes} ${minutes === 1 ? "minute" : "minutes"}`,
35+
].filter(Boolean) as string[];
36+
37+
return parts.length > 1
38+
? parts.slice(0, -1).join(", ") + " and " + parts.at(-1)
39+
: parts[0] ?? "0 minutes";
40+
}
41+
}
42+
43+
export class Day {
44+
public readonly date: Date;
45+
46+
public constructor(date: Date) {
47+
this.date = new Date(date.getTime());
48+
this.date.setHours(0, 0, 0, 0);
49+
}
50+
51+
public static today() {
52+
return new Day(new Date());
53+
}
54+
55+
public is(date: Date): boolean;
56+
public is(day: Day): boolean;
57+
public is(d: Date | Day): boolean {
58+
return d instanceof Day
59+
? d.date.getTime() === this.date.getTime()
60+
: new Day(d).date.getTime() === this.date.getTime();
61+
}
62+
63+
public toISOString() {
64+
const year = this.date.getFullYear();
65+
const month = String(this.date.getMonth() + 1).padStart(2, "0");
66+
const day = String(this.date.getDate()).padStart(2, "0");
67+
68+
return `${year}-${month}-${day}`;
69+
}
70+
71+
public toString() {
72+
return this.date.toLocaleString(undefined, {
73+
month: "long",
74+
day: "numeric",
75+
year: "numeric",
76+
});
77+
}
78+
79+
public add(days: number) {
80+
const newDate = new Date(this.date.getTime());
81+
newDate.setDate(this.date.getDate() + days);
82+
return new Day(newDate);
83+
}
84+
85+
public subtract(days: number) {
86+
const newDate = new Date(this.date.getTime());
87+
newDate.setDate(this.date.getDate() - days);
88+
return new Day(newDate);
89+
}
90+
91+
public next() {
92+
return this.add(1);
93+
}
94+
95+
public previous() {
96+
return this.subtract(1);
97+
}
98+
99+
public getTime() {
100+
return this.date.getTime();
101+
}
102+
}
103+
104+
export class DateTime {
105+
public readonly date: Date;
106+
107+
public constructor(date: Date) {
108+
this.date = new Date(date.getTime());
109+
}
110+
111+
public static now() {
112+
return new DateTime(new Date());
113+
}
114+
115+
public getDay() {
116+
return new Day(this.date);
117+
}
118+
119+
public toISOString() {
120+
return this.date.toISOString();
121+
}
122+
123+
public toString() {
124+
return this.getDay().toString() + " at " + this.toTimeString();
125+
}
126+
127+
public toTimeString() {
128+
return this.date.toLocaleTimeString(undefined, {
129+
hour: "numeric",
130+
minute: "numeric",
131+
});
132+
}
133+
}
134+
}

src/api/BaseComponent.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface BaseComponent {
2+
id: string;
3+
name: { default: string };
4+
status: string;
5+
order: number;
6+
}

src/api/Incident.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Notice } from "./Notice";
2+
3+
export interface Incident extends Notice {
4+
started: string;
5+
resolved: string | null;
6+
duration: number;
7+
impact: string;
8+
}

src/api/InstatusApi.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Site } from "./Site";
22
import { SiteResponse } from "./SiteResponse";
33
import { MetricDataPoint } from "../models/MetricDataPoint";
4+
import { Incident } from "./Incident";
5+
import { Maintenance } from "./Maintenance";
46

57
export class InstatusApi {
68
private readonly base: URL;
@@ -32,4 +34,40 @@ export class InstatusApi {
3234
const data: { data: MetricDataPoint[] } = await res.json();
3335
return data.data;
3436
}
37+
38+
public async getIncidents(): Promise<Incident[]> {
39+
const res = await fetch(new URL("incidents", this.base));
40+
if (!res.ok) {
41+
throw new Error(`Failed to fetch incidents: ${res.status}`);
42+
}
43+
const data: { incidents: Incident[] } = await res.json();
44+
return data.incidents;
45+
}
46+
47+
public async getMaintenances(): Promise<Maintenance[]> {
48+
const res = await fetch(new URL("maintenances", this.base));
49+
if (!res.ok) {
50+
throw new Error(`Failed to fetch maintenances: ${res.status}`);
51+
}
52+
const data: { maintenances: Maintenance[] } = await res.json();
53+
return data.maintenances;
54+
}
55+
56+
public async getIncident(id: string): Promise<Incident> {
57+
const res = await fetch(new URL(`incidents/${id}`, this.base));
58+
if (!res.ok) {
59+
throw new Error(`Failed to fetch incident: ${res.status}`);
60+
}
61+
const data: { incident: Incident } = await res.json();
62+
return data.incident;
63+
}
64+
65+
public async getMaintenance(id: string): Promise<Maintenance> {
66+
const res = await fetch(new URL(`maintenances/${id}`, this.base));
67+
if (!res.ok) {
68+
throw new Error(`Failed to fetch maintenance: ${res.status}`);
69+
}
70+
const data: { maintenance: Maintenance } = await res.json();
71+
return data.maintenance;
72+
}
3573
}

0 commit comments

Comments
 (0)