@elevasis/core 0.15.0 → 0.16.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/index.d.ts +1718 -19
- package/dist/index.js +369 -25
- package/dist/organization-model/index.d.ts +1718 -19
- package/dist/organization-model/index.js +369 -25
- package/dist/test-utils/index.d.ts +1108 -371
- package/dist/test-utils/index.js +357 -17
- package/package.json +5 -1
- package/src/__tests__/publish.test.ts +14 -13
- package/src/__tests__/template-core-compatibility.test.ts +4 -4
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1109 -882
- package/src/auth/multi-tenancy/index.ts +3 -0
- package/src/auth/multi-tenancy/theme-presets.ts +45 -0
- package/src/auth/multi-tenancy/types.ts +57 -83
- package/src/auth/multi-tenancy/users/api-schemas.ts +165 -194
- package/src/business/acquisition/activity-events.ts +13 -4
- package/src/business/acquisition/api-schemas.test.ts +315 -4
- package/src/business/acquisition/api-schemas.ts +122 -8
- package/src/business/acquisition/build-templates.ts +44 -0
- package/src/business/acquisition/crm-next-action.test.ts +262 -0
- package/src/business/acquisition/crm-next-action.ts +220 -0
- package/src/business/acquisition/crm-priority.test.ts +216 -0
- package/src/business/acquisition/crm-priority.ts +349 -0
- package/src/business/acquisition/crm-state-actions.test.ts +151 -160
- package/src/business/acquisition/deal-ownership.test.ts +351 -0
- package/src/business/acquisition/deal-ownership.ts +120 -0
- package/src/business/acquisition/derive-actions.test.ts +101 -37
- package/src/business/acquisition/derive-actions.ts +102 -87
- package/src/business/acquisition/index.ts +10 -0
- package/src/business/acquisition/types.ts +400 -366
- package/src/business/crm/api-schemas.ts +40 -0
- package/src/business/crm/index.ts +1 -0
- package/src/business/deals/api-schemas.ts +79 -0
- package/src/business/deals/index.ts +1 -0
- package/src/business/projects/types.ts +124 -88
- package/src/execution/core/runner-types.ts +61 -80
- package/src/execution/engine/index.ts +4 -3
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -104
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1473
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -102
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -179
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -309
- package/src/execution/engine/tools/integration/tool.ts +255 -253
- package/src/execution/engine/tools/lead-service-types.ts +939 -924
- package/src/execution/engine/tools/messages.ts +43 -0
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -5
- package/src/execution/engine/tools/platform/acquisition/types.ts +5 -2
- package/src/execution/engine/tools/platform/email/types.ts +97 -96
- package/src/execution/engine/tools/registry.ts +4 -3
- package/src/execution/engine/tools/tool-maps.ts +3 -1
- package/src/execution/engine/tools/types.ts +234 -233
- package/src/execution/engine/workflow/types.ts +195 -193
- package/src/execution/external/api-schemas.ts +40 -0
- package/src/execution/external/index.ts +1 -0
- package/src/knowledge/README.md +32 -0
- package/src/knowledge/__tests__/queries.test.ts +504 -0
- package/src/knowledge/format.ts +99 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/queries.ts +256 -0
- package/src/organization-model/__tests__/defaults.test.ts +172 -172
- package/src/organization-model/__tests__/foundation.test.ts +7 -7
- package/src/organization-model/__tests__/icons.test.ts +27 -0
- package/src/organization-model/__tests__/knowledge.test.ts +214 -0
- package/src/organization-model/contracts.ts +17 -15
- package/src/organization-model/defaults.ts +74 -19
- package/src/organization-model/domains/knowledge.ts +53 -0
- package/src/organization-model/domains/navigation.ts +416 -399
- package/src/organization-model/domains/prospecting.ts +204 -1
- package/src/organization-model/domains/sales.test.ts +29 -0
- package/src/organization-model/domains/sales.ts +102 -0
- package/src/organization-model/domains/shared.ts +6 -5
- package/src/organization-model/foundation.ts +10 -6
- package/src/organization-model/graph/build.ts +209 -182
- package/src/organization-model/graph/schema.ts +37 -34
- package/src/organization-model/graph/types.ts +47 -31
- package/src/organization-model/icons.ts +81 -0
- package/src/organization-model/index.ts +8 -3
- package/src/organization-model/organization-model.mdx +1 -1
- package/src/organization-model/published.ts +103 -86
- package/src/organization-model/schema.ts +90 -85
- package/src/organization-model/types.ts +42 -35
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/index.ts +23 -27
- package/src/platform/registry/index.ts +0 -4
- package/src/platform/registry/resource-registry.ts +0 -77
- package/src/platform/registry/serialized-types.ts +148 -219
- package/src/platform/registry/stats-types.ts +60 -60
- package/src/reference/_generated/contracts.md +829 -595
- package/src/supabase/database.types.ts +2978 -2958
|
@@ -6,13 +6,35 @@ export const ProspectingLifecycleStageSchema = DisplayMetadataSchema.extend({
|
|
|
6
6
|
order: z.number().int().min(0)
|
|
7
7
|
})
|
|
8
8
|
|
|
9
|
+
export const ProspectingBuildTemplateStepSchema = DisplayMetadataSchema.extend({
|
|
10
|
+
id: ModelIdSchema,
|
|
11
|
+
primaryEntity: z.enum(['company', 'contact']),
|
|
12
|
+
outputs: z.array(z.enum(['company', 'contact', 'export'])).min(1),
|
|
13
|
+
stageKey: ModelIdSchema,
|
|
14
|
+
dependsOn: z.array(ModelIdSchema).optional(),
|
|
15
|
+
dependencyMode: z.literal('per-record-eligibility'),
|
|
16
|
+
capabilityKey: ModelIdSchema,
|
|
17
|
+
defaultBatchSize: z.number().int().positive(),
|
|
18
|
+
maxBatchSize: z.number().int().positive()
|
|
19
|
+
}).refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
|
|
20
|
+
message: 'defaultBatchSize must be less than or equal to maxBatchSize',
|
|
21
|
+
path: ['defaultBatchSize']
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export const ProspectingBuildTemplateSchema = DisplayMetadataSchema.extend({
|
|
25
|
+
id: ModelIdSchema,
|
|
26
|
+
steps: z.array(ProspectingBuildTemplateStepSchema).min(1)
|
|
27
|
+
})
|
|
28
|
+
|
|
9
29
|
export const OrganizationModelProspectingSchema = z.object({
|
|
10
30
|
listEntityId: ModelIdSchema,
|
|
11
31
|
companyEntityId: ModelIdSchema,
|
|
12
32
|
contactEntityId: ModelIdSchema,
|
|
13
33
|
description: DescriptionSchema.optional(),
|
|
14
34
|
companyStages: z.array(ProspectingLifecycleStageSchema).min(1),
|
|
15
|
-
contactStages: z.array(ProspectingLifecycleStageSchema).min(1)
|
|
35
|
+
contactStages: z.array(ProspectingLifecycleStageSchema).min(1),
|
|
36
|
+
defaultBuildTemplateId: ModelIdSchema,
|
|
37
|
+
buildTemplates: z.array(ProspectingBuildTemplateSchema).min(1)
|
|
16
38
|
})
|
|
17
39
|
|
|
18
40
|
export const DEFAULT_ORGANIZATION_MODEL_PROSPECTING: z.infer<typeof OrganizationModelProspectingSchema> = {
|
|
@@ -29,5 +51,186 @@ export const DEFAULT_ORGANIZATION_MODEL_PROSPECTING: z.infer<typeof Organization
|
|
|
29
51
|
{ id: 'verified', label: 'Verified', order: 2 },
|
|
30
52
|
{ id: 'personalized', label: 'Personalized', order: 3 },
|
|
31
53
|
{ id: 'uploaded', label: 'Uploaded', order: 4 }
|
|
54
|
+
],
|
|
55
|
+
defaultBuildTemplateId: 'local-services',
|
|
56
|
+
buildTemplates: [
|
|
57
|
+
{
|
|
58
|
+
id: 'local-services',
|
|
59
|
+
label: 'Local Services Prospecting',
|
|
60
|
+
description:
|
|
61
|
+
'Curated local-services list build using company sourcing, website analysis, qualification, contact discovery, verification, personalization, and review.',
|
|
62
|
+
steps: [
|
|
63
|
+
{
|
|
64
|
+
id: 'source-companies',
|
|
65
|
+
label: 'Source companies',
|
|
66
|
+
primaryEntity: 'company',
|
|
67
|
+
outputs: ['company'],
|
|
68
|
+
stageKey: 'populated',
|
|
69
|
+
dependencyMode: 'per-record-eligibility',
|
|
70
|
+
capabilityKey: 'lead-gen.company.source',
|
|
71
|
+
defaultBatchSize: 100,
|
|
72
|
+
maxBatchSize: 250
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'analyze-websites',
|
|
76
|
+
label: 'Analyze websites',
|
|
77
|
+
primaryEntity: 'company',
|
|
78
|
+
outputs: ['company'],
|
|
79
|
+
stageKey: 'extracted',
|
|
80
|
+
dependsOn: ['source-companies'],
|
|
81
|
+
dependencyMode: 'per-record-eligibility',
|
|
82
|
+
capabilityKey: 'lead-gen.company.website-extract',
|
|
83
|
+
defaultBatchSize: 50,
|
|
84
|
+
maxBatchSize: 100
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'qualify-companies',
|
|
88
|
+
label: 'Qualify companies',
|
|
89
|
+
primaryEntity: 'company',
|
|
90
|
+
outputs: ['company'],
|
|
91
|
+
stageKey: 'qualified',
|
|
92
|
+
dependsOn: ['analyze-websites'],
|
|
93
|
+
dependencyMode: 'per-record-eligibility',
|
|
94
|
+
capabilityKey: 'lead-gen.company.qualify',
|
|
95
|
+
defaultBatchSize: 100,
|
|
96
|
+
maxBatchSize: 250
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'find-contacts',
|
|
100
|
+
label: 'Find contacts',
|
|
101
|
+
primaryEntity: 'contact',
|
|
102
|
+
outputs: ['contact'],
|
|
103
|
+
stageKey: 'discovered',
|
|
104
|
+
dependsOn: ['qualify-companies'],
|
|
105
|
+
dependencyMode: 'per-record-eligibility',
|
|
106
|
+
capabilityKey: 'lead-gen.contact.discover',
|
|
107
|
+
defaultBatchSize: 50,
|
|
108
|
+
maxBatchSize: 100
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'verify-emails',
|
|
112
|
+
label: 'Verify emails',
|
|
113
|
+
primaryEntity: 'contact',
|
|
114
|
+
outputs: ['contact'],
|
|
115
|
+
stageKey: 'verified',
|
|
116
|
+
dependsOn: ['find-contacts'],
|
|
117
|
+
dependencyMode: 'per-record-eligibility',
|
|
118
|
+
capabilityKey: 'lead-gen.contact.verify-email',
|
|
119
|
+
defaultBatchSize: 100,
|
|
120
|
+
maxBatchSize: 500
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'personalize',
|
|
124
|
+
label: 'Personalize',
|
|
125
|
+
primaryEntity: 'contact',
|
|
126
|
+
outputs: ['contact'],
|
|
127
|
+
stageKey: 'personalized',
|
|
128
|
+
dependsOn: ['verify-emails'],
|
|
129
|
+
dependencyMode: 'per-record-eligibility',
|
|
130
|
+
capabilityKey: 'lead-gen.contact.personalize',
|
|
131
|
+
defaultBatchSize: 25,
|
|
132
|
+
maxBatchSize: 100
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: 'review',
|
|
136
|
+
label: 'Review',
|
|
137
|
+
primaryEntity: 'contact',
|
|
138
|
+
outputs: ['export'],
|
|
139
|
+
stageKey: 'uploaded',
|
|
140
|
+
dependsOn: ['personalize'],
|
|
141
|
+
dependencyMode: 'per-record-eligibility',
|
|
142
|
+
capabilityKey: 'lead-gen.review.outreach-ready',
|
|
143
|
+
defaultBatchSize: 25,
|
|
144
|
+
maxBatchSize: 100
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'dtc-subscription-apollo-clickup',
|
|
150
|
+
label: 'DTC Subscription Apollo Export',
|
|
151
|
+
description:
|
|
152
|
+
'Prospecting pipeline for DTC subscription or subscription-ready brands where Apollo is the source and contact-enrichment layer, Elevasis handles company research and fit scoring, and approved leads export as an approved lead list.',
|
|
153
|
+
steps: [
|
|
154
|
+
{
|
|
155
|
+
id: 'import-apollo-search',
|
|
156
|
+
label: 'Import Apollo search',
|
|
157
|
+
description: 'Pull companies and seed contact data from a predefined Apollo search or list.',
|
|
158
|
+
primaryEntity: 'company',
|
|
159
|
+
outputs: ['company', 'contact'],
|
|
160
|
+
stageKey: 'populated',
|
|
161
|
+
dependencyMode: 'per-record-eligibility',
|
|
162
|
+
capabilityKey: 'lead-gen.company.apollo-import',
|
|
163
|
+
defaultBatchSize: 250,
|
|
164
|
+
maxBatchSize: 1000
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'analyze-websites',
|
|
168
|
+
label: 'Analyze websites',
|
|
169
|
+
description: 'Extract subscription, product, retention, and tech-stack signals from each brand website.',
|
|
170
|
+
primaryEntity: 'company',
|
|
171
|
+
outputs: ['company'],
|
|
172
|
+
stageKey: 'extracted',
|
|
173
|
+
dependsOn: ['import-apollo-search'],
|
|
174
|
+
dependencyMode: 'per-record-eligibility',
|
|
175
|
+
capabilityKey: 'lead-gen.company.website-extract',
|
|
176
|
+
defaultBatchSize: 50,
|
|
177
|
+
maxBatchSize: 100
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 'score-dtc-fit',
|
|
181
|
+
label: 'Score DTC fit',
|
|
182
|
+
description:
|
|
183
|
+
'Classify subscription potential, consumable-product fit, retention maturity, and disqualifiers.',
|
|
184
|
+
primaryEntity: 'company',
|
|
185
|
+
outputs: ['company'],
|
|
186
|
+
stageKey: 'qualified',
|
|
187
|
+
dependsOn: ['analyze-websites'],
|
|
188
|
+
dependencyMode: 'per-record-eligibility',
|
|
189
|
+
capabilityKey: 'lead-gen.company.dtc-subscription-qualify',
|
|
190
|
+
defaultBatchSize: 100,
|
|
191
|
+
maxBatchSize: 250
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'enrich-decision-makers',
|
|
195
|
+
label: 'Enrich decision-makers',
|
|
196
|
+
description:
|
|
197
|
+
'Use Apollo to find qualified contacts such as founders, retention leads, lifecycle leads, and marketing owners.',
|
|
198
|
+
primaryEntity: 'contact',
|
|
199
|
+
outputs: ['contact'],
|
|
200
|
+
stageKey: 'discovered',
|
|
201
|
+
dependsOn: ['score-dtc-fit'],
|
|
202
|
+
dependencyMode: 'per-record-eligibility',
|
|
203
|
+
capabilityKey: 'lead-gen.contact.apollo-decision-maker-enrich',
|
|
204
|
+
defaultBatchSize: 100,
|
|
205
|
+
maxBatchSize: 250
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: 'verify-emails',
|
|
209
|
+
label: 'Verify emails',
|
|
210
|
+
description: 'Verify deliverability before the QC and handoff step.',
|
|
211
|
+
primaryEntity: 'contact',
|
|
212
|
+
outputs: ['contact'],
|
|
213
|
+
stageKey: 'verified',
|
|
214
|
+
dependsOn: ['enrich-decision-makers'],
|
|
215
|
+
dependencyMode: 'per-record-eligibility',
|
|
216
|
+
capabilityKey: 'lead-gen.contact.verify-email',
|
|
217
|
+
defaultBatchSize: 250,
|
|
218
|
+
maxBatchSize: 500
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'review-and-export',
|
|
222
|
+
label: 'Review and export',
|
|
223
|
+
description: 'Operator QC approves or rejects leads, then approved records are exported as a lead list.',
|
|
224
|
+
primaryEntity: 'company',
|
|
225
|
+
outputs: ['export'],
|
|
226
|
+
stageKey: 'uploaded',
|
|
227
|
+
dependsOn: ['verify-emails'],
|
|
228
|
+
dependencyMode: 'per-record-eligibility',
|
|
229
|
+
capabilityKey: 'lead-gen.export.list',
|
|
230
|
+
defaultBatchSize: 100,
|
|
231
|
+
maxBatchSize: 250
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
}
|
|
32
235
|
]
|
|
33
236
|
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
CRM_FOLLOWUP_1_SENT_STATE,
|
|
10
10
|
CRM_FOLLOWUP_2_SENT_STATE,
|
|
11
11
|
CRM_FOLLOWUP_3_SENT_STATE,
|
|
12
|
+
CRM_PRIORITY_BUCKETS,
|
|
13
|
+
DEFAULT_CRM_PRIORITY_RULE_CONFIG,
|
|
12
14
|
getValidStatesForStage,
|
|
13
15
|
findPipeline,
|
|
14
16
|
ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE,
|
|
@@ -96,6 +98,33 @@ describe('CRM state constant shapes', () => {
|
|
|
96
98
|
})
|
|
97
99
|
})
|
|
98
100
|
|
|
101
|
+
describe('DEFAULT_CRM_PRIORITY_RULE_CONFIG', () => {
|
|
102
|
+
it('defines the canonical CRM priority buckets in sort order', () => {
|
|
103
|
+
expect(CRM_PRIORITY_BUCKETS.map((bucket) => bucket.bucketKey)).toEqual([
|
|
104
|
+
'needs_response',
|
|
105
|
+
'follow_up_due',
|
|
106
|
+
'waiting',
|
|
107
|
+
'stale',
|
|
108
|
+
'closed_low'
|
|
109
|
+
])
|
|
110
|
+
expect(CRM_PRIORITY_BUCKETS.map((bucket) => bucket.rank)).toEqual([10, 20, 30, 40, 50])
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('includes display metadata for each bucket', () => {
|
|
114
|
+
for (const bucket of CRM_PRIORITY_BUCKETS) {
|
|
115
|
+
expect(bucket.label).toEqual(expect.any(String))
|
|
116
|
+
expect(bucket.rank).toEqual(expect.any(Number))
|
|
117
|
+
expect(bucket.color).toEqual(expect.any(String))
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('maps CRM states and closed stages into priority rules', () => {
|
|
122
|
+
expect(DEFAULT_CRM_PRIORITY_RULE_CONFIG.closedStageKeys).toEqual(['closed_won', 'closed_lost'])
|
|
123
|
+
expect(DEFAULT_CRM_PRIORITY_RULE_CONFIG.followUpAfterDaysByStateKey.discovery_link_sent).toBe(3)
|
|
124
|
+
expect(DEFAULT_CRM_PRIORITY_RULE_CONFIG.staleAfterDays).toBeGreaterThan(0)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
99
128
|
describe('getValidStatesForStage', () => {
|
|
100
129
|
it('returns all 8 states for the interested stage', () => {
|
|
101
130
|
const states = getValidStatesForStage(CRM_PIPELINE_DEFINITION, 'interested')
|
|
@@ -151,6 +151,24 @@ export interface StatefulPipelineDefinition {
|
|
|
151
151
|
stages: StatefulStageDefinition[]
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
export type CrmPriorityBucketKey = 'needs_response' | 'follow_up_due' | 'waiting' | 'stale' | 'closed_low'
|
|
155
|
+
|
|
156
|
+
export interface CrmPriorityBucketDefinition {
|
|
157
|
+
bucketKey: CrmPriorityBucketKey
|
|
158
|
+
label: string
|
|
159
|
+
/** Lower ranks sort first in deal lists and pipeline columns. */
|
|
160
|
+
rank: number
|
|
161
|
+
/** UI color token. Consumers may map this to their design system. */
|
|
162
|
+
color: string
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface CrmPriorityRuleConfig {
|
|
166
|
+
buckets: CrmPriorityBucketDefinition[]
|
|
167
|
+
closedStageKeys: string[]
|
|
168
|
+
followUpAfterDaysByStateKey: Record<string, number>
|
|
169
|
+
staleAfterDays: number
|
|
170
|
+
}
|
|
171
|
+
|
|
154
172
|
/**
|
|
155
173
|
* Find a pipeline definition by pipeline_key within an array of definitions.
|
|
156
174
|
* Preferred over direct array indexing since the model is array-based (not keyed).
|
|
@@ -252,6 +270,90 @@ export const CRM_PIPELINE_DEFINITION: StatefulPipelineDefinition = {
|
|
|
252
270
|
]
|
|
253
271
|
}
|
|
254
272
|
|
|
273
|
+
export const CRM_PRIORITY_BUCKETS: CrmPriorityBucketDefinition[] = [
|
|
274
|
+
{ bucketKey: 'needs_response', label: 'Needs Response', rank: 10, color: 'red' },
|
|
275
|
+
{ bucketKey: 'follow_up_due', label: 'Follow-up Due', rank: 20, color: 'orange' },
|
|
276
|
+
{ bucketKey: 'waiting', label: 'Waiting', rank: 30, color: 'blue' },
|
|
277
|
+
{ bucketKey: 'stale', label: 'Stale', rank: 40, color: 'gray' },
|
|
278
|
+
{ bucketKey: 'closed_low', label: 'Closed', rank: 50, color: 'dark' }
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
export const DEFAULT_CRM_PRIORITY_RULE_CONFIG: CrmPriorityRuleConfig = {
|
|
282
|
+
buckets: CRM_PRIORITY_BUCKETS,
|
|
283
|
+
closedStageKeys: ['closed_won', 'closed_lost'],
|
|
284
|
+
followUpAfterDaysByStateKey: {
|
|
285
|
+
discovery_link_sent: 3,
|
|
286
|
+
discovery_nudging: 2,
|
|
287
|
+
reply_sent: 3,
|
|
288
|
+
followup_1_sent: 3,
|
|
289
|
+
followup_2_sent: 5,
|
|
290
|
+
followup_3_sent: 7
|
|
291
|
+
},
|
|
292
|
+
staleAfterDays: 14
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// CRM Next-Action Rule Config
|
|
297
|
+
//
|
|
298
|
+
// Maps (state_key, ownership) → suggested action key from DEFAULT_CRM_ACTIONS.
|
|
299
|
+
// Per-org overrides live at organizations.config.crm.next_actions; merged at
|
|
300
|
+
// evaluator read time (same pattern as priority). The evaluator returns the
|
|
301
|
+
// action's `key` string, or null for closed deals / unrecognised combinations.
|
|
302
|
+
// ============================================================================
|
|
303
|
+
|
|
304
|
+
export interface CrmNextActionMapping {
|
|
305
|
+
/** state_key to match, or '*' for any state. */
|
|
306
|
+
stateKey: string
|
|
307
|
+
/** ownership value to match ('us' | 'them'), or '*' for any. */
|
|
308
|
+
ownership: 'us' | 'them' | '*'
|
|
309
|
+
/** Action key from DEFAULT_CRM_ACTIONS (or a custom org-defined key). */
|
|
310
|
+
actionKey: string
|
|
311
|
+
/**
|
|
312
|
+
* When true, this mapping only fires if age >= staleAfterDays.
|
|
313
|
+
* Defaults to false.
|
|
314
|
+
*/
|
|
315
|
+
requiresStale?: boolean
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface CrmNextActionRuleConfig {
|
|
319
|
+
mappings: CrmNextActionMapping[]
|
|
320
|
+
/** Fallback action key when ownership === 'us' and no explicit mapping matches. */
|
|
321
|
+
ownershipUsFallback: string
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Default next-action rule config. Evaluated in order — first match wins.
|
|
326
|
+
*
|
|
327
|
+
* Mapping table (state_key, ownership) → action_key:
|
|
328
|
+
* (discovery_link_sent, 'them') → 'send_nudge' (after staleAfterDays)
|
|
329
|
+
* (discovery_replied, 'us') → 'send_reply'
|
|
330
|
+
* (discovery_nudging, 'them') → 'send_nudge'
|
|
331
|
+
* (*, 'us') → ownershipUsFallback ('send_reply')
|
|
332
|
+
*
|
|
333
|
+
* Closed deals → null (handled before mapping evaluation).
|
|
334
|
+
*/
|
|
335
|
+
export const DEFAULT_CRM_NEXT_ACTION_RULE_CONFIG: CrmNextActionRuleConfig = {
|
|
336
|
+
mappings: [
|
|
337
|
+
{
|
|
338
|
+
stateKey: 'discovery_link_sent',
|
|
339
|
+
ownership: 'them',
|
|
340
|
+
actionKey: 'send_nudge',
|
|
341
|
+
requiresStale: true
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
stateKey: 'discovery_replied',
|
|
345
|
+
ownership: 'us',
|
|
346
|
+
actionKey: 'send_reply'
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
stateKey: 'discovery_nudging',
|
|
350
|
+
ownership: 'them',
|
|
351
|
+
actionKey: 'send_nudge'
|
|
352
|
+
}
|
|
353
|
+
],
|
|
354
|
+
ownershipUsFallback: 'send_reply'
|
|
355
|
+
}
|
|
356
|
+
|
|
255
357
|
/** Common states that appear across multiple stages. */
|
|
256
358
|
const PENDING_STATE: StatefulStateDefinition = { stateKey: 'pending', label: 'Pending' }
|
|
257
359
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { OrganizationModelIconTokenSchema } from '../icons'
|
|
2
3
|
|
|
3
4
|
export const ModelIdSchema = z
|
|
4
5
|
.string()
|
|
@@ -8,10 +9,10 @@ export const ModelIdSchema = z
|
|
|
8
9
|
.regex(/^[a-z0-9]+(?:[-._][a-z0-9]+)*$/, 'IDs must be lowercase and use -, _, or . separators')
|
|
9
10
|
|
|
10
11
|
export const LabelSchema = z.string().trim().min(1).max(120)
|
|
11
|
-
export const DescriptionSchema = z.string().trim().min(1).max(2000)
|
|
12
|
-
export const ColorTokenSchema = z.string().trim().min(1).max(50)
|
|
13
|
-
export const IconNameSchema =
|
|
14
|
-
export const PathSchema = z.string().trim().startsWith('/').max(300)
|
|
12
|
+
export const DescriptionSchema = z.string().trim().min(1).max(2000)
|
|
13
|
+
export const ColorTokenSchema = z.string().trim().min(1).max(50)
|
|
14
|
+
export const IconNameSchema = OrganizationModelIconTokenSchema
|
|
15
|
+
export const PathSchema = z.string().trim().startsWith('/').max(300)
|
|
15
16
|
|
|
16
17
|
export const ReferenceIdsSchema = z.array(ModelIdSchema).default([])
|
|
17
18
|
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { resolveOrganizationModel } from './resolve'
|
|
2
|
+
import type { OrganizationModelBuiltinIconToken } from './icons'
|
|
2
3
|
import type { DeepPartial, OrganizationModel, OrganizationModelFeature } from './types'
|
|
3
4
|
|
|
4
5
|
export type FoundationSurfaceType = 'page' | 'dashboard' | 'graph' | 'detail' | 'list' | 'settings'
|
|
5
6
|
|
|
6
|
-
export type FoundationSurfaceIcon =
|
|
7
|
+
export type FoundationSurfaceIcon = Extract<
|
|
8
|
+
OrganizationModelBuiltinIconToken,
|
|
9
|
+
'feature.dashboard' | 'feature.crm' | 'feature.lead-gen' | 'feature.projects' | 'feature.operations' | 'feature.settings'
|
|
10
|
+
>
|
|
7
11
|
|
|
8
12
|
export interface FoundationNavigationSurface extends Omit<OrganizationModelFeature, 'icon' | 'uiPosition'> {
|
|
9
13
|
icon?: FoundationSurfaceIcon
|
|
@@ -50,11 +54,11 @@ export function createFoundationOrganizationModel(override: DeepPartial<Organiza
|
|
|
50
54
|
const crmFeature = requireCoreFeature('sales.crm')
|
|
51
55
|
|
|
52
56
|
const navigationSurfaces: FoundationNavigationSurface[] = [
|
|
53
|
-
{ ...operationsFeature, id: 'operations', path: '/operations', icon: 'operations', surfaceType: 'dashboard' },
|
|
54
|
-
{ ...projectsFeature, icon: 'projects', surfaceType: 'list' },
|
|
55
|
-
{ ...leadGenFeature, id: 'lead-gen', icon: 'lead-gen', surfaceType: 'list' },
|
|
56
|
-
{ ...crmFeature, id: 'crm', icon: 'crm', surfaceType: 'graph' },
|
|
57
|
-
{ ...requireCoreFeature('settings.account'), id: 'settings', icon: 'settings', surfaceType: 'settings' }
|
|
57
|
+
{ ...operationsFeature, id: 'operations', path: '/operations', icon: 'feature.operations', surfaceType: 'dashboard' },
|
|
58
|
+
{ ...projectsFeature, icon: 'feature.projects', surfaceType: 'list' },
|
|
59
|
+
{ ...leadGenFeature, id: 'lead-gen', icon: 'feature.lead-gen', surfaceType: 'list' },
|
|
60
|
+
{ ...crmFeature, id: 'crm', icon: 'feature.crm', surfaceType: 'graph' },
|
|
61
|
+
{ ...requireCoreFeature('settings.account'), id: 'settings', icon: 'feature.settings', surfaceType: 'settings' }
|
|
58
62
|
]
|
|
59
63
|
|
|
60
64
|
const homeLabel = 'Dashboard'
|