@elevasis/sdk 1.13.1 → 1.14.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 +1 -1
- package/dist/index.d.ts +1007 -1161
- package/dist/index.js +10 -2
- package/dist/test-utils/index.d.ts +945 -1099
- package/dist/test-utils/index.js +5 -3
- package/dist/worker/index.js +0 -2
- package/package.json +2 -2
- package/reference/claude-config/rules/ui.md +8 -8
- package/reference/claude-config/rules/vibe.md +11 -11
- package/reference/scaffold/index.mdx +8 -8
- package/reference/scaffold/recipes/customize-crm-actions.md +408 -408
- package/reference/scaffold/recipes/extend-crm.md +252 -252
- package/reference/scaffold/recipes/index.md +20 -20
- package/reference/scaffold/reference/contracts.md +1479 -1767
|
@@ -1,255 +1,255 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Build and Extend CRM
|
|
3
|
-
description: Map the CRM platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, action definitions, workflow adapters, contracts, and org-model extension boundaries.
|
|
4
|
-
---
|
|
1
|
+
---
|
|
2
|
+
title: Build and Extend CRM
|
|
3
|
+
description: Map the CRM platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, action definitions, workflow adapters, contracts, and org-model extension boundaries.
|
|
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
|
-
# Build and Extend CRM
|
|
10
|
-
|
|
11
|
-
Use this recipe when a downstream project wants to build on the shared CRM instead of forking it.
|
|
12
|
-
|
|
13
|
-
Good trigger phrases:
|
|
14
|
-
|
|
15
|
-
- "Add a CRM reports page."
|
|
16
|
-
- "Build a custom deal workspace."
|
|
17
|
-
- "Read and update deals from a workflow."
|
|
18
|
-
- "Add a Send Quote button."
|
|
19
|
-
- "Change the CRM pipeline stages for this business."
|
|
20
|
-
|
|
21
|
-
CRM is a layered platform surface, not one component:
|
|
22
|
-
|
|
23
|
-
- **Organization OS:** feature gates, sales pipeline semantics, quick access, and labels live in the organization model.
|
|
24
|
-
- **Shared UI:** CRM pages, sidebars, workbench panels, overview widgets, and Kanban/detail components live in `@elevasis/ui`.
|
|
25
|
-
- **Headless hooks:** deal, company, contact, note, task, list, transition, and action hooks live under `@elevasis/ui/hooks`.
|
|
26
|
-
- **Action system:** `ActionDef`, `DEFAULT_CRM_ACTIONS`, `deriveActions`, and provider-level `crmActions` configure deal actions.
|
|
27
|
-
- **Workflow adapters:** `crm` and `acqDb` from `@elevasis/sdk/worker` let workflows read and mutate CRM/acquisition data through platform tools.
|
|
28
|
-
- **Contracts:** generated contract docs expose the current CRM shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
29
|
-
|
|
30
|
-
## Decision Table
|
|
31
|
-
|
|
32
|
-
| User wants | Start here | Notes |
|
|
33
|
-
| --- | --- | --- |
|
|
34
|
-
| Change CRM feature availability, labels, or pipeline stages | `core/config/organization-model.ts` plus `core/config/organization-model.examples.ts` | Treat this as Organization OS work. Use the project's configure ceremony if available. |
|
|
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. |
|
|
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
|
-
| Build a custom deal page | `useDealDetail`, `useDealNotes`, `useDealTasks`, `useExecuteAction` from `@elevasis/ui/hooks` | Use hooks for platform data and compose your own UI. |
|
|
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. |
|
|
39
|
-
| Read or mutate CRM data inside a workflow | `crm` or `acqDb` from `@elevasis/sdk/worker` | `organizationId` is injected server-side by the platform dispatcher. Do not pass it from workflow code. |
|
|
40
|
-
| Add a new persisted CRM column or table | Platform/API migration work, not just scaffold work | Update DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts together. |
|
|
41
|
-
|
|
42
|
-
## Published CRM Surfaces
|
|
43
|
-
|
|
44
|
-
| Surface | Import from | Use for |
|
|
45
|
-
| --- | --- | --- |
|
|
46
|
-
| `crmManifest`, `CRM_ITEMS`, `CrmSidebar`, `CrmSidebarTop`, `CrmSidebarMiddle` | `@elevasis/ui/features/crm` | Feature registration and sidebar composition |
|
|
47
|
-
| `CrmOverview`, `DealsListPage`, `DealDetailPage`, `CompanyDetailPage` | `@elevasis/ui/features/crm` | Shared CRM pages you can route to or wrap |
|
|
48
|
-
| `MyTasksPanel`, `SavedViewsPanel`, `QuickCreateActions` | `@elevasis/ui/features/crm` | Workbench/sidebar panels for custom CRM sidebars |
|
|
49
|
-
| `PipelineFunnelWidget`, `ActivityFeedWidget`, `MetricsStrip` | `@elevasis/ui/features/crm` | Overview widgets for custom dashboards |
|
|
50
|
-
| `KanbanBoard`, `DealKanbanCard`, `DealDrawer` | `@elevasis/ui/components` | Lower-level CRM deal UI primitives |
|
|
51
|
-
| `useDeals`, `useDealDetail`, `useTransitionItem`, `useExecuteAction`, `useDealNotes`, `useDealTasks` | `@elevasis/ui/hooks` | Headless deal and task data access |
|
|
52
|
-
| `useCompanies`, `useContacts`, `useLists`, `useBatchTelemetry` | `@elevasis/ui/hooks` | Acquisition substrate data access |
|
|
53
|
-
| `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, and organization context |
|
|
54
|
-
| `ActionDef`, `DEFAULT_CRM_ACTIONS`, `deriveActions` | `@elevasis/sdk` | Deal action configuration and render-time derivation |
|
|
55
|
-
| `crm`, `acqDb` | `@elevasis/sdk/worker` | Workflow-side CRM/acquisition platform adapters |
|
|
56
|
-
|
|
57
|
-
Read the generated contracts before changing typed boundaries:
|
|
58
|
-
|
|
59
|
-
`node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
## 1. Extend CRM Navigation or Sidebar
|
|
64
|
-
|
|
65
|
-
For a simple nav addition, extend `CRM_ITEMS` and override the CRM feature manifest:
|
|
66
|
-
|
|
67
|
-
```tsx
|
|
68
|
-
// ui/src/routes/__root.tsx
|
|
69
|
-
import { crmManifest, CRM_ITEMS, CrmSidebar, CrmSidebarMiddle } from '@elevasis/ui/features/crm'
|
|
70
|
-
import type { FeatureModule } from '@elevasis/ui/provider'
|
|
71
|
-
import type { NavItem } from '@elevasis/ui/layout'
|
|
72
|
-
import { IconFileText } from '@tabler/icons-react'
|
|
73
|
-
|
|
74
|
-
const customCrmItems: NavItem[] = [
|
|
75
|
-
...CRM_ITEMS,
|
|
76
|
-
{ label: 'Reports', to: '/crm/reports', icon: IconFileText, exact: false }
|
|
77
|
-
]
|
|
78
|
-
|
|
79
|
-
const CustomCrmSidebar = () => (
|
|
80
|
-
<CrmSidebar>
|
|
81
|
-
<CrmSidebarMiddle items={customCrmItems} />
|
|
82
|
-
</CrmSidebar>
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
export const customCrmManifest: FeatureModule = {
|
|
86
|
-
...crmManifest,
|
|
87
|
-
sidebar: CustomCrmSidebar
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Then replace `crmManifest` with `customCrmManifest` in the local `FEATURE_MANIFESTS` array and add the matching route under `ui/src/routes/crm/`.
|
|
92
|
-
|
|
93
|
-
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`.
|
|
94
|
-
|
|
95
|
-
## 2. Wrap Shared CRM Pages
|
|
96
|
-
|
|
97
|
-
If the shared page is close, keep it and wrap it:
|
|
98
|
-
|
|
99
|
-
```tsx
|
|
100
|
-
// ui/src/routes/crm/deals.index.tsx
|
|
101
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
102
|
-
import { DealsListPage } from '@elevasis/ui/features/crm'
|
|
103
|
-
import { Stack } from '@mantine/core'
|
|
104
|
-
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
105
|
-
|
|
106
|
-
export const Route = createFileRoute('/crm/deals/')({
|
|
107
|
-
component: DealsRoute
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
function DealsRoute() {
|
|
111
|
-
return (
|
|
112
|
-
<Stack gap={0}>
|
|
113
|
-
<ProjectAnnouncementBanner context="crm-deals" />
|
|
114
|
-
<DealsListPage />
|
|
115
|
-
</Stack>
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Use the same pattern for `CrmOverview`, `DealDetailPage`, and `CompanyDetailPage`.
|
|
121
|
-
|
|
122
|
-
## 3. Build a Custom Deal Page
|
|
123
|
-
|
|
124
|
-
When the project needs custom layout or additional workflows, use the hooks directly:
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
import { Button, Group, Stack, Textarea } from '@mantine/core'
|
|
128
|
-
import { useState } from 'react'
|
|
129
|
-
import { useDealDetail, useDealNotes, useCreateDealNote, useExecuteAction } from '@elevasis/ui/hooks'
|
|
130
|
-
|
|
131
|
-
export function CustomDealWorkspace({ dealId }: { dealId: string }) {
|
|
132
|
-
const { data: deal, isLoading } = useDealDetail(dealId)
|
|
133
|
-
const { data: notes = [] } = useDealNotes(dealId)
|
|
134
|
-
const createNote = useCreateDealNote()
|
|
135
|
-
const executeAction = useExecuteAction({ dealId })
|
|
136
|
-
const [body, setBody] = useState('')
|
|
137
|
-
|
|
138
|
-
if (isLoading) return null
|
|
139
|
-
if (!deal) return <div>Deal not found</div>
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
<Stack>
|
|
143
|
-
<h1>{deal.contact_email}</h1>
|
|
144
|
-
<Group>
|
|
145
|
-
<Button onClick={() => executeAction.mutate({ key: 'move_to_proposal' })}>
|
|
146
|
-
Move to Proposal
|
|
147
|
-
</Button>
|
|
148
|
-
</Group>
|
|
149
|
-
<Textarea value={body} onChange={(event) => setBody(event.currentTarget.value)} />
|
|
150
|
-
<Button onClick={() => createNote.mutate({ dealId, body })}>Add Note</Button>
|
|
151
|
-
<pre>{JSON.stringify(notes, null, 2)}</pre>
|
|
152
|
-
</Stack>
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
`useExecuteAction` is for platform-known/default action keys. Start custom action UI with the shared `crmActions` provider path; when a project-owned workflow is outside that server-dispatched action set, call `/execute` or `/execute-async` through `useElevasisServices`, or follow [customize-crm-actions.md](customize-crm-actions.md).
|
|
158
|
-
|
|
159
|
-
## 4. Read and Mutate CRM Data in Workflows
|
|
160
|
-
|
|
161
|
-
Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
// operations/src/sales/follow-up-stale-deals.ts
|
|
165
|
-
import type { WorkflowDefinition } from '@elevasis/sdk'
|
|
166
|
-
import { crm } from '@elevasis/sdk/worker'
|
|
167
|
-
import { z } from 'zod'
|
|
168
|
-
|
|
169
|
-
const inputSchema = z.object({
|
|
170
|
-
stage: z.string().default('proposal')
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
const outputSchema = z.object({
|
|
174
|
-
touched: z.number()
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
export const followUpStaleDealsWorkflow: WorkflowDefinition = {
|
|
178
|
-
config: {
|
|
179
|
-
resourceId: 'follow-up-stale-deals',
|
|
180
|
-
name: 'Follow Up Stale Deals',
|
|
181
|
-
type: 'workflow',
|
|
182
|
-
version: '1.0.0',
|
|
183
|
-
status: 'dev',
|
|
184
|
-
links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
|
|
185
|
-
},
|
|
186
|
-
contract: { inputSchema, outputSchema },
|
|
187
|
-
steps: {
|
|
188
|
-
followUp: {
|
|
189
|
-
id: 'followUp',
|
|
190
|
-
name: 'Follow Up',
|
|
191
|
-
inputSchema,
|
|
192
|
-
outputSchema,
|
|
193
|
-
next: null,
|
|
194
|
-
handler: async (input) => {
|
|
195
|
-
const deals = await crm.listDeals({ stage: input.stage })
|
|
196
|
-
|
|
197
|
-
for (const deal of deals) {
|
|
198
|
-
await crm.createDealTask({
|
|
199
|
-
dealId: deal.id,
|
|
200
|
-
title: 'Follow up',
|
|
201
|
-
kind: 'email'
|
|
202
|
-
})
|
|
203
|
-
await crm.recordActivity({
|
|
204
|
-
dealId: deal.id,
|
|
205
|
-
type: 'workflow.follow_up_task_created',
|
|
206
|
-
title: 'Follow-up task created',
|
|
207
|
-
payload: { workflow: 'follow-up-stale-deals' }
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return { touched: deals.length }
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
entryPoint: 'followUp'
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Use `crm` for focused CRM deal reads, notes, tasks, activity, and cleanup. Use `acqDb` when the workflow needs the broader acquisition substrate: companies, contacts, lists, list-stage transitions, enrichment data, social posts, or lower-level deal sync methods.
|
|
220
|
-
|
|
221
|
-
## 5. Customize Pipeline Semantics
|
|
222
|
-
|
|
223
|
-
CRM feature identity remains `sales.crm`, but the organization-model domain is `sales`.
|
|
224
|
-
|
|
225
|
-
For stage labels or stage sets, start from the project reference file:
|
|
226
|
-
|
|
227
|
-
`core/config/organization-model.examples.ts`
|
|
228
|
-
|
|
229
|
-
Then update the project model:
|
|
230
|
-
|
|
231
|
-
`core/config/organization-model.ts`
|
|
232
|
-
|
|
233
|
-
Keep these boundaries straight:
|
|
234
|
-
|
|
235
|
-
- Org-model sales pipelines describe business semantics.
|
|
236
|
-
- `DealStage` and platform action defaults describe the current shared platform deal state vocabulary.
|
|
237
|
-
- UI wrappers can relabel and route around shared surfaces, but changing persisted platform stages requires coordinated core/API/UI updates.
|
|
238
|
-
- Do not add `sales.actions` to the org model in v1. Deal action customization is covered by [customize-crm-actions.md](customize-crm-actions.md).
|
|
239
|
-
|
|
240
|
-
## Verify
|
|
241
|
-
|
|
242
|
-
Run the checks for the surfaces you touched:
|
|
243
|
-
|
|
244
|
-
```bash
|
|
245
|
-
pnpm -C ui run check
|
|
246
|
-
pnpm -C operations run check
|
|
247
|
-
pnpm -C operations exec elevasis-sdk check
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
If you changed platform-level CRM contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
pnpm scaffold:sync
|
|
254
|
-
pnpm scaffold:verify
|
|
255
|
-
```
|
|
8
|
+
|
|
9
|
+
# Build and Extend CRM
|
|
10
|
+
|
|
11
|
+
Use this recipe when a downstream project wants to build on the shared CRM instead of forking it.
|
|
12
|
+
|
|
13
|
+
Good trigger phrases:
|
|
14
|
+
|
|
15
|
+
- "Add a CRM reports page."
|
|
16
|
+
- "Build a custom deal workspace."
|
|
17
|
+
- "Read and update deals from a workflow."
|
|
18
|
+
- "Add a Send Quote button."
|
|
19
|
+
- "Change the CRM pipeline stages for this business."
|
|
20
|
+
|
|
21
|
+
CRM is a layered platform surface, not one component:
|
|
22
|
+
|
|
23
|
+
- **Organization OS:** feature gates, sales pipeline semantics, quick access, and labels live in the organization model.
|
|
24
|
+
- **Shared UI:** CRM pages, sidebars, workbench panels, overview widgets, and Kanban/detail components live in `@elevasis/ui`.
|
|
25
|
+
- **Headless hooks:** deal, company, contact, note, task, list, transition, and action hooks live under `@elevasis/ui/hooks`.
|
|
26
|
+
- **Action system:** `ActionDef`, `DEFAULT_CRM_ACTIONS`, `deriveActions`, and provider-level `crmActions` configure deal actions.
|
|
27
|
+
- **Workflow adapters:** `crm` and `acqDb` from `@elevasis/sdk/worker` let workflows read and mutate CRM/acquisition data through platform tools.
|
|
28
|
+
- **Contracts:** generated contract docs expose the current CRM shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
29
|
+
|
|
30
|
+
## Decision Table
|
|
31
|
+
|
|
32
|
+
| User wants | Start here | Notes |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| Change CRM feature availability, labels, or pipeline stages | `core/config/organization-model.ts` plus `core/config/organization-model.examples.ts` | Treat this as Organization OS work. Use the project's configure ceremony if available. |
|
|
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. |
|
|
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
|
+
| Build a custom deal page | `useDealDetail`, `useDealNotes`, `useDealTasks`, `useExecuteAction` from `@elevasis/ui/hooks` | Use hooks for platform data and compose your own UI. |
|
|
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. |
|
|
39
|
+
| Read or mutate CRM data inside a workflow | `crm` or `acqDb` from `@elevasis/sdk/worker` | `organizationId` is injected server-side by the platform dispatcher. Do not pass it from workflow code. |
|
|
40
|
+
| Add a new persisted CRM column or table | Platform/API migration work, not just scaffold work | Update DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts together. |
|
|
41
|
+
|
|
42
|
+
## Published CRM Surfaces
|
|
43
|
+
|
|
44
|
+
| Surface | Import from | Use for |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `crmManifest`, `CRM_ITEMS`, `CrmSidebar`, `CrmSidebarTop`, `CrmSidebarMiddle` | `@elevasis/ui/features/crm` | Feature registration and sidebar composition |
|
|
47
|
+
| `CrmOverview`, `DealsListPage`, `DealDetailPage`, `CompanyDetailPage` | `@elevasis/ui/features/crm` | Shared CRM pages you can route to or wrap |
|
|
48
|
+
| `MyTasksPanel`, `SavedViewsPanel`, `QuickCreateActions` | `@elevasis/ui/features/crm` | Workbench/sidebar panels for custom CRM sidebars |
|
|
49
|
+
| `PipelineFunnelWidget`, `ActivityFeedWidget`, `MetricsStrip` | `@elevasis/ui/features/crm` | Overview widgets for custom dashboards |
|
|
50
|
+
| `KanbanBoard`, `DealKanbanCard`, `DealDrawer` | `@elevasis/ui/components` | Lower-level CRM deal UI primitives |
|
|
51
|
+
| `useDeals`, `useDealDetail`, `useTransitionItem`, `useExecuteAction`, `useDealNotes`, `useDealTasks` | `@elevasis/ui/hooks` | Headless deal and task data access |
|
|
52
|
+
| `useCompanies`, `useContacts`, `useLists`, `useBatchTelemetry` | `@elevasis/ui/hooks` | Acquisition substrate data access |
|
|
53
|
+
| `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, and organization context |
|
|
54
|
+
| `ActionDef`, `DEFAULT_CRM_ACTIONS`, `deriveActions` | `@elevasis/sdk` | Deal action configuration and render-time derivation |
|
|
55
|
+
| `crm`, `acqDb` | `@elevasis/sdk/worker` | Workflow-side CRM/acquisition platform adapters |
|
|
56
|
+
|
|
57
|
+
Read the generated contracts before changing typed boundaries:
|
|
58
|
+
|
|
59
|
+
`node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
60
|
+
|
|
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
|
+
|
|
63
|
+
## 1. Extend CRM Navigation or Sidebar
|
|
64
|
+
|
|
65
|
+
For a simple nav addition, extend `CRM_ITEMS` and override the CRM feature manifest:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// ui/src/routes/__root.tsx
|
|
69
|
+
import { crmManifest, CRM_ITEMS, CrmSidebar, CrmSidebarMiddle } from '@elevasis/ui/features/crm'
|
|
70
|
+
import type { FeatureModule } from '@elevasis/ui/provider'
|
|
71
|
+
import type { NavItem } from '@elevasis/ui/layout'
|
|
72
|
+
import { IconFileText } from '@tabler/icons-react'
|
|
73
|
+
|
|
74
|
+
const customCrmItems: NavItem[] = [
|
|
75
|
+
...CRM_ITEMS,
|
|
76
|
+
{ label: 'Reports', to: '/crm/reports', icon: IconFileText, exact: false }
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
const CustomCrmSidebar = () => (
|
|
80
|
+
<CrmSidebar>
|
|
81
|
+
<CrmSidebarMiddle items={customCrmItems} />
|
|
82
|
+
</CrmSidebar>
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
export const customCrmManifest: FeatureModule = {
|
|
86
|
+
...crmManifest,
|
|
87
|
+
sidebar: CustomCrmSidebar
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then replace `crmManifest` with `customCrmManifest` in the local `FEATURE_MANIFESTS` array and add the matching route under `ui/src/routes/crm/`.
|
|
92
|
+
|
|
93
|
+
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`.
|
|
94
|
+
|
|
95
|
+
## 2. Wrap Shared CRM Pages
|
|
96
|
+
|
|
97
|
+
If the shared page is close, keep it and wrap it:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
// ui/src/routes/crm/deals.index.tsx
|
|
101
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
102
|
+
import { DealsListPage } from '@elevasis/ui/features/crm'
|
|
103
|
+
import { Stack } from '@mantine/core'
|
|
104
|
+
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
105
|
+
|
|
106
|
+
export const Route = createFileRoute('/crm/deals/')({
|
|
107
|
+
component: DealsRoute
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
function DealsRoute() {
|
|
111
|
+
return (
|
|
112
|
+
<Stack gap={0}>
|
|
113
|
+
<ProjectAnnouncementBanner context="crm-deals" />
|
|
114
|
+
<DealsListPage />
|
|
115
|
+
</Stack>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use the same pattern for `CrmOverview`, `DealDetailPage`, and `CompanyDetailPage`.
|
|
121
|
+
|
|
122
|
+
## 3. Build a Custom Deal Page
|
|
123
|
+
|
|
124
|
+
When the project needs custom layout or additional workflows, use the hooks directly:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import { Button, Group, Stack, Textarea } from '@mantine/core'
|
|
128
|
+
import { useState } from 'react'
|
|
129
|
+
import { useDealDetail, useDealNotes, useCreateDealNote, useExecuteAction } from '@elevasis/ui/hooks'
|
|
130
|
+
|
|
131
|
+
export function CustomDealWorkspace({ dealId }: { dealId: string }) {
|
|
132
|
+
const { data: deal, isLoading } = useDealDetail(dealId)
|
|
133
|
+
const { data: notes = [] } = useDealNotes(dealId)
|
|
134
|
+
const createNote = useCreateDealNote()
|
|
135
|
+
const executeAction = useExecuteAction({ dealId })
|
|
136
|
+
const [body, setBody] = useState('')
|
|
137
|
+
|
|
138
|
+
if (isLoading) return null
|
|
139
|
+
if (!deal) return <div>Deal not found</div>
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<Stack>
|
|
143
|
+
<h1>{deal.contact_email}</h1>
|
|
144
|
+
<Group>
|
|
145
|
+
<Button onClick={() => executeAction.mutate({ key: 'move_to_proposal' })}>
|
|
146
|
+
Move to Proposal
|
|
147
|
+
</Button>
|
|
148
|
+
</Group>
|
|
149
|
+
<Textarea value={body} onChange={(event) => setBody(event.currentTarget.value)} />
|
|
150
|
+
<Button onClick={() => createNote.mutate({ dealId, body })}>Add Note</Button>
|
|
151
|
+
<pre>{JSON.stringify(notes, null, 2)}</pre>
|
|
152
|
+
</Stack>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`useExecuteAction` is for platform-known/default action keys. Start custom action UI with the shared `crmActions` provider path; when a project-owned workflow is outside that server-dispatched action set, call `/execute` or `/execute-async` through `useElevasisServices`, or follow [customize-crm-actions.md](customize-crm-actions.md).
|
|
158
|
+
|
|
159
|
+
## 4. Read and Mutate CRM Data in Workflows
|
|
160
|
+
|
|
161
|
+
Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// operations/src/sales/follow-up-stale-deals.ts
|
|
165
|
+
import type { WorkflowDefinition } from '@elevasis/sdk'
|
|
166
|
+
import { crm } from '@elevasis/sdk/worker'
|
|
167
|
+
import { z } from 'zod'
|
|
168
|
+
|
|
169
|
+
const inputSchema = z.object({
|
|
170
|
+
stage: z.string().default('proposal')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const outputSchema = z.object({
|
|
174
|
+
touched: z.number()
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
export const followUpStaleDealsWorkflow: WorkflowDefinition = {
|
|
178
|
+
config: {
|
|
179
|
+
resourceId: 'follow-up-stale-deals',
|
|
180
|
+
name: 'Follow Up Stale Deals',
|
|
181
|
+
type: 'workflow',
|
|
182
|
+
version: '1.0.0',
|
|
183
|
+
status: 'dev',
|
|
184
|
+
links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
|
|
185
|
+
},
|
|
186
|
+
contract: { inputSchema, outputSchema },
|
|
187
|
+
steps: {
|
|
188
|
+
followUp: {
|
|
189
|
+
id: 'followUp',
|
|
190
|
+
name: 'Follow Up',
|
|
191
|
+
inputSchema,
|
|
192
|
+
outputSchema,
|
|
193
|
+
next: null,
|
|
194
|
+
handler: async (input) => {
|
|
195
|
+
const deals = await crm.listDeals({ stage: input.stage })
|
|
196
|
+
|
|
197
|
+
for (const deal of deals) {
|
|
198
|
+
await crm.createDealTask({
|
|
199
|
+
dealId: deal.id,
|
|
200
|
+
title: 'Follow up',
|
|
201
|
+
kind: 'email'
|
|
202
|
+
})
|
|
203
|
+
await crm.recordActivity({
|
|
204
|
+
dealId: deal.id,
|
|
205
|
+
type: 'workflow.follow_up_task_created',
|
|
206
|
+
title: 'Follow-up task created',
|
|
207
|
+
payload: { workflow: 'follow-up-stale-deals' }
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { touched: deals.length }
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
entryPoint: 'followUp'
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Use `crm` for focused CRM deal reads, notes, tasks, activity, and cleanup. Use `acqDb` when the workflow needs the broader acquisition substrate: companies, contacts, lists, list-stage transitions, enrichment data, social posts, or lower-level deal sync methods.
|
|
220
|
+
|
|
221
|
+
## 5. Customize Pipeline Semantics
|
|
222
|
+
|
|
223
|
+
CRM feature identity remains `sales.crm`, but the organization-model domain is `sales`.
|
|
224
|
+
|
|
225
|
+
For stage labels or stage sets, start from the project reference file:
|
|
226
|
+
|
|
227
|
+
`core/config/organization-model.examples.ts`
|
|
228
|
+
|
|
229
|
+
Then update the project model:
|
|
230
|
+
|
|
231
|
+
`core/config/organization-model.ts`
|
|
232
|
+
|
|
233
|
+
Keep these boundaries straight:
|
|
234
|
+
|
|
235
|
+
- Org-model sales pipelines describe business semantics.
|
|
236
|
+
- `DealStage` and platform action defaults describe the current shared platform deal state vocabulary.
|
|
237
|
+
- UI wrappers can relabel and route around shared surfaces, but changing persisted platform stages requires coordinated core/API/UI updates.
|
|
238
|
+
- Do not add `sales.actions` to the org model in v1. Deal action customization is covered by [customize-crm-actions.md](customize-crm-actions.md).
|
|
239
|
+
|
|
240
|
+
## Verify
|
|
241
|
+
|
|
242
|
+
Run the checks for the surfaces you touched:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
pnpm -C ui run check
|
|
246
|
+
pnpm -C operations run check
|
|
247
|
+
pnpm -C operations exec elevasis-sdk check
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
If you changed platform-level CRM contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
pnpm scaffold:sync
|
|
254
|
+
pnpm scaffold:verify
|
|
255
|
+
```
|
|
@@ -22,23 +22,23 @@ You want a new shell feature (e.g., `analytics`) with its own nav entry, sidebar
|
|
|
22
22
|
**[Add a Resource](add-a-resource.md)**
|
|
23
23
|
You want a new workflow or agent deployed to the platform. Covers `WorkflowDefinition` authoring, `DeploymentSpec` registration, relationship declarations, domain mapping, and CLI verification.
|
|
24
24
|
|
|
25
|
-
**[Gate by Feature or Admin](gate-by-feature-or-admin.md)**
|
|
26
|
-
You want to restrict a route, nav item, or UI element by feature flag or admin role. Covers the decision table, `FeatureGuard`, `AdminGuard`, nav-entry visibility fields, per-member `MembershipFeatureConfig` overrides, and the settings-asymmetry gotcha.
|
|
27
|
-
|
|
28
|
-
**[Build and Extend CRM](extend-crm.md)**
|
|
29
|
-
You want to build on the shared CRM without forking it: add CRM routes, compose sidebars/pages, use deal/company/contact hooks, mutate CRM data from workflows, or understand which contracts and adapters form the extension surface.
|
|
30
|
-
|
|
31
|
-
**[Build and Extend Lead Gen](extend-lead-gen.md)**
|
|
32
|
-
You want to build on the shared lead-gen system without forking it: add lead-gen routes, compose sidebars/pages, use list/company/contact/artifact/touchpoint hooks, mutate list data from workflows, or understand which contracts and adapters form the extension surface.
|
|
33
|
-
|
|
34
|
-
**[Customize CRM Actions](customize-crm-actions.md)**
|
|
35
|
-
You want to add, hide, or replace CRM deal action buttons, configure the shared `crmActions` provider path, or call a project-owned workflow from custom UI when server-side action dispatch constraints require it. Covers `ActionDef`, `DEFAULT_CRM_ACTIONS`, provider wiring, and the current v1 boundary for custom action dispatch.
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Reference docs these recipes link into
|
|
40
|
-
|
|
41
|
-
- [glossary.md](../reference/glossary.md) -- term disambiguation for Feature, Resource, featureId, Topology, Settings asymmetry
|
|
42
|
-
- [contracts.md](../reference/contracts.md) -- TypeScript shapes: `FeatureModule`, `FeatureNavEntry`, `OrganizationModel`, `MembershipFeatureConfig`, CRM deal types, lead-gen list/member/artifact/touchpoint types, `CrmToolMap`, `LeadToolMap`, `ListToolMap`, `ActionDef`
|
|
43
|
-
- [feature-flags-and-gating.md](../ui/feature-flags-and-gating.md) -- full three-concept gating model
|
|
44
|
-
- [workflow-recipes.md](../operations/workflow-recipes.md) -- workflow anatomy, adapters, trigger patterns
|
|
25
|
+
**[Gate by Feature or Admin](gate-by-feature-or-admin.md)**
|
|
26
|
+
You want to restrict a route, nav item, or UI element by feature flag or admin role. Covers the decision table, `FeatureGuard`, `AdminGuard`, nav-entry visibility fields, per-member `MembershipFeatureConfig` overrides, and the settings-asymmetry gotcha.
|
|
27
|
+
|
|
28
|
+
**[Build and Extend CRM](extend-crm.md)**
|
|
29
|
+
You want to build on the shared CRM without forking it: add CRM routes, compose sidebars/pages, use deal/company/contact hooks, mutate CRM data from workflows, or understand which contracts and adapters form the extension surface.
|
|
30
|
+
|
|
31
|
+
**[Build and Extend Lead Gen](extend-lead-gen.md)**
|
|
32
|
+
You want to build on the shared lead-gen system without forking it: add lead-gen routes, compose sidebars/pages, use list/company/contact/artifact/touchpoint hooks, mutate list data from workflows, or understand which contracts and adapters form the extension surface.
|
|
33
|
+
|
|
34
|
+
**[Customize CRM Actions](customize-crm-actions.md)**
|
|
35
|
+
You want to add, hide, or replace CRM deal action buttons, configure the shared `crmActions` provider path, or call a project-owned workflow from custom UI when server-side action dispatch constraints require it. Covers `ActionDef`, `DEFAULT_CRM_ACTIONS`, provider wiring, and the current v1 boundary for custom action dispatch.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Reference docs these recipes link into
|
|
40
|
+
|
|
41
|
+
- [glossary.md](../reference/glossary.md) -- term disambiguation for Feature, Resource, featureId, Topology, Settings asymmetry
|
|
42
|
+
- [contracts.md](../reference/contracts.md) -- TypeScript shapes: `FeatureModule`, `FeatureNavEntry`, `OrganizationModel`, `MembershipFeatureConfig`, CRM deal types, lead-gen list/member/artifact/touchpoint types, `CrmToolMap`, `LeadToolMap`, `ListToolMap`, `ActionDef`
|
|
43
|
+
- [feature-flags-and-gating.md](../ui/feature-flags-and-gating.md) -- full three-concept gating model
|
|
44
|
+
- [workflow-recipes.md](../operations/workflow-recipes.md) -- workflow anatomy, adapters, trigger patterns
|