@elevasis/core 0.29.0 → 0.31.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 +5289 -0
- package/dist/auth/index.js +595 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +4 -105
- package/dist/organization-model/index.d.ts +1 -2
- package/dist/organization-model/index.js +4 -105
- package/dist/test-utils/index.d.ts +20 -0
- package/dist/test-utils/index.js +3 -104
- package/package.json +5 -1
- package/src/__tests__/publish.test.ts +8 -7
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +376 -446
- package/src/auth/__tests__/access-key-coverage.test.ts +42 -0
- package/src/auth/__tests__/access-key-scan.ts +117 -0
- package/src/auth/__tests__/access-keys.test.ts +81 -0
- package/src/auth/__tests__/access-model.test.ts +257 -0
- package/src/auth/__tests__/access-test-fixtures.ts +50 -0
- package/src/auth/__tests__/key-catalog-drift.test.ts +33 -0
- package/src/auth/__tests__/platform-admin-bypass-parity.test.ts +67 -0
- package/src/auth/access-keys.ts +173 -0
- package/src/auth/access-model.ts +185 -0
- package/src/auth/index.ts +6 -2
- package/src/auth/multi-tenancy/memberships/membership.ts +2 -4
- package/src/auth/multi-tenancy/permissions.ts +1 -1
- package/src/auth/multi-tenancy/types.ts +3 -12
- 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/domains/entities.ts +0 -103
- 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/reference/glossary.md +8 -6
- package/src/supabase/database.types.ts +10 -0
|
@@ -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
|
+
}
|