@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.
Files changed (122) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +97 -0
  2. package/.turbo/turbo-build.log +98 -0
  3. package/CHANGELOG.md +246 -0
  4. package/LICENSE +21 -0
  5. package/README.md +139 -0
  6. package/dist/crm-pipeline.feature.d.ts +12 -0
  7. package/dist/crm-pipeline.feature.d.ts.map +1 -0
  8. package/dist/crm-pipeline.feature.js +159 -0
  9. package/dist/crm-pipeline.feature.js.map +1 -0
  10. package/dist/deal/deal.enum.d.ts +14 -0
  11. package/dist/deal/deal.enum.d.ts.map +1 -0
  12. package/dist/deal/deal.enum.js +25 -0
  13. package/dist/deal/deal.enum.js.map +1 -0
  14. package/dist/deal/deal.operation.d.ts +513 -0
  15. package/dist/deal/deal.operation.d.ts.map +1 -0
  16. package/dist/deal/deal.operation.js +270 -0
  17. package/dist/deal/deal.operation.js.map +1 -0
  18. package/dist/deal/deal.schema.d.ts +300 -0
  19. package/dist/deal/deal.schema.d.ts.map +1 -0
  20. package/dist/deal/deal.schema.js +286 -0
  21. package/dist/deal/deal.schema.js.map +1 -0
  22. package/dist/deal/index.d.ts +4 -0
  23. package/dist/deal/index.js +5 -0
  24. package/dist/docs/crm-pipeline.docblock.d.ts +1 -0
  25. package/dist/docs/crm-pipeline.docblock.js +100 -0
  26. package/dist/docs/crm-pipeline.docblock.js.map +1 -0
  27. package/dist/docs/index.d.ts +1 -0
  28. package/dist/docs/index.js +1 -0
  29. package/dist/entities/company.entity.d.ts +40 -0
  30. package/dist/entities/company.entity.d.ts.map +1 -0
  31. package/dist/entities/company.entity.js +63 -0
  32. package/dist/entities/company.entity.js.map +1 -0
  33. package/dist/entities/contact.entity.d.ts +44 -0
  34. package/dist/entities/contact.entity.d.ts.map +1 -0
  35. package/dist/entities/contact.entity.js +78 -0
  36. package/dist/entities/contact.entity.js.map +1 -0
  37. package/dist/entities/deal.entity.d.ts +73 -0
  38. package/dist/entities/deal.entity.d.ts.map +1 -0
  39. package/dist/entities/deal.entity.js +120 -0
  40. package/dist/entities/deal.entity.js.map +1 -0
  41. package/dist/entities/index.d.ts +15 -0
  42. package/dist/entities/index.d.ts.map +1 -0
  43. package/dist/entities/index.js +33 -0
  44. package/dist/entities/index.js.map +1 -0
  45. package/dist/entities/task.entity.d.ts +65 -0
  46. package/dist/entities/task.entity.d.ts.map +1 -0
  47. package/dist/entities/task.entity.js +129 -0
  48. package/dist/entities/task.entity.js.map +1 -0
  49. package/dist/events/contact.event.d.ts +29 -0
  50. package/dist/events/contact.event.d.ts.map +1 -0
  51. package/dist/events/contact.event.js +45 -0
  52. package/dist/events/contact.event.js.map +1 -0
  53. package/dist/events/deal.event.d.ts +111 -0
  54. package/dist/events/deal.event.d.ts.map +1 -0
  55. package/dist/events/deal.event.js +172 -0
  56. package/dist/events/deal.event.js.map +1 -0
  57. package/dist/events/index.d.ts +4 -0
  58. package/dist/events/index.js +5 -0
  59. package/dist/events/task.event.d.ts +29 -0
  60. package/dist/events/task.event.d.ts.map +1 -0
  61. package/dist/events/task.event.js +45 -0
  62. package/dist/events/task.event.js.map +1 -0
  63. package/dist/example.d.ts +37 -0
  64. package/dist/example.d.ts.map +1 -0
  65. package/dist/example.js +46 -0
  66. package/dist/example.js.map +1 -0
  67. package/dist/handlers/deal.handlers.d.ts +94 -0
  68. package/dist/handlers/deal.handlers.d.ts.map +1 -0
  69. package/dist/handlers/deal.handlers.js +120 -0
  70. package/dist/handlers/deal.handlers.js.map +1 -0
  71. package/dist/handlers/index.d.ts +3 -0
  72. package/dist/handlers/index.js +4 -0
  73. package/dist/handlers/mock-data.d.ts +49 -0
  74. package/dist/handlers/mock-data.d.ts.map +1 -0
  75. package/dist/handlers/mock-data.js +188 -0
  76. package/dist/handlers/mock-data.js.map +1 -0
  77. package/dist/index.d.ts +34 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +43 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/operations/index.d.ts +5 -0
  82. package/dist/operations/index.js +6 -0
  83. package/dist/presentations/dashboard.presentation.d.ts +15 -0
  84. package/dist/presentations/dashboard.presentation.d.ts.map +1 -0
  85. package/dist/presentations/dashboard.presentation.js +59 -0
  86. package/dist/presentations/dashboard.presentation.js.map +1 -0
  87. package/dist/presentations/index.d.ts +3 -0
  88. package/dist/presentations/index.js +4 -0
  89. package/dist/presentations/pipeline.presentation.d.ts +23 -0
  90. package/dist/presentations/pipeline.presentation.d.ts.map +1 -0
  91. package/dist/presentations/pipeline.presentation.js +119 -0
  92. package/dist/presentations/pipeline.presentation.js.map +1 -0
  93. package/example.ts +1 -0
  94. package/package.json +105 -0
  95. package/src/crm-pipeline.feature.ts +96 -0
  96. package/src/deal/deal.enum.ts +21 -0
  97. package/src/deal/deal.operation.ts +291 -0
  98. package/src/deal/deal.schema.ts +154 -0
  99. package/src/deal/index.ts +26 -0
  100. package/src/docs/crm-pipeline.docblock.ts +98 -0
  101. package/src/docs/index.ts +1 -0
  102. package/src/entities/company.entity.ts +77 -0
  103. package/src/entities/contact.entity.ts +93 -0
  104. package/src/entities/deal.entity.ts +160 -0
  105. package/src/entities/index.ts +45 -0
  106. package/src/entities/task.entity.ts +137 -0
  107. package/src/events/contact.event.ts +31 -0
  108. package/src/events/deal.event.ts +104 -0
  109. package/src/events/index.ts +3 -0
  110. package/src/events/task.event.ts +28 -0
  111. package/src/example.ts +30 -0
  112. package/src/handlers/deal.handlers.ts +253 -0
  113. package/src/handlers/index.ts +27 -0
  114. package/src/handlers/mock-data.ts +198 -0
  115. package/src/index.ts +31 -0
  116. package/src/operations/index.ts +20 -0
  117. package/src/presentations/dashboard.presentation.ts +60 -0
  118. package/src/presentations/index.ts +2 -0
  119. package/src/presentations/pipeline.presentation.ts +118 -0
  120. package/tsconfig.json +10 -0
  121. package/tsconfig.tsbuildinfo +1 -0
  122. 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,3 @@
1
+ export * from './contact.event';
2
+ export * from './deal.event';
3
+ export * from './task.event';
@@ -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';