@checkstack/about-frontend 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/package.json +29 -0
- package/src/AboutMenuItem.tsx +18 -0
- package/src/AboutPage.tsx +289 -0
- package/src/index.tsx +31 -0
- package/tsconfig.json +6 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @checkstack/about-frontend
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3589199: Add About page with platform information, license, contact details, and version information
|
|
8
|
+
|
|
9
|
+
- New `about-common` package with plugin metadata
|
|
10
|
+
- New `about-frontend` package with the About page and user menu item
|
|
11
|
+
- New `/api/about` backend endpoint exposing core version and loaded plugin versions
|
|
12
|
+
- Accessible via "About Checkstack" in the user menu dropdown
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [3589199]
|
|
17
|
+
- @checkstack/about-common@0.2.0
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@checkstack/about-frontend",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "src/index.tsx",
|
|
6
|
+
"checkstack": {
|
|
7
|
+
"type": "frontend"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"lint": "bun run lint:code",
|
|
12
|
+
"lint:code": "eslint . --max-warnings 0"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@checkstack/about-common": "0.1.0",
|
|
16
|
+
"@checkstack/frontend-api": "0.3.8",
|
|
17
|
+
"@checkstack/common": "0.6.4",
|
|
18
|
+
"@checkstack/ui": "1.2.0",
|
|
19
|
+
"react": "^18.2.0",
|
|
20
|
+
"react-router-dom": "^6.22.0",
|
|
21
|
+
"lucide-react": "^0.344.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.0.0",
|
|
25
|
+
"@types/react": "^18.2.0",
|
|
26
|
+
"@checkstack/tsconfig": "0.0.4",
|
|
27
|
+
"@checkstack/scripts": "0.1.2"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useNavigate } from "react-router-dom";
|
|
2
|
+
import { Info } from "lucide-react";
|
|
3
|
+
import { DropdownMenuItem } from "@checkstack/ui";
|
|
4
|
+
import { resolveRoute } from "@checkstack/common";
|
|
5
|
+
import { aboutRoutes } from "./index";
|
|
6
|
+
|
|
7
|
+
export function AboutMenuItem() {
|
|
8
|
+
const navigate = useNavigate();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<DropdownMenuItem
|
|
12
|
+
onClick={() => navigate(resolveRoute(aboutRoutes.routes.page))}
|
|
13
|
+
icon={<Info className="h-4 w-4" />}
|
|
14
|
+
>
|
|
15
|
+
About Checkstack
|
|
16
|
+
</DropdownMenuItem>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { Info, Github, Mail, Scale, ExternalLink, Package } from "lucide-react";
|
|
3
|
+
import {
|
|
4
|
+
PageLayout,
|
|
5
|
+
Card,
|
|
6
|
+
CardHeader,
|
|
7
|
+
CardTitle,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardContent,
|
|
10
|
+
Badge,
|
|
11
|
+
LoadingSpinner,
|
|
12
|
+
} from "@checkstack/ui";
|
|
13
|
+
|
|
14
|
+
interface PluginInfo {
|
|
15
|
+
name: string;
|
|
16
|
+
version: string;
|
|
17
|
+
type: "backend" | "frontend" | "common";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface AboutInfo {
|
|
21
|
+
coreVersion: string;
|
|
22
|
+
plugins: PluginInfo[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Formats a raw package name for display.
|
|
27
|
+
* Strips `@checkstack/` prefix and converts to title case.
|
|
28
|
+
* e.g., "@checkstack/healthcheck-http-backend" → "Healthcheck HTTP"
|
|
29
|
+
*/
|
|
30
|
+
function formatPluginName(name: string): string {
|
|
31
|
+
const stripped = name.replace("@checkstack/", "");
|
|
32
|
+
// Remove type suffixes (-backend, -frontend, -common)
|
|
33
|
+
const withoutSuffix = stripped
|
|
34
|
+
.replace(/-backend$/, "")
|
|
35
|
+
.replace(/-frontend$/, "")
|
|
36
|
+
.replace(/-common$/, "");
|
|
37
|
+
// Title case with dashes as spaces
|
|
38
|
+
return withoutSuffix
|
|
39
|
+
.split("-")
|
|
40
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
41
|
+
.join(" ");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns a badge variant for a plugin type.
|
|
46
|
+
*/
|
|
47
|
+
function typeBadgeVariant(type: string): "default" | "secondary" | "info" {
|
|
48
|
+
switch (type) {
|
|
49
|
+
case "backend": {
|
|
50
|
+
return "default";
|
|
51
|
+
}
|
|
52
|
+
case "frontend": {
|
|
53
|
+
return "info";
|
|
54
|
+
}
|
|
55
|
+
default: {
|
|
56
|
+
return "secondary";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function AboutPage() {
|
|
62
|
+
const [aboutInfo, setAboutInfo] = useState<AboutInfo | undefined>();
|
|
63
|
+
const [loading, setLoading] = useState(true);
|
|
64
|
+
const [error, setError] = useState<string | undefined>();
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const fetchAboutInfo = async () => {
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch("/api/about");
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`Failed to fetch about info: ${response.status}`);
|
|
72
|
+
}
|
|
73
|
+
const data = (await response.json()) as AboutInfo;
|
|
74
|
+
setAboutInfo(data);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
setError(
|
|
77
|
+
error instanceof Error ? error.message : "Failed to load about info",
|
|
78
|
+
);
|
|
79
|
+
} finally {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
void fetchAboutInfo();
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<PageLayout
|
|
89
|
+
title="About Checkstack"
|
|
90
|
+
icon={Info}
|
|
91
|
+
subtitle="Platform information, license, and version details"
|
|
92
|
+
>
|
|
93
|
+
{/* Hero Section */}
|
|
94
|
+
<Card>
|
|
95
|
+
<CardHeader>
|
|
96
|
+
<div className="flex items-start gap-4">
|
|
97
|
+
<div className="rounded-xl bg-primary/10 p-3">
|
|
98
|
+
<Package className="h-8 w-8 text-primary" />
|
|
99
|
+
</div>
|
|
100
|
+
<div className="space-y-1">
|
|
101
|
+
<CardTitle className="text-xl">Checkstack</CardTitle>
|
|
102
|
+
<CardDescription className="text-base">
|
|
103
|
+
The Modern Status Page & Monitoring Platform
|
|
104
|
+
</CardDescription>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</CardHeader>
|
|
108
|
+
<CardContent>
|
|
109
|
+
<p className="text-sm text-muted-foreground leading-relaxed">
|
|
110
|
+
Monitor your systems, keep users informed, and maintain trust.
|
|
111
|
+
Checkstack combines the power of a status page, uptime monitoring,
|
|
112
|
+
and incident management into a single, extensible platform.
|
|
113
|
+
</p>
|
|
114
|
+
<div className="flex flex-wrap gap-3 mt-4">
|
|
115
|
+
<a
|
|
116
|
+
href="https://github.com/enyineer/checkstack"
|
|
117
|
+
target="_blank"
|
|
118
|
+
rel="noopener noreferrer"
|
|
119
|
+
className="inline-flex items-center gap-2 rounded-lg border border-border bg-secondary/50 px-4 py-2 text-sm font-medium text-foreground hover:bg-secondary transition-colors"
|
|
120
|
+
>
|
|
121
|
+
<Github className="h-4 w-4" />
|
|
122
|
+
GitHub Repository
|
|
123
|
+
<ExternalLink className="h-3 w-3 text-muted-foreground" />
|
|
124
|
+
</a>
|
|
125
|
+
<a
|
|
126
|
+
href="mailto:hi@enking.dev"
|
|
127
|
+
className="inline-flex items-center gap-2 rounded-lg border border-border bg-secondary/50 px-4 py-2 text-sm font-medium text-foreground hover:bg-secondary transition-colors"
|
|
128
|
+
>
|
|
129
|
+
<Mail className="h-4 w-4" />
|
|
130
|
+
hi@enking.dev
|
|
131
|
+
</a>
|
|
132
|
+
</div>
|
|
133
|
+
</CardContent>
|
|
134
|
+
</Card>
|
|
135
|
+
|
|
136
|
+
{/* License Section */}
|
|
137
|
+
<Card>
|
|
138
|
+
<CardHeader>
|
|
139
|
+
<div className="flex items-center gap-3">
|
|
140
|
+
<Scale className="h-5 w-5 text-muted-foreground" />
|
|
141
|
+
<CardTitle className="text-lg">License</CardTitle>
|
|
142
|
+
</div>
|
|
143
|
+
<CardDescription>
|
|
144
|
+
Checkstack is licensed under the{" "}
|
|
145
|
+
<strong className="text-foreground">
|
|
146
|
+
Elastic License 2.0 (ELv2)
|
|
147
|
+
</strong>
|
|
148
|
+
</CardDescription>
|
|
149
|
+
</CardHeader>
|
|
150
|
+
<CardContent>
|
|
151
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
152
|
+
<div className="rounded-lg bg-success/5 border border-success/20 p-4">
|
|
153
|
+
<h4 className="text-sm font-semibold text-success mb-2">
|
|
154
|
+
✅ Allowed
|
|
155
|
+
</h4>
|
|
156
|
+
<ul className="space-y-1 text-sm text-muted-foreground">
|
|
157
|
+
<li>Internal company use</li>
|
|
158
|
+
<li>Personal projects</li>
|
|
159
|
+
<li>Research & education</li>
|
|
160
|
+
<li>Modification & redistribution</li>
|
|
161
|
+
<li>Building applications on top</li>
|
|
162
|
+
</ul>
|
|
163
|
+
</div>
|
|
164
|
+
<div className="rounded-lg bg-destructive/5 border border-destructive/20 p-4">
|
|
165
|
+
<h4 className="text-sm font-semibold text-destructive mb-2">
|
|
166
|
+
❌ Not Allowed
|
|
167
|
+
</h4>
|
|
168
|
+
<ul className="space-y-1 text-sm text-muted-foreground">
|
|
169
|
+
<li>Selling as a managed SaaS</li>
|
|
170
|
+
<li>Removing license protections</li>
|
|
171
|
+
</ul>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<p className="text-xs text-muted-foreground mt-4">
|
|
175
|
+
Need a commercial license to provide Checkstack as a managed / SaaS
|
|
176
|
+
service?{" "}
|
|
177
|
+
<a
|
|
178
|
+
href="mailto:hi@enking.dev"
|
|
179
|
+
className="text-primary hover:underline"
|
|
180
|
+
>
|
|
181
|
+
Contact us
|
|
182
|
+
</a>
|
|
183
|
+
</p>
|
|
184
|
+
</CardContent>
|
|
185
|
+
</Card>
|
|
186
|
+
|
|
187
|
+
{/* Version Information Section */}
|
|
188
|
+
<Card>
|
|
189
|
+
<CardHeader>
|
|
190
|
+
<div className="flex items-center gap-3">
|
|
191
|
+
<Info className="h-5 w-5 text-muted-foreground" />
|
|
192
|
+
<CardTitle className="text-lg">Version Information</CardTitle>
|
|
193
|
+
</div>
|
|
194
|
+
</CardHeader>
|
|
195
|
+
<CardContent>
|
|
196
|
+
{loading && (
|
|
197
|
+
<div className="flex justify-center py-6">
|
|
198
|
+
<LoadingSpinner />
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
|
|
202
|
+
{error && (
|
|
203
|
+
<div className="rounded-lg bg-destructive/10 border border-destructive/20 p-4 text-sm text-destructive">
|
|
204
|
+
{error}
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
|
|
208
|
+
{aboutInfo && (
|
|
209
|
+
<div className="space-y-6">
|
|
210
|
+
{/* Core Version */}
|
|
211
|
+
<div className="flex items-center justify-between rounded-lg bg-secondary/50 border border-border p-4">
|
|
212
|
+
<div>
|
|
213
|
+
<p className="text-sm font-medium text-foreground">
|
|
214
|
+
Checkstack Core
|
|
215
|
+
</p>
|
|
216
|
+
<p className="text-xs text-muted-foreground">
|
|
217
|
+
Backend platform engine
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
<Badge variant="default" className="font-mono text-xs">
|
|
221
|
+
v{aboutInfo.coreVersion}
|
|
222
|
+
</Badge>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
{/* Plugin Versions Table */}
|
|
226
|
+
{aboutInfo.plugins.length > 0 && (
|
|
227
|
+
<div>
|
|
228
|
+
<h4 className="text-sm font-medium text-foreground mb-3">
|
|
229
|
+
Loaded Plugins ({aboutInfo.plugins.length})
|
|
230
|
+
</h4>
|
|
231
|
+
<div className="rounded-lg border border-border overflow-hidden">
|
|
232
|
+
<table className="w-full text-sm">
|
|
233
|
+
<thead>
|
|
234
|
+
<tr className="border-b border-border bg-muted/50">
|
|
235
|
+
<th className="text-left py-2.5 px-4 font-medium text-muted-foreground">
|
|
236
|
+
Plugin
|
|
237
|
+
</th>
|
|
238
|
+
<th className="text-left py-2.5 px-4 font-medium text-muted-foreground">
|
|
239
|
+
Type
|
|
240
|
+
</th>
|
|
241
|
+
<th className="text-right py-2.5 px-4 font-medium text-muted-foreground">
|
|
242
|
+
Version
|
|
243
|
+
</th>
|
|
244
|
+
</tr>
|
|
245
|
+
</thead>
|
|
246
|
+
<tbody>
|
|
247
|
+
{aboutInfo.plugins.map((plugin) => (
|
|
248
|
+
<tr
|
|
249
|
+
key={plugin.name}
|
|
250
|
+
className="border-b border-border last:border-0 hover:bg-muted/30 transition-colors"
|
|
251
|
+
>
|
|
252
|
+
<td className="py-2.5 px-4">
|
|
253
|
+
<span className="font-medium text-foreground">
|
|
254
|
+
{formatPluginName(plugin.name)}
|
|
255
|
+
</span>
|
|
256
|
+
<span className="block text-xs text-muted-foreground font-mono">
|
|
257
|
+
{plugin.name}
|
|
258
|
+
</span>
|
|
259
|
+
</td>
|
|
260
|
+
<td className="py-2.5 px-4">
|
|
261
|
+
<Badge
|
|
262
|
+
variant={typeBadgeVariant(plugin.type)}
|
|
263
|
+
className="text-[10px]"
|
|
264
|
+
>
|
|
265
|
+
{plugin.type}
|
|
266
|
+
</Badge>
|
|
267
|
+
</td>
|
|
268
|
+
<td className="py-2.5 px-4 text-right font-mono text-muted-foreground">
|
|
269
|
+
{plugin.version}
|
|
270
|
+
</td>
|
|
271
|
+
</tr>
|
|
272
|
+
))}
|
|
273
|
+
</tbody>
|
|
274
|
+
</table>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
)}
|
|
280
|
+
</CardContent>
|
|
281
|
+
</Card>
|
|
282
|
+
|
|
283
|
+
{/* Footer Attribution */}
|
|
284
|
+
<p className="text-center text-xs text-muted-foreground py-4">
|
|
285
|
+
Built with ❤️ for reliability engineers everywhere
|
|
286
|
+
</p>
|
|
287
|
+
</PageLayout>
|
|
288
|
+
);
|
|
289
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFrontendPlugin,
|
|
3
|
+
UserMenuItemsBottomSlot,
|
|
4
|
+
} from "@checkstack/frontend-api";
|
|
5
|
+
import { createRoutes } from "@checkstack/common";
|
|
6
|
+
import { pluginMetadata } from "@checkstack/about-common";
|
|
7
|
+
import { AboutPage } from "./AboutPage";
|
|
8
|
+
import { AboutMenuItem } from "./AboutMenuItem";
|
|
9
|
+
|
|
10
|
+
export const aboutRoutes = createRoutes(pluginMetadata.pluginId, {
|
|
11
|
+
page: "/",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const aboutPlugin = createFrontendPlugin({
|
|
15
|
+
metadata: pluginMetadata,
|
|
16
|
+
routes: [
|
|
17
|
+
{
|
|
18
|
+
route: aboutRoutes.routes.page,
|
|
19
|
+
element: <AboutPage />,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
extensions: [
|
|
23
|
+
{
|
|
24
|
+
id: "about.user-menu.link",
|
|
25
|
+
slot: UserMenuItemsBottomSlot,
|
|
26
|
+
component: AboutMenuItem,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default aboutPlugin;
|