@contractspec/example.crm-pipeline 1.44.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/.turbo/turbo-build$colon$bundle.log +97 -0
- package/.turbo/turbo-build.log +98 -0
- package/CHANGELOG.md +246 -0
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/crm-pipeline.feature.d.ts +12 -0
- package/dist/crm-pipeline.feature.d.ts.map +1 -0
- package/dist/crm-pipeline.feature.js +159 -0
- package/dist/crm-pipeline.feature.js.map +1 -0
- package/dist/deal/deal.enum.d.ts +14 -0
- package/dist/deal/deal.enum.d.ts.map +1 -0
- package/dist/deal/deal.enum.js +25 -0
- package/dist/deal/deal.enum.js.map +1 -0
- package/dist/deal/deal.operation.d.ts +513 -0
- package/dist/deal/deal.operation.d.ts.map +1 -0
- package/dist/deal/deal.operation.js +270 -0
- package/dist/deal/deal.operation.js.map +1 -0
- package/dist/deal/deal.schema.d.ts +300 -0
- package/dist/deal/deal.schema.d.ts.map +1 -0
- package/dist/deal/deal.schema.js +286 -0
- package/dist/deal/deal.schema.js.map +1 -0
- package/dist/deal/index.d.ts +4 -0
- package/dist/deal/index.js +5 -0
- package/dist/docs/crm-pipeline.docblock.d.ts +1 -0
- package/dist/docs/crm-pipeline.docblock.js +100 -0
- package/dist/docs/crm-pipeline.docblock.js.map +1 -0
- package/dist/docs/index.d.ts +1 -0
- package/dist/docs/index.js +1 -0
- package/dist/entities/company.entity.d.ts +40 -0
- package/dist/entities/company.entity.d.ts.map +1 -0
- package/dist/entities/company.entity.js +63 -0
- package/dist/entities/company.entity.js.map +1 -0
- package/dist/entities/contact.entity.d.ts +44 -0
- package/dist/entities/contact.entity.d.ts.map +1 -0
- package/dist/entities/contact.entity.js +78 -0
- package/dist/entities/contact.entity.js.map +1 -0
- package/dist/entities/deal.entity.d.ts +73 -0
- package/dist/entities/deal.entity.d.ts.map +1 -0
- package/dist/entities/deal.entity.js +120 -0
- package/dist/entities/deal.entity.js.map +1 -0
- package/dist/entities/index.d.ts +15 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +33 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/task.entity.d.ts +65 -0
- package/dist/entities/task.entity.d.ts.map +1 -0
- package/dist/entities/task.entity.js +129 -0
- package/dist/entities/task.entity.js.map +1 -0
- package/dist/events/contact.event.d.ts +29 -0
- package/dist/events/contact.event.d.ts.map +1 -0
- package/dist/events/contact.event.js +45 -0
- package/dist/events/contact.event.js.map +1 -0
- package/dist/events/deal.event.d.ts +111 -0
- package/dist/events/deal.event.d.ts.map +1 -0
- package/dist/events/deal.event.js +172 -0
- package/dist/events/deal.event.js.map +1 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.js +5 -0
- package/dist/events/task.event.d.ts +29 -0
- package/dist/events/task.event.d.ts.map +1 -0
- package/dist/events/task.event.js +45 -0
- package/dist/events/task.event.js.map +1 -0
- package/dist/example.d.ts +37 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +46 -0
- package/dist/example.js.map +1 -0
- package/dist/handlers/deal.handlers.d.ts +94 -0
- package/dist/handlers/deal.handlers.d.ts.map +1 -0
- package/dist/handlers/deal.handlers.js +120 -0
- package/dist/handlers/deal.handlers.js.map +1 -0
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.js +4 -0
- package/dist/handlers/mock-data.d.ts +49 -0
- package/dist/handlers/mock-data.d.ts.map +1 -0
- package/dist/handlers/mock-data.js +188 -0
- package/dist/handlers/mock-data.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/index.d.ts +5 -0
- package/dist/operations/index.js +6 -0
- package/dist/presentations/dashboard.presentation.d.ts +15 -0
- package/dist/presentations/dashboard.presentation.d.ts.map +1 -0
- package/dist/presentations/dashboard.presentation.js +59 -0
- package/dist/presentations/dashboard.presentation.js.map +1 -0
- package/dist/presentations/index.d.ts +3 -0
- package/dist/presentations/index.js +4 -0
- package/dist/presentations/pipeline.presentation.d.ts +23 -0
- package/dist/presentations/pipeline.presentation.d.ts.map +1 -0
- package/dist/presentations/pipeline.presentation.js +119 -0
- package/dist/presentations/pipeline.presentation.js.map +1 -0
- package/example.ts +1 -0
- package/package.json +105 -0
- package/src/crm-pipeline.feature.ts +96 -0
- package/src/deal/deal.enum.ts +21 -0
- package/src/deal/deal.operation.ts +291 -0
- package/src/deal/deal.schema.ts +154 -0
- package/src/deal/index.ts +26 -0
- package/src/docs/crm-pipeline.docblock.ts +98 -0
- package/src/docs/index.ts +1 -0
- package/src/entities/company.entity.ts +77 -0
- package/src/entities/contact.entity.ts +93 -0
- package/src/entities/deal.entity.ts +160 -0
- package/src/entities/index.ts +45 -0
- package/src/entities/task.entity.ts +137 -0
- package/src/events/contact.event.ts +31 -0
- package/src/events/deal.event.ts +104 -0
- package/src/events/index.ts +3 -0
- package/src/events/task.event.ts +28 -0
- package/src/example.ts +30 -0
- package/src/handlers/deal.handlers.ts +253 -0
- package/src/handlers/index.ts +27 -0
- package/src/handlers/mock-data.ts +198 -0
- package/src/index.ts +31 -0
- package/src/operations/index.ts +20 -0
- package/src/presentations/dashboard.presentation.ts +60 -0
- package/src/presentations/index.ts +2 -0
- package/src/presentations/pipeline.presentation.ts +118 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +7 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineEntity,
|
|
3
|
+
defineEntityEnum,
|
|
4
|
+
field,
|
|
5
|
+
index,
|
|
6
|
+
} from '@contractspec/lib.schema';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Task type enum.
|
|
10
|
+
*/
|
|
11
|
+
export const TaskTypeEnum = defineEntityEnum({
|
|
12
|
+
name: 'TaskType',
|
|
13
|
+
values: ['CALL', 'EMAIL', 'MEETING', 'TODO', 'FOLLOW_UP', 'OTHER'] as const,
|
|
14
|
+
schema: 'crm',
|
|
15
|
+
description: 'Type of CRM task.',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Task priority enum.
|
|
20
|
+
*/
|
|
21
|
+
export const TaskPriorityEnum = defineEntityEnum({
|
|
22
|
+
name: 'TaskPriority',
|
|
23
|
+
values: ['LOW', 'NORMAL', 'HIGH', 'URGENT'] as const,
|
|
24
|
+
schema: 'crm',
|
|
25
|
+
description: 'Priority of a task.',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Task status enum.
|
|
30
|
+
*/
|
|
31
|
+
export const TaskStatusEnum = defineEntityEnum({
|
|
32
|
+
name: 'TaskStatus',
|
|
33
|
+
values: ['PENDING', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'] as const,
|
|
34
|
+
schema: 'crm',
|
|
35
|
+
description: 'Status of a task.',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Task entity - follow-up activities.
|
|
40
|
+
*/
|
|
41
|
+
export const TaskEntity = defineEntity({
|
|
42
|
+
name: 'Task',
|
|
43
|
+
description: 'A task or follow-up activity.',
|
|
44
|
+
schema: 'crm',
|
|
45
|
+
map: 'task',
|
|
46
|
+
fields: {
|
|
47
|
+
id: field.id(),
|
|
48
|
+
title: field.string({ description: 'Task title' }),
|
|
49
|
+
description: field.string({ isOptional: true }),
|
|
50
|
+
|
|
51
|
+
// Type and priority
|
|
52
|
+
type: field.enum('TaskType', { default: 'TODO' }),
|
|
53
|
+
priority: field.enum('TaskPriority', { default: 'NORMAL' }),
|
|
54
|
+
status: field.enum('TaskStatus', { default: 'PENDING' }),
|
|
55
|
+
|
|
56
|
+
// Schedule
|
|
57
|
+
dueDate: field.dateTime({ isOptional: true }),
|
|
58
|
+
reminderAt: field.dateTime({ isOptional: true }),
|
|
59
|
+
|
|
60
|
+
// Associations (polymorphic)
|
|
61
|
+
contactId: field.string({ isOptional: true }),
|
|
62
|
+
dealId: field.string({ isOptional: true }),
|
|
63
|
+
companyId: field.string({ isOptional: true }),
|
|
64
|
+
|
|
65
|
+
// Ownership
|
|
66
|
+
organizationId: field.foreignKey(),
|
|
67
|
+
assignedTo: field.foreignKey({ description: 'User assigned to this task' }),
|
|
68
|
+
createdBy: field.foreignKey(),
|
|
69
|
+
|
|
70
|
+
// Completion
|
|
71
|
+
completedAt: field.dateTime({ isOptional: true }),
|
|
72
|
+
completedBy: field.string({ isOptional: true }),
|
|
73
|
+
|
|
74
|
+
// Timestamps
|
|
75
|
+
createdAt: field.createdAt(),
|
|
76
|
+
updatedAt: field.updatedAt(),
|
|
77
|
+
|
|
78
|
+
// Relations
|
|
79
|
+
contact: field.belongsTo('Contact', ['contactId'], ['id']),
|
|
80
|
+
deal: field.belongsTo('Deal', ['dealId'], ['id']),
|
|
81
|
+
company: field.belongsTo('Company', ['companyId'], ['id']),
|
|
82
|
+
},
|
|
83
|
+
indexes: [
|
|
84
|
+
index.on(['organizationId', 'assignedTo', 'status']),
|
|
85
|
+
index.on(['dueDate', 'status']),
|
|
86
|
+
index.on(['contactId']),
|
|
87
|
+
index.on(['dealId']),
|
|
88
|
+
],
|
|
89
|
+
enums: [TaskTypeEnum, TaskPriorityEnum, TaskStatusEnum],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Activity entity - interaction history.
|
|
94
|
+
*/
|
|
95
|
+
export const ActivityEntity = defineEntity({
|
|
96
|
+
name: 'Activity',
|
|
97
|
+
description: 'An activity/interaction logged in the CRM.',
|
|
98
|
+
schema: 'crm',
|
|
99
|
+
map: 'activity',
|
|
100
|
+
fields: {
|
|
101
|
+
id: field.id(),
|
|
102
|
+
type: field.enum('TaskType'),
|
|
103
|
+
subject: field.string(),
|
|
104
|
+
description: field.string({ isOptional: true }),
|
|
105
|
+
|
|
106
|
+
// Associations
|
|
107
|
+
contactId: field.string({ isOptional: true }),
|
|
108
|
+
dealId: field.string({ isOptional: true }),
|
|
109
|
+
companyId: field.string({ isOptional: true }),
|
|
110
|
+
|
|
111
|
+
// Ownership
|
|
112
|
+
organizationId: field.foreignKey(),
|
|
113
|
+
performedBy: field.foreignKey(),
|
|
114
|
+
|
|
115
|
+
// Outcome
|
|
116
|
+
outcome: field.string({ isOptional: true }),
|
|
117
|
+
|
|
118
|
+
// Timing
|
|
119
|
+
occurredAt: field.dateTime(),
|
|
120
|
+
duration: field.int({
|
|
121
|
+
isOptional: true,
|
|
122
|
+
description: 'Duration in minutes',
|
|
123
|
+
}),
|
|
124
|
+
|
|
125
|
+
// Timestamps
|
|
126
|
+
createdAt: field.createdAt(),
|
|
127
|
+
|
|
128
|
+
// Relations
|
|
129
|
+
contact: field.belongsTo('Contact', ['contactId'], ['id']),
|
|
130
|
+
deal: field.belongsTo('Deal', ['dealId'], ['id']),
|
|
131
|
+
company: field.belongsTo('Company', ['companyId'], ['id']),
|
|
132
|
+
},
|
|
133
|
+
indexes: [
|
|
134
|
+
index.on(['contactId', 'occurredAt']),
|
|
135
|
+
index.on(['dealId', 'occurredAt']),
|
|
136
|
+
],
|
|
137
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ScalarTypeEnum, defineSchemaModel } from '@contractspec/lib.schema';
|
|
2
|
+
import { defineEvent } from '@contractspec/lib.contracts';
|
|
3
|
+
|
|
4
|
+
// ============ Contact Event Payloads ============
|
|
5
|
+
|
|
6
|
+
const ContactCreatedPayload = defineSchemaModel({
|
|
7
|
+
name: 'ContactCreatedPayload',
|
|
8
|
+
description: 'Payload when a contact is created',
|
|
9
|
+
fields: {
|
|
10
|
+
contactId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
11
|
+
email: { type: ScalarTypeEnum.EmailAddress(), isOptional: true },
|
|
12
|
+
organizationId: {
|
|
13
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
14
|
+
isOptional: false,
|
|
15
|
+
},
|
|
16
|
+
ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
17
|
+
createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const ContactCreatedEvent = defineEvent({
|
|
22
|
+
meta: {
|
|
23
|
+
key: 'contact.created',
|
|
24
|
+
version: 1,
|
|
25
|
+
description: 'A new contact has been created.',
|
|
26
|
+
stability: 'stable',
|
|
27
|
+
owners: ['@crm-team'],
|
|
28
|
+
tags: ['contact', 'created'],
|
|
29
|
+
},
|
|
30
|
+
payload: ContactCreatedPayload,
|
|
31
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ScalarTypeEnum, defineSchemaModel } from '@contractspec/lib.schema';
|
|
2
|
+
import { defineEvent } from '@contractspec/lib.contracts';
|
|
3
|
+
|
|
4
|
+
// ============ Deal Event Payloads ============
|
|
5
|
+
|
|
6
|
+
const DealCreatedPayload = defineSchemaModel({
|
|
7
|
+
name: 'DealCreatedPayload',
|
|
8
|
+
description: 'Payload when a deal is created',
|
|
9
|
+
fields: {
|
|
10
|
+
dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
11
|
+
name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
12
|
+
value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
13
|
+
pipelineId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
14
|
+
stageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
15
|
+
ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
16
|
+
createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const DealMovedPayload = defineSchemaModel({
|
|
21
|
+
name: 'DealMovedEventPayload',
|
|
22
|
+
description: 'Payload when a deal is moved to another stage',
|
|
23
|
+
fields: {
|
|
24
|
+
dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
25
|
+
fromStageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
26
|
+
toStageId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
27
|
+
movedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
28
|
+
movedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const DealWonPayload = defineSchemaModel({
|
|
33
|
+
name: 'DealWonEventPayload',
|
|
34
|
+
description: 'Payload when a deal is won',
|
|
35
|
+
fields: {
|
|
36
|
+
dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
37
|
+
value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
38
|
+
currency: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
39
|
+
contactId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
40
|
+
companyId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
41
|
+
ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
42
|
+
wonAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const DealLostPayload = defineSchemaModel({
|
|
47
|
+
name: 'DealLostEventPayload',
|
|
48
|
+
description: 'Payload when a deal is lost',
|
|
49
|
+
fields: {
|
|
50
|
+
dealId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
51
|
+
value: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
52
|
+
reason: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
53
|
+
ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
54
|
+
lostAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const DealCreatedEvent = defineEvent({
|
|
59
|
+
meta: {
|
|
60
|
+
key: 'deal.created',
|
|
61
|
+
version: 1,
|
|
62
|
+
description: 'A new deal has been created.',
|
|
63
|
+
stability: 'stable',
|
|
64
|
+
owners: ['@crm-team'],
|
|
65
|
+
tags: ['deal', 'created'],
|
|
66
|
+
},
|
|
67
|
+
payload: DealCreatedPayload,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const DealMovedEvent = defineEvent({
|
|
71
|
+
meta: {
|
|
72
|
+
key: 'deal.moved',
|
|
73
|
+
version: 1,
|
|
74
|
+
description: 'A deal has been moved to a different stage.',
|
|
75
|
+
stability: 'stable',
|
|
76
|
+
owners: ['@crm-team'],
|
|
77
|
+
tags: ['deal', 'moved'],
|
|
78
|
+
},
|
|
79
|
+
payload: DealMovedPayload,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export const DealWonEvent = defineEvent({
|
|
83
|
+
meta: {
|
|
84
|
+
key: 'deal.won',
|
|
85
|
+
version: 1,
|
|
86
|
+
description: 'A deal has been won.',
|
|
87
|
+
stability: 'stable',
|
|
88
|
+
owners: ['@crm-team'],
|
|
89
|
+
tags: ['deal', 'won'],
|
|
90
|
+
},
|
|
91
|
+
payload: DealWonPayload,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const DealLostEvent = defineEvent({
|
|
95
|
+
meta: {
|
|
96
|
+
key: 'deal.lost',
|
|
97
|
+
version: 1,
|
|
98
|
+
description: 'A deal has been lost.',
|
|
99
|
+
stability: 'stable',
|
|
100
|
+
owners: ['@crm-team'],
|
|
101
|
+
tags: ['deal', 'lost'],
|
|
102
|
+
},
|
|
103
|
+
payload: DealLostPayload,
|
|
104
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ScalarTypeEnum, defineSchemaModel } from '@contractspec/lib.schema';
|
|
2
|
+
import { defineEvent } from '@contractspec/lib.contracts';
|
|
3
|
+
|
|
4
|
+
// ============ Task Event Payloads ============
|
|
5
|
+
|
|
6
|
+
const TaskCompletedPayload = defineSchemaModel({
|
|
7
|
+
name: 'TaskCompletedPayload',
|
|
8
|
+
description: 'Payload when a task is completed',
|
|
9
|
+
fields: {
|
|
10
|
+
taskId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
11
|
+
type: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
12
|
+
assignedTo: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
13
|
+
completedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
14
|
+
completedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const TaskCompletedEvent = defineEvent({
|
|
19
|
+
meta: {
|
|
20
|
+
key: 'task.completed',
|
|
21
|
+
version: 1,
|
|
22
|
+
description: 'A task has been completed.',
|
|
23
|
+
stability: 'stable',
|
|
24
|
+
owners: ['@crm-team'],
|
|
25
|
+
tags: ['task', 'lifecycle'],
|
|
26
|
+
},
|
|
27
|
+
payload: TaskCompletedPayload,
|
|
28
|
+
});
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const example = {
|
|
2
|
+
id: 'crm-pipeline',
|
|
3
|
+
title: 'CRM Pipeline',
|
|
4
|
+
summary: 'Sales CRM with contacts, companies, deals, pipelines, and tasks.',
|
|
5
|
+
tags: ['crm', 'sales', 'pipeline', 'deals'],
|
|
6
|
+
kind: 'template',
|
|
7
|
+
visibility: 'public',
|
|
8
|
+
docs: {
|
|
9
|
+
rootDocId: 'docs.examples.crm-pipeline',
|
|
10
|
+
},
|
|
11
|
+
entrypoints: {
|
|
12
|
+
packageName: '@contractspec/example.crm-pipeline',
|
|
13
|
+
feature: './feature',
|
|
14
|
+
contracts: './contracts',
|
|
15
|
+
presentations: './presentations',
|
|
16
|
+
handlers: './handlers',
|
|
17
|
+
docs: './docs',
|
|
18
|
+
},
|
|
19
|
+
surfaces: {
|
|
20
|
+
templates: true,
|
|
21
|
+
sandbox: {
|
|
22
|
+
enabled: true,
|
|
23
|
+
modes: ['playground', 'specs', 'builder', 'markdown', 'evolution'],
|
|
24
|
+
},
|
|
25
|
+
studio: { enabled: true, installable: true },
|
|
26
|
+
mcp: { enabled: true },
|
|
27
|
+
},
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
export default example;
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock handlers for Deal contracts
|
|
3
|
+
*/
|
|
4
|
+
import { MOCK_DEALS, MOCK_STAGES } from './mock-data';
|
|
5
|
+
|
|
6
|
+
// Types inferred from contract schemas
|
|
7
|
+
export interface Deal {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
value: number;
|
|
11
|
+
currency: string;
|
|
12
|
+
pipelineId: string;
|
|
13
|
+
stageId: string;
|
|
14
|
+
status: 'OPEN' | 'WON' | 'LOST' | 'STALE';
|
|
15
|
+
contactId?: string;
|
|
16
|
+
companyId?: string;
|
|
17
|
+
ownerId: string;
|
|
18
|
+
expectedCloseDate?: Date;
|
|
19
|
+
createdAt: Date;
|
|
20
|
+
updatedAt: Date;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CreateDealInput {
|
|
24
|
+
name: string;
|
|
25
|
+
value: number;
|
|
26
|
+
currency?: string;
|
|
27
|
+
pipelineId: string;
|
|
28
|
+
stageId: string;
|
|
29
|
+
contactId?: string;
|
|
30
|
+
companyId?: string;
|
|
31
|
+
expectedCloseDate?: Date;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MoveDealInput {
|
|
35
|
+
dealId: string;
|
|
36
|
+
stageId: string;
|
|
37
|
+
position?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WinDealInput {
|
|
41
|
+
dealId: string;
|
|
42
|
+
wonSource?: string;
|
|
43
|
+
notes?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface LoseDealInput {
|
|
47
|
+
dealId: string;
|
|
48
|
+
lostReason: string;
|
|
49
|
+
notes?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ListDealsInput {
|
|
53
|
+
pipelineId?: string;
|
|
54
|
+
stageId?: string;
|
|
55
|
+
status?: 'OPEN' | 'WON' | 'LOST' | 'all';
|
|
56
|
+
ownerId?: string;
|
|
57
|
+
search?: string;
|
|
58
|
+
limit?: number;
|
|
59
|
+
offset?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ListDealsOutput {
|
|
63
|
+
deals: Deal[];
|
|
64
|
+
total: number;
|
|
65
|
+
totalValue: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Mock handler for ListDealsContract
|
|
70
|
+
*/
|
|
71
|
+
export async function mockListDealsHandler(
|
|
72
|
+
input: ListDealsInput
|
|
73
|
+
): Promise<ListDealsOutput> {
|
|
74
|
+
const {
|
|
75
|
+
pipelineId,
|
|
76
|
+
stageId,
|
|
77
|
+
status,
|
|
78
|
+
ownerId,
|
|
79
|
+
search,
|
|
80
|
+
limit = 20,
|
|
81
|
+
offset = 0,
|
|
82
|
+
} = input;
|
|
83
|
+
|
|
84
|
+
let filtered = [...MOCK_DEALS];
|
|
85
|
+
|
|
86
|
+
if (pipelineId) {
|
|
87
|
+
filtered = filtered.filter((d) => d.pipelineId === pipelineId);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (stageId) {
|
|
91
|
+
filtered = filtered.filter((d) => d.stageId === stageId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (status && status !== 'all') {
|
|
95
|
+
filtered = filtered.filter((d) => d.status === status);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (ownerId) {
|
|
99
|
+
filtered = filtered.filter((d) => d.ownerId === ownerId);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (search) {
|
|
103
|
+
const q = search.toLowerCase();
|
|
104
|
+
filtered = filtered.filter((d) => d.name.toLowerCase().includes(q));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Sort by value descending
|
|
108
|
+
filtered.sort((a, b) => b.value - a.value);
|
|
109
|
+
|
|
110
|
+
const total = filtered.length;
|
|
111
|
+
const totalValue = filtered.reduce((sum, d) => sum + d.value, 0);
|
|
112
|
+
const deals = filtered.slice(offset, offset + limit);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
deals,
|
|
116
|
+
total,
|
|
117
|
+
totalValue,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Mock handler for CreateDealContract
|
|
123
|
+
*/
|
|
124
|
+
export async function mockCreateDealHandler(
|
|
125
|
+
input: CreateDealInput,
|
|
126
|
+
context: { ownerId: string }
|
|
127
|
+
): Promise<Deal> {
|
|
128
|
+
const now = new Date();
|
|
129
|
+
|
|
130
|
+
const deal: Deal = {
|
|
131
|
+
id: `deal-${Date.now()}`,
|
|
132
|
+
name: input.name,
|
|
133
|
+
value: input.value,
|
|
134
|
+
currency: input.currency ?? 'USD',
|
|
135
|
+
pipelineId: input.pipelineId,
|
|
136
|
+
stageId: input.stageId,
|
|
137
|
+
status: 'OPEN',
|
|
138
|
+
contactId: input.contactId,
|
|
139
|
+
companyId: input.companyId,
|
|
140
|
+
ownerId: context.ownerId,
|
|
141
|
+
expectedCloseDate: input.expectedCloseDate,
|
|
142
|
+
createdAt: now,
|
|
143
|
+
updatedAt: now,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
MOCK_DEALS.push(deal);
|
|
147
|
+
|
|
148
|
+
return deal;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Mock handler for MoveDealContract
|
|
153
|
+
*/
|
|
154
|
+
export async function mockMoveDealHandler(input: MoveDealInput): Promise<Deal> {
|
|
155
|
+
const dealIndex = MOCK_DEALS.findIndex((d) => d.id === input.dealId);
|
|
156
|
+
if (dealIndex === -1) {
|
|
157
|
+
throw new Error('NOT_FOUND');
|
|
158
|
+
}
|
|
159
|
+
const deal = MOCK_DEALS[dealIndex];
|
|
160
|
+
if (!deal) {
|
|
161
|
+
throw new Error('NOT_FOUND');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const stage = MOCK_STAGES.find((s) => s.id === input.stageId);
|
|
165
|
+
if (!stage) {
|
|
166
|
+
throw new Error('INVALID_STAGE');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const updatedDeal: Deal = {
|
|
170
|
+
...deal,
|
|
171
|
+
stageId: input.stageId,
|
|
172
|
+
updatedAt: new Date(),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
MOCK_DEALS[dealIndex] = updatedDeal;
|
|
176
|
+
|
|
177
|
+
return updatedDeal;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Mock handler for WinDealContract
|
|
182
|
+
*/
|
|
183
|
+
export async function mockWinDealHandler(input: WinDealInput): Promise<Deal> {
|
|
184
|
+
const dealIndex = MOCK_DEALS.findIndex((d) => d.id === input.dealId);
|
|
185
|
+
if (dealIndex === -1) {
|
|
186
|
+
throw new Error('NOT_FOUND');
|
|
187
|
+
}
|
|
188
|
+
const deal = MOCK_DEALS[dealIndex];
|
|
189
|
+
if (!deal) {
|
|
190
|
+
throw new Error('NOT_FOUND');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const updatedDeal: Deal = {
|
|
194
|
+
...deal,
|
|
195
|
+
status: 'WON' as const,
|
|
196
|
+
updatedAt: new Date(),
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
MOCK_DEALS[dealIndex] = updatedDeal;
|
|
200
|
+
|
|
201
|
+
return updatedDeal;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Mock handler for LoseDealContract
|
|
206
|
+
*/
|
|
207
|
+
export async function mockLoseDealHandler(input: LoseDealInput): Promise<Deal> {
|
|
208
|
+
const dealIndex = MOCK_DEALS.findIndex((d) => d.id === input.dealId);
|
|
209
|
+
if (dealIndex === -1) {
|
|
210
|
+
throw new Error('NOT_FOUND');
|
|
211
|
+
}
|
|
212
|
+
const deal = MOCK_DEALS[dealIndex];
|
|
213
|
+
if (!deal) {
|
|
214
|
+
throw new Error('NOT_FOUND');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const updatedDeal: Deal = {
|
|
218
|
+
...deal,
|
|
219
|
+
status: 'LOST' as const,
|
|
220
|
+
updatedAt: new Date(),
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
MOCK_DEALS[dealIndex] = updatedDeal;
|
|
224
|
+
|
|
225
|
+
return updatedDeal;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get deals grouped by stage for Kanban view
|
|
230
|
+
*/
|
|
231
|
+
export async function mockGetDealsByStageHandler(input: {
|
|
232
|
+
pipelineId: string;
|
|
233
|
+
}): Promise<Record<string, Deal[]>> {
|
|
234
|
+
const deals = MOCK_DEALS.filter(
|
|
235
|
+
(d) => d.pipelineId === input.pipelineId && d.status === 'OPEN'
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const grouped: Record<string, Deal[]> = {};
|
|
239
|
+
for (const stage of MOCK_STAGES) {
|
|
240
|
+
grouped[stage.id] = deals.filter((d) => d.stageId === stage.id);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return grouped;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get pipeline stages
|
|
248
|
+
*/
|
|
249
|
+
export async function mockGetPipelineStagesHandler(input: {
|
|
250
|
+
pipelineId: string;
|
|
251
|
+
}) {
|
|
252
|
+
return MOCK_STAGES.filter((s) => s.pipelineId === input.pipelineId);
|
|
253
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock handlers for crm-pipeline example
|
|
3
|
+
*
|
|
4
|
+
* These handlers provide mock implementations of all contracts
|
|
5
|
+
* for use in demos, tests, and the sandbox environment.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Mock data
|
|
9
|
+
export * from './mock-data';
|
|
10
|
+
|
|
11
|
+
// Deal handlers
|
|
12
|
+
export {
|
|
13
|
+
mockListDealsHandler,
|
|
14
|
+
mockCreateDealHandler,
|
|
15
|
+
mockMoveDealHandler,
|
|
16
|
+
mockWinDealHandler,
|
|
17
|
+
mockLoseDealHandler,
|
|
18
|
+
mockGetDealsByStageHandler,
|
|
19
|
+
mockGetPipelineStagesHandler,
|
|
20
|
+
type Deal,
|
|
21
|
+
type CreateDealInput,
|
|
22
|
+
type MoveDealInput,
|
|
23
|
+
type WinDealInput,
|
|
24
|
+
type LoseDealInput,
|
|
25
|
+
type ListDealsInput,
|
|
26
|
+
type ListDealsOutput,
|
|
27
|
+
} from './deal.handlers';
|