@elevasis/core 0.30.0 → 0.32.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/auth/index.d.ts +58 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.js +73 -109
- package/dist/knowledge/index.d.ts +10 -2
- package/dist/organization-model/index.d.ts +16 -5
- package/dist/organization-model/index.js +73 -109
- package/dist/test-utils/index.d.ts +55 -2
- package/dist/test-utils/index.js +72 -108
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +376 -446
- package/src/business/acquisition/api-schemas.test.ts +69 -5
- package/src/business/acquisition/crm-state-actions.test.ts +24 -6
- package/src/business/pdf/sections/__tests__/proposal-document.test.ts +146 -0
- package/src/business/pdf/sections/acceptance.ts +114 -112
- package/src/business/pdf/sections/proposal-document.ts +206 -200
- package/src/execution/engine/index.ts +440 -439
- package/src/execution/engine/tools/integration/types/clickup.ts +57 -0
- package/src/execution/engine/tools/integration/types/index.ts +20 -19
- package/src/execution/engine/tools/tool-maps.ts +16 -0
- package/src/organization-model/__tests__/domains/entities.test.ts +35 -56
- package/src/organization-model/__tests__/domains/passthrough-extensibility.test.ts +199 -0
- package/src/organization-model/domains/branding.ts +58 -16
- package/src/organization-model/domains/entities.ts +0 -103
- package/src/organization-model/domains/identity.ts +122 -94
- package/src/organization-model/domains/sales.test.ts +35 -28
- package/src/organization-model/domains/sales.ts +0 -85
- package/src/organization-model/published.ts +0 -1
- package/src/organization-model/schema.ts +2 -2
- package/src/reference/_generated/contracts.md +0 -94
- package/src/supabase/database.types.ts +45 -0
|
@@ -1,9 +1,73 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { type CrmPriorityRuleConfig, type StatefulPipelineDefinition } from '../../organization-model/domains/sales'
|
|
3
|
+
|
|
4
|
+
// Inline fixture for lead-gen pipeline stage/state validation tests.
|
|
5
|
+
// The canonical constants live in @repo/elevasis-core; @repo/core cannot depend on it.
|
|
6
|
+
const LEAD_GEN_PIPELINE_DEFINITIONS: Record<string, StatefulPipelineDefinition[]> = {
|
|
7
|
+
'acq.list-member': [
|
|
8
|
+
{
|
|
9
|
+
pipelineKey: 'lead-gen',
|
|
10
|
+
label: 'Lead Generation',
|
|
11
|
+
entityKey: 'acq.list-member',
|
|
12
|
+
stages: [
|
|
13
|
+
{
|
|
14
|
+
stageKey: 'outreach',
|
|
15
|
+
label: 'Outreach',
|
|
16
|
+
states: [
|
|
17
|
+
{ stateKey: 'pending', label: 'Pending' },
|
|
18
|
+
{ stateKey: 'personalized', label: 'Personalized' },
|
|
19
|
+
{ stateKey: 'uploaded', label: 'Uploaded' },
|
|
20
|
+
{ stateKey: 'interested', label: 'Interested' }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
stageKey: 'prospecting',
|
|
25
|
+
label: 'Prospecting',
|
|
26
|
+
states: [
|
|
27
|
+
{ stateKey: 'pending', label: 'Pending' },
|
|
28
|
+
{ stateKey: 'discovered', label: 'Discovered' },
|
|
29
|
+
{ stateKey: 'verified', label: 'Verified' }
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{ stageKey: 'qualification', label: 'Qualification', states: [{ stateKey: 'pending', label: 'Pending' }] }
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
'acq.list-company': [
|
|
37
|
+
{
|
|
38
|
+
pipelineKey: 'lead-gen',
|
|
39
|
+
label: 'Lead Generation',
|
|
40
|
+
entityKey: 'acq.list-company',
|
|
41
|
+
stages: [
|
|
42
|
+
{
|
|
43
|
+
stageKey: 'outreach',
|
|
44
|
+
label: 'Outreach',
|
|
45
|
+
states: [
|
|
46
|
+
{ stateKey: 'pending', label: 'Pending' },
|
|
47
|
+
{ stateKey: 'uploaded', label: 'Uploaded' }
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
stageKey: 'prospecting',
|
|
52
|
+
label: 'Prospecting',
|
|
53
|
+
states: [
|
|
54
|
+
{ stateKey: 'pending', label: 'Pending' },
|
|
55
|
+
{ stateKey: 'populated', label: 'Populated' },
|
|
56
|
+
{ stateKey: 'extracted', label: 'Extracted' }
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
stageKey: 'qualification',
|
|
61
|
+
label: 'Qualification',
|
|
62
|
+
states: [
|
|
63
|
+
{ stateKey: 'pending', label: 'Pending' },
|
|
64
|
+
{ stateKey: 'qualified', label: 'Qualified' }
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
7
71
|
import { DEFAULT_ORGANIZATION_MODEL } from '../../organization-model/defaults'
|
|
8
72
|
import type { OrganizationModel } from '../../organization-model/types'
|
|
9
73
|
import { CrmPriorityOverrideSchema, evaluateCrmDealPriority, resolveCrmPriorityRuleConfig } from './crm-priority'
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
LEAD_GEN_PIPELINE_DEFINITIONS,
|
|
4
|
-
type StatefulPipelineDefinition
|
|
5
|
-
} from '../../organization-model/domains/sales'
|
|
2
|
+
import { type StatefulPipelineDefinition } from '../../organization-model/domains/sales'
|
|
6
3
|
import { ActivityEventSchema } from './activity-events'
|
|
7
4
|
import { DealStageSchema, TransitionItemRequestSchema } from './api-schemas'
|
|
8
5
|
import { deriveActions } from './derive-actions'
|
|
@@ -188,8 +185,29 @@ describe('CRM stage and transition vocabulary contracts', () => {
|
|
|
188
185
|
}
|
|
189
186
|
})
|
|
190
187
|
|
|
191
|
-
it('accepts
|
|
192
|
-
|
|
188
|
+
it('accepts caller-supplied lead-gen stage/state pairs in transition requests', () => {
|
|
189
|
+
const leadGenPipelines: Record<string, StatefulPipelineDefinition[]> = {
|
|
190
|
+
'acq.list-member': [
|
|
191
|
+
{
|
|
192
|
+
pipelineKey: 'lead-gen',
|
|
193
|
+
label: 'Lead Gen',
|
|
194
|
+
entityKey: 'leadgen.contact',
|
|
195
|
+
stages: [
|
|
196
|
+
{
|
|
197
|
+
stageKey: 'outreach',
|
|
198
|
+
label: 'Outreach',
|
|
199
|
+
color: 'blue',
|
|
200
|
+
states: [
|
|
201
|
+
{ stateKey: 'personalized', label: 'Personalized' },
|
|
202
|
+
{ stateKey: 'contacted', label: 'Contacted' }
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const pipelineDefinitions of Object.values(leadGenPipelines)) {
|
|
193
211
|
for (const pipeline of pipelineDefinitions) {
|
|
194
212
|
for (const stage of pipeline.stages) {
|
|
195
213
|
for (const state of stage.states) {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the shared PDF proposal document builder.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that provider branding is fully caller-supplied and no
|
|
5
|
+
* hardcoded "Elevasis" strings leak into the shared @repo/core builder.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest'
|
|
9
|
+
import { buildProposalDocument } from '../proposal-document'
|
|
10
|
+
import { acceptanceSection } from '../acceptance'
|
|
11
|
+
import type { AutomationConfig } from '../types'
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Fixtures
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
const sampleAutomation: AutomationConfig = {
|
|
18
|
+
title: 'Test Automation',
|
|
19
|
+
howItWorks: 'Does stuff automatically.',
|
|
20
|
+
workflow: ['Step 1', 'Step 2'],
|
|
21
|
+
impact: ['Saves time'],
|
|
22
|
+
hoursPerWeek: 5,
|
|
23
|
+
annualSavings: 10000
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const baseConfig = {
|
|
27
|
+
company: 'Acme Corp',
|
|
28
|
+
contact: 'Jane Doe',
|
|
29
|
+
vertical: 'Tech',
|
|
30
|
+
solutions: 'Automation',
|
|
31
|
+
executiveSummary: 'Summary text.',
|
|
32
|
+
automations: [sampleAutomation],
|
|
33
|
+
tier: 'tier2' as const,
|
|
34
|
+
initialFee: 5000,
|
|
35
|
+
monthlyFee: 2500
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// buildProposalDocument
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
describe('buildProposalDocument', () => {
|
|
43
|
+
it('sets metadata.author from providerName', () => {
|
|
44
|
+
const doc = buildProposalDocument({ ...baseConfig, providerName: 'Elevasis' })
|
|
45
|
+
expect(doc.metadata.author).toBe('Elevasis')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('sets metadata.author to a different caller-supplied value', () => {
|
|
49
|
+
const doc = buildProposalDocument({ ...baseConfig, providerName: 'Acme Solutions' })
|
|
50
|
+
expect(doc.metadata.author).toBe('Acme Solutions')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('does not hardcode "Elevasis" in metadata when a different providerName is given', () => {
|
|
54
|
+
const doc = buildProposalDocument({ ...baseConfig, providerName: 'OtherProvider' })
|
|
55
|
+
expect(doc.metadata.author).not.toBe('Elevasis')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('includes the providerName in the acceptance page text', () => {
|
|
59
|
+
const doc = buildProposalDocument({ ...baseConfig, providerName: 'MyAgency' })
|
|
60
|
+
// The acceptance page is the last page
|
|
61
|
+
const lastPage = doc.pages[doc.pages.length - 1]
|
|
62
|
+
const allText = JSON.stringify(lastPage)
|
|
63
|
+
expect(allText).toContain('MyAgency')
|
|
64
|
+
expect(allText).not.toContain('Elevasis')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('includes correct metadata title', () => {
|
|
68
|
+
const doc = buildProposalDocument({ ...baseConfig, providerName: 'Elevasis' })
|
|
69
|
+
expect(doc.metadata.title).toBe('Proposal for Acme Corp')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('builds the correct number of pages (cover + summary + automations + summary-investment + acceptance)', () => {
|
|
73
|
+
const doc = buildProposalDocument({ ...baseConfig, providerName: 'Elevasis' })
|
|
74
|
+
// 1 cover + 1 summary + 1 automation + 1 summary-investment + 1 acceptance = 5
|
|
75
|
+
expect(doc.pages).toHaveLength(5)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// acceptanceSection
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
describe('acceptanceSection', () => {
|
|
84
|
+
it('uses providerName in the acceptance statement', () => {
|
|
85
|
+
const page = acceptanceSection({
|
|
86
|
+
companyName: 'Acme Corp',
|
|
87
|
+
contactName: 'Jane Doe',
|
|
88
|
+
tier: 'tier2',
|
|
89
|
+
initialFee: 5000,
|
|
90
|
+
providerName: 'Elevasis'
|
|
91
|
+
})
|
|
92
|
+
const text = JSON.stringify(page)
|
|
93
|
+
expect(text).toContain('authorizes Elevasis to proceed')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('uses a different providerName when supplied', () => {
|
|
97
|
+
const page = acceptanceSection({
|
|
98
|
+
companyName: 'Acme Corp',
|
|
99
|
+
contactName: 'Jane Doe',
|
|
100
|
+
tier: 'tier1',
|
|
101
|
+
initialFee: 3000,
|
|
102
|
+
providerName: 'AnotherAgency'
|
|
103
|
+
})
|
|
104
|
+
const text = JSON.stringify(page)
|
|
105
|
+
expect(text).toContain('authorizes AnotherAgency to proceed')
|
|
106
|
+
expect(text).not.toContain('Elevasis')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('includes companyName and contactName in the acceptance statement', () => {
|
|
110
|
+
const page = acceptanceSection({
|
|
111
|
+
companyName: 'TestCo',
|
|
112
|
+
contactName: 'Bob Smith',
|
|
113
|
+
tier: 'tier2',
|
|
114
|
+
initialFee: 4000,
|
|
115
|
+
providerName: 'MyProvider'
|
|
116
|
+
})
|
|
117
|
+
const text = JSON.stringify(page)
|
|
118
|
+
expect(text).toContain('Bob Smith')
|
|
119
|
+
expect(text).toContain('TestCo')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('omits monthly fee term when monthlyFee is not provided', () => {
|
|
123
|
+
const page = acceptanceSection({
|
|
124
|
+
companyName: 'Acme',
|
|
125
|
+
contactName: 'Alice',
|
|
126
|
+
tier: 'tier1',
|
|
127
|
+
initialFee: 2000,
|
|
128
|
+
providerName: 'Elevasis'
|
|
129
|
+
})
|
|
130
|
+
const text = JSON.stringify(page)
|
|
131
|
+
expect(text).not.toContain('Monthly fee')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('includes monthly fee term when monthlyFee is provided', () => {
|
|
135
|
+
const page = acceptanceSection({
|
|
136
|
+
companyName: 'Acme',
|
|
137
|
+
contactName: 'Alice',
|
|
138
|
+
tier: 'tier2',
|
|
139
|
+
initialFee: 5000,
|
|
140
|
+
monthlyFee: 1500,
|
|
141
|
+
providerName: 'Elevasis'
|
|
142
|
+
})
|
|
143
|
+
const text = JSON.stringify(page)
|
|
144
|
+
expect(text).toContain('Monthly fee')
|
|
145
|
+
})
|
|
146
|
+
})
|
|
@@ -1,112 +1,114 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Acceptance Section Builder
|
|
3
|
-
*
|
|
4
|
-
* Creates a dedicated signature/acceptance page for proposals.
|
|
5
|
-
* This page contains minimal terms and signature fields placement area.
|
|
6
|
-
*
|
|
7
|
-
* Pattern: Content-only configuration, styling handled by theme system.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* import { acceptanceSection } from '@repo/core/pdf/sections'
|
|
12
|
-
*
|
|
13
|
-
* const page = acceptanceSection({
|
|
14
|
-
* companyName: 'Acme Corp',
|
|
15
|
-
* contactName: 'John Smith',
|
|
16
|
-
* tier: 'tier2',
|
|
17
|
-
* initialFee: 5000,
|
|
18
|
-
* monthlyFee: 2500 // optional
|
|
19
|
-
* })
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import type { Page } from '../types'
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Configuration for acceptance section
|
|
27
|
-
*/
|
|
28
|
-
export interface AcceptanceSectionConfig {
|
|
29
|
-
/** Company name for personalization */
|
|
30
|
-
companyName: string
|
|
31
|
-
/** Contact name for personalization */
|
|
32
|
-
contactName: string
|
|
33
|
-
/** Service tier being proposed */
|
|
34
|
-
tier: 'tier1' | 'tier2' | 'tier3' | 'custom'
|
|
35
|
-
/** Initial fee amount in dollars (one-time implementation fee) */
|
|
36
|
-
initialFee: number
|
|
37
|
-
/** Monthly fee amount in dollars (optional, for ongoing support) */
|
|
38
|
-
monthlyFee?: number
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* -
|
|
48
|
-
* -
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Acceptance Section Builder
|
|
3
|
+
*
|
|
4
|
+
* Creates a dedicated signature/acceptance page for proposals.
|
|
5
|
+
* This page contains minimal terms and signature fields placement area.
|
|
6
|
+
*
|
|
7
|
+
* Pattern: Content-only configuration, styling handled by theme system.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { acceptanceSection } from '@repo/core/pdf/sections'
|
|
12
|
+
*
|
|
13
|
+
* const page = acceptanceSection({
|
|
14
|
+
* companyName: 'Acme Corp',
|
|
15
|
+
* contactName: 'John Smith',
|
|
16
|
+
* tier: 'tier2',
|
|
17
|
+
* initialFee: 5000,
|
|
18
|
+
* monthlyFee: 2500 // optional
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { Page } from '../types'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for acceptance section
|
|
27
|
+
*/
|
|
28
|
+
export interface AcceptanceSectionConfig {
|
|
29
|
+
/** Company name for personalization */
|
|
30
|
+
companyName: string
|
|
31
|
+
/** Contact name for personalization */
|
|
32
|
+
contactName: string
|
|
33
|
+
/** Service tier being proposed */
|
|
34
|
+
tier: 'tier1' | 'tier2' | 'tier3' | 'custom'
|
|
35
|
+
/** Initial fee amount in dollars (one-time implementation fee) */
|
|
36
|
+
initialFee: number
|
|
37
|
+
/** Monthly fee amount in dollars (optional, for ongoing support) */
|
|
38
|
+
monthlyFee?: number
|
|
39
|
+
/** Name of the service provider issuing the proposal (e.g. "Elevasis") */
|
|
40
|
+
providerName: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build acceptance page with simplified terms and signature area
|
|
45
|
+
*
|
|
46
|
+
* Creates a dedicated page at the end of the proposal with:
|
|
47
|
+
* - Pricing summary
|
|
48
|
+
* - Key terms (audit timeline, training, support, guarantee)
|
|
49
|
+
* - Acceptance statement
|
|
50
|
+
* - Space for electronic signature fields (added by SignatureAPI)
|
|
51
|
+
*
|
|
52
|
+
* @param config - Acceptance section configuration
|
|
53
|
+
* @returns Page structure for terms and acceptance
|
|
54
|
+
*/
|
|
55
|
+
export function acceptanceSection(config: AcceptanceSectionConfig): Page {
|
|
56
|
+
const { companyName, contactName, tier, initialFee, monthlyFee, providerName } = config
|
|
57
|
+
|
|
58
|
+
const tierDescriptions = {
|
|
59
|
+
tier1: 'Standard Implementation',
|
|
60
|
+
tier2: 'Professional Implementation',
|
|
61
|
+
tier3: 'Enterprise Implementation',
|
|
62
|
+
custom: 'Custom Implementation'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Build simplified terms list
|
|
66
|
+
const terms: string[] = [
|
|
67
|
+
`Initial fee: $${initialFee.toLocaleString()} (${tierDescriptions[tier]})`,
|
|
68
|
+
...(monthlyFee ? [`Monthly fee: $${monthlyFee.toLocaleString()}/month`] : []),
|
|
69
|
+
'Detailed audit begins within 5 business days of payment',
|
|
70
|
+
'Training materials provided',
|
|
71
|
+
...(monthlyFee ? ['Ongoing maintenance, support, and optimization included'] : []),
|
|
72
|
+
'Payment link provided via email after signing',
|
|
73
|
+
'60-day money-back guarantee'
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
header: { title: 'Agreement & Terms', variant: 'accent' },
|
|
78
|
+
footer: true,
|
|
79
|
+
sections: [
|
|
80
|
+
{
|
|
81
|
+
type: 'card',
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: 'list',
|
|
85
|
+
ordered: false,
|
|
86
|
+
items: terms
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
content: `By signing below, ${contactName} of ${companyName} authorizes ${providerName} to proceed.`,
|
|
93
|
+
variant: 'body'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
type: 'card',
|
|
97
|
+
title: 'Signature',
|
|
98
|
+
variant: 'light',
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: 'text',
|
|
102
|
+
content: 'Electronic signature will be captured below via SignatureAPI',
|
|
103
|
+
variant: 'caption'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: 'text',
|
|
107
|
+
content: '\n\n\n\n\n',
|
|
108
|
+
variant: 'body'
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|