@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,292 @@
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 pipelineRoutes: import("hono/hono-base").HonoBase<Env, {
9
+ "/pipelines": {
10
+ $get: {
11
+ input: {};
12
+ output: {
13
+ data: {
14
+ id: string;
15
+ entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
16
+ name: string;
17
+ isDefault: boolean;
18
+ sortOrder: number;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }[];
22
+ total: number;
23
+ limit: number;
24
+ offset: number;
25
+ };
26
+ outputFormat: "json";
27
+ status: import("hono/utils/http-status").ContentfulStatusCode;
28
+ };
29
+ };
30
+ } & {
31
+ "/pipelines": {
32
+ $post: {
33
+ input: {};
34
+ output: {
35
+ data: {
36
+ name: string;
37
+ createdAt: string;
38
+ updatedAt: string;
39
+ entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
40
+ id: string;
41
+ isDefault: boolean;
42
+ sortOrder: number;
43
+ } | undefined;
44
+ };
45
+ outputFormat: "json";
46
+ status: 201;
47
+ };
48
+ };
49
+ } & {
50
+ "/pipelines/:id": {
51
+ $get: {
52
+ input: {
53
+ param: {
54
+ id: string;
55
+ };
56
+ };
57
+ output: {
58
+ error: string;
59
+ };
60
+ outputFormat: "json";
61
+ status: 404;
62
+ } | {
63
+ input: {
64
+ param: {
65
+ id: string;
66
+ };
67
+ };
68
+ output: {
69
+ data: {
70
+ id: string;
71
+ entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
72
+ name: string;
73
+ isDefault: boolean;
74
+ sortOrder: number;
75
+ createdAt: string;
76
+ updatedAt: string;
77
+ };
78
+ };
79
+ outputFormat: "json";
80
+ status: import("hono/utils/http-status").ContentfulStatusCode;
81
+ };
82
+ };
83
+ } & {
84
+ "/pipelines/:id": {
85
+ $patch: {
86
+ input: {
87
+ param: {
88
+ id: string;
89
+ };
90
+ };
91
+ output: {
92
+ error: string;
93
+ };
94
+ outputFormat: "json";
95
+ status: 404;
96
+ } | {
97
+ input: {
98
+ param: {
99
+ id: string;
100
+ };
101
+ };
102
+ output: {
103
+ data: {
104
+ id: string;
105
+ entityType: "organization" | "person" | "opportunity" | "quote" | "activity";
106
+ name: string;
107
+ isDefault: boolean;
108
+ sortOrder: number;
109
+ createdAt: string;
110
+ updatedAt: string;
111
+ };
112
+ };
113
+ outputFormat: "json";
114
+ status: import("hono/utils/http-status").ContentfulStatusCode;
115
+ };
116
+ };
117
+ } & {
118
+ "/pipelines/:id": {
119
+ $delete: {
120
+ input: {
121
+ param: {
122
+ id: string;
123
+ };
124
+ };
125
+ output: {
126
+ error: string;
127
+ };
128
+ outputFormat: "json";
129
+ status: 404;
130
+ } | {
131
+ input: {
132
+ param: {
133
+ id: string;
134
+ };
135
+ };
136
+ output: {
137
+ success: true;
138
+ };
139
+ outputFormat: "json";
140
+ status: import("hono/utils/http-status").ContentfulStatusCode;
141
+ };
142
+ };
143
+ } & {
144
+ "/stages": {
145
+ $get: {
146
+ input: {};
147
+ output: {
148
+ data: {
149
+ id: string;
150
+ pipelineId: string;
151
+ name: string;
152
+ sortOrder: number;
153
+ probability: number | null;
154
+ isClosed: boolean;
155
+ isWon: boolean;
156
+ isLost: boolean;
157
+ createdAt: string;
158
+ updatedAt: string;
159
+ }[];
160
+ total: number;
161
+ limit: number;
162
+ offset: number;
163
+ };
164
+ outputFormat: "json";
165
+ status: import("hono/utils/http-status").ContentfulStatusCode;
166
+ };
167
+ };
168
+ } & {
169
+ "/stages": {
170
+ $post: {
171
+ input: {};
172
+ output: {
173
+ data: {
174
+ name: string;
175
+ createdAt: string;
176
+ updatedAt: string;
177
+ id: string;
178
+ sortOrder: number;
179
+ pipelineId: string;
180
+ probability: number | null;
181
+ isClosed: boolean;
182
+ isWon: boolean;
183
+ isLost: boolean;
184
+ } | undefined;
185
+ };
186
+ outputFormat: "json";
187
+ status: 201;
188
+ };
189
+ };
190
+ } & {
191
+ "/stages/:id": {
192
+ $get: {
193
+ input: {
194
+ param: {
195
+ id: string;
196
+ };
197
+ };
198
+ output: {
199
+ error: string;
200
+ };
201
+ outputFormat: "json";
202
+ status: 404;
203
+ } | {
204
+ input: {
205
+ param: {
206
+ id: string;
207
+ };
208
+ };
209
+ output: {
210
+ data: {
211
+ id: string;
212
+ pipelineId: string;
213
+ name: string;
214
+ sortOrder: number;
215
+ probability: number | null;
216
+ isClosed: boolean;
217
+ isWon: boolean;
218
+ isLost: boolean;
219
+ createdAt: string;
220
+ updatedAt: string;
221
+ };
222
+ };
223
+ outputFormat: "json";
224
+ status: import("hono/utils/http-status").ContentfulStatusCode;
225
+ };
226
+ };
227
+ } & {
228
+ "/stages/:id": {
229
+ $patch: {
230
+ input: {
231
+ param: {
232
+ id: string;
233
+ };
234
+ };
235
+ output: {
236
+ error: string;
237
+ };
238
+ outputFormat: "json";
239
+ status: 404;
240
+ } | {
241
+ input: {
242
+ param: {
243
+ id: string;
244
+ };
245
+ };
246
+ output: {
247
+ data: {
248
+ id: string;
249
+ pipelineId: string;
250
+ name: string;
251
+ sortOrder: number;
252
+ probability: number | null;
253
+ isClosed: boolean;
254
+ isWon: boolean;
255
+ isLost: boolean;
256
+ createdAt: string;
257
+ updatedAt: string;
258
+ };
259
+ };
260
+ outputFormat: "json";
261
+ status: import("hono/utils/http-status").ContentfulStatusCode;
262
+ };
263
+ };
264
+ } & {
265
+ "/stages/:id": {
266
+ $delete: {
267
+ input: {
268
+ param: {
269
+ id: string;
270
+ };
271
+ };
272
+ output: {
273
+ error: string;
274
+ };
275
+ outputFormat: "json";
276
+ status: 404;
277
+ } | {
278
+ input: {
279
+ param: {
280
+ id: string;
281
+ };
282
+ };
283
+ output: {
284
+ success: true;
285
+ };
286
+ outputFormat: "json";
287
+ status: import("hono/utils/http-status").ContentfulStatusCode;
288
+ };
289
+ };
290
+ }, "/", "/stages/:id">;
291
+ export {};
292
+ //# sourceMappingURL=pipelines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipelines.d.ts","sourceRoot":"","sources":["../../src/routes/pipelines.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,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAoEvB,CAAA"}
@@ -0,0 +1,58 @@
1
+ import { Hono } from "hono";
2
+ import { crmService } from "../service/index.js";
3
+ import { insertPipelineSchema, insertStageSchema, pipelineListQuerySchema, stageListQuerySchema, updatePipelineSchema, updateStageSchema, } from "../validation.js";
4
+ export const pipelineRoutes = new Hono()
5
+ .get("/pipelines", async (c) => {
6
+ const query = pipelineListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
7
+ return c.json(await crmService.listPipelines(c.get("db"), query));
8
+ })
9
+ .post("/pipelines", async (c) => {
10
+ return c.json({
11
+ data: await crmService.createPipeline(c.get("db"), insertPipelineSchema.parse(await c.req.json())),
12
+ }, 201);
13
+ })
14
+ .get("/pipelines/:id", async (c) => {
15
+ const row = await crmService.getPipelineById(c.get("db"), c.req.param("id"));
16
+ if (!row)
17
+ return c.json({ error: "Pipeline not found" }, 404);
18
+ return c.json({ data: row });
19
+ })
20
+ .patch("/pipelines/:id", async (c) => {
21
+ const row = await crmService.updatePipeline(c.get("db"), c.req.param("id"), updatePipelineSchema.parse(await c.req.json()));
22
+ if (!row)
23
+ return c.json({ error: "Pipeline not found" }, 404);
24
+ return c.json({ data: row });
25
+ })
26
+ .delete("/pipelines/:id", async (c) => {
27
+ const row = await crmService.deletePipeline(c.get("db"), c.req.param("id"));
28
+ if (!row)
29
+ return c.json({ error: "Pipeline not found" }, 404);
30
+ return c.json({ success: true });
31
+ })
32
+ .get("/stages", async (c) => {
33
+ const query = stageListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
34
+ return c.json(await crmService.listStages(c.get("db"), query));
35
+ })
36
+ .post("/stages", async (c) => {
37
+ return c.json({
38
+ data: await crmService.createStage(c.get("db"), insertStageSchema.parse(await c.req.json())),
39
+ }, 201);
40
+ })
41
+ .get("/stages/:id", async (c) => {
42
+ const row = await crmService.getStageById(c.get("db"), c.req.param("id"));
43
+ if (!row)
44
+ return c.json({ error: "Stage not found" }, 404);
45
+ return c.json({ data: row });
46
+ })
47
+ .patch("/stages/:id", async (c) => {
48
+ const row = await crmService.updateStage(c.get("db"), c.req.param("id"), updateStageSchema.parse(await c.req.json()));
49
+ if (!row)
50
+ return c.json({ error: "Stage not found" }, 404);
51
+ return c.json({ data: row });
52
+ })
53
+ .delete("/stages/:id", async (c) => {
54
+ const row = await crmService.deleteStage(c.get("db"), c.req.param("id"));
55
+ if (!row)
56
+ return c.json({ error: "Stage not found" }, 404);
57
+ return c.json({ success: true });
58
+ });
@@ -0,0 +1,283 @@
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 quoteRoutes: import("hono/hono-base").HonoBase<Env, {
9
+ "/quotes": {
10
+ $get: {
11
+ input: {};
12
+ output: {
13
+ data: {
14
+ id: string;
15
+ opportunityId: string;
16
+ status: "archived" | "draft" | "sent" | "accepted" | "expired" | "rejected";
17
+ validUntil: string | null;
18
+ currency: string;
19
+ subtotalAmountCents: number;
20
+ taxAmountCents: number;
21
+ totalAmountCents: number;
22
+ notes: string | null;
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ archivedAt: string | null;
26
+ }[];
27
+ total: number;
28
+ limit: number;
29
+ offset: number;
30
+ };
31
+ outputFormat: "json";
32
+ status: import("hono/utils/http-status").ContentfulStatusCode;
33
+ };
34
+ };
35
+ } & {
36
+ "/quotes": {
37
+ $post: {
38
+ input: {};
39
+ output: {
40
+ data: {
41
+ opportunityId: string;
42
+ createdAt: string;
43
+ updatedAt: string;
44
+ notes: string | null;
45
+ id: string;
46
+ status: "archived" | "draft" | "sent" | "accepted" | "expired" | "rejected";
47
+ archivedAt: string | null;
48
+ currency: string;
49
+ validUntil: string | null;
50
+ subtotalAmountCents: number;
51
+ taxAmountCents: number;
52
+ totalAmountCents: number;
53
+ } | undefined;
54
+ };
55
+ outputFormat: "json";
56
+ status: 201;
57
+ };
58
+ };
59
+ } & {
60
+ "/quotes/:id": {
61
+ $get: {
62
+ input: {
63
+ param: {
64
+ id: string;
65
+ };
66
+ };
67
+ output: {
68
+ error: string;
69
+ };
70
+ outputFormat: "json";
71
+ status: 404;
72
+ } | {
73
+ input: {
74
+ param: {
75
+ id: string;
76
+ };
77
+ };
78
+ output: {
79
+ data: {
80
+ id: string;
81
+ opportunityId: string;
82
+ status: "archived" | "draft" | "sent" | "accepted" | "expired" | "rejected";
83
+ validUntil: string | null;
84
+ currency: string;
85
+ subtotalAmountCents: number;
86
+ taxAmountCents: number;
87
+ totalAmountCents: number;
88
+ notes: string | null;
89
+ createdAt: string;
90
+ updatedAt: string;
91
+ archivedAt: string | null;
92
+ };
93
+ };
94
+ outputFormat: "json";
95
+ status: import("hono/utils/http-status").ContentfulStatusCode;
96
+ };
97
+ };
98
+ } & {
99
+ "/quotes/:id": {
100
+ $patch: {
101
+ input: {
102
+ param: {
103
+ id: string;
104
+ };
105
+ };
106
+ output: {
107
+ error: string;
108
+ };
109
+ outputFormat: "json";
110
+ status: 404;
111
+ } | {
112
+ input: {
113
+ param: {
114
+ id: string;
115
+ };
116
+ };
117
+ output: {
118
+ data: {
119
+ id: string;
120
+ opportunityId: string;
121
+ status: "archived" | "draft" | "sent" | "accepted" | "expired" | "rejected";
122
+ validUntil: string | null;
123
+ currency: string;
124
+ subtotalAmountCents: number;
125
+ taxAmountCents: number;
126
+ totalAmountCents: number;
127
+ notes: string | null;
128
+ createdAt: string;
129
+ updatedAt: string;
130
+ archivedAt: string | null;
131
+ };
132
+ };
133
+ outputFormat: "json";
134
+ status: import("hono/utils/http-status").ContentfulStatusCode;
135
+ };
136
+ };
137
+ } & {
138
+ "/quotes/:id": {
139
+ $delete: {
140
+ input: {
141
+ param: {
142
+ id: string;
143
+ };
144
+ };
145
+ output: {
146
+ error: string;
147
+ };
148
+ outputFormat: "json";
149
+ status: 404;
150
+ } | {
151
+ input: {
152
+ param: {
153
+ id: string;
154
+ };
155
+ };
156
+ output: {
157
+ success: true;
158
+ };
159
+ outputFormat: "json";
160
+ status: import("hono/utils/http-status").ContentfulStatusCode;
161
+ };
162
+ };
163
+ } & {
164
+ "/quotes/:id/lines": {
165
+ $get: {
166
+ input: {
167
+ param: {
168
+ id: string;
169
+ };
170
+ };
171
+ output: {
172
+ data: {
173
+ id: string;
174
+ quoteId: string;
175
+ productId: string | null;
176
+ supplierServiceId: string | null;
177
+ description: string;
178
+ quantity: number;
179
+ unitPriceAmountCents: number;
180
+ totalAmountCents: number;
181
+ currency: string;
182
+ createdAt: string;
183
+ updatedAt: string;
184
+ }[];
185
+ };
186
+ outputFormat: "json";
187
+ status: import("hono/utils/http-status").ContentfulStatusCode;
188
+ };
189
+ };
190
+ } & {
191
+ "/quotes/:id/lines": {
192
+ $post: {
193
+ input: {
194
+ param: {
195
+ id: string;
196
+ };
197
+ };
198
+ output: {
199
+ data: {
200
+ quoteId: string;
201
+ createdAt: string;
202
+ updatedAt: string;
203
+ description: string;
204
+ id: string;
205
+ productId: string | null;
206
+ supplierServiceId: string | null;
207
+ quantity: number;
208
+ unitPriceAmountCents: number;
209
+ currency: string;
210
+ totalAmountCents: number;
211
+ } | undefined;
212
+ };
213
+ outputFormat: "json";
214
+ status: 201;
215
+ };
216
+ };
217
+ } & {
218
+ "/quote-lines/:id": {
219
+ $patch: {
220
+ input: {
221
+ param: {
222
+ id: string;
223
+ };
224
+ };
225
+ output: {
226
+ error: string;
227
+ };
228
+ outputFormat: "json";
229
+ status: 404;
230
+ } | {
231
+ input: {
232
+ param: {
233
+ id: string;
234
+ };
235
+ };
236
+ output: {
237
+ data: {
238
+ id: string;
239
+ quoteId: string;
240
+ productId: string | null;
241
+ supplierServiceId: string | null;
242
+ description: string;
243
+ quantity: number;
244
+ unitPriceAmountCents: number;
245
+ totalAmountCents: number;
246
+ currency: string;
247
+ createdAt: string;
248
+ updatedAt: string;
249
+ };
250
+ };
251
+ outputFormat: "json";
252
+ status: import("hono/utils/http-status").ContentfulStatusCode;
253
+ };
254
+ };
255
+ } & {
256
+ "/quote-lines/:id": {
257
+ $delete: {
258
+ input: {
259
+ param: {
260
+ id: string;
261
+ };
262
+ };
263
+ output: {
264
+ error: string;
265
+ };
266
+ outputFormat: "json";
267
+ status: 404;
268
+ } | {
269
+ input: {
270
+ param: {
271
+ id: string;
272
+ };
273
+ };
274
+ output: {
275
+ success: true;
276
+ };
277
+ outputFormat: "json";
278
+ status: import("hono/utils/http-status").ContentfulStatusCode;
279
+ };
280
+ };
281
+ }, "/", "/quote-lines/:id">;
282
+ export {};
283
+ //# sourceMappingURL=quotes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../../src/routes/quotes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAYjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA+DpB,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { Hono } from "hono";
2
+ import { crmService } from "../service/index.js";
3
+ import { insertQuoteLineSchema, insertQuoteSchema, quoteListQuerySchema, updateQuoteLineSchema, updateQuoteSchema, } from "../validation.js";
4
+ export const quoteRoutes = new Hono()
5
+ .get("/quotes", async (c) => {
6
+ const query = quoteListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
7
+ return c.json(await crmService.listQuotes(c.get("db"), query));
8
+ })
9
+ .post("/quotes", async (c) => {
10
+ return c.json({
11
+ data: await crmService.createQuote(c.get("db"), insertQuoteSchema.parse(await c.req.json())),
12
+ }, 201);
13
+ })
14
+ .get("/quotes/:id", async (c) => {
15
+ const row = await crmService.getQuoteById(c.get("db"), c.req.param("id"));
16
+ if (!row)
17
+ return c.json({ error: "Quote not found" }, 404);
18
+ return c.json({ data: row });
19
+ })
20
+ .patch("/quotes/:id", async (c) => {
21
+ const row = await crmService.updateQuote(c.get("db"), c.req.param("id"), updateQuoteSchema.parse(await c.req.json()));
22
+ if (!row)
23
+ return c.json({ error: "Quote not found" }, 404);
24
+ return c.json({ data: row });
25
+ })
26
+ .delete("/quotes/:id", async (c) => {
27
+ const row = await crmService.deleteQuote(c.get("db"), c.req.param("id"));
28
+ if (!row)
29
+ return c.json({ error: "Quote not found" }, 404);
30
+ return c.json({ success: true });
31
+ })
32
+ .get("/quotes/:id/lines", async (c) => {
33
+ return c.json({ data: await crmService.listQuoteLines(c.get("db"), c.req.param("id")) });
34
+ })
35
+ .post("/quotes/:id/lines", async (c) => {
36
+ return c.json({
37
+ data: await crmService.createQuoteLine(c.get("db"), c.req.param("id"), insertQuoteLineSchema.parse(await c.req.json())),
38
+ }, 201);
39
+ })
40
+ .patch("/quote-lines/:id", async (c) => {
41
+ const row = await crmService.updateQuoteLine(c.get("db"), c.req.param("id"), updateQuoteLineSchema.parse(await c.req.json()));
42
+ if (!row)
43
+ return c.json({ error: "Quote line not found" }, 404);
44
+ return c.json({ data: row });
45
+ })
46
+ .delete("/quote-lines/:id", async (c) => {
47
+ const row = await crmService.deleteQuoteLine(c.get("db"), c.req.param("id"));
48
+ if (!row)
49
+ return c.json({ error: "Quote line not found" }, 404);
50
+ return c.json({ success: true });
51
+ });