@rebasepro/agent-skills 0.0.1-canary.4829d6e

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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/package.json +21 -0
  4. package/skills/rebase-admin/SKILL.md +816 -0
  5. package/skills/rebase-api/SKILL.md +673 -0
  6. package/skills/rebase-api/references/.gitkeep +3 -0
  7. package/skills/rebase-auth/SKILL.md +1323 -0
  8. package/skills/rebase-auth/references/.gitkeep +3 -0
  9. package/skills/rebase-backend-postgres/SKILL.md +633 -0
  10. package/skills/rebase-backend-postgres/references/.gitkeep +3 -0
  11. package/skills/rebase-basics/SKILL.md +757 -0
  12. package/skills/rebase-basics/references/.gitkeep +3 -0
  13. package/skills/rebase-collections/SKILL.md +1421 -0
  14. package/skills/rebase-collections/references/.gitkeep +3 -0
  15. package/skills/rebase-cron-jobs/SKILL.md +704 -0
  16. package/skills/rebase-cron-jobs/references/.gitkeep +1 -0
  17. package/skills/rebase-custom-functions/SKILL.md +281 -0
  18. package/skills/rebase-deployment/SKILL.md +894 -0
  19. package/skills/rebase-deployment/references/.gitkeep +3 -0
  20. package/skills/rebase-design-language/SKILL.md +692 -0
  21. package/skills/rebase-email/SKILL.md +701 -0
  22. package/skills/rebase-email/references/.gitkeep +1 -0
  23. package/skills/rebase-entity-history/SKILL.md +485 -0
  24. package/skills/rebase-entity-history/references/.gitkeep +1 -0
  25. package/skills/rebase-local-env-setup/SKILL.md +189 -0
  26. package/skills/rebase-local-env-setup/references/.gitkeep +3 -0
  27. package/skills/rebase-realtime/SKILL.md +759 -0
  28. package/skills/rebase-realtime/references/.gitkeep +3 -0
  29. package/skills/rebase-sdk/SKILL.md +617 -0
  30. package/skills/rebase-sdk/references/.gitkeep +0 -0
  31. package/skills/rebase-security/SKILL.md +646 -0
  32. package/skills/rebase-storage/SKILL.md +989 -0
  33. package/skills/rebase-storage/references/.gitkeep +3 -0
  34. package/skills/rebase-studio/SKILL.md +772 -0
  35. package/skills/rebase-studio/references/.gitkeep +3 -0
  36. package/skills/rebase-ui-components/SKILL.md +1579 -0
  37. package/skills/rebase-ui-components/references/.gitkeep +3 -0
  38. package/skills/rebase-webhooks/SKILL.md +618 -0
  39. package/skills/rebase-webhooks/references/.gitkeep +1 -0
@@ -0,0 +1,816 @@
1
+ ---
2
+ name: rebase-admin
3
+ description: Guide for navigating the Rebase admin CMS, opening entities in side drawers, building URLs, embedding collection panels, using the collection registry, and programmatic navigation. Use this skill when an agent or user needs to navigate to a collection view, open a entity in the side panel/drawer, build admin URLs, embed a collection inside a custom page, use the entity selection dialog, or access CMS-specific controllers.
4
+ ---
5
+
6
+ # Rebase Admin (`@rebasepro/admin`)
7
+
8
+ The `@rebasepro/admin` package provides the CMS layer for Rebase. It handles collection views, entity editing, navigation, side panels (drawers), URL routing, breadcrumbs, and the full CMS context. This skill covers the **programmatic APIs** for navigating and interacting with the admin.
9
+
10
+ > **IMPORTANT FOR AGENTS:** All hooks in this skill must be called **inside** the `<RebaseShell>` component tree. They rely on React contexts provided by `<RebaseNavigation>`, `<SideEntityProvider>`, and `<RebaseRouteDefs>`.
11
+
12
+ ## Quick Reference — Common Tasks
13
+
14
+ | Task | Hook / Component | Package |
15
+ |------|-----------------|---------|
16
+ | Open entity in side drawer | `useSidePanel()` | `@rebasepro/admin` |
17
+ | Navigate to a collection view | `useUrlController()` | `@rebasepro/admin` |
18
+ | Look up a collection by slug | `useCollectionRegistryController()` | `@rebasepro/admin` |
19
+ | Embed a collection in a custom page | `<CollectionPanel>` | `@rebasepro/admin` |
20
+ | Add custom top-level views | `<RebaseAdmin views={[...]}>` | `@rebasepro/admin` |
21
+ | Open a entity selection dialog | `useSelectionDialog()` | `@rebasepro/admin` |
22
+ | Open a custom side dialog | `useSideDialogsController()` | `@rebasepro/admin` |
23
+ | Set breadcrumbs | `useBreadcrumbsController()` | `@rebasepro/admin` |
24
+ | Access full CMS context | `useCMSContext()` | `@rebasepro/admin` |
25
+ | Access navigation state & views | `useNavigationStateController()` | `@rebasepro/admin` |
26
+
27
+ ---
28
+
29
+ ## 1. Opening Entities in the Side Drawer
30
+
31
+ Use `useSidePanel()` to open, replace, or close entity side panels (the sliding drawer that shows entity forms).
32
+
33
+ ```typescript
34
+ import { useSidePanel } from "@rebasepro/admin";
35
+ ```
36
+
37
+ ### SidePanelController Interface
38
+
39
+ ```typescript
40
+ interface SidePanelController {
41
+ /** Close the last (topmost) panel */
42
+ close: () => void;
43
+
44
+ /** Open a new entity side panel */
45
+ open: <M extends Record<string, unknown>>(props: EntitySidePanelProps<M>) => void;
46
+
47
+ /** Replace the last open panel with a new one */
48
+ replace: <M extends Record<string, unknown>>(props: EntitySidePanelProps<M>) => void;
49
+ }
50
+ ```
51
+
52
+ ### EntitySidePanelProps
53
+
54
+ ```typescript
55
+ interface EntitySidePanelProps<M extends Record<string, unknown> = Record<string, unknown>> {
56
+ /** Absolute path of the entity's collection (e.g. "products") */
57
+ path: string;
58
+
59
+ /** ID of the entity. Omit to create a new entity. */
60
+ entityId?: string | number;
61
+
62
+ /** Set to true to create a copy of an existing entity */
63
+ copy?: boolean;
64
+
65
+ /** Open with a specific sub-collection tab selected */
66
+ selectedTab?: string;
67
+
68
+ /** Override the width of the form panel (e.g. "600px") */
69
+ width?: number | string;
70
+
71
+ /** Explicit collection config (auto-resolved from navigation if omitted) */
72
+ collection?: CollectionConfig<M>;
73
+
74
+ /** Whether to update the browser URL when opening (default: true) */
75
+ updateUrl?: boolean;
76
+
77
+ /** Callback when the entity is saved/updated */
78
+ onUpdate?: (params: { entity: Entity<M> }) => void;
79
+
80
+ /** Callback when the panel is closed */
81
+ onClose?: () => void;
82
+
83
+ /** Close the panel automatically after saving */
84
+ closeOnSave?: boolean;
85
+
86
+ /** Override form properties */
87
+ formProps?: Record<string, unknown>;
88
+
89
+ /** Show a full-screen toggle button */
90
+ allowFullScreen?: boolean;
91
+
92
+ /** Pre-populate form values when creating a new entity (only when entityId is not set) */
93
+ defaultValues?: Partial<M>;
94
+ }
95
+ ```
96
+
97
+ ### Examples
98
+
99
+ **Open an existing entity for editing:**
100
+ ```tsx
101
+ const sideEntityController = useSidePanel();
102
+
103
+ // Open the product with ID "abc123" in the side drawer
104
+ sideEntityController.open({
105
+ path: "products",
106
+ entityId: "abc123"
107
+ });
108
+ ```
109
+
110
+ **Create a new entity with pre-filled values:**
111
+ ```tsx
112
+ sideEntityController.open({
113
+ path: "products",
114
+ defaultValues: {
115
+ name: "New Product",
116
+ status: "draft"
117
+ }
118
+ });
119
+ ```
120
+
121
+ **Open with a callback on save:**
122
+ ```tsx
123
+ sideEntityController.open({
124
+ path: "orders",
125
+ entityId: orderId,
126
+ closeOnSave: true,
127
+ onUpdate: ({ entity }) => {
128
+ console.log("Saved:", entity.id, entity.values);
129
+ }
130
+ });
131
+ ```
132
+
133
+ **Replace the current panel (instead of stacking):**
134
+ ```tsx
135
+ sideEntityController.replace({
136
+ path: "clients",
137
+ entityId: "xyz789"
138
+ });
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 2. Navigating to Collection Views
144
+
145
+ Use `useUrlController()` to build URLs and navigate programmatically within the admin.
146
+
147
+ ```typescript
148
+ import { useUrlController } from "@rebasepro/admin";
149
+ ```
150
+
151
+ ### UrlController Interface
152
+
153
+ ```typescript
154
+ type UrlController = {
155
+ /** Base path for the CMS (default: "/") */
156
+ basePath: string;
157
+
158
+ /** Base path for collection routes (default: "/c") */
159
+ baseCollectionPath: string;
160
+
161
+ /** Convert a URL path to a data path: "/c/products" → "products" */
162
+ urlPathToDataPath: (cmsPath: string) => string;
163
+
164
+ /** Base URL for the home screen */
165
+ homeUrl: string;
166
+
167
+ /** Check if a URL path belongs to a collection */
168
+ isUrlCollectionPath: (urlPath: string) => boolean;
169
+
170
+ /** Build a URL for a collection: "products" → "/c/products" */
171
+ buildUrlCollectionPath: (path: string) => string;
172
+
173
+ /** Build a URL for a custom view or app path */
174
+ buildAppUrlPath: (path: string) => string;
175
+
176
+ /** Resolve collection IDs in a path to database paths */
177
+ resolveDatabasePathsFrom: (path: string) => string;
178
+
179
+ /** Navigate to a route (wraps react-router's navigate) */
180
+ navigate: (to: string, options?: NavigateOptions) => void;
181
+ };
182
+ ```
183
+
184
+ ### Examples
185
+
186
+ **Navigate to a collection view:**
187
+ ```tsx
188
+ const urlController = useUrlController();
189
+
190
+ // Navigate to the "products" collection
191
+ const url = urlController.buildUrlCollectionPath("products");
192
+ urlController.navigate(url);
193
+ // This navigates to "/c/products"
194
+ ```
195
+
196
+ **Navigate to a specific entity within a collection:**
197
+ ```tsx
198
+ const url = urlController.buildUrlCollectionPath("products/abc123");
199
+ urlController.navigate(url);
200
+ // This navigates to "/c/products/abc123"
201
+ ```
202
+
203
+ **Navigate to a custom view:**
204
+ ```tsx
205
+ const url = urlController.buildAppUrlPath("my-dashboard");
206
+ urlController.navigate(url);
207
+ ```
208
+
209
+ **Navigate and replace history (no back button):**
210
+ ```tsx
211
+ urlController.navigate(
212
+ urlController.buildUrlCollectionPath("orders"),
213
+ { replace: true }
214
+ );
215
+ ```
216
+
217
+ **Navigate to home:**
218
+ ```tsx
219
+ urlController.navigate(urlController.homeUrl);
220
+ ```
221
+
222
+ > **IMPORTANT FOR AGENTS:** The URL structure is `basePath + baseCollectionPath + "/" + slug`. By default this produces `/c/products`. Use `buildUrlCollectionPath()` instead of manually constructing URLs.
223
+
224
+ ---
225
+
226
+ ## 3. Collection Registry
227
+
228
+ Use `useCollectionRegistryController()` to look up registered collections by slug.
229
+
230
+ ```typescript
231
+ import { useCollectionRegistryController } from "@rebasepro/admin";
232
+ ```
233
+
234
+ ### CollectionRegistryController Interface
235
+
236
+ ```typescript
237
+ type CollectionRegistryController<
238
+ DB = Record<string, unknown>,
239
+ EC extends CollectionConfig = CollectionConfig
240
+ > = {
241
+ /** All registered collections */
242
+ collections?: CollectionConfig[];
243
+
244
+ /** Whether the registry is ready */
245
+ initialised: boolean;
246
+
247
+ /** Get a collection by slug or path */
248
+ getCollection: <K extends keyof DB>(slugOrPath: Extract<K, string>, includeUserOverride?: boolean) => EC | undefined;
249
+
250
+ /** Get the raw (un-normalized) collection config — for the Visual Editor only */
251
+ getRawCollection: (slugOrPath: string) => EC | undefined;
252
+
253
+ /** Get all parent entity references for a path */
254
+ getParentReferencesFromPath: (path: string) => EntityReference[];
255
+
256
+ /** Get parent collection slugs for a path */
257
+ getParentCollectionSlugs: (path: string) => string[];
258
+
259
+ /** Get parent entity IDs for a path */
260
+ getParentEntityIds: (path: string) => string[];
261
+
262
+ /** Resolve IDs to paths */
263
+ convertIdsToPaths: (ids: string[]) => string[];
264
+ };
265
+ ```
266
+
267
+ ### Example
268
+
269
+ ```tsx
270
+ const registry = useCollectionRegistryController();
271
+
272
+ // Check if a collection exists
273
+ const products = registry.getCollection("products");
274
+ if (products) {
275
+ console.log("Collection name:", products.name);
276
+ console.log("Properties:", Object.keys(products.properties));
277
+ }
278
+
279
+ // List all collections
280
+ registry.collections?.forEach(c => {
281
+ console.log(c.slug, c.name);
282
+ });
283
+ ```
284
+
285
+ ---
286
+
287
+ ## 4. Embedding Collections with CollectionPanel
288
+
289
+ `CollectionPanel` is a high-level wrapper for embedding collection views inside custom pages (dashboards, home pages, entity detail views).
290
+
291
+ ```typescript
292
+ import { CollectionPanel } from "@rebasepro/admin";
293
+ ```
294
+
295
+ ### CollectionPanelProps
296
+
297
+ ```typescript
298
+ type CollectionPanelProps = {
299
+ /** Collection slug to display (e.g. "tasks") — required */
300
+ path: string;
301
+
302
+ /** Title above the collection. `false` to hide. Defaults to collection name. */
303
+ title?: string | false;
304
+
305
+ /** Force a view mode (table, card, etc.) */
306
+ viewMode?: ViewMode;
307
+
308
+ /** Override sort: [fieldName, direction] */
309
+ sort?: [string, "asc" | "desc"];
310
+
311
+ /** Max entities to display */
312
+ limit?: number;
313
+
314
+ /** Sync filter/sort with URL params (default: false) */
315
+ updateUrl?: boolean;
316
+
317
+ /** Entity open mode when clicking */
318
+ openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
319
+
320
+ /** Container CSS class */
321
+ className?: string;
322
+
323
+ /** Additional collection-level overrides */
324
+ collectionOverrides?: Partial<CollectionConfig>;
325
+ };
326
+ ```
327
+
328
+ ### Examples
329
+
330
+ ```tsx
331
+ // Simple embedded collection
332
+ <CollectionPanel path="tasks" title="Pending Tasks" />
333
+
334
+ // Table with sorting and limit
335
+ <CollectionPanel
336
+ path="clients"
337
+ viewMode="table"
338
+ limit={10}
339
+ sort={["createdAt", "desc"]}
340
+ />
341
+
342
+ // With a default filter
343
+ <CollectionPanel
344
+ path="orders"
345
+ title={false}
346
+ openEntityMode="dialog"
347
+ collectionOverrides={{
348
+ defaultFilter: { status: ["!=", "completed"] }
349
+ }}
350
+ />
351
+ ```
352
+
353
+ > **IMPORTANT FOR AGENTS:** `CollectionPanel` defaults `updateUrl` to `false` so embedded panels don't hijack the browser URL. Only set `updateUrl={true}` if you explicitly need URL sync.
354
+
355
+ ---
356
+
357
+ ## 5. Entity Selection Dialog
358
+
359
+ Use `useSelectionDialog()` to open a side dialog for selecting entities (same mechanism used by reference fields).
360
+
361
+ ```typescript
362
+ import { useSelectionDialog } from "@rebasepro/admin";
363
+ ```
364
+
365
+ ### Usage
366
+
367
+ ```tsx
368
+ function MyComponent() {
369
+ const { open, close } = useSelectionDialog<Product>({
370
+ path: "products",
371
+ onSingleEntitySelected: (entity) => {
372
+ console.log("Selected:", entity.id);
373
+ close();
374
+ }
375
+ });
376
+
377
+ return <Button onClick={open}>Select Product</Button>;
378
+ }
379
+ ```
380
+
381
+ The hook accepts all `SelectionProps` except `path` (which you pass separately), plus an `onClose` callback.
382
+
383
+ ---
384
+
385
+ ## 6. Side Dialogs (Generic)
386
+
387
+ Use `useSideDialogsController()` to open arbitrary side panels with custom React content. This is the lower-level mechanism behind entity side panels.
388
+
389
+ ```typescript
390
+ import { useSideDialogsController } from "@rebasepro/admin";
391
+ ```
392
+
393
+ ### SideDialogsController Interface
394
+
395
+ ```typescript
396
+ interface SideDialogsController {
397
+ /** Close the last panel */
398
+ close: () => void;
399
+
400
+ /** Currently open panels */
401
+ sidePanels: SideDialogPanelProps[];
402
+
403
+ /** Override all panels */
404
+ setSidePanels: (panels: SideDialogPanelProps[]) => void;
405
+
406
+ /** Open one or multiple side panels */
407
+ open: (panelProps: SideDialogPanelProps | SideDialogPanelProps[]) => void;
408
+
409
+ /** Replace the last panel */
410
+ replace: (panelProps: SideDialogPanelProps | SideDialogPanelProps[]) => void;
411
+ }
412
+ ```
413
+
414
+ ### SideDialogPanelProps
415
+
416
+ ```typescript
417
+ interface SideDialogPanelProps {
418
+ /** Unique key identifying this panel */
419
+ key: string;
420
+
421
+ /** React component to render inside the panel */
422
+ component: React.ReactNode;
423
+
424
+ /** Optional panel width (e.g. "90vw", "600px") */
425
+ width?: string;
426
+
427
+ /** When open, update the browser URL to this path */
428
+ urlPath?: string;
429
+
430
+ /** URL to navigate to when the panel is closed and there's no history */
431
+ parentUrlPath?: string;
432
+
433
+ /** Callback when the panel is closed */
434
+ onClose?: () => void;
435
+
436
+ /** Store additional data on the panel */
437
+ additional?: unknown;
438
+ }
439
+ ```
440
+
441
+ ### Example
442
+
443
+ ```tsx
444
+ const sideDialogs = useSideDialogsController();
445
+
446
+ sideDialogs.open({
447
+ key: "my-custom-panel",
448
+ component: <MyCustomView data={someData} />,
449
+ width: "600px",
450
+ onClose: () => console.log("Panel closed")
451
+ });
452
+ ```
453
+
454
+ ---
455
+
456
+ ## 7. Breadcrumbs
457
+
458
+ Use `useBreadcrumbsController()` to read or set the breadcrumb trail.
459
+
460
+ ```typescript
461
+ import { useBreadcrumbsController } from "@rebasepro/admin";
462
+ ```
463
+
464
+ ### BreadcrumbsController Interface
465
+
466
+ ```typescript
467
+ interface BreadcrumbsController {
468
+ breadcrumbs: BreadcrumbEntry[];
469
+ set: (props: { breadcrumbs: BreadcrumbEntry[] }) => void;
470
+ updateCount: (id: string, count: number | null | undefined) => void;
471
+ }
472
+
473
+ interface BreadcrumbEntry {
474
+ title: string;
475
+ url: string;
476
+ count?: number | null; // undefined = N/A, null = loading, number = loaded
477
+ id?: string; // for targeted count updates
478
+ }
479
+ ```
480
+
481
+ ### Example
482
+
483
+ ```tsx
484
+ const breadcrumbs = useBreadcrumbsController();
485
+
486
+ breadcrumbs.set({
487
+ breadcrumbs: [
488
+ { title: "Dashboard", url: "/" },
489
+ { title: "Products", url: "/c/products", id: "products", count: null },
490
+ ]
491
+ });
492
+
493
+ // Later, update the count without re-setting everything
494
+ breadcrumbs.updateCount("products", 42);
495
+ ```
496
+
497
+ ---
498
+
499
+ ## 8. Navigation State
500
+
501
+ Use `useNavigationStateController()` to access registered views, loading state, and trigger navigation refresh.
502
+
503
+ ```typescript
504
+ import { useNavigationStateController } from "@rebasepro/admin";
505
+ ```
506
+
507
+ ### NavigationStateController Interface
508
+
509
+ ```typescript
510
+ type NavigationStateController = {
511
+ /** Custom views added to the main navigation */
512
+ views?: AppView[];
513
+
514
+ /** Custom views added to admin navigation */
515
+ adminViews?: AppView[];
516
+
517
+ /** Top-level navigation entries and groups */
518
+ topLevelNavigation?: NavigationResult;
519
+
520
+ /** Whether navigation is still loading */
521
+ loading: boolean;
522
+
523
+ /** Error during navigation loading */
524
+ navigationLoadingError?: unknown;
525
+
526
+ /** Force a navigation recalculation */
527
+ refreshNavigation: () => void;
528
+
529
+ /** Registered plugins */
530
+ plugins?: RebasePlugin[];
531
+ };
532
+ ```
533
+
534
+ ---
535
+
536
+ ## 9. CMS Context (All-in-One)
537
+
538
+ Use `useCMSContext()` to get the full CMS context combining the core `RebaseContext` with all CMS-specific controllers.
539
+
540
+ ```typescript
541
+ import { useCMSContext } from "@rebasepro/admin";
542
+ ```
543
+
544
+ ### CMSContext Type
545
+
546
+ ```typescript
547
+ type CMSContext = RebaseContext & {
548
+ sideEntityController: SidePanelController;
549
+ sideDialogsController: SideDialogsController;
550
+ urlController: UrlController;
551
+ navigationStateController: NavigationStateController;
552
+ collectionRegistryController: CollectionRegistryController;
553
+ };
554
+ ```
555
+
556
+ ### Example
557
+
558
+ ```tsx
559
+ const context = useCMSContext();
560
+
561
+ // Access any controller
562
+ context.sideEntityController.open({ path: "products", entityId: "abc" });
563
+ context.urlController.navigate(context.urlController.buildUrlCollectionPath("orders"));
564
+ context.collectionRegistryController.getCollection("products");
565
+ context.authController; // from RebaseContext
566
+ context.data; // DataSource from RebaseContext
567
+ ```
568
+
569
+ > **TIP:** Use `useCMSContext()` instead of `useRebaseContext()` when you need CMS controllers (side panels, navigation, URL). Use `useRebaseContext()` from `@rebasepro/app` when you only need core context (auth, data, storage).
570
+
571
+ ---
572
+
573
+ ## 10. Common Patterns
574
+
575
+ ### Navigate to a collection and open a entity
576
+
577
+ ```tsx
578
+ import { useUrlController, useSidePanel } from "@rebasepro/admin";
579
+
580
+ function navigateAndOpen() {
581
+ const urlController = useUrlController();
582
+ const sideEntityController = useSidePanel();
583
+
584
+ // First navigate to the collection
585
+ urlController.navigate(urlController.buildUrlCollectionPath("products"));
586
+
587
+ // Then open the entity in the side drawer
588
+ sideEntityController.open({
589
+ path: "products",
590
+ entityId: "abc123"
591
+ });
592
+ }
593
+ ```
594
+
595
+ ### Open entity from a custom view without navigating
596
+
597
+ ```tsx
598
+ import { useSidePanel } from "@rebasepro/admin";
599
+
600
+ function MyCustomView() {
601
+ const sideEntityController = useSidePanel();
602
+
603
+ return (
604
+ <button onClick={() => sideEntityController.open({
605
+ path: "products",
606
+ entityId: "abc123",
607
+ updateUrl: false // don't change the URL
608
+ })}>
609
+ View Product
610
+ </button>
611
+ );
612
+ }
613
+ ```
614
+
615
+ ### Programmatic entity creation from a custom view
616
+
617
+ ```tsx
618
+ import { useSidePanel } from "@rebasepro/admin";
619
+
620
+ function CreateButton() {
621
+ const sideEntity = useSidePanel();
622
+
623
+ return (
624
+ <button onClick={() => sideEntity.open({
625
+ path: "orders",
626
+ defaultValues: {
627
+ status: "pending",
628
+ createdAt: new Date()
629
+ },
630
+ closeOnSave: true,
631
+ onUpdate: ({ entity }) => {
632
+ console.log("Created order:", entity.id);
633
+ }
634
+ })}>
635
+ New Order
636
+ </button>
637
+ );
638
+ }
639
+ ```
640
+
641
+ ---
642
+
643
+ ## 10. Custom Top-Level Views
644
+
645
+ Add custom pages to the main CMS navigation using the `views` prop on `<RebaseAdmin>`. Views appear alongside collections in the sidebar and home page.
646
+
647
+ ### AppView Interface
648
+
649
+ ```typescript
650
+ interface AppView {
651
+ slug: string; // URL path segment (e.g. "dashboard")
652
+ name: string; // Display name in navigation
653
+ view: React.ReactNode; // Component to render
654
+ icon?: string | React.ReactNode; // Lucide icon key or custom element
655
+ group?: string; // Navigation group (default: "Views")
656
+ description?: string; // Optional description (Markdown)
657
+ hideFromNavigation?: boolean; // Hidden from sidebar but still routable
658
+ nestedRoutes?: boolean; // Register slug/* wildcard route
659
+ roles?: string[]; // Only show to users with at least one matching role
660
+ }
661
+ ```
662
+
663
+ ### Static Views
664
+
665
+ ```tsx
666
+ <RebaseAdmin
667
+ collections={collections}
668
+ views={[
669
+ { slug: "dashboard", name: "Dashboard", icon: "LayoutDashboard", view: <Dashboard /> },
670
+ { slug: "reports", name: "Reports", icon: "FileText", view: <Reports />, group: "Analytics" },
671
+ { slug: "audit-log", name: "Audit Log", icon: "ScrollText", view: <AuditLog />, roles: ["admin"] },
672
+ ]}
673
+ />
674
+ ```
675
+
676
+ ### Builder Function (Role-Aware)
677
+
678
+ Pass a function instead of an array to dynamically resolve views based on the current user:
679
+
680
+ ```tsx
681
+ <RebaseAdmin
682
+ collections={collections}
683
+ views={({ user, authController, data }) => [
684
+ { slug: "dashboard", name: "Dashboard", icon: "LayoutDashboard", view: <Dashboard /> },
685
+ ...(user?.roles?.includes("analyst")
686
+ ? [{ slug: "reports", name: "Reports", icon: "FileText", view: <Reports /> }]
687
+ : []),
688
+ ]}
689
+ />
690
+ ```
691
+
692
+ The builder receives `{ user, authController, data }` and can return a `Promise<AppView[]>` for async resolution.
693
+
694
+ ### Plugin-Contributed Views
695
+
696
+ Plugins can also contribute views via the `views` property on `RebasePlugin`:
697
+
698
+ ```tsx
699
+ const myPlugin: RebasePlugin = {
700
+ key: "analytics",
701
+ views: [
702
+ { slug: "analytics", name: "Analytics", icon: "BarChart3", view: <Analytics /> }
703
+ ]
704
+ };
705
+
706
+ <RebaseAdmin plugins={[myPlugin]} />
707
+ ```
708
+
709
+ All views (CMS, builder, plugin) are merged in order: **CMS views → Studio dev views → Plugin views**.
710
+
711
+ ### Role Filtering
712
+
713
+ The `roles` field provides declarative access control. When set, the view is excluded entirely (not just hidden from nav) if the user doesn't have at least one matching role:
714
+
715
+ ```tsx
716
+ // Only visible to admin users
717
+ { slug: "admin-panel", name: "Admin", view: <AdminPanel />, roles: ["admin"] }
718
+
719
+ // Visible to admin OR editor
720
+ { slug: "editor", name: "Editor", view: <Editor />, roles: ["admin", "editor"] }
721
+
722
+ // Visible to everyone (roles omitted)
723
+ { slug: "dashboard", name: "Dashboard", view: <Dashboard /> }
724
+ ```
725
+
726
+ > **IMPORTANT FOR AGENTS:** The `roles` filter applies to ALL views — CMS views, builder-returned views, and plugin views. Use `roles` for simple role gates and the builder function for dynamic/async conditions. Both compose.
727
+
728
+ ### Navigation Grouping
729
+
730
+ Views participate in the same navigation group system as collections. Use the `group` property on the view, or control grouping centrally via `navigationGroupMappings`:
731
+
732
+ ```tsx
733
+ <RebaseAdmin
734
+ collections={collections}
735
+ views={[
736
+ { slug: "dashboard", name: "Dashboard", view: <Dashboard />, group: "Analytics" },
737
+ { slug: "reports", name: "Reports", view: <Reports />, group: "Analytics" },
738
+ ]}
739
+ navigationGroupMappings={[
740
+ { name: "Content", entries: ["posts", "pages"] },
741
+ { name: "Analytics", entries: ["dashboard", "reports"] },
742
+ ]}
743
+ />
744
+ ```
745
+
746
+ ---
747
+
748
+ ## Exported Components
749
+
750
+ The admin package exports the following components (from `@rebasepro/admin`):
751
+
752
+ | Component | Description |
753
+ |-----------|-------------|
754
+ | `RebaseAdmin` | Declarative CMS config (collections, views, editor) — renders nothing |
755
+ | `RebaseShell` | App shell (drawer, nav, routes, layout) — renders the actual UI |
756
+ | `CollectionPanel` | Embed a collection view inside custom pages |
757
+ | `DataCollectionView` | The collection view component |
758
+ | `EntityCustomView` | Entity detail/edit view |
759
+ | `EntityPreview` | Reference/relation preview chip |
760
+ | `EntityCard` | Card representation of a entity |
761
+ | `SideDialogs` | Side dialog container |
762
+ | `SideEntityProvider` | Context provider for side entity controller |
763
+ | `EntitySelectionTable` | Table for selecting entities |
764
+ | `Scaffold` | Layout scaffold component |
765
+ | `AppBar` | Top app bar |
766
+ | `Drawer` / `DefaultDrawer` | Sidebar drawer |
767
+ | `RebaseAuthGate` | Auth-gated wrapper |
768
+ | `RebaseNavigation` | Navigation provider |
769
+ | `RebaseLayout` | Layout wrapper |
770
+ | `RebaseRouteDefs` | Route definitions |
771
+
772
+ ## Exported Hooks
773
+
774
+ | Hook | Description |
775
+ |------|-------------|
776
+ | `useSidePanel()` | Open/close entity side panels |
777
+ | `useSideDialogsController()` | Open/close generic side dialogs |
778
+ | `useUrlController()` | Build URLs and navigate |
779
+ | `useNavigationStateController()` | Access navigation state |
780
+ | `useCollectionRegistryController()` | Look up collections by slug |
781
+ | `useBreadcrumbsController()` | Read/set breadcrumbs |
782
+ | `useCMSContext()` | Full CMS context (core + CMS controllers) |
783
+ | `useSelectionDialog()` | Open entity selection dialog |
784
+ | `useSelectionController()` | Multi-select controller |
785
+ | `useHistory()` | Entity version history |
786
+ | `useApp()` | App-level utilities |
787
+
788
+ ## Exported Utilities
789
+
790
+ | Utility | Description |
791
+ |---------|-------------|
792
+ | `addInitialSlash(path)` | Ensure path starts with `/` |
793
+ | `removeInitialSlash(path)` | Strip leading `/` |
794
+ | `removeTrailingSlash(path)` | Strip trailing `/` |
795
+ | `removeInitialAndTrailingSlashes(path)` | Strip both |
796
+ | `getLastSegment(path)` | Get last path segment |
797
+ | `getCollectionBySlugWithin(collections, slug)` | Find collection in array |
798
+ | `mergeEntityActions(...)` | Merge entity action arrays |
799
+ | `resolveEntityAction(...)` | Resolve a entity action |
800
+ | `resolveEntityView(...)` | Resolve a entity view |
801
+ | `isReferenceProperty(prop)` | Check if property is a reference |
802
+ | `isRelationProperty(prop)` | Check if property is a relation |
803
+ | `getIconForProperty(prop)` | Get icon for a property type |
804
+
805
+ ## Built-in Entity Actions
806
+
807
+ ```typescript
808
+ import {
809
+ editEntityAction,
810
+ copyEntityAction,
811
+ deleteEntityAction,
812
+ resetPasswordAction
813
+ } from "@rebasepro/admin";
814
+ ```
815
+
816
+ These are pre-built `EntityAction` objects that can be added to a collection's `entityActions` array.