@checkstack/catalog-frontend 0.3.7 → 0.3.9

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,41 @@
1
1
  # @checkstack/catalog-frontend
2
2
 
3
+ ## 0.3.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 223081d: Add icon support to PageLayout and improve mobile responsiveness
8
+
9
+ **PageLayout Icons:**
10
+
11
+ - Added required `icon` prop to `PageLayout` and `PageHeader` components that accepts a Lucide icon component reference
12
+ - Icons are rendered with consistent `h-6 w-6 text-primary` styling
13
+ - Updated all page components to include appropriate icons in their headers
14
+
15
+ **Mobile Layout Improvements:**
16
+
17
+ - Standardized responsive padding in main app shell (`p-3` on mobile, `p-6` on desktop)
18
+ - Added `CardHeaderRow` component for mobile-safe card headers with proper wrapping
19
+ - Improved `DateRangeFilter` responsive behavior with vertical stacking on mobile
20
+ - Migrated pages to use `PageLayout` for consistent responsive behavior
21
+
22
+ - Updated dependencies [223081d]
23
+ - @checkstack/ui@0.5.0
24
+ - @checkstack/auth-frontend@0.5.5
25
+
26
+ ## 0.3.8
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [db1f56f]
31
+ - Updated dependencies [538e45d]
32
+ - @checkstack/common@0.6.0
33
+ - @checkstack/ui@0.4.1
34
+ - @checkstack/auth-frontend@0.5.4
35
+ - @checkstack/catalog-common@1.2.4
36
+ - @checkstack/frontend-api@0.3.3
37
+ - @checkstack/notification-common@0.2.3
38
+
3
39
  ## 0.3.7
4
40
 
5
41
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/catalog-frontend",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "scripts": {
@@ -12,21 +12,20 @@ import {
12
12
  catalogAccess,
13
13
  } from "@checkstack/catalog-common";
14
14
  import {
15
- SectionHeader,
15
+ PageLayout,
16
16
  Card,
17
17
  CardHeader,
18
+ CardHeaderRow,
18
19
  CardTitle,
19
20
  CardContent,
20
21
  Button,
21
22
  Label,
22
- LoadingSpinner,
23
23
  EmptyState,
24
- AccessDenied,
25
24
  EditableText,
26
25
  ConfirmationModal,
27
26
  useToast,
28
27
  } from "@checkstack/ui";
29
- import { Plus, Trash2, LayoutGrid, Server, Settings, Edit } from "lucide-react";
28
+ import { Plus, Trash2, LayoutGrid, Server, Edit } from "lucide-react";
30
29
  import { SystemEditor } from "./SystemEditor";
31
30
  import { GroupEditor } from "./GroupEditor";
32
31
 
@@ -36,7 +35,7 @@ export const CatalogConfigPage = () => {
36
35
  const toast = useToast();
37
36
  const [searchParams, setSearchParams] = useSearchParams();
38
37
  const { allowed: canManage, loading: accessLoading } = accessApi.useAccess(
39
- catalogAccess.system.manage
38
+ catalogAccess.system.manage,
40
39
  );
41
40
 
42
41
  const [selectedGroupId, setSelectedGroupId] = useState("");
@@ -104,7 +103,7 @@ export const CatalogConfigPage = () => {
104
103
  },
105
104
  onError: (error) => {
106
105
  toast.error(
107
- error instanceof Error ? error.message : "Failed to create system"
106
+ error instanceof Error ? error.message : "Failed to create system",
108
107
  );
109
108
  },
110
109
  });
@@ -118,7 +117,7 @@ export const CatalogConfigPage = () => {
118
117
  },
119
118
  onError: (error) => {
120
119
  toast.error(
121
- error instanceof Error ? error.message : "Failed to update system"
120
+ error instanceof Error ? error.message : "Failed to update system",
122
121
  );
123
122
  },
124
123
  });
@@ -131,7 +130,7 @@ export const CatalogConfigPage = () => {
131
130
  },
132
131
  onError: (error) => {
133
132
  toast.error(
134
- error instanceof Error ? error.message : "Failed to delete system"
133
+ error instanceof Error ? error.message : "Failed to delete system",
135
134
  );
136
135
  },
137
136
  });
@@ -144,7 +143,7 @@ export const CatalogConfigPage = () => {
144
143
  },
145
144
  onError: (error) => {
146
145
  toast.error(
147
- error instanceof Error ? error.message : "Failed to create group"
146
+ error instanceof Error ? error.message : "Failed to create group",
148
147
  );
149
148
  },
150
149
  });
@@ -157,7 +156,7 @@ export const CatalogConfigPage = () => {
157
156
  },
158
157
  onError: (error) => {
159
158
  toast.error(
160
- error instanceof Error ? error.message : "Failed to delete group"
159
+ error instanceof Error ? error.message : "Failed to delete group",
161
160
  );
162
161
  },
163
162
  });
@@ -169,7 +168,7 @@ export const CatalogConfigPage = () => {
169
168
  },
170
169
  onError: (error) => {
171
170
  toast.error(
172
- error instanceof Error ? error.message : "Failed to update group name"
171
+ error instanceof Error ? error.message : "Failed to update group name",
173
172
  );
174
173
  throw error;
175
174
  },
@@ -183,7 +182,9 @@ export const CatalogConfigPage = () => {
183
182
  },
184
183
  onError: (error) => {
185
184
  toast.error(
186
- error instanceof Error ? error.message : "Failed to add system to group"
185
+ error instanceof Error
186
+ ? error.message
187
+ : "Failed to add system to group",
187
188
  );
188
189
  },
189
190
  });
@@ -198,7 +199,7 @@ export const CatalogConfigPage = () => {
198
199
  toast.error(
199
200
  error instanceof Error
200
201
  ? error.message
201
- : "Failed to remove system from group"
202
+ : "Failed to remove system from group",
202
203
  );
203
204
  },
204
205
  });
@@ -259,37 +260,33 @@ export const CatalogConfigPage = () => {
259
260
  updateGroupMutation.mutate({ id, data: { name: newName } });
260
261
  };
261
262
 
262
- if (loading || accessLoading) return <LoadingSpinner />;
263
-
264
- if (!canManage) {
265
- return <AccessDenied />;
266
- }
267
-
268
263
  const selectedGroup = groups.find((g) => g.id === selectedGroupId);
269
264
  const availableSystems = systems.filter(
270
- (s) => !selectedGroup?.systemIds?.includes(s.id)
265
+ (s) => !selectedGroup?.systemIds?.includes(s.id),
271
266
  );
272
267
 
273
268
  return (
274
- <div className="space-y-8">
275
- <SectionHeader
276
- title="Catalog Management"
277
- description="Manage systems and logical groups within your infrastructure"
278
- icon={<Settings className="w-6 h-6 text-primary" />}
279
- />
280
-
269
+ <PageLayout
270
+ title="Catalog Management"
271
+ subtitle="Manage systems and logical groups within your infrastructure"
272
+ icon={Server}
273
+ loading={loading || accessLoading}
274
+ allowed={canManage}
275
+ >
281
276
  <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
282
277
  {/* Systems Management */}
283
278
  <Card>
284
- <CardHeader className="flex flex-row items-center justify-between">
285
- <CardTitle className="flex items-center gap-2">
286
- <Server className="w-5 h-5 text-muted-foreground" />
287
- Systems
288
- </CardTitle>
289
- <Button size="sm" onClick={() => setIsSystemEditorOpen(true)}>
290
- <Plus className="w-4 h-4 mr-2" />
291
- Add System
292
- </Button>
279
+ <CardHeader>
280
+ <CardHeaderRow>
281
+ <CardTitle className="flex items-center gap-2">
282
+ <Server className="w-5 h-5 text-muted-foreground" />
283
+ Systems
284
+ </CardTitle>
285
+ <Button size="sm" onClick={() => setIsSystemEditorOpen(true)}>
286
+ <Plus className="w-4 h-4 mr-2" />
287
+ Add System
288
+ </Button>
289
+ </CardHeaderRow>
293
290
  </CardHeader>
294
291
  <CardContent className="space-y-4">
295
292
  {systems.length === 0 ? (
@@ -347,15 +344,17 @@ export const CatalogConfigPage = () => {
347
344
 
348
345
  {/* Groups Management */}
349
346
  <Card>
350
- <CardHeader className="flex flex-row items-center justify-between">
351
- <CardTitle className="flex items-center gap-2">
352
- <LayoutGrid className="w-5 h-5 text-muted-foreground" />
353
- Groups
354
- </CardTitle>
355
- <Button size="sm" onClick={() => setIsGroupEditorOpen(true)}>
356
- <Plus className="w-4 h-4 mr-2" />
357
- Add Group
358
- </Button>
347
+ <CardHeader>
348
+ <CardHeaderRow>
349
+ <CardTitle className="flex items-center gap-2">
350
+ <LayoutGrid className="w-5 h-5 text-muted-foreground" />
351
+ Groups
352
+ </CardTitle>
353
+ <Button size="sm" onClick={() => setIsGroupEditorOpen(true)}>
354
+ <Plus className="w-4 h-4 mr-2" />
355
+ Add Group
356
+ </Button>
357
+ </CardHeaderRow>
359
358
  </CardHeader>
360
359
  <CardContent className="space-y-4">
361
360
  {groups.length === 0 ? (
@@ -516,6 +515,6 @@ export const CatalogConfigPage = () => {
516
515
  confirmText="Delete"
517
516
  variant="danger"
518
517
  />
519
- </div>
518
+ </PageLayout>
520
519
  );
521
520
  };
@@ -1,5 +1,7 @@
1
1
  import React from "react";
2
2
  import { useApi, loggerApiRef } from "@checkstack/frontend-api";
3
+ import { PageLayout } from "@checkstack/ui";
4
+ import { Layers } from "lucide-react";
3
5
 
4
6
  export const CatalogPage = () => {
5
7
  const logger = useApi(loggerApiRef);
@@ -9,9 +11,8 @@ export const CatalogPage = () => {
9
11
  }, [logger]);
10
12
 
11
13
  return (
12
- <div className="p-4 rounded-lg bg-white shadow">
13
- <h2 className="text-2xl font-semibold mb-4">Catalog</h2>
14
+ <PageLayout title="Catalog" icon={Layers}>
14
15
  <p className="text-muted-foreground">Welcome to the Service Catalog.</p>
15
- </div>
16
+ </PageLayout>
16
17
  );
17
18
  };
@@ -17,7 +17,7 @@ import {
17
17
  CardHeader,
18
18
  CardTitle,
19
19
  CardContent,
20
- LoadingSpinner,
20
+ PageLayout,
21
21
  SubscribeButton,
22
22
  useToast,
23
23
  BackLink,
@@ -74,7 +74,7 @@ export const SystemDetailPage: React.FC = () => {
74
74
  },
75
75
  onError: (error) => {
76
76
  toast.error(
77
- error instanceof Error ? error.message : "Failed to subscribe"
77
+ error instanceof Error ? error.message : "Failed to subscribe",
78
78
  );
79
79
  },
80
80
  });
@@ -87,7 +87,7 @@ export const SystemDetailPage: React.FC = () => {
87
87
  },
88
88
  onError: (error) => {
89
89
  toast.error(
90
- error instanceof Error ? error.message : "Failed to unsubscribe"
90
+ error instanceof Error ? error.message : "Failed to unsubscribe",
91
91
  );
92
92
  },
93
93
  });
@@ -103,7 +103,7 @@ export const SystemDetailPage: React.FC = () => {
103
103
  useEffect(() => {
104
104
  if (groupsData && systemId) {
105
105
  const systemGroups = groupsData.filter((group) =>
106
- group.systemIds?.includes(systemId)
106
+ group.systemIds?.includes(systemId),
107
107
  );
108
108
  setGroups(systemGroups);
109
109
  }
@@ -129,26 +129,32 @@ export const SystemDetailPage: React.FC = () => {
129
129
  unsubscribeMutation.mutate({ groupId: getSystemGroupId() });
130
130
  };
131
131
 
132
- if (loading) {
133
- return (
134
- <div className="flex items-center justify-center min-h-[400px]">
135
- <LoadingSpinner />
136
- </div>
137
- );
138
- }
132
+ // Actions for the page header
133
+ const headerActions = (
134
+ <div className="flex items-center gap-4 flex-wrap">
135
+ {session && (
136
+ <SubscribeButton
137
+ isSubscribed={isSubscribed}
138
+ onSubscribe={handleSubscribe}
139
+ onUnsubscribe={handleUnsubscribe}
140
+ loading={
141
+ subscriptionLoading ||
142
+ subscribeMutation.isPending ||
143
+ unsubscribeMutation.isPending
144
+ }
145
+ />
146
+ )}
147
+ <BackLink onClick={() => navigate("/")}>Back to Dashboard</BackLink>
148
+ </div>
149
+ );
139
150
 
140
- if (notFound || !system) {
151
+ if (notFound) {
141
152
  return (
142
- <div className="space-y-6">
143
- <div className="flex items-center justify-between">
144
- <div className="flex items-center gap-3">
145
- <Activity className="h-8 w-8 text-primary" />
146
- <h1 className="text-3xl font-bold text-foreground">
147
- System Not Found
148
- </h1>
149
- </div>
150
- <BackLink onClick={() => navigate("/")}>Back to Dashboard</BackLink>
151
- </div>
153
+ <PageLayout
154
+ title="System Not Found"
155
+ icon={Activity}
156
+ actions={headerActions}
157
+ >
152
158
  <Card className="border-destructive/30 bg-destructive/10">
153
159
  <CardContent className="p-12 text-center">
154
160
  <p className="text-destructive">
@@ -156,35 +162,23 @@ export const SystemDetailPage: React.FC = () => {
156
162
  </p>
157
163
  </CardContent>
158
164
  </Card>
159
- </div>
165
+ </PageLayout>
160
166
  );
161
167
  }
162
168
 
163
- return (
164
- <div className="space-y-6 animate-in fade-in duration-500">
165
- {/* System Name with Subscribe Button and Back Link */}
166
- <div className="flex items-center justify-between">
167
- <div className="flex items-center gap-3">
168
- <Activity className="h-8 w-8 text-primary" />
169
- <h1 className="text-3xl font-bold text-foreground">{system.name}</h1>
170
- </div>
171
- <div className="flex items-center gap-4">
172
- {session && (
173
- <SubscribeButton
174
- isSubscribed={isSubscribed}
175
- onSubscribe={handleSubscribe}
176
- onUnsubscribe={handleUnsubscribe}
177
- loading={
178
- subscriptionLoading ||
179
- subscribeMutation.isPending ||
180
- unsubscribeMutation.isPending
181
- }
182
- />
183
- )}
184
- <BackLink onClick={() => navigate("/")}>Back to Dashboard</BackLink>
185
- </div>
186
- </div>
169
+ // Guard for TypeScript - PageLayout already handles loading state
170
+ if (!system) {
171
+ return;
172
+ }
187
173
 
174
+ return (
175
+ <PageLayout
176
+ title={system.name}
177
+ icon={Activity}
178
+ loading={loading}
179
+ actions={headerActions}
180
+ maxWidth="full"
181
+ >
188
182
  {/* Top Extension Slot for urgent items like maintenance alerts */}
189
183
  <ExtensionSlot slot={SystemDetailsTopSlot} context={{ system }} />
190
184
 
@@ -310,8 +304,7 @@ export const SystemDetailPage: React.FC = () => {
310
304
  </Card>
311
305
  )}
312
306
 
313
- {/* Extension Slot for System Details */}
314
307
  <ExtensionSlot slot={SystemDetailsSlot} context={{ system }} />
315
- </div>
308
+ </PageLayout>
316
309
  );
317
310
  };