@checkstack/notification-frontend 0.3.0 → 0.4.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 CHANGED
@@ -1,5 +1,197 @@
1
1
  # @checkstack/notification-frontend
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3547670: Wire the new tips infrastructure across the frontends:
8
+
9
+ **Empty-state coaching.** Replace generic "no items" copy with onboarding
10
+ guidance — short description, three numbered steps and a primary CTA — on
11
+ every EmptyState that has a meaningful next action. Affects: catalog
12
+ (systems + groups), dashboard, health-check page, integrations (subscriptions
13
+
14
+ - provider connections), GitOps providers + secrets, GitOps provenance,
15
+ SLO config + overview, maintenance config, satellites, plugin manager,
16
+ incident config, announcements. Read-only EmptyStates (incident history,
17
+ maintenance history, plugin events) get clearer descriptions explaining
18
+ what would populate them.
19
+
20
+ **First-run anchored tips.** Add `<Tip>` popovers to the most important
21
+ "Create" affordances so first-time users see a one-line explanation of
22
+ what they're about to make and why it matters: catalog “Add System” /
23
+ “Add Group”, healthcheck “Create Check”, integrations “New Subscription”,
24
+ GitOps “Add Provider”, SLO “Create SLO”, maintenance “Create Maintenance”,
25
+ satellite “Create Satellite”, plugin-manager “Install plugin”, incident
26
+ “Report Incident”, announcement “New Announcement”. Each tip is dismissed
27
+ per user (server-backed when signed in, localStorage otherwise) and
28
+ namespaced through `qualifyTipId(plugin, …)` so it cannot escape the
29
+ plugin's own namespace.
30
+
31
+ **Welcome banner on the dashboard.** A `<TipBanner>` at the top of the
32
+ dashboard introduces Checkstack's main flow ("add a system, then a health
33
+ check") with a one-click jump into the catalog.
34
+
35
+ ### Patch Changes
36
+
37
+ - 3547670: Add a `<Tip>` next to the title in the notification-subscriptions modal
38
+ that explains the two things that confuse first-time users: (1) the
39
+ difference between subscribing to an individual system vs. subscribing
40
+ to a whole group (groups auto-include systems added later), and (2) the
41
+ difference between "Subscribe to all" and toggling specific rows for
42
+ just the event types they care about. Tip id:
43
+ `notification.subscriptions.intro`.
44
+ - 950d6ec: Fix mobile UserMenu items rendering at zero height, group menu items by
45
+ section, and unstack cramped card headers on small viewports.
46
+
47
+ - **UserMenu mobile bug**: On mobile, the user-menu Sheet rendered every
48
+ menu item as a grid row, which combined with `flex-shrink: 1` on each
49
+ item collapsed the buttons whose internal layout uses `display: flex`
50
+ (the items registered with `useNavigate` rather than `<Link>`) to zero
51
+ content height. Switched the mobile container to a flex column with
52
+ `[&>*]:shrink-0` and added `min-h-0` so the sheet scrolls correctly
53
+ when the list overflows.
54
+
55
+ - **UserMenu grouping**: Slot extensions now accept an optional `group`
56
+ field. The user menu buckets `UserMenuItemsSlot` extensions by `group`
57
+ and renders each group under a labeled header (`Workspace`,
58
+ `Reliability`, `Configuration`, `Documentation`, `Account`). Existing
59
+ core plugins are tagged with the appropriate group; third-party plugins
60
+ can pick any of these or supply their own label. Untagged extensions
61
+ render last with no header. `UserMenuItemsBottomSlot` is unaffected.
62
+
63
+ - **Card header responsiveness**: `CardHeaderRow` (the primitive shared by
64
+ Incident, Maintenance, Auth, Catalog, GitOps and other config cards) now
65
+ stacks vertically on narrow viewports and only switches to a single row
66
+ at the `sm` breakpoint, so titles and adjacent filter controls (e.g.
67
+ status `Select`, "Show resolved" checkbox) no longer cram together on
68
+ mobile. Refactored the Incident and Maintenance config pages to use the
69
+ primitive instead of a hand-rolled `flex items-center justify-between`
70
+ row, and made their `Select` triggers full-width on mobile.
71
+
72
+ - Updated dependencies [42abfff]
73
+ - Updated dependencies [3547670]
74
+ - Updated dependencies [1ef2e79]
75
+ - Updated dependencies [aa89bc5]
76
+ - Updated dependencies [3547670]
77
+ - Updated dependencies [950d6ec]
78
+ - Updated dependencies [3547670]
79
+ - @checkstack/common@0.9.0
80
+ - @checkstack/ui@1.8.0
81
+ - @checkstack/frontend-api@0.5.0
82
+ - @checkstack/tips-frontend@0.2.0
83
+ - @checkstack/auth-frontend@0.6.0
84
+ - @checkstack/notification-common@1.0.2
85
+ - @checkstack/signal-frontend@0.1.2
86
+
87
+ ## 0.3.1
88
+
89
+ ### Patch Changes
90
+
91
+ - 50e5f5f: Runtime plugin system: install + uninstall plugins from npm, GitHub releases
92
+ (including private GitHub Enterprise instances), or tarball uploads at
93
+ runtime, with multi-package bundles, dependency-derived compatibility checks,
94
+ multi-instance coordination via a Postgres artifact store, and
95
+ single-coordinator destructive cleanup.
96
+
97
+ Highlights:
98
+
99
+ - New `PluginSource` discriminated union and `PluginInstaller` /
100
+ `PluginInstallerRegistry` interfaces in `@checkstack/backend-api`. The
101
+ GitHub variant accepts an optional `apiBaseUrl` so deployments backed by
102
+ GitHub Enterprise can install from `https://ghe.example.com/api/v3`
103
+ instead of `api.github.com`.
104
+ - New `installPackageMetadataSchema` (Zod) in `@checkstack/common` validates
105
+ every plugin's `package.json` at install time. Required fields: `name`,
106
+ `version`, `description`, `author`, `license`, `checkstack.type`,
107
+ `checkstack.pluginId`. Optional: `checkstack.bundle`,
108
+ `checkstack.usageInstructions`, `checkstack.allowInstallScripts`.
109
+ - New `pluginManagerContract` in `@checkstack/pluginmanager-common` with
110
+ `list`, `previewInstall`, `install`, `previewUninstall`, `uninstall`, and
111
+ `events` procedures.
112
+ - New `@checkstack/pluginmanager-frontend` admin UI: installed-plugins list
113
+ with per-row uninstall (typed-confirmation modal, schema/configs/cascade
114
+ toggles), install page with NPM / Tarball Upload / GitHub Release tabs
115
+ (Catalog tab disabled — coming soon), and an events page surfacing the
116
+ install/uninstall audit log.
117
+ - New `bunx @checkstack/scripts plugin-pack` CLI for plugin authors —
118
+ per-package mode produces an npm-shaped tarball; `--bundle` mode produces
119
+ an outer tarball containing every sibling declared in
120
+ `package.json#checkstack.bundle`. Published to npm so external authors
121
+ can `bunx` it directly without a workspace checkout.
122
+ - Compatibility derived from `package.json#dependencies` ranges
123
+ (`semver.satisfies` against the platform's loaded `@checkstack/*`
124
+ versions) — no separate `compatibility` field.
125
+ - Multi-instance: originator persists artifacts + `plugins` rows + broadcasts
126
+ install/uninstall; receiving instances do in-process register/unregister
127
+ only. Destructive ops (drop schema, delete plugin_configs, delete
128
+ artifacts, delete `plugins` rows) run exactly once on the originator.
129
+ - Fresh-instance bootstrap: `loadPlugins()` hydrates any
130
+ `is_uninstallable=true` plugin missing from `node_modules` from the
131
+ artifact store before normal Phase 1 register.
132
+ - New schema: `plugin_artifacts` (tarball storage), `plugin_install_events`
133
+ (audit/error log). `plugins` extended with `version`, `metadata`,
134
+ `source`, `bundle_id`, `is_primary`. Local plugin sync now writes
135
+ `version` from each plugin's `package.json` so the admin UI shows real
136
+ versions instead of `—`.
137
+ - Tarball-upload endpoint (`POST /api/pluginmanager/upload-tarball`) for
138
+ the install UI; access-gated by `pluginmanager.plugin.manage`.
139
+ - Plugin Manager menu link added to the user menu (main grid, alongside
140
+ Profile / Notification Settings / etc.).
141
+
142
+ Cross-cutting changes:
143
+
144
+ - Backend request/response logging now flows through `rootLogger` (winston)
145
+ instead of `hono/logger`. 5xx responses include the response body inline
146
+ so swallowed early-return errors are visible in the log.
147
+ - The `/api/:pluginId/*` dispatcher now logs which core service is missing
148
+ or which `pluginId` had no metadata when it 500s.
149
+ - New `registerCorePluginMetadata` on `PluginManager` for core routers
150
+ (like the plugin manager itself) that need their metadata visible to the
151
+ RPC dispatcher without going through the full plugin lifecycle.
152
+ - ESLint: `unicorn/no-null` is now disabled globally. Drizzle distinguishes
153
+ between `null` (writes a real SQL NULL) and `undefined` (skip the column
154
+ on insert), so treating them as interchangeable produced latent bugs at
155
+ the persistence boundary. The bulk of the patch-bumped packages above
156
+ reflect lint-fix touches that landed when this rule was relaxed.
157
+ - Workspace-wide license normalization to `Elastic-2.0` (matches
158
+ `LICENSE.md`). Every `package.json` in the workspace now declares the
159
+ same SPDX identifier; the patch bumps capture this.
160
+
161
+ Plugin packages (every `plugins/*`): added a `pack` npm script
162
+ (`bunx @checkstack/scripts plugin-pack`), mirrored each plugin's
163
+ `pluginId` from `plugin-metadata.ts` into `package.json#checkstack.pluginId`
164
+ so install-time validation passes, stubbed any missing required metadata
165
+ fields (`description`, `author`, `license`), and added
166
+ `checkstack.bundle` to multi-package plugin primaries (telegram, rcon, ssh,
167
+ jira, queue-bullmq, queue-memory, cache-memory).
168
+
169
+ Breaking changes:
170
+
171
+ - The legacy single-method `PluginInstaller` interface (`install(packageName)`)
172
+ is removed. Callers must use `coreServices.pluginInstallerRegistry`.
173
+ - The old `pluginAdminContract` and `createPluginAdminRouter` are removed.
174
+ Replaced by `pluginManagerContract` in `@checkstack/pluginmanager-common`
175
+ and `createPluginManagerRouter` in `core/backend`.
176
+ - `@checkstack/test-utils-backend` no longer exports
177
+ `createMockPluginInstaller` / `MockPluginInstaller` (the legacy interface
178
+ it shimmed is gone).
179
+
180
+ Note: bumps are limited to `minor` (for packages with new public API
181
+ surface) and `patch` (for downstream consumers, license normalization,
182
+ and lint fixes). No `major` bumps despite the `PluginInstaller` removal —
183
+ the legacy interface had no third-party consumers in the wild before this
184
+ runtime plugin system landed, and the contract surface is the same shape
185
+ modulo the rename.
186
+
187
+ - Updated dependencies [50e5f5f]
188
+ - @checkstack/common@0.8.0
189
+ - @checkstack/signal-frontend@0.1.1
190
+ - @checkstack/ui@1.7.1
191
+ - @checkstack/auth-frontend@0.5.33
192
+ - @checkstack/frontend-api@0.4.2
193
+ - @checkstack/notification-common@1.0.1
194
+
3
195
  ## 0.3.0
4
196
 
5
197
  ### Minor Changes
package/package.json CHANGED
@@ -1,31 +1,33 @@
1
1
  {
2
2
  "name": "@checkstack/notification-frontend",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
+ "license": "Elastic-2.0",
4
5
  "type": "module",
5
6
  "main": "src/index.tsx",
6
7
  "checkstack": {
7
8
  "type": "frontend"
8
9
  },
9
10
  "scripts": {
10
- "typecheck": "tsc --noEmit",
11
+ "typecheck": "tsgo -b",
11
12
  "lint": "bun run lint:code",
12
13
  "lint:code": "eslint . --max-warnings 0"
13
14
  },
14
15
  "dependencies": {
15
- "@checkstack/notification-common": "0.3.0",
16
- "@checkstack/frontend-api": "0.4.0",
17
- "@checkstack/auth-frontend": "0.5.31",
18
- "@checkstack/signal-frontend": "0.1.0",
19
- "@checkstack/common": "0.7.0",
20
- "@checkstack/ui": "1.6.1",
16
+ "@checkstack/auth-frontend": "0.5.33",
17
+ "@checkstack/common": "0.8.0",
18
+ "@checkstack/frontend-api": "0.4.2",
19
+ "@checkstack/notification-common": "1.0.1",
20
+ "@checkstack/signal-frontend": "0.1.1",
21
+ "@checkstack/tips-frontend": "0.1.0",
22
+ "@checkstack/ui": "1.7.1",
23
+ "lucide-react": "^0.344.0",
21
24
  "react": "^18.2.0",
22
- "react-router-dom": "^6.22.0",
23
- "lucide-react": "^0.344.0"
25
+ "react-router-dom": "^6.22.0"
24
26
  },
25
27
  "devDependencies": {
26
28
  "typescript": "^5.0.0",
27
29
  "@types/react": "^18.2.0",
28
- "@checkstack/tsconfig": "0.0.5",
29
- "@checkstack/scripts": "0.1.2"
30
+ "@checkstack/tsconfig": "0.0.7",
31
+ "@checkstack/scripts": "0.3.0"
30
32
  }
31
33
  }
@@ -15,9 +15,11 @@ import { usePluginClient } from "@checkstack/frontend-api";
15
15
  import {
16
16
  NotificationApi,
17
17
  subscriptionGroupId,
18
+ pluginMetadata as notificationPluginMetadata,
18
19
  type NotificationTarget,
19
20
  } from "@checkstack/notification-common";
20
21
  import { extractErrorMessage } from "@checkstack/common";
22
+ import { Tip } from "@checkstack/tips-frontend";
21
23
  import { SubscriptionRow } from "./SubscriptionRow";
22
24
 
23
25
  export interface NotificationSubscriptionsManagerProps<TResource> {
@@ -111,14 +113,10 @@ export function NotificationSubscriptionsManager<TResource>({
111
113
  .filter((id) => statusMap[id])
112
114
  .map((groupId) => unsubscribeMutation.mutateAsync({ groupId })),
113
115
  );
114
- toast.success(
115
- `Unsubscribed from all notifications for ${resourceLabel}`,
116
- );
116
+ toast.success(`Unsubscribed from all notifications for ${resourceLabel}`);
117
117
  void refetchStatus();
118
118
  } catch (error) {
119
- toast.error(
120
- extractErrorMessage(error, "Failed to unsubscribe from all"),
121
- );
119
+ toast.error(extractErrorMessage(error, "Failed to unsubscribe from all"));
122
120
  }
123
121
  };
124
122
 
@@ -148,25 +146,58 @@ export function NotificationSubscriptionsManager<TResource>({
148
146
  <DialogContent size="lg">
149
147
  <DialogHeader>
150
148
  <DialogTitle className="flex items-center gap-2">
151
- <Bell className="h-4 w-4" />
149
+ <Bell className="w-4 h-4" />
152
150
  Notifications for {resourceLabel}
151
+ <Tip
152
+ plugin={notificationPluginMetadata}
153
+ id="subscriptions.intro"
154
+ title="How notification subscriptions work"
155
+ description={
156
+ <div className="space-y-2">
157
+ <p>
158
+ <strong>System vs. group.</strong> Subscribing to a system
159
+ means you'll only hear about that one thing. Subscribing
160
+ to a group means you'll hear about every system inside
161
+ that group — useful if a teammate adds new systems later,
162
+ since the group covers them automatically.
163
+ </p>
164
+ <p>
165
+ <strong>All vs. specific types.</strong> Each row below is
166
+ a different kind of event (incidents, anomalies, …). Use
167
+ “Subscribe to all” at the top to opt in to everything, or
168
+ toggle rows individually if you only care about, say,
169
+ critical incidents.
170
+ </p>
171
+ <p>
172
+ Where things actually reach you (Slack, email, Telegram,
173
+ …) is configured once in <em>Notification Settings</em>{" "}
174
+ and applies to every subscription.
175
+ </p>
176
+ </div>
177
+ }
178
+ side="bottom"
179
+ align="start"
180
+ contentClassName="w-96"
181
+ >
182
+ <span className="sr-only">Subscription help</span>
183
+ </Tip>
153
184
  </DialogTitle>
154
185
  <DialogDescription>
155
- Choose which notification types you want to receive for
156
- this {target.resourceKind}.
186
+ Choose which notification types you want to receive for this{" "}
187
+ {target.resourceKind}.
157
188
  </DialogDescription>
158
189
  </DialogHeader>
159
190
 
160
191
  <div className="space-y-3">
161
192
  {totalCount === 0 ? (
162
- <div className="rounded-md border border-border bg-muted/20 p-4 text-sm text-muted-foreground">
193
+ <div className="p-4 text-sm border rounded-md border-border bg-muted/20 text-muted-foreground">
163
194
  No notification types are available for this{" "}
164
195
  {target.resourceKind} yet.
165
196
  </div>
166
197
  ) : (
167
198
  <>
168
- <div className="flex items-center justify-between gap-3 rounded-md border border-border bg-muted/20 p-3">
169
- <div className="text-sm min-w-0">
199
+ <div className="flex items-center justify-between gap-3 p-3 border rounded-md border-border bg-muted/20">
200
+ <div className="min-w-0 text-sm">
170
201
  <div className="font-medium">
171
202
  {subscribedCount} of {totalCount} subscribed
172
203
  </div>
@@ -183,7 +214,7 @@ export function NotificationSubscriptionsManager<TResource>({
183
214
  disabled={isPending}
184
215
  onClick={handleUnsubscribeAll}
185
216
  >
186
- <BellOff className="mr-1 h-3 w-3" />
217
+ <BellOff className="w-3 h-3 mr-1" />
187
218
  Unsubscribe from all
188
219
  </Button>
189
220
  ) : (
@@ -194,13 +225,13 @@ export function NotificationSubscriptionsManager<TResource>({
194
225
  disabled={isPending}
195
226
  onClick={handleSubscribeAll}
196
227
  >
197
- <BellRing className="mr-1 h-3 w-3" />
228
+ <BellRing className="w-3 h-3 mr-1" />
198
229
  Subscribe to all
199
230
  </Button>
200
231
  )}
201
232
  </div>
202
233
  </div>
203
- <div className="overflow-hidden rounded-md border border-border">
234
+ <div className="overflow-hidden border rounded-md border-border">
204
235
  {specs.map((spec) => {
205
236
  const groupId = subscriptionGroupId(spec, resourceKey);
206
237
  return (
package/src/index.tsx CHANGED
@@ -57,6 +57,7 @@ export const notificationPlugin = createFrontendPlugin({
57
57
  createSlotExtension(UserMenuItemsSlot, {
58
58
  id: "notification.user.setting",
59
59
  component: NotificationUserMenuItems,
60
+ metadata: { group: "Configuration" },
60
61
  }),
61
62
  ],
62
63
  });
@@ -17,7 +17,9 @@ import type { EnrichedSubscription } from "@checkstack/notification-common";
17
17
  import {
18
18
  NotificationApi,
19
19
  notificationAccess,
20
+ pluginMetadata as notificationPluginMetadata,
20
21
  } from "@checkstack/notification-common";
22
+ import { TipBanner } from "@checkstack/tips-frontend";
21
23
  import {
22
24
  StrategyCard,
23
25
  type DeliveryStrategy,
@@ -246,6 +248,13 @@ export const NotificationSettingsPage = () => {
246
248
  return (
247
249
  <PageLayout title="Notification Settings" icon={Bell} loading={subsLoading}>
248
250
  <div className="space-y-8">
251
+ <TipBanner
252
+ plugin={notificationPluginMetadata}
253
+ id="settings.intro"
254
+ title="Two pieces fit together: channels and subscriptions"
255
+ description="A channel is how you'd like to be reached (Slack DM, email, Telegram, etc.). A subscription is what you want to be reached about (a system group, a specific incident severity). Connect at least one channel here, then subscribe from the dashboard or the system detail pages."
256
+ />
257
+
249
258
  {/* Your Notification Channels - All users */}
250
259
  <section>
251
260
  <SectionHeader
package/tsconfig.json CHANGED
@@ -2,5 +2,28 @@
2
2
  "extends": "@checkstack/tsconfig/frontend.json",
3
3
  "include": [
4
4
  "src"
5
+ ],
6
+ "references": [
7
+ {
8
+ "path": "../auth-frontend"
9
+ },
10
+ {
11
+ "path": "../common"
12
+ },
13
+ {
14
+ "path": "../frontend-api"
15
+ },
16
+ {
17
+ "path": "../notification-common"
18
+ },
19
+ {
20
+ "path": "../signal-frontend"
21
+ },
22
+ {
23
+ "path": "../tips-frontend"
24
+ },
25
+ {
26
+ "path": "../ui"
27
+ }
5
28
  ]
6
- }
29
+ }