@tidecloak/ui-framework 0.0.1

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.
Files changed (48) hide show
  1. package/README.md +377 -0
  2. package/dist/index.d.mts +2739 -0
  3. package/dist/index.d.ts +2739 -0
  4. package/dist/index.js +12869 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +12703 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +54 -0
  9. package/src/components/common/ActionButton.tsx +234 -0
  10. package/src/components/common/EmptyState.tsx +140 -0
  11. package/src/components/common/LoadingSkeleton.tsx +121 -0
  12. package/src/components/common/RefreshButton.tsx +127 -0
  13. package/src/components/common/StatusBadge.tsx +177 -0
  14. package/src/components/common/index.ts +31 -0
  15. package/src/components/data-table/DataTable.tsx +201 -0
  16. package/src/components/data-table/PaginatedTable.tsx +247 -0
  17. package/src/components/data-table/index.ts +2 -0
  18. package/src/components/dialogs/CollapsibleSection.tsx +184 -0
  19. package/src/components/dialogs/ConfirmDialog.tsx +264 -0
  20. package/src/components/dialogs/DetailDialog.tsx +228 -0
  21. package/src/components/dialogs/index.ts +3 -0
  22. package/src/components/index.ts +5 -0
  23. package/src/components/pages/base/ApprovalsPageBase.tsx +680 -0
  24. package/src/components/pages/base/LogsPageBase.tsx +581 -0
  25. package/src/components/pages/base/RolesPageBase.tsx +1470 -0
  26. package/src/components/pages/base/TemplatesPageBase.tsx +761 -0
  27. package/src/components/pages/base/UsersPageBase.tsx +843 -0
  28. package/src/components/pages/base/index.ts +58 -0
  29. package/src/components/pages/connected/ApprovalsPage.tsx +797 -0
  30. package/src/components/pages/connected/LogsPage.tsx +267 -0
  31. package/src/components/pages/connected/RolesPage.tsx +525 -0
  32. package/src/components/pages/connected/TemplatesPage.tsx +181 -0
  33. package/src/components/pages/connected/UsersPage.tsx +237 -0
  34. package/src/components/pages/connected/index.ts +36 -0
  35. package/src/components/pages/index.ts +5 -0
  36. package/src/components/tabs/TabsView.tsx +300 -0
  37. package/src/components/tabs/index.ts +1 -0
  38. package/src/components/ui/index.tsx +1001 -0
  39. package/src/hooks/index.ts +3 -0
  40. package/src/hooks/useAutoRefresh.ts +119 -0
  41. package/src/hooks/usePagination.ts +152 -0
  42. package/src/hooks/useSelection.ts +81 -0
  43. package/src/index.ts +256 -0
  44. package/src/theme.ts +185 -0
  45. package/src/tide/index.ts +19 -0
  46. package/src/tide/tidePolicy.ts +270 -0
  47. package/src/types/index.ts +484 -0
  48. package/src/utils/index.ts +121 -0
package/README.md ADDED
@@ -0,0 +1,377 @@
1
+ # @tide/ui
2
+
3
+ Ready-to-use admin pages for Tide. Manage users, roles, approvals, logs, and templates with minimal setup.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @tide/ui @tidecloak/react @tidecloak/js
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { QueryClient, QueryClientProvider, UsersPage } from '@tide/ui';
15
+ import { TideCloakContextProvider, useTideCloak, Authenticated, Unauthenticated } from '@tidecloak/react';
16
+ import tidecloakConfig from './tidecloak.json';
17
+
18
+ const queryClient = new QueryClient();
19
+
20
+ function App() {
21
+ return (
22
+ <QueryClientProvider client={queryClient}>
23
+ <TideCloakContextProvider config={tidecloakConfig}>
24
+ <Authenticated>
25
+ <AdminDashboard />
26
+ </Authenticated>
27
+ <Unauthenticated>
28
+ <LoginPage />
29
+ </Unauthenticated>
30
+ </TideCloakContextProvider>
31
+ </QueryClientProvider>
32
+ );
33
+ }
34
+
35
+ function AdminDashboard() {
36
+ const { AdminAPI } = useTideCloak();
37
+
38
+ return <UsersPage adminAPI={AdminAPI} title="Users" />;
39
+ }
40
+
41
+ function LoginPage() {
42
+ const { login } = useTideCloak();
43
+
44
+ return <button onClick={login}>Login</button>;
45
+ }
46
+ ```
47
+
48
+ That's it. You have a working user management page.
49
+
50
+ ## Available Pages
51
+
52
+ ### UsersPage
53
+ Manage users and assign roles.
54
+
55
+ ```tsx
56
+ <UsersPage adminAPI={AdminAPI} />
57
+ ```
58
+
59
+ ### RolesPage
60
+ Create roles with policy support and approval workflows.
61
+
62
+ ```tsx
63
+ import { createLocalStoragePolicyAPI, createLocalStorageTemplateAPI } from '@tide/ui';
64
+
65
+ <RolesPage
66
+ adminAPI={AdminAPI}
67
+ policyAPI={createLocalStoragePolicyAPI()}
68
+ templateAPI={createLocalStorageTemplateAPI()}
69
+ tideContext={tideContext}
70
+ currentUsername={username}
71
+ />
72
+ ```
73
+
74
+ ### ApprovalsPage
75
+ Review and approve pending changes.
76
+
77
+ ```tsx
78
+ <ApprovalsPage
79
+ adminAPI={AdminAPI}
80
+ tideContext={tideContext}
81
+ currentUsername={username}
82
+ />
83
+ ```
84
+
85
+ ### LogsPage
86
+ View access and policy activity.
87
+
88
+ ```tsx
89
+ import { createLocalStoragePolicyLogsAPI } from '@tide/ui';
90
+
91
+ <LogsPage
92
+ adminAPI={AdminAPI}
93
+ policyLogsAPI={createLocalStoragePolicyLogsAPI()}
94
+ />
95
+ ```
96
+
97
+ ### TemplatesPage
98
+ Manage policy templates.
99
+
100
+ ```tsx
101
+ import { createLocalStorageTemplateAPI } from '@tide/ui';
102
+
103
+ <TemplatesPage api={createLocalStorageTemplateAPI()} />
104
+ ```
105
+
106
+ ## Full Example
107
+
108
+ ```tsx
109
+ import { StrictMode } from 'react';
110
+ import { createRoot } from 'react-dom/client';
111
+ import {
112
+ QueryClient, QueryClientProvider,
113
+ UsersPage, RolesPage, ApprovalsPage, LogsPage, TemplatesPage,
114
+ createLocalStorageTemplateAPI,
115
+ createLocalStoragePolicyAPI,
116
+ createLocalStorageAccessMetadataAPI,
117
+ createLocalStoragePolicyLogsAPI,
118
+ Tabs, TabsList, TabsTrigger, TabsContent,
119
+ } from '@tide/ui';
120
+ import { TideCloakContextProvider, useTideCloak, Authenticated, Unauthenticated } from '@tidecloak/react';
121
+ import tidecloakConfig from './tidecloak.json';
122
+
123
+ const queryClient = new QueryClient();
124
+ const templateAPI = createLocalStorageTemplateAPI();
125
+ const policyAPI = createLocalStoragePolicyAPI();
126
+ const accessMetadataAPI = createLocalStorageAccessMetadataAPI();
127
+ const policyLogsAPI = createLocalStoragePolicyLogsAPI();
128
+
129
+ function AdminDashboard() {
130
+ const {
131
+ AdminAPI,
132
+ logout,
133
+ isInitializing,
134
+ getValueFromIdToken,
135
+ initializeTideRequest,
136
+ getVendorId,
137
+ getResource,
138
+ approveTideRequests,
139
+ } = useTideCloak();
140
+
141
+ const tideContext = { initializeTideRequest, getVendorId, getResource, approveTideRequests };
142
+ const username = getValueFromIdToken('preferred_username') ?? undefined;
143
+
144
+ if (isInitializing) {
145
+ return <p>Loading...</p>;
146
+ }
147
+
148
+ return (
149
+ <div>
150
+ <header>
151
+ <h1>Admin Dashboard</h1>
152
+ <p>Signed in as {username}</p>
153
+ <button onClick={logout}>Logout</button>
154
+ </header>
155
+
156
+ <Tabs defaultValue="users">
157
+ <TabsList>
158
+ <TabsTrigger value="users">Users</TabsTrigger>
159
+ <TabsTrigger value="roles">Roles</TabsTrigger>
160
+ <TabsTrigger value="approvals">Approvals</TabsTrigger>
161
+ <TabsTrigger value="logs">Logs</TabsTrigger>
162
+ <TabsTrigger value="templates">Templates</TabsTrigger>
163
+ </TabsList>
164
+
165
+ <TabsContent value="users">
166
+ <UsersPage adminAPI={AdminAPI} accessMetadataAPI={accessMetadataAPI} />
167
+ </TabsContent>
168
+
169
+ <TabsContent value="roles">
170
+ <RolesPage
171
+ adminAPI={AdminAPI}
172
+ templateAPI={templateAPI}
173
+ policyAPI={policyAPI}
174
+ policyLogsAPI={policyLogsAPI}
175
+ tideContext={tideContext}
176
+ currentUsername={username}
177
+ />
178
+ </TabsContent>
179
+
180
+ <TabsContent value="approvals">
181
+ <ApprovalsPage
182
+ adminAPI={AdminAPI}
183
+ tideContext={tideContext}
184
+ accessMetadataAPI={accessMetadataAPI}
185
+ policyLogsAPI={policyLogsAPI}
186
+ currentUsername={username}
187
+ />
188
+ </TabsContent>
189
+
190
+ <TabsContent value="logs">
191
+ <LogsPage adminAPI={AdminAPI} policyLogsAPI={policyLogsAPI} />
192
+ </TabsContent>
193
+
194
+ <TabsContent value="templates">
195
+ <TemplatesPage api={templateAPI} />
196
+ </TabsContent>
197
+ </Tabs>
198
+ </div>
199
+ );
200
+ }
201
+
202
+ function LoginPage() {
203
+ const { login } = useTideCloak();
204
+
205
+ return (
206
+ <div>
207
+ <h1>Welcome</h1>
208
+ <button onClick={login}>Login</button>
209
+ </div>
210
+ );
211
+ }
212
+
213
+ createRoot(document.getElementById('root')!).render(
214
+ <StrictMode>
215
+ <QueryClientProvider client={queryClient}>
216
+ <TideCloakContextProvider config={tidecloakConfig}>
217
+ <Authenticated>
218
+ <AdminDashboard />
219
+ </Authenticated>
220
+ <Unauthenticated>
221
+ <LoginPage />
222
+ </Unauthenticated>
223
+ </TideCloakContextProvider>
224
+ </QueryClientProvider>
225
+ </StrictMode>
226
+ );
227
+ ```
228
+
229
+ ## Storage Adapters
230
+
231
+ For development, use the localStorage adapters. They work instantly but show a warning that data won't persist across devices.
232
+
233
+ ```tsx
234
+ createLocalStorageTemplateAPI() // Policy templates
235
+ createLocalStoragePolicyAPI() // Role policies
236
+ createLocalStorageAccessMetadataAPI() // Change set metadata
237
+ createLocalStoragePolicyLogsAPI() // Policy activity logs
238
+ createLocalStoragePolicyApprovalsAPI() // Policy approvals
239
+ ```
240
+
241
+ For production, implement your own:
242
+
243
+ ```tsx
244
+ const myPolicyAPI: PolicyAPI = {
245
+ getPolicy: (roleName) => fetch(`/api/policies/${roleName}`).then(r => r.json()),
246
+ upsertPolicy: (policy) => fetch('/api/policies', { method: 'POST', body: JSON.stringify(policy) }),
247
+ deletePolicy: (roleName) => fetch(`/api/policies/${roleName}`, { method: 'DELETE' }),
248
+ };
249
+ ```
250
+
251
+ ## Tide Enclave Integration
252
+
253
+ Pass `tideContext` to enable the Tide approval enclave popup for cryptographic signing:
254
+
255
+ ```tsx
256
+ const {
257
+ initializeTideRequest,
258
+ getVendorId,
259
+ getResource,
260
+ approveTideRequests,
261
+ } = useTideCloak();
262
+
263
+ const tideContext = { initializeTideRequest, getVendorId, getResource, approveTideRequests };
264
+
265
+ <ApprovalsPage tideContext={tideContext} />
266
+ <RolesPage tideContext={tideContext} />
267
+ ```
268
+
269
+ ## Custom Data Fetching
270
+
271
+ Need full control? Use the base components:
272
+
273
+ ```tsx
274
+ import { UsersPageBase } from '@tide/ui';
275
+
276
+ <UsersPageBase
277
+ fetchUsers={() => myApi.getUsers()}
278
+ fetchRoles={() => myApi.getRoles()}
279
+ onCreate={(data) => myApi.createUser(data)}
280
+ onUpdateProfile={(data) => myApi.updateUser(data)}
281
+ onUpdateRoles={(data) => myApi.updateRoles(data)}
282
+ onDelete={(id) => myApi.deleteUser(id)}
283
+ />
284
+ ```
285
+
286
+ Available base components: `UsersPageBase`, `RolesPageBase`, `ApprovalsPageBase`, `LogsPageBase`, `TemplatesPageBase`
287
+
288
+ ## Built-in UI Components
289
+
290
+ Works out of the box with inline styles. No Tailwind or CSS required.
291
+
292
+ ```tsx
293
+ import {
294
+ Card, CardContent, Button, Badge, Input, Label,
295
+ Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
296
+ Dialog, DialogContent, DialogHeader, DialogTitle,
297
+ Tabs, TabsList, TabsTrigger, TabsContent,
298
+ } from '@tide/ui';
299
+ ```
300
+
301
+ Or pass your own shadcn/ui components:
302
+
303
+ ```tsx
304
+ import { Button } from '@/components/ui/button';
305
+
306
+ <RolesPage components={{ Button }} />
307
+ ```
308
+
309
+ ## Theming
310
+
311
+ Inject theme CSS variables for dark mode support and consistent styling:
312
+
313
+ ```tsx
314
+ import { injectThemeCSS } from '@tide/ui';
315
+
316
+ // Call once at app init
317
+ injectThemeCSS();
318
+ ```
319
+
320
+ Or use the theme constants directly:
321
+
322
+ ```tsx
323
+ import { colors, spacing, radius, focusRing } from '@tide/ui';
324
+
325
+ const myStyle = {
326
+ color: colors.foreground.primary,
327
+ padding: spacing.md,
328
+ borderRadius: radius.lg,
329
+ };
330
+ ```
331
+
332
+ Supports `prefers-color-scheme: dark` and `prefers-reduced-motion: reduce` automatically.
333
+
334
+ ## Hooks
335
+
336
+ ### useAutoRefresh
337
+
338
+ Auto-refresh data at configurable intervals with error handling:
339
+
340
+ ```tsx
341
+ import { useAutoRefresh } from '@tide/ui';
342
+
343
+ const { secondsRemaining, refreshNow, lastError, failureCount, isStopped } = useAutoRefresh({
344
+ intervalSeconds: 15,
345
+ refresh: fetchData,
346
+ isBlocked: isLoading,
347
+ onError: (err) => console.error('Refresh failed:', err),
348
+ maxRetries: 3, // stops after 3 consecutive failures
349
+ });
350
+
351
+ // Show countdown or error state
352
+ {lastError && <span>Refresh failed: {lastError.message}</span>}
353
+ {secondsRemaining && <span>Refreshing in {secondsRemaining}s</span>}
354
+ ```
355
+
356
+ ## Accessibility
357
+
358
+ All components include proper ARIA attributes:
359
+
360
+ - `role="status"` and `aria-busy` for loading states
361
+ - `role="alertdialog"` with `aria-modal` for confirmation dialogs
362
+ - `aria-label` on interactive elements
363
+ - `prefers-reduced-motion` support for animations
364
+
365
+ ## API Reference
366
+
367
+ | Adapter | Purpose |
368
+ |---------|---------|
369
+ | `TemplateAPI` | Store policy templates |
370
+ | `PolicyAPI` | Store role policies |
371
+ | `PolicyLogsAPI` | Log policy activity |
372
+ | `PolicyApprovalsAPI` | Track pending policy approvals |
373
+ | `AccessMetadataAPI` | Track change set metadata |
374
+
375
+ ## License
376
+
377
+ MIT