@contractspec/example.crm-pipeline 3.7.5 → 3.7.7

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.
Files changed (106) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/AGENTS.md +51 -33
  3. package/CHANGELOG.md +16 -0
  4. package/README.md +66 -148
  5. package/dist/browser/events/contact.event.js +1 -1
  6. package/dist/browser/events/deal.event.js +1 -1
  7. package/dist/browser/events/index.js +3 -3
  8. package/dist/browser/events/task.event.js +1 -1
  9. package/dist/browser/index.js +293 -293
  10. package/dist/browser/ui/CrmDashboard.js +221 -221
  11. package/dist/browser/ui/CrmDealCard.js +5 -5
  12. package/dist/browser/ui/CrmPipelineBoard.js +13 -13
  13. package/dist/browser/ui/hooks/index.js +2 -2
  14. package/dist/browser/ui/hooks/useDealList.js +1 -1
  15. package/dist/browser/ui/hooks/useDealMutations.js +1 -1
  16. package/dist/browser/ui/index.js +290 -290
  17. package/dist/browser/ui/modals/CreateDealModal.js +12 -12
  18. package/dist/browser/ui/modals/DealActionsModal.js +21 -21
  19. package/dist/browser/ui/modals/index.js +33 -33
  20. package/dist/browser/ui/renderers/index.js +116 -116
  21. package/dist/browser/ui/renderers/pipeline.renderer.js +97 -97
  22. package/dist/deal/index.d.ts +2 -2
  23. package/dist/events/contact.event.js +1 -1
  24. package/dist/events/deal.event.js +1 -1
  25. package/dist/events/index.js +3 -3
  26. package/dist/events/task.event.js +1 -1
  27. package/dist/handlers/index.d.ts +2 -2
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +293 -293
  30. package/dist/node/events/contact.event.js +1 -1
  31. package/dist/node/events/deal.event.js +1 -1
  32. package/dist/node/events/index.js +3 -3
  33. package/dist/node/events/task.event.js +1 -1
  34. package/dist/node/index.js +293 -293
  35. package/dist/node/ui/CrmDashboard.js +221 -221
  36. package/dist/node/ui/CrmDealCard.js +5 -5
  37. package/dist/node/ui/CrmPipelineBoard.js +13 -13
  38. package/dist/node/ui/hooks/index.js +2 -2
  39. package/dist/node/ui/hooks/useDealList.js +1 -1
  40. package/dist/node/ui/hooks/useDealMutations.js +1 -1
  41. package/dist/node/ui/index.js +290 -290
  42. package/dist/node/ui/modals/CreateDealModal.js +12 -12
  43. package/dist/node/ui/modals/DealActionsModal.js +21 -21
  44. package/dist/node/ui/modals/index.js +33 -33
  45. package/dist/node/ui/renderers/index.js +116 -116
  46. package/dist/node/ui/renderers/pipeline.renderer.js +97 -97
  47. package/dist/operations/index.d.ts +1 -1
  48. package/dist/ui/CrmDashboard.js +221 -221
  49. package/dist/ui/CrmDealCard.js +5 -5
  50. package/dist/ui/CrmPipelineBoard.js +13 -13
  51. package/dist/ui/hooks/index.d.ts +2 -2
  52. package/dist/ui/hooks/index.js +2 -2
  53. package/dist/ui/hooks/useDealList.js +1 -1
  54. package/dist/ui/hooks/useDealMutations.d.ts +9 -0
  55. package/dist/ui/hooks/useDealMutations.js +1 -1
  56. package/dist/ui/index.d.ts +3 -3
  57. package/dist/ui/index.js +290 -290
  58. package/dist/ui/modals/CreateDealModal.js +12 -12
  59. package/dist/ui/modals/DealActionsModal.js +21 -21
  60. package/dist/ui/modals/index.js +33 -33
  61. package/dist/ui/renderers/index.d.ts +1 -1
  62. package/dist/ui/renderers/index.js +116 -116
  63. package/dist/ui/renderers/pipeline.renderer.d.ts +1 -1
  64. package/dist/ui/renderers/pipeline.renderer.js +97 -97
  65. package/package.json +14 -14
  66. package/src/crm-pipeline.feature.ts +86 -86
  67. package/src/deal/deal.enum.ts +8 -8
  68. package/src/deal/deal.operation.ts +255 -255
  69. package/src/deal/deal.schema.ts +92 -92
  70. package/src/deal/deal.test-spec.ts +48 -48
  71. package/src/deal/index.ts +17 -19
  72. package/src/docs/crm-pipeline.docblock.ts +43 -43
  73. package/src/entities/company.entity.ts +52 -52
  74. package/src/entities/contact.entity.ts +67 -67
  75. package/src/entities/deal.entity.ts +134 -134
  76. package/src/entities/index.ts +27 -27
  77. package/src/entities/task.entity.ts +105 -105
  78. package/src/events/contact.event.ts +22 -22
  79. package/src/events/deal.event.ts +77 -77
  80. package/src/events/task.event.ts +19 -19
  81. package/src/example.ts +32 -32
  82. package/src/handlers/crm.handlers.ts +358 -357
  83. package/src/handlers/deal.handlers.ts +179 -179
  84. package/src/handlers/index.ts +18 -19
  85. package/src/handlers/mock-data.ts +167 -167
  86. package/src/index.ts +11 -11
  87. package/src/operations/index.ts +16 -16
  88. package/src/presentations/dashboard.presentation.ts +45 -45
  89. package/src/presentations/pipeline.presentation.ts +90 -90
  90. package/src/seeders/index.ts +26 -26
  91. package/src/shared/overlay-types.ts +23 -23
  92. package/src/ui/CrmDashboard.tsx +256 -256
  93. package/src/ui/CrmDealCard.tsx +64 -64
  94. package/src/ui/CrmPipelineBoard.tsx +105 -105
  95. package/src/ui/hooks/index.ts +3 -3
  96. package/src/ui/hooks/useDealList.ts +85 -85
  97. package/src/ui/hooks/useDealMutations.ts +151 -150
  98. package/src/ui/index.ts +5 -10
  99. package/src/ui/modals/CreateDealModal.tsx +217 -217
  100. package/src/ui/modals/DealActionsModal.tsx +390 -390
  101. package/src/ui/overlays/demo-overlays.ts +43 -43
  102. package/src/ui/renderers/index.ts +4 -3
  103. package/src/ui/renderers/pipeline.markdown.ts +165 -165
  104. package/src/ui/renderers/pipeline.renderer.tsx +17 -16
  105. package/tsconfig.json +7 -8
  106. package/tsdown.config.js +7 -3
@@ -5,150 +5,150 @@ import { DealStatusEnum, DealStatusFilterEnum } from './deal.enum';
5
5
  * A deal in the CRM pipeline.
6
6
  */
7
7
  export const DealModel = defineSchemaModel({
8
- name: 'Deal',
9
- description: 'A deal in the CRM pipeline',
10
- fields: {
11
- id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
12
- name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
13
- value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
14
- currency: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
15
- pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
16
- stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
17
- status: { type: DealStatusEnum, isOptional: false },
18
- contactId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
19
- companyId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
20
- ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
21
- expectedCloseDate: { type: ScalarTypeEnum.DateTime(), isOptional: true },
22
- createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
23
- updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
24
- },
8
+ name: 'Deal',
9
+ description: 'A deal in the CRM pipeline',
10
+ fields: {
11
+ id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
12
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
13
+ value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
14
+ currency: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
15
+ pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
16
+ stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
17
+ status: { type: DealStatusEnum, isOptional: false },
18
+ contactId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
19
+ companyId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
20
+ ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
21
+ expectedCloseDate: { type: ScalarTypeEnum.DateTime(), isOptional: true },
22
+ createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
23
+ updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
24
+ },
25
25
  });
26
26
 
27
27
  /**
28
28
  * Input for creating a deal.
29
29
  */
30
30
  export const CreateDealInputModel = defineSchemaModel({
31
- name: 'CreateDealInput',
32
- description: 'Input for creating a deal',
33
- fields: {
34
- name: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
35
- value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
36
- currency: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
37
- pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
38
- stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
39
- contactId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
40
- companyId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
41
- expectedCloseDate: { type: ScalarTypeEnum.DateTime(), isOptional: true },
42
- },
31
+ name: 'CreateDealInput',
32
+ description: 'Input for creating a deal',
33
+ fields: {
34
+ name: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
35
+ value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
36
+ currency: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
37
+ pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
38
+ stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
39
+ contactId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
40
+ companyId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
41
+ expectedCloseDate: { type: ScalarTypeEnum.DateTime(), isOptional: true },
42
+ },
43
43
  });
44
44
 
45
45
  /**
46
46
  * Input for moving a deal to another stage.
47
47
  */
48
48
  export const MoveDealInputModel = defineSchemaModel({
49
- name: 'MoveDealInput',
50
- description: 'Input for moving a deal to another stage',
51
- fields: {
52
- dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
53
- stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
54
- position: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
55
- },
49
+ name: 'MoveDealInput',
50
+ description: 'Input for moving a deal to another stage',
51
+ fields: {
52
+ dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
53
+ stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
54
+ position: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
55
+ },
56
56
  });
57
57
 
58
58
  /**
59
59
  * Payload for deal moved event.
60
60
  */
61
61
  export const DealMovedPayloadModel = defineSchemaModel({
62
- name: 'DealMovedPayload',
63
- fields: {
64
- dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
65
- fromStage: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
66
- toStage: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
67
- },
62
+ name: 'DealMovedPayload',
63
+ fields: {
64
+ dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
65
+ fromStage: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
66
+ toStage: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
67
+ },
68
68
  });
69
69
 
70
70
  /**
71
71
  * Input for marking a deal as won.
72
72
  */
73
73
  export const WinDealInputModel = defineSchemaModel({
74
- name: 'WinDealInput',
75
- description: 'Input for marking a deal as won',
76
- fields: {
77
- dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
78
- wonSource: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
79
- notes: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
80
- },
74
+ name: 'WinDealInput',
75
+ description: 'Input for marking a deal as won',
76
+ fields: {
77
+ dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
78
+ wonSource: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
79
+ notes: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
80
+ },
81
81
  });
82
82
 
83
83
  /**
84
84
  * Payload for deal won event.
85
85
  */
86
86
  export const DealWonPayloadModel = defineSchemaModel({
87
- name: 'DealWonPayload',
88
- fields: {
89
- dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
90
- value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
91
- },
87
+ name: 'DealWonPayload',
88
+ fields: {
89
+ dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
90
+ value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
91
+ },
92
92
  });
93
93
 
94
94
  /**
95
95
  * Input for marking a deal as lost.
96
96
  */
97
97
  export const LoseDealInputModel = defineSchemaModel({
98
- name: 'LoseDealInput',
99
- description: 'Input for marking a deal as lost',
100
- fields: {
101
- dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
102
- lostReason: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
103
- notes: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
104
- },
98
+ name: 'LoseDealInput',
99
+ description: 'Input for marking a deal as lost',
100
+ fields: {
101
+ dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
102
+ lostReason: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
103
+ notes: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
104
+ },
105
105
  });
106
106
 
107
107
  /**
108
108
  * Payload for deal lost event.
109
109
  */
110
110
  export const DealLostPayloadModel = defineSchemaModel({
111
- name: 'DealLostPayload',
112
- fields: {
113
- dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
114
- reason: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
115
- },
111
+ name: 'DealLostPayload',
112
+ fields: {
113
+ dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
114
+ reason: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
115
+ },
116
116
  });
117
117
 
118
118
  /**
119
119
  * Input for listing deals.
120
120
  */
121
121
  export const ListDealsInputModel = defineSchemaModel({
122
- name: 'ListDealsInput',
123
- description: 'Input for listing deals',
124
- fields: {
125
- pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
126
- stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
127
- status: { type: DealStatusFilterEnum, isOptional: true },
128
- ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
129
- search: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
130
- limit: {
131
- type: ScalarTypeEnum.Int_unsecure(),
132
- isOptional: true,
133
- defaultValue: 20,
134
- },
135
- offset: {
136
- type: ScalarTypeEnum.Int_unsecure(),
137
- isOptional: true,
138
- defaultValue: 0,
139
- },
140
- },
122
+ name: 'ListDealsInput',
123
+ description: 'Input for listing deals',
124
+ fields: {
125
+ pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
126
+ stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
127
+ status: { type: DealStatusFilterEnum, isOptional: true },
128
+ ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
129
+ search: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
130
+ limit: {
131
+ type: ScalarTypeEnum.Int_unsecure(),
132
+ isOptional: true,
133
+ defaultValue: 20,
134
+ },
135
+ offset: {
136
+ type: ScalarTypeEnum.Int_unsecure(),
137
+ isOptional: true,
138
+ defaultValue: 0,
139
+ },
140
+ },
141
141
  });
142
142
 
143
143
  /**
144
144
  * Output for listing deals.
145
145
  */
146
146
  export const ListDealsOutputModel = defineSchemaModel({
147
- name: 'ListDealsOutput',
148
- description: 'Output for listing deals',
149
- fields: {
150
- deals: { type: DealModel, isArray: true, isOptional: false },
151
- total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
152
- totalValue: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
153
- },
147
+ name: 'ListDealsOutput',
148
+ description: 'Output for listing deals',
149
+ fields: {
150
+ deals: { type: DealModel, isArray: true, isOptional: false },
151
+ total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
152
+ totalValue: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
153
+ },
154
154
  });
@@ -1,55 +1,55 @@
1
1
  import { defineTestSpec } from '@contractspec/lib.contracts-spec/tests';
2
2
 
3
3
  export const dealListTest = defineTestSpec({
4
- meta: {
5
- key: 'test.crm.deal.list',
6
- version: '1.0.0',
7
- owners: ['@example.crm-pipeline'],
8
- description: 'Test for listing deals',
9
- stability: 'stable',
10
- tags: ['test'],
11
- },
12
- target: {
13
- type: 'operation',
14
- operation: { key: 'crm.deal.list', version: '1.0.0' },
15
- },
16
- scenarios: [
17
- {
18
- key: 'success',
19
- when: { operation: { key: 'crm.deal.list' } },
20
- then: [{ type: 'expectOutput', match: {} }],
21
- },
22
- {
23
- key: 'error',
24
- when: { operation: { key: 'crm.deal.list' } },
25
- then: [{ type: 'expectError' }],
26
- },
27
- ],
4
+ meta: {
5
+ key: 'test.crm.deal.list',
6
+ version: '1.0.0',
7
+ owners: ['@example.crm-pipeline'],
8
+ description: 'Test for listing deals',
9
+ stability: 'stable',
10
+ tags: ['test'],
11
+ },
12
+ target: {
13
+ type: 'operation',
14
+ operation: { key: 'crm.deal.list', version: '1.0.0' },
15
+ },
16
+ scenarios: [
17
+ {
18
+ key: 'success',
19
+ when: { operation: { key: 'crm.deal.list' } },
20
+ then: [{ type: 'expectOutput', match: {} }],
21
+ },
22
+ {
23
+ key: 'error',
24
+ when: { operation: { key: 'crm.deal.list' } },
25
+ then: [{ type: 'expectError' }],
26
+ },
27
+ ],
28
28
  });
29
29
 
30
30
  export const dealMoveTest = defineTestSpec({
31
- meta: {
32
- key: 'test.crm.deal.move',
33
- version: '1.0.0',
34
- owners: ['@example.crm-pipeline'],
35
- description: 'Test for moving deal',
36
- stability: 'stable',
37
- tags: ['test'],
38
- },
39
- target: {
40
- type: 'operation',
41
- operation: { key: 'crm.deal.move', version: '1.0.0' },
42
- },
43
- scenarios: [
44
- {
45
- key: 'success',
46
- when: { operation: { key: 'crm.deal.move' } },
47
- then: [{ type: 'expectOutput', match: {} }],
48
- },
49
- {
50
- key: 'error',
51
- when: { operation: { key: 'crm.deal.move' } },
52
- then: [{ type: 'expectError' }],
53
- },
54
- ],
31
+ meta: {
32
+ key: 'test.crm.deal.move',
33
+ version: '1.0.0',
34
+ owners: ['@example.crm-pipeline'],
35
+ description: 'Test for moving deal',
36
+ stability: 'stable',
37
+ tags: ['test'],
38
+ },
39
+ target: {
40
+ type: 'operation',
41
+ operation: { key: 'crm.deal.move', version: '1.0.0' },
42
+ },
43
+ scenarios: [
44
+ {
45
+ key: 'success',
46
+ when: { operation: { key: 'crm.deal.move' } },
47
+ then: [{ type: 'expectOutput', match: {} }],
48
+ },
49
+ {
50
+ key: 'error',
51
+ when: { operation: { key: 'crm.deal.move' } },
52
+ then: [{ type: 'expectError' }],
53
+ },
54
+ ],
55
55
  });
package/src/deal/index.ts CHANGED
@@ -3,24 +3,22 @@
3
3
  */
4
4
 
5
5
  export { DealStatusEnum, DealStatusFilterEnum } from './deal.enum';
6
-
7
- export {
8
- DealModel,
9
- CreateDealInputModel,
10
- MoveDealInputModel,
11
- DealMovedPayloadModel,
12
- WinDealInputModel,
13
- DealWonPayloadModel,
14
- LoseDealInputModel,
15
- DealLostPayloadModel,
16
- ListDealsInputModel,
17
- ListDealsOutputModel,
18
- } from './deal.schema';
19
-
20
6
  export {
21
- CreateDealContract,
22
- MoveDealContract,
23
- WinDealContract,
24
- LoseDealContract,
25
- ListDealsContract,
7
+ CreateDealContract,
8
+ ListDealsContract,
9
+ LoseDealContract,
10
+ MoveDealContract,
11
+ WinDealContract,
26
12
  } from './deal.operation';
13
+ export {
14
+ CreateDealInputModel,
15
+ DealLostPayloadModel,
16
+ DealModel,
17
+ DealMovedPayloadModel,
18
+ DealWonPayloadModel,
19
+ ListDealsInputModel,
20
+ ListDealsOutputModel,
21
+ LoseDealInputModel,
22
+ MoveDealInputModel,
23
+ WinDealInputModel,
24
+ } from './deal.schema';
@@ -2,16 +2,16 @@ import type { DocBlock } from '@contractspec/lib.contracts-spec/docs';
2
2
  import { registerDocBlocks } from '@contractspec/lib.contracts-spec/docs';
3
3
 
4
4
  const crmPipelineDocBlocks: DocBlock[] = [
5
- {
6
- id: 'docs.examples.crm-pipeline.goal',
7
- title: 'CRM Pipeline — Goal',
8
- summary:
9
- 'Deals, stages, contacts, companies, and tasks with auditable stage movement.',
10
- kind: 'goal',
11
- visibility: 'public',
12
- route: '/docs/examples/crm-pipeline/goal',
13
- tags: ['crm', 'goal'],
14
- body: `## Why it matters
5
+ {
6
+ id: 'docs.examples.crm-pipeline.goal',
7
+ title: 'CRM Pipeline — Goal',
8
+ summary:
9
+ 'Deals, stages, contacts, companies, and tasks with auditable stage movement.',
10
+ kind: 'goal',
11
+ visibility: 'public',
12
+ route: '/docs/examples/crm-pipeline/goal',
13
+ tags: ['crm', 'goal'],
14
+ body: `## Why it matters
15
15
  - Regenerable CRM flow for deals/stages without code drift.
16
16
  - Ensures stage movement, tasks, and contacts stay aligned across surfaces.
17
17
 
@@ -22,16 +22,16 @@ const crmPipelineDocBlocks: DocBlock[] = [
22
22
  ## Success criteria
23
23
  - Stage/state changes emit events and remain declarative in spec.
24
24
  - PII (contacts) is scoped/redacted in presentations.`,
25
- },
26
- {
27
- id: 'docs.examples.crm-pipeline.usage',
28
- title: 'CRM Pipeline — Usage',
29
- summary: 'How to seed, extend, and regenerate the CRM pipeline.',
30
- kind: 'usage',
31
- visibility: 'public',
32
- route: '/docs/examples/crm-pipeline/usage',
33
- tags: ['crm', 'usage'],
34
- body: `## Setup
25
+ },
26
+ {
27
+ id: 'docs.examples.crm-pipeline.usage',
28
+ title: 'CRM Pipeline — Usage',
29
+ summary: 'How to seed, extend, and regenerate the CRM pipeline.',
30
+ kind: 'usage',
31
+ visibility: 'public',
32
+ route: '/docs/examples/crm-pipeline/usage',
33
+ tags: ['crm', 'usage'],
34
+ body: `## Setup
35
35
  1) Seed (if available) or create pipeline stages, deals, contacts, companies, tasks.
36
36
  2) Configure Notifications for stage changes/tasks; set policy.pii for contact data.
37
37
 
@@ -64,17 +64,17 @@ const crmPipelineDocBlocks: DocBlock[] = [
64
64
  4) Wire the generated handler into your existing router.
65
65
  5) Expand to events and presentations as you add surface areas.
66
66
  `,
67
- },
68
- {
69
- id: 'docs.examples.crm-pipeline.reference',
70
- title: 'CRM Pipeline — Reference',
71
- summary:
72
- 'Entities, contracts, events, and presentations for the CRM template.',
73
- kind: 'reference',
74
- visibility: 'public',
75
- route: '/docs/examples/crm-pipeline',
76
- tags: ['crm', 'reference'],
77
- body: `## Entities
67
+ },
68
+ {
69
+ id: 'docs.examples.crm-pipeline.reference',
70
+ title: 'CRM Pipeline — Reference',
71
+ summary:
72
+ 'Entities, contracts, events, and presentations for the CRM template.',
73
+ kind: 'reference',
74
+ visibility: 'public',
75
+ route: '/docs/examples/crm-pipeline',
76
+ tags: ['crm', 'reference'],
77
+ body: `## Entities
78
78
  - Contact, Company, Deal, Pipeline, Stage, Task.
79
79
 
80
80
  ## Contracts
@@ -89,17 +89,17 @@ const crmPipelineDocBlocks: DocBlock[] = [
89
89
  ## Notes
90
90
  - Stage definitions should be declarative; enforce via spec and regeneration.
91
91
  - Use Notifications for deal/task updates; Audit Trail for state changes.`,
92
- },
93
- {
94
- id: 'docs.examples.crm-pipeline.constraints',
95
- title: 'CRM Pipeline — Constraints & Safety',
96
- summary:
97
- 'Internal guardrails for stages, PII, and regeneration semantics in the CRM template.',
98
- kind: 'reference',
99
- visibility: 'internal',
100
- route: '/docs/examples/crm-pipeline/constraints',
101
- tags: ['crm', 'constraints', 'internal'],
102
- body: `## Constraints
92
+ },
93
+ {
94
+ id: 'docs.examples.crm-pipeline.constraints',
95
+ title: 'CRM Pipeline — Constraints & Safety',
96
+ summary:
97
+ 'Internal guardrails for stages, PII, and regeneration semantics in the CRM template.',
98
+ kind: 'reference',
99
+ visibility: 'internal',
100
+ route: '/docs/examples/crm-pipeline/constraints',
101
+ tags: ['crm', 'constraints', 'internal'],
102
+ body: `## Constraints
103
103
  - Stage definitions/order must remain declarative; no imperative overrides in code.
104
104
  - Events to emit: deal.created, stage.moved, task.completed, contact.updated (minimum).
105
105
  - Regeneration should not alter stage semantics without explicit spec change.
@@ -112,7 +112,7 @@ const crmPipelineDocBlocks: DocBlock[] = [
112
112
  - Add fixtures for stage move rules and SLA/task changes.
113
113
  - Ensure Audit/Notifications remain wired for stage and task events.
114
114
  - Use Feature Flags for experimental stages/SLAs; default safe/off.`,
115
- },
115
+ },
116
116
  ];
117
117
 
118
118
  registerDocBlocks(crmPipelineDocBlocks);
@@ -1,77 +1,77 @@
1
1
  import {
2
- defineEntity,
3
- defineEntityEnum,
4
- field,
5
- index,
2
+ defineEntity,
3
+ defineEntityEnum,
4
+ field,
5
+ index,
6
6
  } from '@contractspec/lib.schema';
7
7
 
8
8
  /**
9
9
  * Company size enum.
10
10
  */
11
11
  export const CompanySizeEnum = defineEntityEnum({
12
- name: 'CompanySize',
13
- values: ['STARTUP', 'SMALL', 'MEDIUM', 'LARGE', 'ENTERPRISE'] as const,
14
- schema: 'crm',
15
- description: 'Size category of a company.',
12
+ name: 'CompanySize',
13
+ values: ['STARTUP', 'SMALL', 'MEDIUM', 'LARGE', 'ENTERPRISE'] as const,
14
+ schema: 'crm',
15
+ description: 'Size category of a company.',
16
16
  });
17
17
 
18
18
  /**
19
19
  * Company entity - organization/account.
20
20
  */
21
21
  export const CompanyEntity = defineEntity({
22
- name: 'Company',
23
- description: 'A company/organization in the CRM.',
24
- schema: 'crm',
25
- map: 'company',
26
- fields: {
27
- id: field.id({ description: 'Unique company ID' }),
22
+ name: 'Company',
23
+ description: 'A company/organization in the CRM.',
24
+ schema: 'crm',
25
+ map: 'company',
26
+ fields: {
27
+ id: field.id({ description: 'Unique company ID' }),
28
28
 
29
- // Basic info
30
- name: field.string({ description: 'Company name' }),
31
- domain: field.string({ isOptional: true, description: 'Website domain' }),
32
- website: field.url({ isOptional: true }),
29
+ // Basic info
30
+ name: field.string({ description: 'Company name' }),
31
+ domain: field.string({ isOptional: true, description: 'Website domain' }),
32
+ website: field.url({ isOptional: true }),
33
33
 
34
- // Industry
35
- industry: field.string({ isOptional: true }),
34
+ // Industry
35
+ industry: field.string({ isOptional: true }),
36
36
 
37
- // Size
38
- size: field.enum('CompanySize', { isOptional: true }),
39
- employeeCount: field.int({ isOptional: true }),
40
- annualRevenue: field.decimal({ isOptional: true }),
37
+ // Size
38
+ size: field.enum('CompanySize', { isOptional: true }),
39
+ employeeCount: field.int({ isOptional: true }),
40
+ annualRevenue: field.decimal({ isOptional: true }),
41
41
 
42
- // Ownership
43
- organizationId: field.foreignKey(),
44
- ownerId: field.foreignKey({ description: 'Account owner' }),
42
+ // Ownership
43
+ organizationId: field.foreignKey(),
44
+ ownerId: field.foreignKey({ description: 'Account owner' }),
45
45
 
46
- // Contact info
47
- phone: field.string({ isOptional: true }),
48
- email: field.email({ isOptional: true }),
46
+ // Contact info
47
+ phone: field.string({ isOptional: true }),
48
+ email: field.email({ isOptional: true }),
49
49
 
50
- // Address
51
- address: field.string({ isOptional: true }),
52
- city: field.string({ isOptional: true }),
53
- state: field.string({ isOptional: true }),
54
- country: field.string({ isOptional: true }),
55
- postalCode: field.string({ isOptional: true }),
50
+ // Address
51
+ address: field.string({ isOptional: true }),
52
+ city: field.string({ isOptional: true }),
53
+ state: field.string({ isOptional: true }),
54
+ country: field.string({ isOptional: true }),
55
+ postalCode: field.string({ isOptional: true }),
56
56
 
57
- // Social
58
- linkedInUrl: field.url({ isOptional: true }),
57
+ // Social
58
+ linkedInUrl: field.url({ isOptional: true }),
59
59
 
60
- // Notes
61
- description: field.string({ isOptional: true }),
62
- tags: field.string({ isArray: true }),
60
+ // Notes
61
+ description: field.string({ isOptional: true }),
62
+ tags: field.string({ isArray: true }),
63
63
 
64
- // Custom fields
65
- customFields: field.json({ isOptional: true }),
64
+ // Custom fields
65
+ customFields: field.json({ isOptional: true }),
66
66
 
67
- // Timestamps
68
- createdAt: field.createdAt(),
69
- updatedAt: field.updatedAt(),
67
+ // Timestamps
68
+ createdAt: field.createdAt(),
69
+ updatedAt: field.updatedAt(),
70
70
 
71
- // Relations
72
- contacts: field.hasMany('Contact'),
73
- deals: field.hasMany('Deal'),
74
- },
75
- indexes: [index.on(['organizationId', 'ownerId']), index.on(['domain'])],
76
- enums: [CompanySizeEnum],
71
+ // Relations
72
+ contacts: field.hasMany('Contact'),
73
+ deals: field.hasMany('Deal'),
74
+ },
75
+ indexes: [index.on(['organizationId', 'ownerId']), index.on(['domain'])],
76
+ enums: [CompanySizeEnum],
77
77
  });