@elevasis/sdk 1.21.0 → 1.22.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 +951 -171
- package/dist/index.d.ts +632 -341
- package/dist/index.js +3102 -142
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +19 -1
- package/dist/test-utils/index.d.ts +313 -4
- package/dist/test-utils/index.js +3246 -281
- package/dist/worker/index.js +3041 -80
- package/package.json +3 -3
- package/reference/claude-config/hooks/post-edit-validate.mjs +98 -98
- package/reference/claude-config/hooks/scaffold-registry-reminder.mjs +188 -188
- package/reference/claude-config/hooks/tool-failure-recovery.mjs +73 -73
- package/reference/claude-config/registries/graph-skills.json +4 -4
- package/reference/claude-config/registries/knowledge-flags.json +0 -2
- package/reference/claude-config/rules/active-change-index.md +80 -80
- package/reference/claude-config/rules/agent-start-here.md +277 -277
- package/reference/claude-config/rules/deployment.md +57 -57
- package/reference/claude-config/rules/error-handling.md +56 -56
- package/reference/claude-config/rules/execution.md +40 -40
- package/reference/claude-config/rules/frontend.md +4 -4
- package/reference/claude-config/rules/observability.md +31 -31
- package/reference/claude-config/rules/operations.md +29 -17
- package/reference/claude-config/rules/organization-model.md +110 -84
- package/reference/claude-config/rules/organization-os.md +115 -113
- package/reference/claude-config/rules/package-taxonomy.md +33 -33
- package/reference/claude-config/rules/platform.md +42 -42
- package/reference/claude-config/rules/shared-types.md +49 -46
- package/reference/claude-config/rules/task-tracking.md +47 -47
- package/reference/claude-config/rules/ui.md +200 -200
- package/reference/claude-config/rules/vibe.md +235 -235
- package/reference/claude-config/scripts/statusline-command.js +18 -18
- package/reference/claude-config/settings.json +34 -34
- package/reference/claude-config/skills/deploy/{SKILL.md → skill.md} +156 -156
- package/reference/claude-config/skills/dsp/SKILL.md +66 -66
- package/reference/claude-config/skills/elevasis/SKILL.md +235 -235
- package/reference/claude-config/skills/explore/SKILL.md +6 -6
- package/reference/claude-config/skills/git-sync/SKILL.md +126 -126
- package/reference/claude-config/skills/knowledge/SKILL.md +314 -299
- package/reference/claude-config/skills/knowledge/operations/codify-level-a.md +100 -100
- package/reference/claude-config/skills/knowledge/operations/codify-level-b.md +159 -159
- package/reference/claude-config/skills/knowledge/operations/customers.md +109 -109
- package/reference/claude-config/skills/knowledge/operations/features.md +76 -76
- package/reference/claude-config/skills/knowledge/operations/goals.md +118 -118
- package/reference/claude-config/skills/knowledge/operations/identity.md +93 -93
- package/reference/claude-config/skills/knowledge/operations/labels.md +94 -94
- package/reference/claude-config/skills/knowledge/operations/offerings.md +109 -109
- package/reference/claude-config/skills/knowledge/operations/roles.md +99 -99
- package/reference/claude-config/skills/knowledge/operations/techStack.md +30 -30
- package/reference/claude-config/skills/project/SKILL.md +1088 -1088
- package/reference/claude-config/skills/run-ui/SKILL.md +73 -73
- package/reference/claude-config/skills/save/SKILL.md +3 -3
- package/reference/claude-config/skills/setup/SKILL.md +275 -275
- package/reference/claude-config/skills/status/SKILL.md +59 -59
- package/reference/claude-config/skills/submit-request/SKILL.md +180 -180
- package/reference/claude-config/skills/sync/SKILL.md +47 -47
- package/reference/claude-config/skills/tutorial/SKILL.md +259 -259
- package/reference/claude-config/skills/tutorial/progress-template.md +74 -74
- package/reference/claude-config/skills/tutorial/technical.md +1303 -1303
- package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -890
- package/reference/claude-config/sync-notes/2026-04-22-git-sync-and-sync-notes.md +27 -27
- package/reference/claude-config/sync-notes/2026-04-22-lead-gen-deliverability-removal.md +30 -30
- package/reference/claude-config/sync-notes/2026-04-24-test-utils-and-template-tests.md +73 -73
- package/reference/claude-config/sync-notes/2026-04-24-ui-consolidation-and-sdk-cli-train.md +86 -86
- package/reference/claude-config/sync-notes/2026-04-25-auth-role-system-and-settings-roles.md +55 -55
- package/reference/claude-config/sync-notes/2026-04-27-crm-hitl-action-layer-cutover.md +97 -97
- package/reference/claude-config/sync-notes/2026-04-27-lead-gen-substrate-train.md +112 -112
- package/reference/claude-config/sync-notes/2026-04-29-crm-state-and-lead-gen-processing-status.md +93 -93
- package/reference/claude-config/sync-notes/2026-05-02-crm-ownership-next-action.md +58 -58
- package/reference/claude-config/sync-notes/2026-05-02-template-hardcode-workos-config.md +56 -56
- package/reference/claude-config/sync-notes/2026-05-04-elevasis-workspace.md +71 -71
- package/reference/claude-config/sync-notes/2026-05-04-knowledge-bundle.md +83 -83
- package/reference/claude-config/sync-notes/2026-05-04-template-skills-run-ui-and-tutorial.md +59 -59
- package/reference/claude-config/sync-notes/2026-05-05-list-builder.md +42 -42
- package/reference/claude-config/sync-notes/2026-05-06-crm-spine.md +60 -60
- package/reference/claude-config/sync-notes/2026-05-06-sdk-changes-release-train.md +37 -37
- package/reference/claude-config/sync-notes/2026-05-07-sdk-changes-release-train.md +34 -34
- package/reference/claude-config/sync-notes/2026-05-08-resource-governance-scaffold-guidance.md +38 -38
- package/reference/claude-config/sync-notes/2026-05-09-clients-domain.md +32 -32
- package/reference/claude-config/sync-notes/2026-05-09-command-system.md +33 -33
- package/reference/claude-config/sync-notes/2026-05-09-resource-governance-and-misc.md +69 -69
- package/reference/claude-config/sync-notes/2026-05-12-sdk-ready-release-train.md +30 -30
- package/reference/claude-config/sync-notes/2026-05-14-organization-model-ontology-refactor.md +42 -0
- package/reference/claude-config/sync-notes/README.md +43 -43
- package/reference/cli.mdx +808 -808
- package/reference/concepts.mdx +146 -146
- package/reference/deployment/api.mdx +297 -297
- package/reference/deployment/command-center.mdx +209 -209
- package/reference/deployment/index.mdx +195 -195
- package/reference/deployment/provided-features.mdx +107 -107
- package/reference/deployment/ui-execution.mdx +250 -250
- package/reference/examples/organization-model.ts +146 -83
- package/reference/framework/agent.mdx +156 -156
- package/reference/framework/index.mdx +195 -195
- package/reference/framework/interaction-guidance.mdx +182 -182
- package/reference/framework/memory.mdx +326 -326
- package/reference/framework/project-structure.mdx +282 -282
- package/reference/framework/tutorial-system.mdx +135 -135
- package/reference/getting-started.mdx +142 -142
- package/reference/index.mdx +106 -106
- package/reference/packages/core/src/README.md +14 -14
- package/reference/packages/core/src/business/README.md +2 -2
- package/reference/packages/core/src/knowledge/README.md +32 -32
- package/reference/packages/core/src/organization-model/README.md +149 -149
- package/reference/packages/core/src/test-utils/README.md +37 -37
- package/reference/packages/ui/src/api/README.md +18 -18
- package/reference/packages/ui/src/app/README.md +24 -24
- package/reference/packages/ui/src/auth/README.md +18 -18
- package/reference/packages/ui/src/components/README.md +24 -24
- package/reference/packages/ui/src/execution/README.md +16 -16
- package/reference/packages/ui/src/features/README.md +28 -28
- package/reference/packages/ui/src/graph/README.md +16 -16
- package/reference/packages/ui/src/hooks/README.md +23 -23
- package/reference/packages/ui/src/initialization/README.md +19 -19
- package/reference/packages/ui/src/knowledge/README.md +31 -31
- package/reference/packages/ui/src/organization/README.md +18 -18
- package/reference/packages/ui/src/profile/README.md +19 -19
- package/reference/packages/ui/src/provider/README.md +32 -32
- package/reference/packages/ui/src/router/README.md +18 -18
- package/reference/packages/ui/src/sse/README.md +13 -13
- package/reference/packages/ui/src/test-utils/README.md +7 -7
- package/reference/packages/ui/src/theme/README.md +23 -23
- package/reference/packages/ui/src/theme/presets/README.md +19 -19
- package/reference/packages/ui/src/types/README.md +16 -16
- package/reference/packages/ui/src/utils/README.md +18 -18
- package/reference/packages/ui/src/zustand/README.md +18 -18
- package/reference/platform-tools/adapters-integration.mdx +301 -301
- package/reference/platform-tools/adapters-platform.mdx +553 -553
- package/reference/platform-tools/index.mdx +217 -217
- package/reference/platform-tools/type-safety.mdx +82 -82
- package/reference/resources/index.mdx +349 -349
- package/reference/resources/patterns.mdx +449 -449
- package/reference/resources/types.mdx +116 -116
- package/reference/roadmap.mdx +165 -165
- package/reference/runtime.mdx +173 -173
- package/reference/scaffold/core/organization-graph.mdx +110 -90
- package/reference/scaffold/core/organization-model.mdx +226 -219
- package/reference/scaffold/index.mdx +67 -67
- package/reference/scaffold/operations/propagation-pipeline.md +77 -77
- package/reference/scaffold/operations/scaffold-maintenance.md +12 -12
- package/reference/scaffold/operations/workflow-recipes.md +138 -138
- package/reference/scaffold/recipes/add-a-feature.md +308 -88
- package/reference/scaffold/recipes/add-a-resource.md +134 -110
- package/reference/scaffold/recipes/customize-knowledge-browser.md +5 -5
- package/reference/scaffold/recipes/customize-organization-model.md +273 -138
- package/reference/scaffold/recipes/extend-a-base-entity.md +8 -8
- package/reference/scaffold/recipes/extend-crm.md +3 -3
- package/reference/scaffold/recipes/extend-lead-gen.md +400 -400
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +118 -118
- package/reference/scaffold/recipes/index.md +46 -46
- package/reference/scaffold/recipes/query-the-knowledge-graph.md +197 -170
- package/reference/scaffold/reference/contracts.md +2101 -2096
- package/reference/scaffold/reference/glossary.md +76 -76
- package/reference/scaffold/ui/composition-extensibility.mdx +233 -233
- package/reference/scaffold/ui/customization.md +243 -243
- package/reference/scaffold/ui/feature-flags-and-gating.md +46 -46
- package/reference/scaffold/ui/feature-shell.mdx +72 -72
- package/reference/scaffold/ui/recipes.md +221 -214
- package/reference/spine/spine-primer.md +96 -96
- package/reference/templates/index.mdx +47 -47
- package/reference/troubleshooting.mdx +223 -223
|
@@ -1,403 +1,403 @@
|
|
|
1
1
|
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
|
|
2
2
|
<!-- Regenerate: pnpm scaffold:sync -->
|
|
3
3
|
|
|
4
|
-
---
|
|
5
|
-
title: Build and Extend Lead Gen
|
|
6
|
-
description: Map the lead-gen platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, list/member state, artifacts, workflow adapters, contracts, and org-model extension boundaries.
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Build and Extend Lead Gen
|
|
10
|
-
|
|
11
|
-
Use this recipe when a downstream project wants to build on the shared lead-gen system instead of forking it.
|
|
12
|
-
|
|
13
|
-
Good trigger phrases:
|
|
14
|
-
|
|
15
|
-
- "Add a lead-gen reports page."
|
|
16
|
-
- "Build a campaign creator surface."
|
|
17
|
-
- "Customize the lead-gen sidebar."
|
|
18
|
-
- "Show artifacts for a campaign."
|
|
19
|
-
- "Read or update lead-gen lists from a workflow."
|
|
20
|
-
- "Change the lead-gen lifecycle for this business."
|
|
21
|
-
|
|
22
|
-
Lead gen is a layered platform surface, not one component:
|
|
23
|
-
|
|
24
|
-
- **Organization OS:** prospecting semantics, System access, pipeline labels, and quick access live in the organization model.
|
|
25
|
-
- **Shared UI:** lead-gen overview, lists, list detail, companies, contacts, sidebars, and drawer components live in `@elevasis/ui`.
|
|
26
|
-
- **Headless hooks:** lists, companies, contacts, artifacts, list members, transitions, and derived actions live under `@elevasis/ui/hooks`.
|
|
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
|
-
- **Workflow adapters:** `acqDb` and `list` from `@elevasis/sdk/worker` let workflows read and mutate lead-gen data through platform tools.
|
|
29
|
-
- **Contracts:** generated contract docs expose the current lead-gen shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
30
|
-
|
|
31
|
-
## Decision Table
|
|
32
|
-
|
|
33
|
-
| User wants | Start here | Notes |
|
|
34
|
-
| ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
35
|
-
| Change lead-gen System availability, labels, quick access, pipeline semantics, or resource descriptors | `core/config/organization-model.ts` | Treat this as Organization OS work. Use the project's configure ceremony if available. |
|
|
36
|
-
| 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
|
-
| 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. |
|
|
38
|
-
| 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. |
|
|
39
|
-
| Render artifacts or list-member detail | `LeadGenListDetailPage`, `useArtifacts`, `useCreateArtifact`, `useListMember` | Artifacts are a substrate primitive. Keep vertical-specific rendering local until there are repeated use cases. |
|
|
40
|
-
| Read or mutate lead-gen data inside a workflow | `acqDb` or `list` from `@elevasis/sdk/worker` | `organizationId` is injected server-side by the platform dispatcher. Do not pass it from workflow code. |
|
|
41
|
-
| Add a new persisted lead-gen field, artifact kind, or transition API | Platform/API migration work, not just scaffold work | Update DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts together. |
|
|
42
|
-
|
|
43
|
-
## Published Lead-Gen Surfaces
|
|
44
|
-
|
|
45
|
-
| Surface | Import from | Use for |
|
|
46
|
-
| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
47
|
-
| `leadGenManifest`, `LEAD_GEN_ITEMS`, `LeadGenSidebar`, `LeadGenSidebarTop`, `LeadGenSidebarMiddle` | `@elevasis/ui/features/lead-gen` | Feature registration and sidebar composition |
|
|
48
|
-
| `LeadGenOverviewPage`, `LeadGenListsPage`, `LeadGenListDetailPage`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | `@elevasis/ui/features/lead-gen` | Shared lead-gen pages you can route to or wrap |
|
|
49
|
-
| `ListActionsProvider`, `useListActions`, `ListBuilderWorkflow`, `ListBuilderRegistry`, `LeadGenActionKey` | `@elevasis/ui/features/lead-gen` | List Builder workflow registry, slot-based field contracts, and project-owned action wiring |
|
|
50
|
-
| `LeadGenRouteShell`, `LIST_TEMPLATE_OPTIONS`, `buildListConfig` | `@elevasis/ui/features/lead-gen` | Route shell helpers and list creation config helpers (contact/company detail surfaces are now `ContactDetailPage` / `CompanyDetailPage` from `@elevasis/ui/features/crm`) |
|
|
51
|
-
| `useLists`, `useList`, `useListsTelemetry`, `useListProgress`, `useListExecutions`, `useCreateList`, `useUpdateList`, `useUpdateListConfig`, `useDeleteList` | `@elevasis/ui/hooks` | Headless list and telemetry data access |
|
|
52
|
-
| `useWorkflowExecution`, `useExecutionSSE`, `useAddCompaniesToList`, `useRemoveCompaniesFromList`, `useAddContactsToList` | `@elevasis/ui/hooks` | List Builder workflow triggering, live execution tailing, and list membership mutations |
|
|
53
|
-
| `useCompanies`, `useCompany`, `useContacts`, `useContact` | `@elevasis/ui/hooks` | Acquisition company/contact data access |
|
|
54
|
-
| `useArtifacts`, `useCreateArtifact`, `useListMembers`, `useListMember` | `@elevasis/ui/hooks` | Lead-gen substrate data access |
|
|
55
|
-
| `useTransitionList`, `useTransitionListMember`, `useTransitionListCompany`, `useDeriveActions` | `@elevasis/ui/hooks` | Stateful transition mutations and contextual action derivation |
|
|
56
|
-
| `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, organization context, and `listActions` registry injection |
|
|
57
|
-
| `acqDb`, `list` | `@elevasis/sdk/worker` | Workflow-side acquisition and list-scoped platform adapters |
|
|
58
|
-
|
|
59
|
-
Read the generated contracts before changing typed boundaries:
|
|
60
|
-
|
|
61
|
-
`node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
62
|
-
|
|
63
|
-
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.
|
|
64
|
-
|
|
65
|
-
## 1. Extend Lead-Gen Navigation or Sidebar
|
|
66
|
-
|
|
67
|
-
For a simple nav addition, extend `LEAD_GEN_ITEMS` and override the lead-gen system manifest:
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
// ui/src/routes/__root.tsx
|
|
71
|
-
import { leadGenManifest, LEAD_GEN_ITEMS, LeadGenSidebar, LeadGenSidebarMiddle } from '@elevasis/ui/features/lead-gen'
|
|
72
|
-
import type { SystemModule } from '@elevasis/ui/provider'
|
|
73
|
-
import type { NavItem } from '@elevasis/ui/layout'
|
|
74
|
-
import { IconChartBar } from '@tabler/icons-react'
|
|
75
|
-
|
|
76
|
-
const customLeadGenItems: NavItem[] = [
|
|
77
|
-
...LEAD_GEN_ITEMS,
|
|
78
|
-
{ label: 'Reports', to: '/lead-gen/reports', icon: IconChartBar, exact: false }
|
|
79
|
-
]
|
|
80
|
-
|
|
81
|
-
const CustomLeadGenSidebar = () => (
|
|
82
|
-
<LeadGenSidebar>
|
|
83
|
-
<LeadGenSidebarMiddle items={customLeadGenItems} />
|
|
84
|
-
</LeadGenSidebar>
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
export const customLeadGenManifest: SystemModule = {
|
|
88
|
-
...leadGenManifest,
|
|
89
|
-
sidebar: CustomLeadGenSidebar
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Then replace `leadGenManifest` with `customLeadGenManifest` in the local `SYSTEM_MANIFESTS` array and add the matching route under `ui/src/routes/lead-gen/`.
|
|
94
|
-
|
|
95
|
-
For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
|
|
96
|
-
|
|
97
|
-
## 2. Wrap Shared Lead-Gen Pages
|
|
98
|
-
|
|
99
|
-
If the shared page is close, keep it and wrap it:
|
|
100
|
-
|
|
101
|
-
```tsx
|
|
102
|
-
// ui/src/routes/lead-gen/lists.index.tsx
|
|
103
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
104
|
-
import { LeadGenListsPage } from '@elevasis/ui/features/lead-gen'
|
|
105
|
-
import { Stack } from '@mantine/core'
|
|
106
|
-
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
107
|
-
|
|
108
|
-
export const Route = createFileRoute('/lead-gen/lists/')({
|
|
109
|
-
component: ListsRoute
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
function ListsRoute() {
|
|
113
|
-
return (
|
|
114
|
-
<Stack gap={0}>
|
|
115
|
-
<ProjectAnnouncementBanner context="lead-gen-lists" />
|
|
116
|
-
<LeadGenListsPage />
|
|
117
|
-
</Stack>
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
For list detail routes, pass the route param into `LeadGenListDetailPage`:
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
126
|
-
import { LeadGenListDetailPage } from '@elevasis/ui/features/lead-gen'
|
|
127
|
-
|
|
128
|
-
export const Route = createFileRoute('/lead-gen/lists/$listId')({
|
|
129
|
-
component: ListDetailRoute
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
function ListDetailRoute() {
|
|
133
|
-
const { listId } = Route.useParams()
|
|
134
|
-
return <LeadGenListDetailPage listId={listId} />
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Use the same wrapping pattern for `LeadGenOverviewPage`, `LeadGenCompaniesPage`, and `LeadGenContactsPage`.
|
|
139
|
-
|
|
140
|
-
For the real-time List Builder workspace, add a thin route that passes the route param into `ListBuilderPage`:
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
144
|
-
import { ListBuilderPage } from '@elevasis/ui/features/lead-gen'
|
|
145
|
-
|
|
146
|
-
export const Route = createFileRoute('/lead-gen/list-builder/$listId')({
|
|
147
|
-
component: ListBuilderRoute
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
function ListBuilderRoute() {
|
|
151
|
-
const { listId } = Route.useParams()
|
|
152
|
-
return <ListBuilderPage listId={listId} />
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Register project workflow actions at the provider boundary. The shared UI owns the contract; each project owns workflow ids, action keys, default inputs, and field components.
|
|
157
|
-
|
|
158
|
-
Each registry entry declares a Zod `schema` and a `layout` of declarative field hints (`StepConfigLayout<Input>`). The shared `StepConfigForm` renders the layout, validates against the schema, and wires `value`/`onChange` for you — no per-action React components. The List Builder right column renders the form as `Configuration | Advanced | Runs` tabs with a sticky action footer; `RunWorkflowModal` renders the same form inline. Omit the `advanced:` section when a step has none.
|
|
159
|
-
|
|
160
|
-
Available field component variants: `textinput`, `textarea`, `numberinput`, `switch`, `segmented`, `select`, `multiselect`, `tags`, `json`. Field hints support `label`, `description`, `placeholder`, `min`/`max`/`step` (numbers), `options` (selects), and `when: (values) => boolean` for conditional visibility.
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
import * as z from 'zod'
|
|
164
|
-
import { type ListBuilderRegistry } from '@elevasis/ui/features/lead-gen'
|
|
165
|
-
import type { StepConfigLayout } from '@elevasis/ui/components/forms'
|
|
166
|
-
import { ElevasisUIProvider } from '@elevasis/ui/provider'
|
|
167
|
-
import { resourceDescriptors } from '@core/config/organization-model'
|
|
168
|
-
|
|
169
|
-
const companyCleanupInputSchema = z.object({
|
|
170
|
-
listId: z.string().uuid(),
|
|
171
|
-
targetDescription: z.string(),
|
|
172
|
-
dryRun: z.boolean().default(true),
|
|
173
|
-
batchSize: z.number().int().min(1).default(20),
|
|
174
|
-
mode: z.enum(['mock', 'live']).default('live')
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
type CompanyCleanupInput = z.infer<typeof companyCleanupInputSchema>
|
|
178
|
-
const companyCleanupResource = resourceDescriptors.companyCleanup
|
|
179
|
-
|
|
180
|
-
const companyCleanupLayout: StepConfigLayout<CompanyCleanupInput> = {
|
|
181
|
-
sections: [
|
|
182
|
-
{
|
|
183
|
-
id: 'configuration',
|
|
184
|
-
fields: [
|
|
185
|
-
{
|
|
186
|
-
path: 'targetDescription',
|
|
187
|
-
component: 'textarea',
|
|
188
|
-
label: 'Target description',
|
|
189
|
-
placeholder: 'independent veterinary clinics in Orange County'
|
|
190
|
-
},
|
|
191
|
-
{ path: 'dryRun', component: 'switch', label: 'Dry run' }
|
|
192
|
-
]
|
|
193
|
-
}
|
|
194
|
-
],
|
|
195
|
-
advanced: {
|
|
196
|
-
id: 'advanced',
|
|
197
|
-
fields: [
|
|
198
|
-
{ path: 'batchSize', component: 'numberinput', label: 'Batch size', min: 1 },
|
|
199
|
-
{
|
|
200
|
-
path: 'mode',
|
|
201
|
-
component: 'segmented',
|
|
202
|
-
options: [
|
|
203
|
-
{ value: 'mock', label: 'Mock' },
|
|
204
|
-
{ value: 'live', label: 'Live' }
|
|
205
|
-
],
|
|
206
|
-
when: () => import.meta.env.DEV
|
|
207
|
-
}
|
|
208
|
-
]
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const listActions: ListBuilderRegistry = [
|
|
213
|
-
{
|
|
214
|
-
resourceId: companyCleanupResource.id,
|
|
215
|
-
workflowId: companyCleanupResource.id,
|
|
216
|
-
actionKey: 'lead-gen.company.cleanup',
|
|
217
|
-
label: 'Company Cleanup',
|
|
218
|
-
description: 'Normalize and clean company records for the list.',
|
|
219
|
-
category: 'utility',
|
|
220
|
-
stagesAffected: ['populated'],
|
|
221
|
-
schema: companyCleanupInputSchema,
|
|
222
|
-
layout: companyCleanupLayout,
|
|
223
|
-
defaultInput: (list) => ({
|
|
224
|
-
listId: list.id,
|
|
225
|
-
targetDescription: '',
|
|
226
|
-
dryRun: true,
|
|
227
|
-
batchSize: 20
|
|
228
|
-
})
|
|
229
|
-
}
|
|
230
|
-
]
|
|
231
|
-
|
|
232
|
-
export function App() {
|
|
233
|
-
return (
|
|
234
|
-
<ElevasisUIProvider listActions={listActions} {...providerProps}>
|
|
235
|
-
{children}
|
|
236
|
-
</ElevasisUIProvider>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
Author the `schema` in a browser-safe sibling file (no Node-only imports) so it can be reused by the workflow runtime AND the UI form. See `apollo-import-schema.ts` in the platform for the canonical split pattern.
|
|
242
|
-
|
|
243
|
-
Per-section split rule (operator-grade vs power-user):
|
|
244
|
-
|
|
245
|
-
- **Configuration:** target/limit, dry-run toggle, mode (preview/finalize), and any field an operator changes between runs.
|
|
246
|
-
- **Advanced:** concurrency, batch size, timeouts, credential names, workflow chaining, dev-only mode toggles. Omit the `advanced:` key entirely when a step has none.
|
|
247
|
-
- **`defaultInput`:** seeds the form before the user touches anything; must include `listId: list.id`.
|
|
248
|
-
|
|
249
|
-
## 3. Build a Custom Campaign Workspace
|
|
250
|
-
|
|
251
|
-
When the project needs custom layout or vertical-specific rendering, use the hooks directly:
|
|
252
|
-
|
|
253
|
-
```tsx
|
|
254
|
-
import { Badge, Button, Card, Group, Stack, Text } from '@mantine/core'
|
|
255
|
-
import { useArtifacts, useList, useListMembers, useTransitionListMember } from '@elevasis/ui/hooks'
|
|
256
|
-
|
|
257
|
-
export function CampaignWorkspace({ listId }: { listId: string }) {
|
|
258
|
-
const listQuery = useList(listId)
|
|
259
|
-
const membersQuery = useListMembers({ listId })
|
|
260
|
-
const artifactsQuery = useArtifacts({ ownerKind: 'list', ownerId: listId })
|
|
261
|
-
const transitionMember = useTransitionListMember()
|
|
262
|
-
|
|
263
|
-
const list = listQuery.data
|
|
264
|
-
if (!list) return null
|
|
265
|
-
|
|
266
|
-
const firstMember = membersQuery.data?.members[0]
|
|
267
|
-
|
|
268
|
-
return (
|
|
269
|
-
<Stack>
|
|
270
|
-
<Group justify="space-between">
|
|
271
|
-
<div>
|
|
272
|
-
<Text fw={700}>{list.name}</Text>
|
|
273
|
-
<Text size="sm" c="dimmed">{list.description}</Text>
|
|
274
|
-
</div>
|
|
275
|
-
<Badge>{list.stateKey}</Badge>
|
|
276
|
-
</Group>
|
|
277
|
-
|
|
278
|
-
<Card withBorder>
|
|
279
|
-
<Text size="sm">Artifacts: {artifactsQuery.data?.artifacts.length ?? 0}</Text>
|
|
280
|
-
</Card>
|
|
281
|
-
|
|
282
|
-
{firstMember ? (
|
|
283
|
-
<Button
|
|
284
|
-
onClick={() =>
|
|
285
|
-
transitionMember.mutate({
|
|
286
|
-
memberId: firstMember.id,
|
|
287
|
-
listId,
|
|
288
|
-
pipelineKey: 'lead-gen',
|
|
289
|
-
stageKey: 'prospecting',
|
|
290
|
-
stateKey: 'verified'
|
|
291
|
-
})
|
|
292
|
-
}
|
|
293
|
-
>
|
|
294
|
-
Mark First Member Verified
|
|
295
|
-
</Button>
|
|
296
|
-
) : null}
|
|
297
|
-
</Stack>
|
|
298
|
-
)
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Use `useArtifacts({ ownerKind, ownerId })` for durable JSON artifacts like audits, research summaries, snapshots, exports, or model outputs.
|
|
303
|
-
|
|
304
|
-
## 4. Read and Mutate Lead-Gen Data in Workflows
|
|
305
|
-
|
|
306
|
-
Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
|
|
307
|
-
|
|
308
|
-
External projects should define workflow input/output schemas in `@shared/types`; the example below assumes those shared schemas already exist.
|
|
309
|
-
|
|
310
|
-
```ts
|
|
311
|
-
// operations/src/sales/qualify-list.ts
|
|
312
|
-
import type { WorkflowDefinition } from '@elevasis/sdk'
|
|
313
|
-
import { acqDb, list } from '@elevasis/sdk/worker'
|
|
314
|
-
import { qualifyListInputSchema, qualifyListOutputSchema } from '@shared/types'
|
|
315
|
-
import { resourceDescriptors } from '@core/config/organization-model'
|
|
316
|
-
|
|
317
|
-
export const qualifyListWorkflow: WorkflowDefinition = {
|
|
318
|
-
config: {
|
|
319
|
-
resource: resourceDescriptors.qualifyList,
|
|
320
|
-
resourceId: resourceDescriptors.qualifyList.id,
|
|
321
|
-
name: 'Qualify List',
|
|
322
|
-
type: resourceDescriptors.qualifyList.kind,
|
|
323
|
-
version: '1.0.0',
|
|
324
|
-
status: 'dev',
|
|
325
|
-
},
|
|
326
|
-
contract: { inputSchema: qualifyListInputSchema, outputSchema: qualifyListOutputSchema },
|
|
327
|
-
steps: {
|
|
328
|
-
qualify: {
|
|
329
|
-
id: 'qualify',
|
|
330
|
-
name: 'Qualify',
|
|
331
|
-
inputSchema: qualifyListInputSchema,
|
|
332
|
-
outputSchema: qualifyListOutputSchema,
|
|
333
|
-
next: null,
|
|
334
|
-
handler: async (input, context) => {
|
|
335
|
-
const config = await list.getConfig({ listId: input.listId })
|
|
336
|
-
const contacts = await acqDb.listContacts({ listId: input.listId, limit: 100, offset: 0 })
|
|
337
|
-
|
|
338
|
-
for (const contact of contacts.data) {
|
|
339
|
-
await list.updateContactStage({
|
|
340
|
-
listId: input.listId,
|
|
341
|
-
contactId: contact.id,
|
|
342
|
-
stage: 'verified',
|
|
343
|
-
executionId: context.executionId
|
|
344
|
-
})
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
await list.recordExecution({
|
|
348
|
-
listId: input.listId,
|
|
349
|
-
executionId: context.executionId,
|
|
350
|
-
configSnapshot: config
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
return { touched: contacts.data.length }
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
entryPoint: 'qualify'
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
Use `list` for focused list-scoped workflow operations: fetch list config, record a list execution, and update company/contact list stages. Use `acqDb` when the workflow needs broader acquisition operations: create/update lists, companies, contacts, add contacts to lists, bulk import, enrichment, social posts, or CRM deal sync methods.
|
|
362
|
-
|
|
363
|
-
Keep these boundaries straight:
|
|
364
|
-
|
|
365
|
-
- Browser/UI code uses `@elevasis/ui/hooks`.
|
|
366
|
-
- Workflow code uses `@elevasis/sdk/worker` adapters.
|
|
367
|
-
- `organizationId` is injected by the platform dispatcher. Do not pass it from workflow code.
|
|
368
|
-
- Persisted API shapes are owned by core/API contracts, not route-local TypeScript.
|
|
369
|
-
|
|
370
|
-
## 5. Customize Lead-Gen Semantics
|
|
371
|
-
|
|
372
|
-
The lead-gen shell module key is `lead-gen`, the Organization OS System id is `sales.lead-gen`, and the action currently used by the shared manifest is `leadgen.lists.manage`.
|
|
373
|
-
|
|
374
|
-
For feature toggles, labels, lifecycle semantics, or resource descriptors, update the project model:
|
|
375
|
-
|
|
376
|
-
`core/config/organization-model.ts`
|
|
377
|
-
|
|
378
|
-
The platform-side lead-gen stateful vocabulary is defined for:
|
|
379
|
-
|
|
380
|
-
- `acq.list`
|
|
381
|
-
- `acq.list-member`
|
|
382
|
-
- `acq.list-company`
|
|
383
|
-
|
|
384
|
-
The generated contracts expose `StatefulPipelineDefinition` and `LEAD_GEN_PIPELINE_DEFINITIONS` so downstream agents can inspect the current pipeline/stage/state vocabulary before adding UI labels or workflow transitions.
|
|
385
|
-
|
|
386
|
-
Changing persisted platform stages, adding new owner kinds, or extending artifact schema requires coordinated core/API/UI updates. Relabeling, route wrapping, sidebar changes, and project-owned rendering usually stay in the downstream project.
|
|
387
|
-
|
|
388
|
-
## Verify
|
|
389
|
-
|
|
390
|
-
Run the checks for the surfaces you touched:
|
|
391
|
-
|
|
392
|
-
```bash
|
|
393
|
-
pnpm -C ui run check
|
|
394
|
-
pnpm -C operations run check
|
|
395
|
-
pnpm -C operations exec elevasis-sdk check
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
If you changed platform-level lead-gen contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
pnpm scaffold:sync
|
|
402
|
-
pnpm scaffold:verify
|
|
403
|
-
```
|
|
4
|
+
---
|
|
5
|
+
title: Build and Extend Lead Gen
|
|
6
|
+
description: Map the lead-gen platform primitives available to SDK projects: shared UI pages, sidebar composition, data hooks, list/member state, artifacts, workflow adapters, contracts, and org-model extension boundaries.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Build and Extend Lead Gen
|
|
10
|
+
|
|
11
|
+
Use this recipe when a downstream project wants to build on the shared lead-gen system instead of forking it.
|
|
12
|
+
|
|
13
|
+
Good trigger phrases:
|
|
14
|
+
|
|
15
|
+
- "Add a lead-gen reports page."
|
|
16
|
+
- "Build a campaign creator surface."
|
|
17
|
+
- "Customize the lead-gen sidebar."
|
|
18
|
+
- "Show artifacts for a campaign."
|
|
19
|
+
- "Read or update lead-gen lists from a workflow."
|
|
20
|
+
- "Change the lead-gen lifecycle for this business."
|
|
21
|
+
|
|
22
|
+
Lead gen is a layered platform surface, not one component:
|
|
23
|
+
|
|
24
|
+
- **Organization OS:** prospecting semantics, System access, pipeline labels, and quick access live in the organization model.
|
|
25
|
+
- **Shared UI:** lead-gen overview, lists, list detail, companies, contacts, sidebars, and drawer components live in `@elevasis/ui`.
|
|
26
|
+
- **Headless hooks:** lists, companies, contacts, artifacts, list members, transitions, and derived actions live under `@elevasis/ui/hooks`.
|
|
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
|
+
- **Workflow adapters:** `acqDb` and `list` from `@elevasis/sdk/worker` let workflows read and mutate lead-gen data through platform tools.
|
|
29
|
+
- **Contracts:** generated contract docs expose the current lead-gen shapes in `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`.
|
|
30
|
+
|
|
31
|
+
## Decision Table
|
|
32
|
+
|
|
33
|
+
| User wants | Start here | Notes |
|
|
34
|
+
| ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| Change lead-gen System availability, labels, quick access, pipeline semantics, or resource descriptors | `core/config/organization-model.ts` | Treat this as Organization OS work. Use the project's configure ceremony if available. |
|
|
36
|
+
| 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
|
+
| 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. |
|
|
38
|
+
| 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. |
|
|
39
|
+
| Render artifacts or list-member detail | `LeadGenListDetailPage`, `useArtifacts`, `useCreateArtifact`, `useListMember` | Artifacts are a substrate primitive. Keep vertical-specific rendering local until there are repeated use cases. |
|
|
40
|
+
| Read or mutate lead-gen data inside a workflow | `acqDb` or `list` from `@elevasis/sdk/worker` | `organizationId` is injected server-side by the platform dispatcher. Do not pass it from workflow code. |
|
|
41
|
+
| Add a new persisted lead-gen field, artifact kind, or transition API | Platform/API migration work, not just scaffold work | Update DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts together. |
|
|
42
|
+
|
|
43
|
+
## Published Lead-Gen Surfaces
|
|
44
|
+
|
|
45
|
+
| Surface | Import from | Use for |
|
|
46
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| `leadGenManifest`, `LEAD_GEN_ITEMS`, `LeadGenSidebar`, `LeadGenSidebarTop`, `LeadGenSidebarMiddle` | `@elevasis/ui/features/lead-gen` | Feature registration and sidebar composition |
|
|
48
|
+
| `LeadGenOverviewPage`, `LeadGenListsPage`, `LeadGenListDetailPage`, `ListBuilderPage`, `LeadGenCompaniesPage`, `LeadGenContactsPage` | `@elevasis/ui/features/lead-gen` | Shared lead-gen pages you can route to or wrap |
|
|
49
|
+
| `ListActionsProvider`, `useListActions`, `ListBuilderWorkflow`, `ListBuilderRegistry`, `LeadGenActionKey` | `@elevasis/ui/features/lead-gen` | List Builder workflow registry, slot-based field contracts, and project-owned action wiring |
|
|
50
|
+
| `LeadGenRouteShell`, `LIST_TEMPLATE_OPTIONS`, `buildListConfig` | `@elevasis/ui/features/lead-gen` | Route shell helpers and list creation config helpers (contact/company detail surfaces are now `ContactDetailPage` / `CompanyDetailPage` from `@elevasis/ui/features/crm`) |
|
|
51
|
+
| `useLists`, `useList`, `useListsTelemetry`, `useListProgress`, `useListExecutions`, `useCreateList`, `useUpdateList`, `useUpdateListConfig`, `useDeleteList` | `@elevasis/ui/hooks` | Headless list and telemetry data access |
|
|
52
|
+
| `useWorkflowExecution`, `useExecutionSSE`, `useAddCompaniesToList`, `useRemoveCompaniesFromList`, `useAddContactsToList` | `@elevasis/ui/hooks` | List Builder workflow triggering, live execution tailing, and list membership mutations |
|
|
53
|
+
| `useCompanies`, `useCompany`, `useContacts`, `useContact` | `@elevasis/ui/hooks` | Acquisition company/contact data access |
|
|
54
|
+
| `useArtifacts`, `useCreateArtifact`, `useListMembers`, `useListMember` | `@elevasis/ui/hooks` | Lead-gen substrate data access |
|
|
55
|
+
| `useTransitionList`, `useTransitionListMember`, `useTransitionListCompany`, `useDeriveActions` | `@elevasis/ui/hooks` | Stateful transition mutations and contextual action derivation |
|
|
56
|
+
| `ElevasisUIProvider`, `ElevasisCoreProvider`, `useElevasisServices` | `@elevasis/ui/provider` | Provider setup, API access, organization context, and `listActions` registry injection |
|
|
57
|
+
| `acqDb`, `list` | `@elevasis/sdk/worker` | Workflow-side acquisition and list-scoped platform adapters |
|
|
58
|
+
|
|
59
|
+
Read the generated contracts before changing typed boundaries:
|
|
60
|
+
|
|
61
|
+
`node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md`
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
## 1. Extend Lead-Gen Navigation or Sidebar
|
|
66
|
+
|
|
67
|
+
For a simple nav addition, extend `LEAD_GEN_ITEMS` and override the lead-gen system manifest:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// ui/src/routes/__root.tsx
|
|
71
|
+
import { leadGenManifest, LEAD_GEN_ITEMS, LeadGenSidebar, LeadGenSidebarMiddle } from '@elevasis/ui/features/lead-gen'
|
|
72
|
+
import type { SystemModule } from '@elevasis/ui/provider'
|
|
73
|
+
import type { NavItem } from '@elevasis/ui/layout'
|
|
74
|
+
import { IconChartBar } from '@tabler/icons-react'
|
|
75
|
+
|
|
76
|
+
const customLeadGenItems: NavItem[] = [
|
|
77
|
+
...LEAD_GEN_ITEMS,
|
|
78
|
+
{ label: 'Reports', to: '/lead-gen/reports', icon: IconChartBar, exact: false }
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
const CustomLeadGenSidebar = () => (
|
|
82
|
+
<LeadGenSidebar>
|
|
83
|
+
<LeadGenSidebarMiddle items={customLeadGenItems} />
|
|
84
|
+
</LeadGenSidebar>
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
export const customLeadGenManifest: SystemModule = {
|
|
88
|
+
...leadGenManifest,
|
|
89
|
+
sidebar: CustomLeadGenSidebar
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Then replace `leadGenManifest` with `customLeadGenManifest` in the local `SYSTEM_MANIFESTS` array and add the matching route under `ui/src/routes/lead-gen/`.
|
|
94
|
+
|
|
95
|
+
For structural changes, compose `LeadGenSidebarTop`, `SubshellNavList`, and `SubshellSidebarSection`. The full sidebar decision tree lives in `node_modules/@elevasis/sdk/reference/scaffold/ui/customization.md`.
|
|
96
|
+
|
|
97
|
+
## 2. Wrap Shared Lead-Gen Pages
|
|
98
|
+
|
|
99
|
+
If the shared page is close, keep it and wrap it:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// ui/src/routes/lead-gen/lists.index.tsx
|
|
103
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
104
|
+
import { LeadGenListsPage } from '@elevasis/ui/features/lead-gen'
|
|
105
|
+
import { Stack } from '@mantine/core'
|
|
106
|
+
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
|
|
107
|
+
|
|
108
|
+
export const Route = createFileRoute('/lead-gen/lists/')({
|
|
109
|
+
component: ListsRoute
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
function ListsRoute() {
|
|
113
|
+
return (
|
|
114
|
+
<Stack gap={0}>
|
|
115
|
+
<ProjectAnnouncementBanner context="lead-gen-lists" />
|
|
116
|
+
<LeadGenListsPage />
|
|
117
|
+
</Stack>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
For list detail routes, pass the route param into `LeadGenListDetailPage`:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
126
|
+
import { LeadGenListDetailPage } from '@elevasis/ui/features/lead-gen'
|
|
127
|
+
|
|
128
|
+
export const Route = createFileRoute('/lead-gen/lists/$listId')({
|
|
129
|
+
component: ListDetailRoute
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
function ListDetailRoute() {
|
|
133
|
+
const { listId } = Route.useParams()
|
|
134
|
+
return <LeadGenListDetailPage listId={listId} />
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use the same wrapping pattern for `LeadGenOverviewPage`, `LeadGenCompaniesPage`, and `LeadGenContactsPage`.
|
|
139
|
+
|
|
140
|
+
For the real-time List Builder workspace, add a thin route that passes the route param into `ListBuilderPage`:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
144
|
+
import { ListBuilderPage } from '@elevasis/ui/features/lead-gen'
|
|
145
|
+
|
|
146
|
+
export const Route = createFileRoute('/lead-gen/list-builder/$listId')({
|
|
147
|
+
component: ListBuilderRoute
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
function ListBuilderRoute() {
|
|
151
|
+
const { listId } = Route.useParams()
|
|
152
|
+
return <ListBuilderPage listId={listId} />
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Register project workflow actions at the provider boundary. The shared UI owns the contract; each project owns workflow ids, action keys, default inputs, and field components.
|
|
157
|
+
|
|
158
|
+
Each registry entry declares a Zod `schema` and a `layout` of declarative field hints (`StepConfigLayout<Input>`). The shared `StepConfigForm` renders the layout, validates against the schema, and wires `value`/`onChange` for you — no per-action React components. The List Builder right column renders the form as `Configuration | Advanced | Runs` tabs with a sticky action footer; `RunWorkflowModal` renders the same form inline. Omit the `advanced:` section when a step has none.
|
|
159
|
+
|
|
160
|
+
Available field component variants: `textinput`, `textarea`, `numberinput`, `switch`, `segmented`, `select`, `multiselect`, `tags`, `json`. Field hints support `label`, `description`, `placeholder`, `min`/`max`/`step` (numbers), `options` (selects), and `when: (values) => boolean` for conditional visibility.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import * as z from 'zod'
|
|
164
|
+
import { type ListBuilderRegistry } from '@elevasis/ui/features/lead-gen'
|
|
165
|
+
import type { StepConfigLayout } from '@elevasis/ui/components/forms'
|
|
166
|
+
import { ElevasisUIProvider } from '@elevasis/ui/provider'
|
|
167
|
+
import { resourceDescriptors } from '@core/config/organization-model'
|
|
168
|
+
|
|
169
|
+
const companyCleanupInputSchema = z.object({
|
|
170
|
+
listId: z.string().uuid(),
|
|
171
|
+
targetDescription: z.string(),
|
|
172
|
+
dryRun: z.boolean().default(true),
|
|
173
|
+
batchSize: z.number().int().min(1).default(20),
|
|
174
|
+
mode: z.enum(['mock', 'live']).default('live')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
type CompanyCleanupInput = z.infer<typeof companyCleanupInputSchema>
|
|
178
|
+
const companyCleanupResource = resourceDescriptors.companyCleanup
|
|
179
|
+
|
|
180
|
+
const companyCleanupLayout: StepConfigLayout<CompanyCleanupInput> = {
|
|
181
|
+
sections: [
|
|
182
|
+
{
|
|
183
|
+
id: 'configuration',
|
|
184
|
+
fields: [
|
|
185
|
+
{
|
|
186
|
+
path: 'targetDescription',
|
|
187
|
+
component: 'textarea',
|
|
188
|
+
label: 'Target description',
|
|
189
|
+
placeholder: 'independent veterinary clinics in Orange County'
|
|
190
|
+
},
|
|
191
|
+
{ path: 'dryRun', component: 'switch', label: 'Dry run' }
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
advanced: {
|
|
196
|
+
id: 'advanced',
|
|
197
|
+
fields: [
|
|
198
|
+
{ path: 'batchSize', component: 'numberinput', label: 'Batch size', min: 1 },
|
|
199
|
+
{
|
|
200
|
+
path: 'mode',
|
|
201
|
+
component: 'segmented',
|
|
202
|
+
options: [
|
|
203
|
+
{ value: 'mock', label: 'Mock' },
|
|
204
|
+
{ value: 'live', label: 'Live' }
|
|
205
|
+
],
|
|
206
|
+
when: () => import.meta.env.DEV
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const listActions: ListBuilderRegistry = [
|
|
213
|
+
{
|
|
214
|
+
resourceId: companyCleanupResource.id,
|
|
215
|
+
workflowId: companyCleanupResource.id,
|
|
216
|
+
actionKey: 'lead-gen.company.cleanup',
|
|
217
|
+
label: 'Company Cleanup',
|
|
218
|
+
description: 'Normalize and clean company records for the list.',
|
|
219
|
+
category: 'utility',
|
|
220
|
+
stagesAffected: ['populated'],
|
|
221
|
+
schema: companyCleanupInputSchema,
|
|
222
|
+
layout: companyCleanupLayout,
|
|
223
|
+
defaultInput: (list) => ({
|
|
224
|
+
listId: list.id,
|
|
225
|
+
targetDescription: '',
|
|
226
|
+
dryRun: true,
|
|
227
|
+
batchSize: 20
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
export function App() {
|
|
233
|
+
return (
|
|
234
|
+
<ElevasisUIProvider listActions={listActions} {...providerProps}>
|
|
235
|
+
{children}
|
|
236
|
+
</ElevasisUIProvider>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Author the `schema` in a browser-safe sibling file (no Node-only imports) so it can be reused by the workflow runtime AND the UI form. See `apollo-import-schema.ts` in the platform for the canonical split pattern.
|
|
242
|
+
|
|
243
|
+
Per-section split rule (operator-grade vs power-user):
|
|
244
|
+
|
|
245
|
+
- **Configuration:** target/limit, dry-run toggle, mode (preview/finalize), and any field an operator changes between runs.
|
|
246
|
+
- **Advanced:** concurrency, batch size, timeouts, credential names, workflow chaining, dev-only mode toggles. Omit the `advanced:` key entirely when a step has none.
|
|
247
|
+
- **`defaultInput`:** seeds the form before the user touches anything; must include `listId: list.id`.
|
|
248
|
+
|
|
249
|
+
## 3. Build a Custom Campaign Workspace
|
|
250
|
+
|
|
251
|
+
When the project needs custom layout or vertical-specific rendering, use the hooks directly:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { Badge, Button, Card, Group, Stack, Text } from '@mantine/core'
|
|
255
|
+
import { useArtifacts, useList, useListMembers, useTransitionListMember } from '@elevasis/ui/hooks'
|
|
256
|
+
|
|
257
|
+
export function CampaignWorkspace({ listId }: { listId: string }) {
|
|
258
|
+
const listQuery = useList(listId)
|
|
259
|
+
const membersQuery = useListMembers({ listId })
|
|
260
|
+
const artifactsQuery = useArtifacts({ ownerKind: 'list', ownerId: listId })
|
|
261
|
+
const transitionMember = useTransitionListMember()
|
|
262
|
+
|
|
263
|
+
const list = listQuery.data
|
|
264
|
+
if (!list) return null
|
|
265
|
+
|
|
266
|
+
const firstMember = membersQuery.data?.members[0]
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<Stack>
|
|
270
|
+
<Group justify="space-between">
|
|
271
|
+
<div>
|
|
272
|
+
<Text fw={700}>{list.name}</Text>
|
|
273
|
+
<Text size="sm" c="dimmed">{list.description}</Text>
|
|
274
|
+
</div>
|
|
275
|
+
<Badge>{list.stateKey}</Badge>
|
|
276
|
+
</Group>
|
|
277
|
+
|
|
278
|
+
<Card withBorder>
|
|
279
|
+
<Text size="sm">Artifacts: {artifactsQuery.data?.artifacts.length ?? 0}</Text>
|
|
280
|
+
</Card>
|
|
281
|
+
|
|
282
|
+
{firstMember ? (
|
|
283
|
+
<Button
|
|
284
|
+
onClick={() =>
|
|
285
|
+
transitionMember.mutate({
|
|
286
|
+
memberId: firstMember.id,
|
|
287
|
+
listId,
|
|
288
|
+
pipelineKey: 'lead-gen',
|
|
289
|
+
stageKey: 'prospecting',
|
|
290
|
+
stateKey: 'verified'
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
>
|
|
294
|
+
Mark First Member Verified
|
|
295
|
+
</Button>
|
|
296
|
+
) : null}
|
|
297
|
+
</Stack>
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Use `useArtifacts({ ownerKind, ownerId })` for durable JSON artifacts like audits, research summaries, snapshots, exports, or model outputs.
|
|
303
|
+
|
|
304
|
+
## 4. Read and Mutate Lead-Gen Data in Workflows
|
|
305
|
+
|
|
306
|
+
Inside deployed workflows, use worker adapters instead of browser hooks or direct database access:
|
|
307
|
+
|
|
308
|
+
External projects should define workflow input/output schemas in `@shared/types`; the example below assumes those shared schemas already exist.
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
// operations/src/sales/qualify-list.ts
|
|
312
|
+
import type { WorkflowDefinition } from '@elevasis/sdk'
|
|
313
|
+
import { acqDb, list } from '@elevasis/sdk/worker'
|
|
314
|
+
import { qualifyListInputSchema, qualifyListOutputSchema } from '@shared/types'
|
|
315
|
+
import { resourceDescriptors } from '@core/config/organization-model'
|
|
316
|
+
|
|
317
|
+
export const qualifyListWorkflow: WorkflowDefinition = {
|
|
318
|
+
config: {
|
|
319
|
+
resource: resourceDescriptors.qualifyList,
|
|
320
|
+
resourceId: resourceDescriptors.qualifyList.id,
|
|
321
|
+
name: 'Qualify List',
|
|
322
|
+
type: resourceDescriptors.qualifyList.kind,
|
|
323
|
+
version: '1.0.0',
|
|
324
|
+
status: 'dev',
|
|
325
|
+
},
|
|
326
|
+
contract: { inputSchema: qualifyListInputSchema, outputSchema: qualifyListOutputSchema },
|
|
327
|
+
steps: {
|
|
328
|
+
qualify: {
|
|
329
|
+
id: 'qualify',
|
|
330
|
+
name: 'Qualify',
|
|
331
|
+
inputSchema: qualifyListInputSchema,
|
|
332
|
+
outputSchema: qualifyListOutputSchema,
|
|
333
|
+
next: null,
|
|
334
|
+
handler: async (input, context) => {
|
|
335
|
+
const config = await list.getConfig({ listId: input.listId })
|
|
336
|
+
const contacts = await acqDb.listContacts({ listId: input.listId, limit: 100, offset: 0 })
|
|
337
|
+
|
|
338
|
+
for (const contact of contacts.data) {
|
|
339
|
+
await list.updateContactStage({
|
|
340
|
+
listId: input.listId,
|
|
341
|
+
contactId: contact.id,
|
|
342
|
+
stage: 'verified',
|
|
343
|
+
executionId: context.executionId
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
await list.recordExecution({
|
|
348
|
+
listId: input.listId,
|
|
349
|
+
executionId: context.executionId,
|
|
350
|
+
configSnapshot: config
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
return { touched: contacts.data.length }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
entryPoint: 'qualify'
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Use `list` for focused list-scoped workflow operations: fetch list config, record a list execution, and update company/contact list stages. Use `acqDb` when the workflow needs broader acquisition operations: create/update lists, companies, contacts, add contacts to lists, bulk import, enrichment, social posts, or CRM deal sync methods.
|
|
362
|
+
|
|
363
|
+
Keep these boundaries straight:
|
|
364
|
+
|
|
365
|
+
- Browser/UI code uses `@elevasis/ui/hooks`.
|
|
366
|
+
- Workflow code uses `@elevasis/sdk/worker` adapters.
|
|
367
|
+
- `organizationId` is injected by the platform dispatcher. Do not pass it from workflow code.
|
|
368
|
+
- Persisted API shapes are owned by core/API contracts, not route-local TypeScript.
|
|
369
|
+
|
|
370
|
+
## 5. Customize Lead-Gen Semantics
|
|
371
|
+
|
|
372
|
+
The lead-gen shell module key is `lead-gen`, the Organization OS System id is `sales.lead-gen`, and the action currently used by the shared manifest is `leadgen.lists.manage`.
|
|
373
|
+
|
|
374
|
+
For feature toggles, labels, lifecycle semantics, or resource descriptors, update the project model:
|
|
375
|
+
|
|
376
|
+
`core/config/organization-model.ts`
|
|
377
|
+
|
|
378
|
+
The platform-side lead-gen stateful vocabulary is defined for:
|
|
379
|
+
|
|
380
|
+
- `acq.list`
|
|
381
|
+
- `acq.list-member`
|
|
382
|
+
- `acq.list-company`
|
|
383
|
+
|
|
384
|
+
The generated contracts expose `StatefulPipelineDefinition` and `LEAD_GEN_PIPELINE_DEFINITIONS` so downstream agents can inspect the current pipeline/stage/state vocabulary before adding UI labels or workflow transitions.
|
|
385
|
+
|
|
386
|
+
Changing persisted platform stages, adding new owner kinds, or extending artifact schema requires coordinated core/API/UI updates. Relabeling, route wrapping, sidebar changes, and project-owned rendering usually stay in the downstream project.
|
|
387
|
+
|
|
388
|
+
## Verify
|
|
389
|
+
|
|
390
|
+
Run the checks for the surfaces you touched:
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
pnpm -C ui run check
|
|
394
|
+
pnpm -C operations run check
|
|
395
|
+
pnpm -C operations exec elevasis-sdk check
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
If you changed platform-level lead-gen contracts in the monorepo, the platform maintainer must also regenerate and verify scaffold output:
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
pnpm scaffold:sync
|
|
402
|
+
pnpm scaffold:verify
|
|
403
|
+
```
|