@voyantjs/crm 0.1.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 (60) hide show
  1. package/LICENSE +109 -0
  2. package/README.md +47 -0
  3. package/dist/booking-extension.d.ts +123 -0
  4. package/dist/booking-extension.d.ts.map +1 -0
  5. package/dist/booking-extension.js +86 -0
  6. package/dist/index.d.ts +14 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +29 -0
  9. package/dist/routes/accounts.d.ts +1203 -0
  10. package/dist/routes/accounts.d.ts.map +1 -0
  11. package/dist/routes/accounts.js +226 -0
  12. package/dist/routes/activities.d.ts +299 -0
  13. package/dist/routes/activities.d.ts.map +1 -0
  14. package/dist/routes/activities.js +61 -0
  15. package/dist/routes/custom-fields.d.ts +256 -0
  16. package/dist/routes/custom-fields.d.ts.map +1 -0
  17. package/dist/routes/custom-fields.js +46 -0
  18. package/dist/routes/index.d.ts +2671 -0
  19. package/dist/routes/index.d.ts.map +1 -0
  20. package/dist/routes/index.js +14 -0
  21. package/dist/routes/opportunities.d.ts +387 -0
  22. package/dist/routes/opportunities.d.ts.map +1 -0
  23. package/dist/routes/opportunities.js +69 -0
  24. package/dist/routes/pipelines.d.ts +292 -0
  25. package/dist/routes/pipelines.d.ts.map +1 -0
  26. package/dist/routes/pipelines.js +58 -0
  27. package/dist/routes/quotes.d.ts +283 -0
  28. package/dist/routes/quotes.d.ts.map +1 -0
  29. package/dist/routes/quotes.js +51 -0
  30. package/dist/schema.d.ts +3478 -0
  31. package/dist/schema.d.ts.map +1 -0
  32. package/dist/schema.js +515 -0
  33. package/dist/service/accounts.d.ts +982 -0
  34. package/dist/service/accounts.d.ts.map +1 -0
  35. package/dist/service/accounts.js +509 -0
  36. package/dist/service/activities.d.ts +486 -0
  37. package/dist/service/activities.d.ts.map +1 -0
  38. package/dist/service/activities.js +114 -0
  39. package/dist/service/custom-fields.d.ts +118 -0
  40. package/dist/service/custom-fields.d.ts.map +1 -0
  41. package/dist/service/custom-fields.js +88 -0
  42. package/dist/service/helpers.d.ts +22 -0
  43. package/dist/service/helpers.d.ts.map +1 -0
  44. package/dist/service/helpers.js +39 -0
  45. package/dist/service/index.d.ts +3329 -0
  46. package/dist/service/index.d.ts.map +1 -0
  47. package/dist/service/index.js +14 -0
  48. package/dist/service/opportunities.d.ts +822 -0
  49. package/dist/service/opportunities.d.ts.map +1 -0
  50. package/dist/service/opportunities.js +117 -0
  51. package/dist/service/pipelines.d.ts +113 -0
  52. package/dist/service/pipelines.d.ts.map +1 -0
  53. package/dist/service/pipelines.js +68 -0
  54. package/dist/service/quotes.d.ts +494 -0
  55. package/dist/service/quotes.d.ts.map +1 -0
  56. package/dist/service/quotes.js +69 -0
  57. package/dist/validation.d.ts +860 -0
  58. package/dist/validation.d.ts.map +1 -0
  59. package/dist/validation.js +315 -0
  60. package/package.json +56 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAUjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAMU,CAAA;AAEhC,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { Hono } from "hono";
2
+ import { accountRoutes } from "./accounts.js";
3
+ import { activityRoutes } from "./activities.js";
4
+ import { customFieldRoutes } from "./custom-fields.js";
5
+ import { opportunityRoutes } from "./opportunities.js";
6
+ import { pipelineRoutes } from "./pipelines.js";
7
+ import { quoteRoutes } from "./quotes.js";
8
+ export const crmRoutes = new Hono()
9
+ .route("/", accountRoutes)
10
+ .route("/", pipelineRoutes)
11
+ .route("/", opportunityRoutes)
12
+ .route("/", quoteRoutes)
13
+ .route("/", activityRoutes)
14
+ .route("/", customFieldRoutes);
@@ -0,0 +1,387 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ type Env = {
3
+ Variables: {
4
+ db: PostgresJsDatabase;
5
+ userId?: string;
6
+ };
7
+ };
8
+ export declare const opportunityRoutes: import("hono/hono-base").HonoBase<Env, {
9
+ "/opportunities": {
10
+ $get: {
11
+ input: {};
12
+ output: {
13
+ data: {
14
+ id: string;
15
+ title: string;
16
+ personId: string | null;
17
+ organizationId: string | null;
18
+ pipelineId: string;
19
+ stageId: string;
20
+ ownerId: string | null;
21
+ status: "archived" | "open" | "won" | "lost";
22
+ valueAmountCents: number | null;
23
+ valueCurrency: string | null;
24
+ expectedCloseDate: string | null;
25
+ source: string | null;
26
+ sourceRef: string | null;
27
+ lostReason: string | null;
28
+ tags: string[];
29
+ createdAt: string;
30
+ updatedAt: string;
31
+ stageChangedAt: string;
32
+ closedAt: string | null;
33
+ }[];
34
+ total: number;
35
+ limit: number;
36
+ offset: number;
37
+ };
38
+ outputFormat: "json";
39
+ status: import("hono/utils/http-status").ContentfulStatusCode;
40
+ };
41
+ };
42
+ } & {
43
+ "/opportunities": {
44
+ $post: {
45
+ input: {};
46
+ output: {
47
+ data: {
48
+ createdAt: string;
49
+ updatedAt: string;
50
+ title: string;
51
+ id: string;
52
+ ownerId: string | null;
53
+ status: "archived" | "open" | "won" | "lost";
54
+ source: string | null;
55
+ sourceRef: string | null;
56
+ tags: string[];
57
+ organizationId: string | null;
58
+ pipelineId: string;
59
+ personId: string | null;
60
+ stageId: string;
61
+ valueAmountCents: number | null;
62
+ valueCurrency: string | null;
63
+ expectedCloseDate: string | null;
64
+ lostReason: string | null;
65
+ stageChangedAt: string;
66
+ closedAt: string | null;
67
+ } | undefined;
68
+ };
69
+ outputFormat: "json";
70
+ status: 201;
71
+ };
72
+ };
73
+ } & {
74
+ "/opportunities/:id": {
75
+ $get: {
76
+ input: {
77
+ param: {
78
+ id: string;
79
+ };
80
+ };
81
+ output: {
82
+ error: string;
83
+ };
84
+ outputFormat: "json";
85
+ status: 404;
86
+ } | {
87
+ input: {
88
+ param: {
89
+ id: string;
90
+ };
91
+ };
92
+ output: {
93
+ data: {
94
+ id: string;
95
+ title: string;
96
+ personId: string | null;
97
+ organizationId: string | null;
98
+ pipelineId: string;
99
+ stageId: string;
100
+ ownerId: string | null;
101
+ status: "archived" | "open" | "won" | "lost";
102
+ valueAmountCents: number | null;
103
+ valueCurrency: string | null;
104
+ expectedCloseDate: string | null;
105
+ source: string | null;
106
+ sourceRef: string | null;
107
+ lostReason: string | null;
108
+ tags: string[];
109
+ createdAt: string;
110
+ updatedAt: string;
111
+ stageChangedAt: string;
112
+ closedAt: string | null;
113
+ };
114
+ };
115
+ outputFormat: "json";
116
+ status: import("hono/utils/http-status").ContentfulStatusCode;
117
+ };
118
+ };
119
+ } & {
120
+ "/opportunities/:id": {
121
+ $patch: {
122
+ input: {
123
+ param: {
124
+ id: string;
125
+ };
126
+ };
127
+ output: {
128
+ error: string;
129
+ };
130
+ outputFormat: "json";
131
+ status: 404;
132
+ } | {
133
+ input: {
134
+ param: {
135
+ id: string;
136
+ };
137
+ };
138
+ output: {
139
+ data: {
140
+ id: string;
141
+ title: string;
142
+ personId: string | null;
143
+ organizationId: string | null;
144
+ pipelineId: string;
145
+ stageId: string;
146
+ ownerId: string | null;
147
+ status: "archived" | "open" | "won" | "lost";
148
+ valueAmountCents: number | null;
149
+ valueCurrency: string | null;
150
+ expectedCloseDate: string | null;
151
+ source: string | null;
152
+ sourceRef: string | null;
153
+ lostReason: string | null;
154
+ tags: string[];
155
+ createdAt: string;
156
+ updatedAt: string;
157
+ stageChangedAt: string;
158
+ closedAt: string | null;
159
+ };
160
+ };
161
+ outputFormat: "json";
162
+ status: import("hono/utils/http-status").ContentfulStatusCode;
163
+ };
164
+ };
165
+ } & {
166
+ "/opportunities/:id": {
167
+ $delete: {
168
+ input: {
169
+ param: {
170
+ id: string;
171
+ };
172
+ };
173
+ output: {
174
+ error: string;
175
+ };
176
+ outputFormat: "json";
177
+ status: 404;
178
+ } | {
179
+ input: {
180
+ param: {
181
+ id: string;
182
+ };
183
+ };
184
+ output: {
185
+ success: true;
186
+ };
187
+ outputFormat: "json";
188
+ status: import("hono/utils/http-status").ContentfulStatusCode;
189
+ };
190
+ };
191
+ } & {
192
+ "/opportunities/:id/participants": {
193
+ $get: {
194
+ input: {
195
+ param: {
196
+ id: string;
197
+ };
198
+ };
199
+ output: {
200
+ data: {
201
+ id: string;
202
+ opportunityId: string;
203
+ personId: string;
204
+ role: "other" | "traveler" | "booker" | "decision_maker" | "finance";
205
+ isPrimary: boolean;
206
+ createdAt: string;
207
+ }[];
208
+ };
209
+ outputFormat: "json";
210
+ status: import("hono/utils/http-status").ContentfulStatusCode;
211
+ };
212
+ };
213
+ } & {
214
+ "/opportunities/:id/participants": {
215
+ $post: {
216
+ input: {
217
+ param: {
218
+ id: string;
219
+ };
220
+ };
221
+ output: {
222
+ data: {
223
+ opportunityId: string;
224
+ createdAt: string;
225
+ isPrimary: boolean;
226
+ role: "other" | "traveler" | "booker" | "decision_maker" | "finance";
227
+ id: string;
228
+ personId: string;
229
+ } | undefined;
230
+ };
231
+ outputFormat: "json";
232
+ status: 201;
233
+ };
234
+ };
235
+ } & {
236
+ "/opportunity-participants/:id": {
237
+ $delete: {
238
+ input: {
239
+ param: {
240
+ id: string;
241
+ };
242
+ };
243
+ output: {
244
+ error: string;
245
+ };
246
+ outputFormat: "json";
247
+ status: 404;
248
+ } | {
249
+ input: {
250
+ param: {
251
+ id: string;
252
+ };
253
+ };
254
+ output: {
255
+ success: true;
256
+ };
257
+ outputFormat: "json";
258
+ status: import("hono/utils/http-status").ContentfulStatusCode;
259
+ };
260
+ };
261
+ } & {
262
+ "/opportunities/:id/products": {
263
+ $get: {
264
+ input: {
265
+ param: {
266
+ id: string;
267
+ };
268
+ };
269
+ output: {
270
+ data: {
271
+ id: string;
272
+ opportunityId: string;
273
+ productId: string | null;
274
+ supplierServiceId: string | null;
275
+ nameSnapshot: string;
276
+ description: string | null;
277
+ quantity: number;
278
+ unitPriceAmountCents: number | null;
279
+ costAmountCents: number | null;
280
+ currency: string | null;
281
+ discountAmountCents: number | null;
282
+ createdAt: string;
283
+ updatedAt: string;
284
+ }[];
285
+ };
286
+ outputFormat: "json";
287
+ status: import("hono/utils/http-status").ContentfulStatusCode;
288
+ };
289
+ };
290
+ } & {
291
+ "/opportunities/:id/products": {
292
+ $post: {
293
+ input: {
294
+ param: {
295
+ id: string;
296
+ };
297
+ };
298
+ output: {
299
+ data: {
300
+ opportunityId: string;
301
+ createdAt: string;
302
+ updatedAt: string;
303
+ description: string | null;
304
+ id: string;
305
+ productId: string | null;
306
+ supplierServiceId: string | null;
307
+ nameSnapshot: string;
308
+ quantity: number;
309
+ unitPriceAmountCents: number | null;
310
+ costAmountCents: number | null;
311
+ currency: string | null;
312
+ discountAmountCents: number | null;
313
+ } | undefined;
314
+ };
315
+ outputFormat: "json";
316
+ status: 201;
317
+ };
318
+ };
319
+ } & {
320
+ "/opportunity-products/:id": {
321
+ $patch: {
322
+ input: {
323
+ param: {
324
+ id: string;
325
+ };
326
+ };
327
+ output: {
328
+ error: string;
329
+ };
330
+ outputFormat: "json";
331
+ status: 404;
332
+ } | {
333
+ input: {
334
+ param: {
335
+ id: string;
336
+ };
337
+ };
338
+ output: {
339
+ data: {
340
+ id: string;
341
+ opportunityId: string;
342
+ productId: string | null;
343
+ supplierServiceId: string | null;
344
+ nameSnapshot: string;
345
+ description: string | null;
346
+ quantity: number;
347
+ unitPriceAmountCents: number | null;
348
+ costAmountCents: number | null;
349
+ currency: string | null;
350
+ discountAmountCents: number | null;
351
+ createdAt: string;
352
+ updatedAt: string;
353
+ };
354
+ };
355
+ outputFormat: "json";
356
+ status: import("hono/utils/http-status").ContentfulStatusCode;
357
+ };
358
+ };
359
+ } & {
360
+ "/opportunity-products/:id": {
361
+ $delete: {
362
+ input: {
363
+ param: {
364
+ id: string;
365
+ };
366
+ };
367
+ output: {
368
+ error: string;
369
+ };
370
+ outputFormat: "json";
371
+ status: 404;
372
+ } | {
373
+ input: {
374
+ param: {
375
+ id: string;
376
+ };
377
+ };
378
+ output: {
379
+ success: true;
380
+ };
381
+ outputFormat: "json";
382
+ status: import("hono/utils/http-status").ContentfulStatusCode;
383
+ };
384
+ };
385
+ }, "/", "/opportunity-products/:id">;
386
+ export {};
387
+ //# sourceMappingURL=opportunities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunities.d.ts","sourceRoot":"","sources":["../../src/routes/opportunities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAajE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAyF1B,CAAA"}
@@ -0,0 +1,69 @@
1
+ import { Hono } from "hono";
2
+ import { crmService } from "../service/index.js";
3
+ import { insertOpportunityParticipantSchema, insertOpportunityProductSchema, insertOpportunitySchema, opportunityListQuerySchema, updateOpportunityProductSchema, updateOpportunitySchema, } from "../validation.js";
4
+ export const opportunityRoutes = new Hono()
5
+ .get("/opportunities", async (c) => {
6
+ const query = opportunityListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
7
+ return c.json(await crmService.listOpportunities(c.get("db"), query));
8
+ })
9
+ .post("/opportunities", async (c) => {
10
+ return c.json({
11
+ data: await crmService.createOpportunity(c.get("db"), insertOpportunitySchema.parse(await c.req.json())),
12
+ }, 201);
13
+ })
14
+ .get("/opportunities/:id", async (c) => {
15
+ const row = await crmService.getOpportunityById(c.get("db"), c.req.param("id"));
16
+ if (!row)
17
+ return c.json({ error: "Opportunity not found" }, 404);
18
+ return c.json({ data: row });
19
+ })
20
+ .patch("/opportunities/:id", async (c) => {
21
+ const row = await crmService.updateOpportunity(c.get("db"), c.req.param("id"), updateOpportunitySchema.parse(await c.req.json()));
22
+ if (!row)
23
+ return c.json({ error: "Opportunity not found" }, 404);
24
+ return c.json({ data: row });
25
+ })
26
+ .delete("/opportunities/:id", async (c) => {
27
+ const row = await crmService.deleteOpportunity(c.get("db"), c.req.param("id"));
28
+ if (!row)
29
+ return c.json({ error: "Opportunity not found" }, 404);
30
+ return c.json({ success: true });
31
+ })
32
+ .get("/opportunities/:id/participants", async (c) => {
33
+ return c.json({
34
+ data: await crmService.listOpportunityParticipants(c.get("db"), c.req.param("id")),
35
+ });
36
+ })
37
+ .post("/opportunities/:id/participants", async (c) => {
38
+ return c.json({
39
+ data: await crmService.createOpportunityParticipant(c.get("db"), c.req.param("id"), insertOpportunityParticipantSchema.parse(await c.req.json())),
40
+ }, 201);
41
+ })
42
+ .delete("/opportunity-participants/:id", async (c) => {
43
+ const row = await crmService.deleteOpportunityParticipant(c.get("db"), c.req.param("id"));
44
+ if (!row)
45
+ return c.json({ error: "Opportunity participant not found" }, 404);
46
+ return c.json({ success: true });
47
+ })
48
+ .get("/opportunities/:id/products", async (c) => {
49
+ return c.json({
50
+ data: await crmService.listOpportunityProducts(c.get("db"), c.req.param("id")),
51
+ });
52
+ })
53
+ .post("/opportunities/:id/products", async (c) => {
54
+ return c.json({
55
+ data: await crmService.createOpportunityProduct(c.get("db"), c.req.param("id"), insertOpportunityProductSchema.parse(await c.req.json())),
56
+ }, 201);
57
+ })
58
+ .patch("/opportunity-products/:id", async (c) => {
59
+ const row = await crmService.updateOpportunityProduct(c.get("db"), c.req.param("id"), updateOpportunityProductSchema.parse(await c.req.json()));
60
+ if (!row)
61
+ return c.json({ error: "Opportunity product not found" }, 404);
62
+ return c.json({ data: row });
63
+ })
64
+ .delete("/opportunity-products/:id", async (c) => {
65
+ const row = await crmService.deleteOpportunityProduct(c.get("db"), c.req.param("id"));
66
+ if (!row)
67
+ return c.json({ error: "Opportunity product not found" }, 404);
68
+ return c.json({ success: true });
69
+ });