@elevasis/sdk 1.39.0 → 1.41.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/dist/cli.cjs +10 -26
- package/dist/index.d.ts +200 -8
- package/dist/index.js +9 -25
- package/dist/node/index.d.ts +180 -8
- package/dist/test-utils/index.d.ts +200 -8
- package/dist/test-utils/index.js +360 -179
- package/dist/types/worker/adapters/llm.d.ts +1 -1
- package/dist/worker/index.js +361 -180
- package/package.json +4 -4
- package/reference/claude-config/hooks/scaffold-registry-reminder.mjs +187 -188
- package/reference/claude-config/sync-notes/2026-07-24-claude-5-models-and-session-surface-fixes.md +116 -0
- package/reference/claude-config/sync-notes/2026-07-27-agent-strict-output-and-turn-drift.md +73 -0
- package/reference/index.mdx +1 -1
- package/reference/packages/core/src/business/README.md +52 -52
- package/reference/rules/frontend.md +1 -1
- package/reference/rules/package-taxonomy.md +1 -1
- package/reference/rules/platform.md +3 -3
- package/reference/scaffold/operations/scaffold-maintenance.md +112 -112
- package/reference/scaffold/operations/workflow-recipes.md +525 -525
- package/reference/scaffold/recipes/customize-crm-actions.md +391 -391
- package/reference/scaffold/recipes/extend-crm.md +4 -4
- package/reference/scaffold/recipes/extend-lead-gen.md +4 -4
- package/reference/scaffold/reference/glossary.md +1 -1
- package/reference/scaffold/ui/customization.md +243 -243
- package/reference/sdk/platform-tools/index.mdx +1 -1
- package/reference/sdk/platform-tools/type-safety.mdx +1 -1
|
@@ -25,14 +25,14 @@ CRM is a layered platform surface, not one component:
|
|
|
25
25
|
- **Headless hooks:** deal, company, contact, note, task, list, transition, and action hooks live under `@elevasis/ui/hooks`.
|
|
26
26
|
- **Action system:** `ActionDef`, `deriveActions`, and the caller-supplied provider-level `crmActions` catalog configure deal actions. The published `@elevasis/sdk` surface ships no default Elevasis action catalog -- projects supply their own `ActionDef[]`.
|
|
27
27
|
- **Workflow adapters:** `crm` and `acqDb` from `@elevasis/sdk/worker` let workflows read and mutate CRM/acquisition data through platform tools.
|
|
28
|
-
- **System API Interface:** `sales.crm` uses the flat `system.apiInterface` marker for its API-backed CRM boundary. Readiness is derived from scoped resources, ontology bindings, required catalogs, and scoped topology grants. Cross-system lead-gen handoff belongs in scoped topology, not a top-level Bridge Contract or a hand-authored handoff interface field. Generated contract docs expose the current CRM shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
28
|
+
- **System API Interface:** `sales.crm` uses the flat `system.apiInterface` marker for its API-backed CRM boundary. Readiness is derived from scoped resources, ontology bindings, required catalogs, and scoped topology grants. Cross-system lead-gen handoff belongs in scoped topology, not a top-level Bridge Contract or a hand-authored handoff interface field. Generated contract docs expose the current CRM shapes in `operations/node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
29
29
|
|
|
30
30
|
## Decision Table
|
|
31
31
|
|
|
32
32
|
| User wants | Start here | Notes |
|
|
33
33
|
| -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
34
|
| Change CRM System availability, labels, pipeline stages, resource descriptors, or API readiness | `core/config/organization-model.ts` | Treat this as Organization OS work. Mark `sales.crm.apiInterface` active only when scoped CRM resources and ontology are ready for API-backed actions. |
|
|
35
|
-
| Add CRM sidebar nav or a CRM route | `@elevasis/ui/features/crm` and `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md` | Prefer manifest/sidebar composition. Do not fork shared source first. |
|
|
35
|
+
| Add CRM sidebar nav or a CRM route | `@elevasis/ui/features/crm` and `operations/node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md` | Prefer manifest/sidebar composition. Do not fork shared source first. |
|
|
36
36
|
| Wrap a shared CRM page with project chrome | `DealsListPage`, `DealDetailPage`, `CrmOverview` from `@elevasis/ui/features/crm` | Keep route files thin and put project-specific logic in local feature modules. |
|
|
37
37
|
| Build a custom deal page | `useDealDetail`, `useDealNotes`, `useDealTasks`, `useExecuteAction` from `@elevasis/ui/hooks` | Use hooks for platform data and compose your own UI. |
|
|
38
38
|
| Add, hide, or replace deal action buttons | [customize-crm-actions.md](customize-crm-actions.md) | Start with the shared `crmActions` provider path; use project-owned UI when a custom workflow path is outside platform-known/default action dispatch constraints. |
|
|
@@ -56,7 +56,7 @@ CRM is a layered platform surface, not one component:
|
|
|
56
56
|
|
|
57
57
|
Read the generated contracts before changing typed boundaries:
|
|
58
58
|
|
|
59
|
-
`node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
59
|
+
`operations/node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
60
60
|
|
|
61
61
|
Look for the **CRM Platform Primitives** section. It includes deal stages, deal rows, task shapes, API schemas, action definitions, and the focused CRM workflow adapter map. The broader acquisition/list adapter maps live under **Lead Gen Platform Primitives**.
|
|
62
62
|
|
|
@@ -92,7 +92,7 @@ export const customCrmManifest: SystemModule = {
|
|
|
92
92
|
|
|
93
93
|
Then replace `crmManifest` with `customCrmManifest` in the local `SYSTEM_MANIFESTS` array and add the matching route under `ui/src/routes/crm/`.
|
|
94
94
|
|
|
95
|
-
For structural changes, compose `CrmSidebarTop`, `SubshellNavList`, `SubshellSidebarSection`, `MyTasksPanel`, and `QuickCreateActions`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
|
|
95
|
+
For structural changes, compose `CrmSidebarTop`, `SubshellNavList`, `SubshellSidebarSection`, `MyTasksPanel`, and `QuickCreateActions`. The full sidebar decision tree lives in `operations/node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
|
|
96
96
|
|
|
97
97
|
## 2. Wrap Shared CRM Pages
|
|
98
98
|
|
|
@@ -27,14 +27,14 @@ Lead gen is a layered platform surface, not one component. Shared packages own s
|
|
|
27
27
|
- **Acquisition substrate:** `acq_lists`, `acq_companies`, `acq_contacts`, `acq_artifacts`, list members, list companies, telemetry, and stateful transition shapes are exposed through generated contracts.
|
|
28
28
|
- **Core readers:** `getLeadGenStageCatalog`, `getAllBuildTemplates`, `getProspectingBuildTemplateOptions`, and `createBuildPlanSnapshotFromTemplateId` from `@elevasis/core` read caller-supplied Organization Model/template values. They do not ship tenant catalogs or default templates.
|
|
29
29
|
- **Workflow adapters:** `acqDb`, `list`, and `listBuilderWorkflow` from `@elevasis/sdk/worker` let workflows read and mutate lead-gen data through platform tools.
|
|
30
|
-
- **System API Interface:** `sales.lead-gen` uses the flat `system.apiInterface` marker for its API-backed lead-gen boundary. Readiness is derived from scoped resources, ontology bindings, required catalogs, and scoped topology grants. Lead-gen-to-CRM handoff uses scoped topology, not a top-level Bridge Contract or a hand-authored handoff interface field. Generated contract docs expose the current lead-gen shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
30
|
+
- **System API Interface:** `sales.lead-gen` uses the flat `system.apiInterface` marker for its API-backed lead-gen boundary. Readiness is derived from scoped resources, ontology bindings, required catalogs, and scoped topology grants. Lead-gen-to-CRM handoff uses scoped topology, not a top-level Bridge Contract or a hand-authored handoff interface field. Generated contract docs expose the current lead-gen shapes in `operations/node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
31
31
|
|
|
32
32
|
## Decision Table
|
|
33
33
|
|
|
34
34
|
| User wants | Start here | Notes |
|
|
35
35
|
| --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
36
36
|
| Change lead-gen System availability, labels, quick access, stage catalogs, build templates, workflow actions, resources, topology, or API readiness | `core/config/organization-model.ts` | Treat this as Organization OS work. Tenant projects own these values. Mark `sales.lead-gen.apiInterface` active only when scoped resources and ontology are ready for API-backed lead-gen actions. |
|
|
37
|
-
| Add lead-gen sidebar nav or a lead-gen route | `@elevasis/ui/features/lead-gen` and `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md` | Prefer manifest/sidebar composition. Do not fork shared source first. |
|
|
37
|
+
| Add lead-gen sidebar nav or a lead-gen route | `@elevasis/ui/features/lead-gen` and `operations/node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md` | Prefer manifest/sidebar composition. Do not fork shared source first. |
|
|
38
38
|
| Wrap a shared lead-gen page with project chrome | `LeadGenOverviewPage`, `LeadGenListsPage`, `LeadGenListDetailPage`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | Keep route files thin and put project-specific behavior in local feature modules. |
|
|
39
39
|
| Build a custom campaign/list workspace | `ListBuilderPage`, `useLists`, `useList`, `useListProgress`, `useListExecutions`, `useWorkflowExecution`, `useExecutionSSE` from `@elevasis/ui` | Use the shared builder when possible; otherwise compose hooks for platform data and workflow execution. |
|
|
40
40
|
| Add or change list-builder workflow buttons/forms | `ui/src/config/listActions.ts` plus `ElevasisUIProvider listActions={listActions}` / `createElevasisApp({ listActions })` | The shared UI owns the registry contract. The project owns workflow ids, action keys, Zod schemas, layouts, and default inputs. |
|
|
@@ -64,7 +64,7 @@ Lead gen is a layered platform surface, not one component. Shared packages own s
|
|
|
64
64
|
|
|
65
65
|
Read the generated contracts before changing typed boundaries:
|
|
66
66
|
|
|
67
|
-
`node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
67
|
+
`operations/node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
68
68
|
|
|
69
69
|
Look for the **Lead Gen Platform Primitives** section. It includes list shapes, company/contact shapes, list config, telemetry, artifacts, list members, stateful pipeline definitions, and workflow adapter method maps.
|
|
70
70
|
|
|
@@ -98,7 +98,7 @@ export const customLeadGenManifest: SystemModule = {
|
|
|
98
98
|
|
|
99
99
|
Then replace `leadGenManifest` with `customLeadGenManifest` in the local `SYSTEM_MANIFESTS` array and add the matching route under `ui/src/routes/lead-gen/`.
|
|
100
100
|
|
|
101
|
-
For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
|
|
101
|
+
For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `operations/node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
|
|
102
102
|
|
|
103
103
|
## 2. Wrap Shared Lead-Gen Pages
|
|
104
104
|
|
|
@@ -16,7 +16,7 @@ description: Terminology disambiguation for Organization OS concepts used in the
|
|
|
16
16
|
|
|
17
17
|
**Boundary invariant** -- Organization Model rule that local resources may bind local System ontology directly, while sibling-System API behavior needs explicit scope through topology. For System Interfaces, lead-gen-to-CRM handoff is allowed through the `sales.lead-gen/crm-handoff` consumer, the `sales.crm/api` provider, scoped resources, and a `metadata.systemInterfaceGrant` topology grant.
|
|
18
18
|
|
|
19
|
-
**Capability catalog** -- generated SDK-bundled reference that lists the closed System Interface profiles supported by the installed package, their required System paths, interface keys, and derived-readiness requirements. Current source: `packages/core/src/reference/_generated/system-interface-capabilities.md`; SDK mirror: `node_modules/@elevasis/sdk/reference/scaffold/reference/system-interface-capabilities.md`.
|
|
19
|
+
**Capability catalog** -- generated SDK-bundled reference that lists the closed System Interface profiles supported by the installed package, their required System paths, interface keys, and derived-readiness requirements. Current source: `packages/core/src/reference/_generated/system-interface-capabilities.md`; SDK mirror: `operations/node_modules/@elevasis/sdk/reference/scaffold/reference/system-interface-capabilities.md`.
|
|
20
20
|
|
|
21
21
|
**Contract** -- the publishable boundary a consumer depends on: Zod schemas, TypeScript types, provider props, resource definitions, resource ontology workflow I/O refs, or generated reference docs. Retired authored System/Bridge Contract surfaces such as `System.contracts`, `bridgeContracts`, and versioned `*@v1` OM contract IDs are not current authoring patterns.
|
|
22
22
|
|
|
@@ -1,246 +1,246 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Customizing Systems
|
|
3
|
-
description: One pattern for customizing system sidebars and pages -- set manifest.sidebar to a component composing the system's published pieces. Decision tree + three worked examples.
|
|
4
|
-
---
|
|
1
|
+
---
|
|
2
|
+
title: Customizing Systems
|
|
3
|
+
description: One pattern for customizing system sidebars and pages -- set manifest.sidebar to a component composing the system's published pieces. Decision tree + three worked examples.
|
|
4
|
+
---
|
|
5
5
|
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
|
|
6
6
|
<!-- Regenerate: pnpm scaffold:sync -->
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
# Customizing Systems
|
|
10
|
-
|
|
11
|
-
**Status:** 🟢 Stable
|
|
12
|
-
|
|
13
|
-
> **Requires `@elevasis/ui` >= 2.8.1.**
|
|
14
|
-
|
|
15
|
-
There is one pattern for customizing a system's sidebar or pages: set `sidebar` on your manifest override to a component that composes the system's published pieces. Pass custom `items` to swap the nav array, or compose `SidebarTop` + `SidebarMiddle` + exported panels directly for structural changes. Never fork a source file as a first resort -- if you copy source you own upstream drift forever.
|
|
16
|
-
|
|
17
|
-
## Decision Tree
|
|
18
|
-
|
|
19
|
-
**Adding or changing nav items?** Extend the exported item array and pass to `<*SidebarMiddle items={...}>`. This covers the vast majority of sidebar customization needs.
|
|
20
|
-
|
|
21
|
-
**Structural changes (injecting panels, reordering sections, wrapping sections)?** Compose `SidebarTop`, `SidebarMiddle`, and exported panels (`MyTasksPanel`, `QuickCreateActions`) directly into your own component and assign it to the manifest's `sidebar` field.
|
|
22
|
-
|
|
23
|
-
**Wrapping pages?** Import the system's page component and wrap with custom chrome in a route file.
|
|
24
|
-
|
|
25
|
-
**Neither fits?** The missing export is a bug in `@elevasis/ui` -- file an issue. Copying source is physically possible but unsupported. You own upstream drift forever.
|
|
26
|
-
|
|
27
|
-
## Worked Example 1: Sidebar Nav Extension
|
|
28
|
-
|
|
29
|
-
Extend CRM's nav by spreading `CRM_ITEMS` and appending a Reports entry. Pass the extended array to `CrmSidebarMiddle`.
|
|
30
|
-
|
|
31
|
-
Wire the override in `ui/src/routes/__root.tsx`, where system manifests are assembled before being passed to `ElevasisSystemsProvider`.
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
// ui/src/routes/__root.tsx (excerpt -- add after existing imports)
|
|
35
|
-
|
|
36
|
-
import { crmManifest, CrmSidebar, CrmSidebarMiddle, CRM_ITEMS } from '@elevasis/ui/features/crm'
|
|
37
|
-
import { IconFileText } from '@tabler/icons-react'
|
|
38
|
-
import type { NavItem } from '@elevasis/ui/layout'
|
|
39
|
-
|
|
40
|
-
// Extend the published CRM nav array with a project-specific Reports entry.
|
|
41
|
-
const customCrmItems: NavItem[] = [
|
|
42
|
-
...CRM_ITEMS,
|
|
43
|
-
{ label: 'Reports', to: '/crm/reports', icon: IconFileText, exact: false }
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
// Compose CrmSidebar (Top + Middle) with the extended item list.
|
|
47
|
-
const MyCrmSidebar = () => (
|
|
48
|
-
<CrmSidebar>
|
|
49
|
-
<CrmSidebarMiddle items={customCrmItems} />
|
|
50
|
-
</CrmSidebar>
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
// Override the manifest's sidebar, keeping every other field unchanged.
|
|
54
|
-
const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Then swap `crmManifest` for `customCrmManifest` in the `SYSTEM_MANIFESTS` array:
|
|
58
|
-
|
|
59
|
-
```tsx
|
|
60
|
-
// In the SYSTEM_MANIFESTS array (same file)
|
|
61
|
-
const SYSTEM_MANIFESTS: SystemModule[] = [
|
|
62
|
-
leadGenManifest,
|
|
63
|
-
customCrmManifest, // <-- replaces crmManifest
|
|
64
|
-
deliveryManifest,
|
|
65
|
-
operationsManifest,
|
|
66
|
-
monitoringManifest,
|
|
67
|
-
settingsManifest
|
|
68
|
-
]
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Your new `/crm/reports` route file still needs to be created in `ui/src/routes/crm/` -- the nav item just controls the link.
|
|
72
|
-
|
|
73
|
-
## Worked Example 2: Sidebar with Injected Panel
|
|
74
|
-
|
|
75
|
-
Inject a custom panel between the nav list and the My Tasks section. Compose the parts explicitly rather than relying on `CrmSidebarMiddle`'s default layout.
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
// ui/src/routes/__root.tsx (excerpt)
|
|
79
|
-
|
|
80
|
-
import {
|
|
81
|
-
crmManifest,
|
|
82
|
-
CrmSidebar,
|
|
83
|
-
CrmSidebarMiddle,
|
|
84
|
-
CrmSidebarTop,
|
|
85
|
-
MyTasksPanel,
|
|
86
|
-
QuickCreateActions,
|
|
87
|
-
CRM_ITEMS
|
|
88
|
-
} from '@elevasis/ui/features/crm'
|
|
89
|
-
import { SubshellNavList, SubshellSidebarSection } from '@elevasis/ui/layout'
|
|
90
|
-
import { Stack } from '@mantine/core'
|
|
91
|
-
import { IconChartBar } from '@tabler/icons-react'
|
|
92
|
-
|
|
93
|
-
// Custom middle that inserts a Pipeline Health panel between nav and My Tasks.
|
|
94
|
-
const MyCrmMiddle = () => (
|
|
95
|
-
<Stack gap={0} style={{ flex: 1, overflowY: 'auto' }}>
|
|
96
|
-
<Stack gap={0} p="sm">
|
|
97
|
-
<SubshellNavList items={CRM_ITEMS} />
|
|
98
|
-
</Stack>
|
|
99
|
-
{/* Inject custom panel here */}
|
|
100
|
-
<SubshellSidebarSection icon={IconChartBar} label="Pipeline Health" withTopBorder />
|
|
101
|
-
<Stack gap={0} p="sm">
|
|
102
|
-
<PipelineHealthWidget />
|
|
103
|
-
</Stack>
|
|
104
|
-
{/* Preserve the My Tasks section from the default middle */}
|
|
105
|
-
<MyTasksPanel footer={<QuickCreateActions showSectionLabel={false} />} showSectionLabel />
|
|
106
|
-
</Stack>
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
// Full sidebar: Top stays unchanged, Middle is replaced.
|
|
110
|
-
const MyCrmSidebar = () => (
|
|
111
|
-
<CrmSidebar>
|
|
112
|
-
<MyCrmMiddle />
|
|
113
|
-
</CrmSidebar>
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
`PipelineHealthWidget` is a component you define in `ui/src/features/crm/` or `ui/src/lib/components/`. Keep route files and `__root.tsx` thin -- push component logic into feature modules.
|
|
120
|
-
|
|
121
|
-
## Worked Example 3: Page Wrapping
|
|
122
|
-
|
|
123
|
-
Import a system's page component and wrap it with custom chrome in a route file. This keeps the system's business logic intact while adding project-specific framing (banners, secondary nav, analytics context, etc.).
|
|
124
|
-
|
|
125
|
-
The exported CRM page components are `DealsListPage` and `DealDetailPage`:
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
// ui/src/routes/crm/deals.index.tsx
|
|
129
|
-
|
|
130
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
131
|
-
import { DealsListPage } from '@elevasis/ui/features/crm'
|
|
132
|
-
import { ProtectedRoute } from '@/features/auth'
|
|
133
|
-
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
134
|
-
import { Stack } from '@mantine/core'
|
|
135
|
-
|
|
136
|
-
export const Route = createFileRoute('/crm/deals/')({
|
|
137
|
-
component: DealsListPageGuarded
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
function DealsListPageGuarded() {
|
|
141
|
-
return (
|
|
142
|
-
<ProtectedRoute>
|
|
143
|
-
<Stack gap={0}>
|
|
144
|
-
<ProjectAnnouncementBanner context="crm-deals" />
|
|
145
|
-
<DealsListPage />
|
|
146
|
-
</Stack>
|
|
147
|
-
</ProtectedRoute>
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
The pattern is identical for `DealDetailPage`. Import the published component, wrap it in your chrome, export as the route component. No forking, no copying source.
|
|
153
|
-
|
|
154
|
-
## Delivery: Three-Section Sidebar
|
|
155
|
-
|
|
156
|
-
Delivery keeps three named sections (Projects, Work, Communication). It exports three separate arrays and `ProjectsSidebarMiddle` accepts three matching optional props -- one per section. There is no single `items` prop.
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
// Adding items to an existing delivery section
|
|
160
|
-
import {
|
|
161
|
-
deliveryManifest,
|
|
162
|
-
ProjectsSidebar,
|
|
163
|
-
ProjectsSidebarMiddle,
|
|
164
|
-
DELIVERY_PROJECT_ITEMS,
|
|
165
|
-
DELIVERY_WORK_ITEMS,
|
|
166
|
-
DELIVERY_COMMUNICATION_ITEMS
|
|
167
|
-
} from '@elevasis/ui/features/delivery'
|
|
168
|
-
import type { NavItem } from '@elevasis/ui/layout'
|
|
169
|
-
import { IconChartBar } from '@tabler/icons-react'
|
|
170
|
-
|
|
171
|
-
const customProjectItems: NavItem[] = [
|
|
172
|
-
...DELIVERY_PROJECT_ITEMS,
|
|
173
|
-
{ label: 'Analytics', to: '/projects/analytics', icon: IconChartBar, exact: false }
|
|
174
|
-
]
|
|
175
|
-
|
|
176
|
-
const MyDeliverySidebar = () => (
|
|
177
|
-
<ProjectsSidebar>
|
|
178
|
-
<ProjectsSidebarMiddle
|
|
179
|
-
projectItems={customProjectItems}
|
|
180
|
-
workItems={DELIVERY_WORK_ITEMS}
|
|
181
|
-
communicationItems={DELIVERY_COMMUNICATION_ITEMS}
|
|
182
|
-
/>
|
|
183
|
-
</ProjectsSidebar>
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
const customDeliveryManifest = { ...deliveryManifest, sidebar: MyDeliverySidebar }
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
**Adding a new section** (not extending an existing one) requires composing from primitives. Use `SubshellSidebarSection` as the section header and `SubshellNavList` to render items inside it:
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
import {
|
|
193
|
-
ProjectsSidebar,
|
|
194
|
-
DELIVERY_PROJECT_ITEMS,
|
|
195
|
-
DELIVERY_WORK_ITEMS,
|
|
196
|
-
DELIVERY_COMMUNICATION_ITEMS
|
|
197
|
-
} from '@elevasis/ui/features/delivery'
|
|
198
|
-
import { SubshellSidebarSection, SubshellNavList } from '@elevasis/ui/layout'
|
|
199
|
-
import { Stack } from '@mantine/core'
|
|
200
|
-
import { IconChartBar, IconListCheck, IconMessageCircle, IconBriefcase } from '@tabler/icons-react'
|
|
201
|
-
import type { NavItem } from '@elevasis/ui/layout'
|
|
202
|
-
|
|
203
|
-
const ANALYTICS_ITEMS: NavItem[] = [
|
|
204
|
-
{ label: 'Dashboard', to: '/projects/analytics', icon: IconChartBar, exact: true }
|
|
205
|
-
]
|
|
206
|
-
|
|
207
|
-
const MyDeliveryMiddle = () => (
|
|
208
|
-
<Stack gap={0} style={{ flex: 1, overflowY: 'auto' }}>
|
|
209
|
-
<Stack gap={0} p="sm">
|
|
210
|
-
<SubshellNavList items={DELIVERY_PROJECT_ITEMS} />
|
|
211
|
-
</Stack>
|
|
212
|
-
<SubshellSidebarSection icon={IconListCheck} label="Work" withTopBorder />
|
|
213
|
-
<Stack gap={0} p="sm">
|
|
214
|
-
<SubshellNavList items={DELIVERY_WORK_ITEMS} />
|
|
215
|
-
</Stack>
|
|
216
|
-
<SubshellSidebarSection icon={IconMessageCircle} label="Communication" withTopBorder />
|
|
217
|
-
<Stack gap={0} p="sm">
|
|
218
|
-
<SubshellNavList items={DELIVERY_COMMUNICATION_ITEMS} />
|
|
219
|
-
</Stack>
|
|
220
|
-
<SubshellSidebarSection icon={IconChartBar} label="Analytics" withTopBorder />
|
|
221
|
-
<Stack gap={0} p="sm">
|
|
222
|
-
<SubshellNavList items={ANALYTICS_ITEMS} />
|
|
223
|
-
</Stack>
|
|
224
|
-
</Stack>
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
const MyDeliverySidebar = () => <ProjectsSidebar><MyDeliveryMiddle /></ProjectsSidebar>
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
This is the same compose-from-primitives path CRM consumers use for structural changes. No special API -- just `SubshellSidebarSection` + `SubshellNavList` from `@elevasis/ui/layout`.
|
|
231
|
-
|
|
232
|
-
For broader CRM extension work across pages, hooks, actions, workflows, and org-model boundaries, start with `node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-crm.md`.
|
|
233
|
-
|
|
234
|
-
For broader lead-gen extension work across pages, hooks, list/member state, artifacts, workflows, and org-model boundaries, start with `node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-lead-gen.md`.
|
|
235
|
-
|
|
236
|
-
## Imports Cheat Sheet
|
|
237
|
-
|
|
238
|
-
| What you need | Import from |
|
|
239
|
-
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
240
|
-
| `CRM_ITEMS`, `CrmSidebar`, `CrmSidebarTop`, `CrmSidebarMiddle`, `MyTasksPanel`, `QuickCreateActions`, `DealsListPage`, `DealDetailPage`, `crmManifest` | `@elevasis/ui/features/crm` |
|
|
241
|
-
| `LEAD_GEN_ITEMS`, `LeadGenSidebar`, `LeadGenSidebarTop`, `LeadGenSidebarMiddle`, `leadGenManifest` | `@elevasis/ui/features/lead-gen` |
|
|
242
|
-
| `DELIVERY_PROJECT_ITEMS`, `DELIVERY_WORK_ITEMS`, `DELIVERY_COMMUNICATION_ITEMS`, `ProjectsSidebar`, `ProjectsSidebarTop`, `ProjectsSidebarMiddle`, `deliveryManifest` | `@elevasis/ui/features/delivery` |
|
|
243
|
-
| `SubshellNavList`, `SubshellNavItem`, `SubshellSidebarSection`, `NavItem` | `@elevasis/ui/layout` |
|
|
244
|
-
| `SystemModule` | `@elevasis/ui/provider` |
|
|
245
|
-
|
|
246
|
-
Operations, monitoring, and settings have no customizable sidebar items. Operations sidebar is route-aware dispatch and not extended via the `items` pattern.
|
|
8
|
+
|
|
9
|
+
# Customizing Systems
|
|
10
|
+
|
|
11
|
+
**Status:** 🟢 Stable
|
|
12
|
+
|
|
13
|
+
> **Requires `@elevasis/ui` >= 2.8.1.**
|
|
14
|
+
|
|
15
|
+
There is one pattern for customizing a system's sidebar or pages: set `sidebar` on your manifest override to a component that composes the system's published pieces. Pass custom `items` to swap the nav array, or compose `SidebarTop` + `SidebarMiddle` + exported panels directly for structural changes. Never fork a source file as a first resort -- if you copy source you own upstream drift forever.
|
|
16
|
+
|
|
17
|
+
## Decision Tree
|
|
18
|
+
|
|
19
|
+
**Adding or changing nav items?** Extend the exported item array and pass to `<*SidebarMiddle items={...}>`. This covers the vast majority of sidebar customization needs.
|
|
20
|
+
|
|
21
|
+
**Structural changes (injecting panels, reordering sections, wrapping sections)?** Compose `SidebarTop`, `SidebarMiddle`, and exported panels (`MyTasksPanel`, `QuickCreateActions`) directly into your own component and assign it to the manifest's `sidebar` field.
|
|
22
|
+
|
|
23
|
+
**Wrapping pages?** Import the system's page component and wrap with custom chrome in a route file.
|
|
24
|
+
|
|
25
|
+
**Neither fits?** The missing export is a bug in `@elevasis/ui` -- file an issue. Copying source is physically possible but unsupported. You own upstream drift forever.
|
|
26
|
+
|
|
27
|
+
## Worked Example 1: Sidebar Nav Extension
|
|
28
|
+
|
|
29
|
+
Extend CRM's nav by spreading `CRM_ITEMS` and appending a Reports entry. Pass the extended array to `CrmSidebarMiddle`.
|
|
30
|
+
|
|
31
|
+
Wire the override in `ui/src/routes/__root.tsx`, where system manifests are assembled before being passed to `ElevasisSystemsProvider`.
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
// ui/src/routes/__root.tsx (excerpt -- add after existing imports)
|
|
35
|
+
|
|
36
|
+
import { crmManifest, CrmSidebar, CrmSidebarMiddle, CRM_ITEMS } from '@elevasis/ui/features/crm'
|
|
37
|
+
import { IconFileText } from '@tabler/icons-react'
|
|
38
|
+
import type { NavItem } from '@elevasis/ui/layout'
|
|
39
|
+
|
|
40
|
+
// Extend the published CRM nav array with a project-specific Reports entry.
|
|
41
|
+
const customCrmItems: NavItem[] = [
|
|
42
|
+
...CRM_ITEMS,
|
|
43
|
+
{ label: 'Reports', to: '/crm/reports', icon: IconFileText, exact: false }
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
// Compose CrmSidebar (Top + Middle) with the extended item list.
|
|
47
|
+
const MyCrmSidebar = () => (
|
|
48
|
+
<CrmSidebar>
|
|
49
|
+
<CrmSidebarMiddle items={customCrmItems} />
|
|
50
|
+
</CrmSidebar>
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
// Override the manifest's sidebar, keeping every other field unchanged.
|
|
54
|
+
const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then swap `crmManifest` for `customCrmManifest` in the `SYSTEM_MANIFESTS` array:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// In the SYSTEM_MANIFESTS array (same file)
|
|
61
|
+
const SYSTEM_MANIFESTS: SystemModule[] = [
|
|
62
|
+
leadGenManifest,
|
|
63
|
+
customCrmManifest, // <-- replaces crmManifest
|
|
64
|
+
deliveryManifest,
|
|
65
|
+
operationsManifest,
|
|
66
|
+
monitoringManifest,
|
|
67
|
+
settingsManifest
|
|
68
|
+
]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Your new `/crm/reports` route file still needs to be created in `ui/src/routes/crm/` -- the nav item just controls the link.
|
|
72
|
+
|
|
73
|
+
## Worked Example 2: Sidebar with Injected Panel
|
|
74
|
+
|
|
75
|
+
Inject a custom panel between the nav list and the My Tasks section. Compose the parts explicitly rather than relying on `CrmSidebarMiddle`'s default layout.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// ui/src/routes/__root.tsx (excerpt)
|
|
79
|
+
|
|
80
|
+
import {
|
|
81
|
+
crmManifest,
|
|
82
|
+
CrmSidebar,
|
|
83
|
+
CrmSidebarMiddle,
|
|
84
|
+
CrmSidebarTop,
|
|
85
|
+
MyTasksPanel,
|
|
86
|
+
QuickCreateActions,
|
|
87
|
+
CRM_ITEMS
|
|
88
|
+
} from '@elevasis/ui/features/crm'
|
|
89
|
+
import { SubshellNavList, SubshellSidebarSection } from '@elevasis/ui/layout'
|
|
90
|
+
import { Stack } from '@mantine/core'
|
|
91
|
+
import { IconChartBar } from '@tabler/icons-react'
|
|
92
|
+
|
|
93
|
+
// Custom middle that inserts a Pipeline Health panel between nav and My Tasks.
|
|
94
|
+
const MyCrmMiddle = () => (
|
|
95
|
+
<Stack gap={0} style={{ flex: 1, overflowY: 'auto' }}>
|
|
96
|
+
<Stack gap={0} p="sm">
|
|
97
|
+
<SubshellNavList items={CRM_ITEMS} />
|
|
98
|
+
</Stack>
|
|
99
|
+
{/* Inject custom panel here */}
|
|
100
|
+
<SubshellSidebarSection icon={IconChartBar} label="Pipeline Health" withTopBorder />
|
|
101
|
+
<Stack gap={0} p="sm">
|
|
102
|
+
<PipelineHealthWidget />
|
|
103
|
+
</Stack>
|
|
104
|
+
{/* Preserve the My Tasks section from the default middle */}
|
|
105
|
+
<MyTasksPanel footer={<QuickCreateActions showSectionLabel={false} />} showSectionLabel />
|
|
106
|
+
</Stack>
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
// Full sidebar: Top stays unchanged, Middle is replaced.
|
|
110
|
+
const MyCrmSidebar = () => (
|
|
111
|
+
<CrmSidebar>
|
|
112
|
+
<MyCrmMiddle />
|
|
113
|
+
</CrmSidebar>
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`PipelineHealthWidget` is a component you define in `ui/src/features/crm/` or `ui/src/lib/components/`. Keep route files and `__root.tsx` thin -- push component logic into feature modules.
|
|
120
|
+
|
|
121
|
+
## Worked Example 3: Page Wrapping
|
|
122
|
+
|
|
123
|
+
Import a system's page component and wrap it with custom chrome in a route file. This keeps the system's business logic intact while adding project-specific framing (banners, secondary nav, analytics context, etc.).
|
|
124
|
+
|
|
125
|
+
The exported CRM page components are `DealsListPage` and `DealDetailPage`:
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
// ui/src/routes/crm/deals.index.tsx
|
|
129
|
+
|
|
130
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
131
|
+
import { DealsListPage } from '@elevasis/ui/features/crm'
|
|
132
|
+
import { ProtectedRoute } from '@/features/auth'
|
|
133
|
+
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
134
|
+
import { Stack } from '@mantine/core'
|
|
135
|
+
|
|
136
|
+
export const Route = createFileRoute('/crm/deals/')({
|
|
137
|
+
component: DealsListPageGuarded
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
function DealsListPageGuarded() {
|
|
141
|
+
return (
|
|
142
|
+
<ProtectedRoute>
|
|
143
|
+
<Stack gap={0}>
|
|
144
|
+
<ProjectAnnouncementBanner context="crm-deals" />
|
|
145
|
+
<DealsListPage />
|
|
146
|
+
</Stack>
|
|
147
|
+
</ProtectedRoute>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The pattern is identical for `DealDetailPage`. Import the published component, wrap it in your chrome, export as the route component. No forking, no copying source.
|
|
153
|
+
|
|
154
|
+
## Delivery: Three-Section Sidebar
|
|
155
|
+
|
|
156
|
+
Delivery keeps three named sections (Projects, Work, Communication). It exports three separate arrays and `ProjectsSidebarMiddle` accepts three matching optional props -- one per section. There is no single `items` prop.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// Adding items to an existing delivery section
|
|
160
|
+
import {
|
|
161
|
+
deliveryManifest,
|
|
162
|
+
ProjectsSidebar,
|
|
163
|
+
ProjectsSidebarMiddle,
|
|
164
|
+
DELIVERY_PROJECT_ITEMS,
|
|
165
|
+
DELIVERY_WORK_ITEMS,
|
|
166
|
+
DELIVERY_COMMUNICATION_ITEMS
|
|
167
|
+
} from '@elevasis/ui/features/delivery'
|
|
168
|
+
import type { NavItem } from '@elevasis/ui/layout'
|
|
169
|
+
import { IconChartBar } from '@tabler/icons-react'
|
|
170
|
+
|
|
171
|
+
const customProjectItems: NavItem[] = [
|
|
172
|
+
...DELIVERY_PROJECT_ITEMS,
|
|
173
|
+
{ label: 'Analytics', to: '/projects/analytics', icon: IconChartBar, exact: false }
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
const MyDeliverySidebar = () => (
|
|
177
|
+
<ProjectsSidebar>
|
|
178
|
+
<ProjectsSidebarMiddle
|
|
179
|
+
projectItems={customProjectItems}
|
|
180
|
+
workItems={DELIVERY_WORK_ITEMS}
|
|
181
|
+
communicationItems={DELIVERY_COMMUNICATION_ITEMS}
|
|
182
|
+
/>
|
|
183
|
+
</ProjectsSidebar>
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
const customDeliveryManifest = { ...deliveryManifest, sidebar: MyDeliverySidebar }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Adding a new section** (not extending an existing one) requires composing from primitives. Use `SubshellSidebarSection` as the section header and `SubshellNavList` to render items inside it:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import {
|
|
193
|
+
ProjectsSidebar,
|
|
194
|
+
DELIVERY_PROJECT_ITEMS,
|
|
195
|
+
DELIVERY_WORK_ITEMS,
|
|
196
|
+
DELIVERY_COMMUNICATION_ITEMS
|
|
197
|
+
} from '@elevasis/ui/features/delivery'
|
|
198
|
+
import { SubshellSidebarSection, SubshellNavList } from '@elevasis/ui/layout'
|
|
199
|
+
import { Stack } from '@mantine/core'
|
|
200
|
+
import { IconChartBar, IconListCheck, IconMessageCircle, IconBriefcase } from '@tabler/icons-react'
|
|
201
|
+
import type { NavItem } from '@elevasis/ui/layout'
|
|
202
|
+
|
|
203
|
+
const ANALYTICS_ITEMS: NavItem[] = [
|
|
204
|
+
{ label: 'Dashboard', to: '/projects/analytics', icon: IconChartBar, exact: true }
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
const MyDeliveryMiddle = () => (
|
|
208
|
+
<Stack gap={0} style={{ flex: 1, overflowY: 'auto' }}>
|
|
209
|
+
<Stack gap={0} p="sm">
|
|
210
|
+
<SubshellNavList items={DELIVERY_PROJECT_ITEMS} />
|
|
211
|
+
</Stack>
|
|
212
|
+
<SubshellSidebarSection icon={IconListCheck} label="Work" withTopBorder />
|
|
213
|
+
<Stack gap={0} p="sm">
|
|
214
|
+
<SubshellNavList items={DELIVERY_WORK_ITEMS} />
|
|
215
|
+
</Stack>
|
|
216
|
+
<SubshellSidebarSection icon={IconMessageCircle} label="Communication" withTopBorder />
|
|
217
|
+
<Stack gap={0} p="sm">
|
|
218
|
+
<SubshellNavList items={DELIVERY_COMMUNICATION_ITEMS} />
|
|
219
|
+
</Stack>
|
|
220
|
+
<SubshellSidebarSection icon={IconChartBar} label="Analytics" withTopBorder />
|
|
221
|
+
<Stack gap={0} p="sm">
|
|
222
|
+
<SubshellNavList items={ANALYTICS_ITEMS} />
|
|
223
|
+
</Stack>
|
|
224
|
+
</Stack>
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
const MyDeliverySidebar = () => <ProjectsSidebar><MyDeliveryMiddle /></ProjectsSidebar>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
This is the same compose-from-primitives path CRM consumers use for structural changes. No special API -- just `SubshellSidebarSection` + `SubshellNavList` from `@elevasis/ui/layout`.
|
|
231
|
+
|
|
232
|
+
For broader CRM extension work across pages, hooks, actions, workflows, and org-model boundaries, start with `operations/node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-crm.md`.
|
|
233
|
+
|
|
234
|
+
For broader lead-gen extension work across pages, hooks, list/member state, artifacts, workflows, and org-model boundaries, start with `operations/node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-lead-gen.md`.
|
|
235
|
+
|
|
236
|
+
## Imports Cheat Sheet
|
|
237
|
+
|
|
238
|
+
| What you need | Import from |
|
|
239
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
240
|
+
| `CRM_ITEMS`, `CrmSidebar`, `CrmSidebarTop`, `CrmSidebarMiddle`, `MyTasksPanel`, `QuickCreateActions`, `DealsListPage`, `DealDetailPage`, `crmManifest` | `@elevasis/ui/features/crm` |
|
|
241
|
+
| `LEAD_GEN_ITEMS`, `LeadGenSidebar`, `LeadGenSidebarTop`, `LeadGenSidebarMiddle`, `leadGenManifest` | `@elevasis/ui/features/lead-gen` |
|
|
242
|
+
| `DELIVERY_PROJECT_ITEMS`, `DELIVERY_WORK_ITEMS`, `DELIVERY_COMMUNICATION_ITEMS`, `ProjectsSidebar`, `ProjectsSidebarTop`, `ProjectsSidebarMiddle`, `deliveryManifest` | `@elevasis/ui/features/delivery` |
|
|
243
|
+
| `SubshellNavList`, `SubshellNavItem`, `SubshellSidebarSection`, `NavItem` | `@elevasis/ui/layout` |
|
|
244
|
+
| `SystemModule` | `@elevasis/ui/provider` |
|
|
245
|
+
|
|
246
|
+
Operations, monitoring, and settings have no customizable sidebar items. Operations sidebar is route-aware dispatch and not extended via the `items` pattern.
|
|
@@ -134,7 +134,7 @@ Call any supported LLM from your workflow with no API keys required. Keys are re
|
|
|
134
134
|
| ------------ | --------------------------------------------------------- |
|
|
135
135
|
| `google` | `gemini-3-flash-preview`, `gemini-3.1-flash-lite-preview` |
|
|
136
136
|
| `openai` | `gpt-5`, `gpt-5.4-mini`, `gpt-5.4-nano` |
|
|
137
|
-
| `anthropic` | `claude-sonnet-
|
|
137
|
+
| `anthropic` | `claude-sonnet-5` |
|
|
138
138
|
| `openrouter` | `openrouter/z-ai/glm-5` |
|
|
139
139
|
|
|
140
140
|
**Key params:** `provider`, `model`, `messages` (`{ role, content }[]`), `responseSchema` (optional JSON Schema), `temperature` (optional).
|
|
@@ -19,7 +19,7 @@ The approach: tighten types where misconfiguration is a realistic foot-gun, and
|
|
|
19
19
|
// Required -- both fields must be specified inline
|
|
20
20
|
await llm.generate({
|
|
21
21
|
provider: 'anthropic',
|
|
22
|
-
model: 'claude-sonnet-
|
|
22
|
+
model: 'claude-sonnet-5',
|
|
23
23
|
messages: [...],
|
|
24
24
|
responseSchema: {...},
|
|
25
25
|
})
|