@kanvas/openclaw-plugin 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.
- package/README.md +193 -0
- package/dist/cli/setup.js +75 -0
- package/dist/client/headers.js +25 -0
- package/dist/client/kanvas-client.js +137 -0
- package/dist/client/multipart.js +37 -0
- package/dist/client/types.js +1 -0
- package/dist/config/env.js +18 -0
- package/dist/config/types.js +1 -0
- package/dist/domains/crm/index.js +590 -0
- package/dist/domains/crm/types.js +1 -0
- package/dist/domains/inventory/index.js +199 -0
- package/dist/domains/orders/index.js +57 -0
- package/dist/domains/social/index.js +374 -0
- package/dist/domains/social/types.js +1 -0
- package/dist/index.js +179 -0
- package/dist/tools/crm.js +343 -0
- package/dist/tools/helpers.js +7 -0
- package/dist/tools/inventory.js +99 -0
- package/dist/tools/orders.js +29 -0
- package/dist/tools/social.js +171 -0
- package/openclaw.plugin.json +64 -0
- package/package.json +43 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { postGraphQLMultipart } from "../../client/multipart.js";
|
|
2
|
+
export class CrmService {
|
|
3
|
+
client;
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
async searchLeads(search, first = 10) {
|
|
8
|
+
const query = `
|
|
9
|
+
query SearchLeads($first: Int, $search: String) {
|
|
10
|
+
leads(first: $first, search: $search) {
|
|
11
|
+
data {
|
|
12
|
+
id
|
|
13
|
+
uuid
|
|
14
|
+
firstname
|
|
15
|
+
lastname
|
|
16
|
+
email
|
|
17
|
+
phone
|
|
18
|
+
created_at
|
|
19
|
+
status { id name }
|
|
20
|
+
type { id name }
|
|
21
|
+
source { id name }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
return this.client.query(query, { first, search });
|
|
27
|
+
}
|
|
28
|
+
async getLead(id) {
|
|
29
|
+
const query = `
|
|
30
|
+
query GetLead($first: Int!, $where: QueryLeadsWhereWhereConditions) {
|
|
31
|
+
leads(first: $first, where: $where) {
|
|
32
|
+
data {
|
|
33
|
+
id
|
|
34
|
+
uuid
|
|
35
|
+
firstname
|
|
36
|
+
lastname
|
|
37
|
+
email
|
|
38
|
+
phone
|
|
39
|
+
description
|
|
40
|
+
branch { id }
|
|
41
|
+
people { id uuid firstname lastname }
|
|
42
|
+
status { id name }
|
|
43
|
+
stage { id name }
|
|
44
|
+
pipeline { id name }
|
|
45
|
+
owner { id uuid displayname }
|
|
46
|
+
receiver { id name uuid }
|
|
47
|
+
followers(first: 25) {
|
|
48
|
+
data {
|
|
49
|
+
id
|
|
50
|
+
uuid
|
|
51
|
+
displayname
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
channel_files(includeParticipants: true, groupByAction: true) {
|
|
55
|
+
total_groups
|
|
56
|
+
groups {
|
|
57
|
+
id
|
|
58
|
+
uuid
|
|
59
|
+
action
|
|
60
|
+
verb
|
|
61
|
+
status
|
|
62
|
+
participant_name
|
|
63
|
+
created_at
|
|
64
|
+
last_message_at
|
|
65
|
+
files {
|
|
66
|
+
id
|
|
67
|
+
name
|
|
68
|
+
url
|
|
69
|
+
file_type
|
|
70
|
+
size
|
|
71
|
+
verification_status
|
|
72
|
+
verification_message
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
channels {
|
|
77
|
+
id
|
|
78
|
+
name
|
|
79
|
+
slug
|
|
80
|
+
uuid
|
|
81
|
+
entity_id
|
|
82
|
+
entity_namespace
|
|
83
|
+
}
|
|
84
|
+
participants {
|
|
85
|
+
people {
|
|
86
|
+
id
|
|
87
|
+
uuid
|
|
88
|
+
name
|
|
89
|
+
}
|
|
90
|
+
relationship {
|
|
91
|
+
id
|
|
92
|
+
name
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
events {
|
|
96
|
+
id
|
|
97
|
+
uuid
|
|
98
|
+
name
|
|
99
|
+
description
|
|
100
|
+
created_at
|
|
101
|
+
type { name }
|
|
102
|
+
eventStatus { name }
|
|
103
|
+
versions {
|
|
104
|
+
data {
|
|
105
|
+
id
|
|
106
|
+
name
|
|
107
|
+
version_number
|
|
108
|
+
start_at
|
|
109
|
+
end_at
|
|
110
|
+
dates {
|
|
111
|
+
id
|
|
112
|
+
date
|
|
113
|
+
start_time
|
|
114
|
+
end_time
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
files {
|
|
120
|
+
data {
|
|
121
|
+
id
|
|
122
|
+
uuid
|
|
123
|
+
name
|
|
124
|
+
url
|
|
125
|
+
type
|
|
126
|
+
created_at
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
return this.client.query(query, {
|
|
134
|
+
first: 1,
|
|
135
|
+
where: [{ column: "ID", operator: "EQ", value: id }],
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async createLead(input) {
|
|
139
|
+
const mutation = `
|
|
140
|
+
mutation CreateLead($input: LeadInput!) {
|
|
141
|
+
createLead(input: $input) {
|
|
142
|
+
id
|
|
143
|
+
uuid
|
|
144
|
+
title
|
|
145
|
+
firstname
|
|
146
|
+
lastname
|
|
147
|
+
email
|
|
148
|
+
phone
|
|
149
|
+
description
|
|
150
|
+
branch { id }
|
|
151
|
+
status { id name }
|
|
152
|
+
type { id name }
|
|
153
|
+
source { id name }
|
|
154
|
+
pipeline { id name }
|
|
155
|
+
stage { id name }
|
|
156
|
+
people { id uuid firstname lastname }
|
|
157
|
+
organization { name }
|
|
158
|
+
created_at
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
return this.client.query(mutation, { input });
|
|
163
|
+
}
|
|
164
|
+
async updateLead(id, input) {
|
|
165
|
+
const mutation = `
|
|
166
|
+
mutation UpdateLead($id: ID!, $input: LeadUpdateInput!) {
|
|
167
|
+
updateLead(id: $id, input: $input) {
|
|
168
|
+
id
|
|
169
|
+
uuid
|
|
170
|
+
title
|
|
171
|
+
description
|
|
172
|
+
status { id name }
|
|
173
|
+
stage { id name }
|
|
174
|
+
pipeline { id name }
|
|
175
|
+
owner { id uuid displayname }
|
|
176
|
+
receiver { id uuid name }
|
|
177
|
+
updated_at
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
181
|
+
return this.client.query(mutation, { id, input });
|
|
182
|
+
}
|
|
183
|
+
async changeLeadOwner(input) {
|
|
184
|
+
return this.updateLead(String(input.leadId), {
|
|
185
|
+
branch_id: input.branch_id,
|
|
186
|
+
people_id: input.people_id,
|
|
187
|
+
title: input.title,
|
|
188
|
+
description: input.description,
|
|
189
|
+
leads_owner_id: input.leads_owner_id,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async changeLeadReceiver(input) {
|
|
193
|
+
return this.updateLead(String(input.leadId), {
|
|
194
|
+
branch_id: input.branch_id,
|
|
195
|
+
people_id: input.people_id,
|
|
196
|
+
title: input.title,
|
|
197
|
+
description: input.description,
|
|
198
|
+
receiver_id: input.receiver_id,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async addLeadParticipant(input) {
|
|
202
|
+
const mutation = `
|
|
203
|
+
mutation AddLeadParticipant($input: LeadsParticipantsInput!) {
|
|
204
|
+
addLeadParticipant(input: $input)
|
|
205
|
+
}
|
|
206
|
+
`;
|
|
207
|
+
return this.client.query(mutation, { input });
|
|
208
|
+
}
|
|
209
|
+
async removeLeadParticipant(input) {
|
|
210
|
+
const mutation = `
|
|
211
|
+
mutation RemoveLeadParticipant($input: LeadsParticipantsInput!) {
|
|
212
|
+
removeLeadParticipant(input: $input)
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
return this.client.query(mutation, { input });
|
|
216
|
+
}
|
|
217
|
+
async followLead(input) {
|
|
218
|
+
const mutation = `
|
|
219
|
+
mutation FollowLead($input: FollowInput!) {
|
|
220
|
+
followLead(input: $input)
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
return this.client.query(mutation, { input });
|
|
224
|
+
}
|
|
225
|
+
async unFollowLead(input) {
|
|
226
|
+
const mutation = `
|
|
227
|
+
mutation UnFollowLead($input: FollowInput!) {
|
|
228
|
+
unFollowLead(input: $input)
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
return this.client.query(mutation, { input });
|
|
232
|
+
}
|
|
233
|
+
async deleteLead(id) {
|
|
234
|
+
const mutation = `
|
|
235
|
+
mutation DeleteLead($id: ID!) {
|
|
236
|
+
deleteLead(id: $id)
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
return this.client.query(mutation, { id });
|
|
240
|
+
}
|
|
241
|
+
async restoreLead(id) {
|
|
242
|
+
const mutation = `
|
|
243
|
+
mutation RestoreLead($id: ID!) {
|
|
244
|
+
restoreLead(id: $id)
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
return this.client.query(mutation, { id });
|
|
248
|
+
}
|
|
249
|
+
async markLeadOutcome(id, status, reason_lost) {
|
|
250
|
+
const mutation = `
|
|
251
|
+
mutation LeadWonOrLost($id: ID!, $status: LeadStatusEnum!, $reason_lost: String) {
|
|
252
|
+
leadWonOrLost(id: $id, status: $status, reason_lost: $reason_lost) {
|
|
253
|
+
id
|
|
254
|
+
uuid
|
|
255
|
+
title
|
|
256
|
+
reason_lost
|
|
257
|
+
status { id name }
|
|
258
|
+
updated_at
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
`;
|
|
262
|
+
return this.client.query(mutation, { id, status, reason_lost });
|
|
263
|
+
}
|
|
264
|
+
async createLeadAppointment(input) {
|
|
265
|
+
const mutation = `
|
|
266
|
+
mutation CreateEvent($input: EventInput!) {
|
|
267
|
+
createEvent(input: $input) {
|
|
268
|
+
id
|
|
269
|
+
uuid
|
|
270
|
+
name
|
|
271
|
+
description
|
|
272
|
+
start_at
|
|
273
|
+
end_at
|
|
274
|
+
created_at
|
|
275
|
+
resources {
|
|
276
|
+
id
|
|
277
|
+
resources_id
|
|
278
|
+
resources_type
|
|
279
|
+
}
|
|
280
|
+
versions {
|
|
281
|
+
data {
|
|
282
|
+
id
|
|
283
|
+
start_at
|
|
284
|
+
end_at
|
|
285
|
+
dates {
|
|
286
|
+
id
|
|
287
|
+
date
|
|
288
|
+
start_time
|
|
289
|
+
end_time
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
`;
|
|
296
|
+
return this.client.query(mutation, { input });
|
|
297
|
+
}
|
|
298
|
+
async addLeadMessage(input) {
|
|
299
|
+
const mutation = `
|
|
300
|
+
mutation CreateMessage($input: MessageInput!) {
|
|
301
|
+
createMessage(input: $input) {
|
|
302
|
+
id
|
|
303
|
+
uuid
|
|
304
|
+
slug
|
|
305
|
+
message
|
|
306
|
+
created_at
|
|
307
|
+
messageType {
|
|
308
|
+
name
|
|
309
|
+
}
|
|
310
|
+
channels {
|
|
311
|
+
id
|
|
312
|
+
slug
|
|
313
|
+
name
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
`;
|
|
318
|
+
return this.client.query(mutation, {
|
|
319
|
+
input: {
|
|
320
|
+
message: input.message,
|
|
321
|
+
message_verb: "comment",
|
|
322
|
+
channel_slug: input.channel_slug,
|
|
323
|
+
parent_id: input.parent_id,
|
|
324
|
+
is_public: input.is_public,
|
|
325
|
+
custom_fields: input.custom_fields,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
async addLeadMessageByLeadId(input) {
|
|
330
|
+
const channelSlug = await this.getLeadPrimaryChannelSlug(String(input.leadId));
|
|
331
|
+
if (!channelSlug) {
|
|
332
|
+
throw new Error(`No lead channel found for lead ${input.leadId}`);
|
|
333
|
+
}
|
|
334
|
+
const created = await this.addLeadMessage({
|
|
335
|
+
channel_slug: channelSlug,
|
|
336
|
+
message: input.message,
|
|
337
|
+
parent_id: input.parent_id,
|
|
338
|
+
is_public: input.is_public,
|
|
339
|
+
custom_fields: input.custom_fields,
|
|
340
|
+
});
|
|
341
|
+
if (!input.attachments?.length) {
|
|
342
|
+
return created;
|
|
343
|
+
}
|
|
344
|
+
const messageId = created.data?.createMessage?.id;
|
|
345
|
+
if (!messageId) {
|
|
346
|
+
throw new Error("Lead message created but message id was missing for attachment upload");
|
|
347
|
+
}
|
|
348
|
+
const attachmentResult = await this.attachFilesToMessage(messageId, input.attachments);
|
|
349
|
+
return {
|
|
350
|
+
...created,
|
|
351
|
+
attachments: attachmentResult,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
async listLeadMessages(channelSlug, first = 50, page = 1) {
|
|
355
|
+
const query = `
|
|
356
|
+
query LeadMessages($first: Int, $page: Int, $channelSlug: String!) {
|
|
357
|
+
messages(
|
|
358
|
+
first: $first
|
|
359
|
+
page: $page
|
|
360
|
+
orderBy: [{ column: CREATED_AT, order: DESC }]
|
|
361
|
+
hasChannel: { column: SLUG, operator: EQ, value: $channelSlug }
|
|
362
|
+
) {
|
|
363
|
+
data {
|
|
364
|
+
id
|
|
365
|
+
uuid
|
|
366
|
+
slug
|
|
367
|
+
message
|
|
368
|
+
is_public
|
|
369
|
+
created_at
|
|
370
|
+
user {
|
|
371
|
+
id
|
|
372
|
+
uuid
|
|
373
|
+
firstname
|
|
374
|
+
lastname
|
|
375
|
+
displayname
|
|
376
|
+
}
|
|
377
|
+
messageType {
|
|
378
|
+
name
|
|
379
|
+
}
|
|
380
|
+
files {
|
|
381
|
+
data {
|
|
382
|
+
id
|
|
383
|
+
uuid
|
|
384
|
+
name
|
|
385
|
+
url
|
|
386
|
+
type
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
children(first: 10) {
|
|
390
|
+
data {
|
|
391
|
+
id
|
|
392
|
+
uuid
|
|
393
|
+
message
|
|
394
|
+
created_at
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
|
+
return this.client.query(query, { first, page, channelSlug });
|
|
402
|
+
}
|
|
403
|
+
async getLeadPrimaryChannelSlug(leadId) {
|
|
404
|
+
const response = await this.client.query(`
|
|
405
|
+
query LeadChannels($first: Int!, $where: QueryLeadsWhereWhereConditions) {
|
|
406
|
+
leads(first: $first, where: $where) {
|
|
407
|
+
data {
|
|
408
|
+
channels {
|
|
409
|
+
id
|
|
410
|
+
slug
|
|
411
|
+
name
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
`, {
|
|
417
|
+
first: 1,
|
|
418
|
+
where: [{ column: "ID", operator: "EQ", value: leadId }],
|
|
419
|
+
});
|
|
420
|
+
return response.data?.leads?.data?.[0]?.channels?.[0]?.slug ?? null;
|
|
421
|
+
}
|
|
422
|
+
async attachFileToLead(input) {
|
|
423
|
+
const query = `
|
|
424
|
+
mutation AttachFileToLead($id: ID!, $file: Upload!, $params: Mixed) {
|
|
425
|
+
attachFileToLead(id: $id, file: $file, params: $params) {
|
|
426
|
+
id
|
|
427
|
+
uuid
|
|
428
|
+
title
|
|
429
|
+
files {
|
|
430
|
+
data {
|
|
431
|
+
id
|
|
432
|
+
uuid
|
|
433
|
+
name
|
|
434
|
+
url
|
|
435
|
+
type
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
`;
|
|
441
|
+
return postGraphQLMultipart({
|
|
442
|
+
config: this.client.getConfig(),
|
|
443
|
+
query,
|
|
444
|
+
variables: {
|
|
445
|
+
id: input.leadId,
|
|
446
|
+
file: null,
|
|
447
|
+
params: input.params ?? null,
|
|
448
|
+
},
|
|
449
|
+
files: [
|
|
450
|
+
{
|
|
451
|
+
key: "variables.file",
|
|
452
|
+
fileName: input.fileName,
|
|
453
|
+
contentType: input.contentType,
|
|
454
|
+
content: input.content,
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async attachFilesToLead(input) {
|
|
460
|
+
const query = `
|
|
461
|
+
mutation AttachFilesToLead($id: ID!, $file: [Upload!]!, $params: Mixed) {
|
|
462
|
+
attachFilesToLead(id: $id, file: $file, params: $params) {
|
|
463
|
+
id
|
|
464
|
+
uuid
|
|
465
|
+
title
|
|
466
|
+
files {
|
|
467
|
+
data {
|
|
468
|
+
id
|
|
469
|
+
uuid
|
|
470
|
+
name
|
|
471
|
+
url
|
|
472
|
+
type
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
`;
|
|
478
|
+
return postGraphQLMultipart({
|
|
479
|
+
config: this.client.getConfig(),
|
|
480
|
+
query,
|
|
481
|
+
variables: {
|
|
482
|
+
id: input.leadId,
|
|
483
|
+
file: new Array(input.files.length).fill(null),
|
|
484
|
+
params: input.params ?? null,
|
|
485
|
+
},
|
|
486
|
+
files: input.files.map((file, index) => ({
|
|
487
|
+
key: `variables.file.${index}`,
|
|
488
|
+
fileName: file.fileName,
|
|
489
|
+
contentType: file.contentType,
|
|
490
|
+
content: file.content,
|
|
491
|
+
})),
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
async attachFileToMessage(messageId, file, fileName, contentType) {
|
|
495
|
+
const query = `
|
|
496
|
+
mutation AttachFileToMessage($message_id: ID!, $file: Upload!) {
|
|
497
|
+
attachFileToMessage(message_id: $message_id, file: $file) {
|
|
498
|
+
id
|
|
499
|
+
uuid
|
|
500
|
+
message
|
|
501
|
+
files {
|
|
502
|
+
data {
|
|
503
|
+
id
|
|
504
|
+
uuid
|
|
505
|
+
name
|
|
506
|
+
url
|
|
507
|
+
type
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
`;
|
|
513
|
+
return postGraphQLMultipart({
|
|
514
|
+
config: this.client.getConfig(),
|
|
515
|
+
query,
|
|
516
|
+
variables: {
|
|
517
|
+
message_id: messageId,
|
|
518
|
+
file: null,
|
|
519
|
+
},
|
|
520
|
+
files: [
|
|
521
|
+
{
|
|
522
|
+
key: "variables.file",
|
|
523
|
+
fileName,
|
|
524
|
+
contentType,
|
|
525
|
+
content: file,
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
async attachFilesToMessage(messageId, files) {
|
|
531
|
+
return Promise.all(files.map((file) => this.attachFileToMessage(messageId, file.content, file.fileName, file.contentType)));
|
|
532
|
+
}
|
|
533
|
+
async listLeadStatuses(first = 50) {
|
|
534
|
+
const query = `
|
|
535
|
+
query LeadStatuses($first: Int) {
|
|
536
|
+
leadStatuses(first: $first) {
|
|
537
|
+
data {
|
|
538
|
+
id
|
|
539
|
+
name
|
|
540
|
+
is_default
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
`;
|
|
545
|
+
return this.client.query(query, { first });
|
|
546
|
+
}
|
|
547
|
+
async listLeadSources(first = 50) {
|
|
548
|
+
const query = `
|
|
549
|
+
query LeadSources($first: Int) {
|
|
550
|
+
leadSources(first: $first) {
|
|
551
|
+
data {
|
|
552
|
+
id
|
|
553
|
+
name
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
`;
|
|
558
|
+
return this.client.query(query, { first });
|
|
559
|
+
}
|
|
560
|
+
async listLeadTypes(first = 50) {
|
|
561
|
+
const query = `
|
|
562
|
+
query LeadTypes($first: Int) {
|
|
563
|
+
leadTypes(first: $first) {
|
|
564
|
+
data {
|
|
565
|
+
id
|
|
566
|
+
name
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
`;
|
|
571
|
+
return this.client.query(query, { first });
|
|
572
|
+
}
|
|
573
|
+
async listPipelines(first = 50) {
|
|
574
|
+
const query = `
|
|
575
|
+
query Pipelines($first: Int) {
|
|
576
|
+
pipelines(first: $first) {
|
|
577
|
+
data {
|
|
578
|
+
id
|
|
579
|
+
name
|
|
580
|
+
stages {
|
|
581
|
+
id
|
|
582
|
+
name
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
`;
|
|
588
|
+
return this.client.query(query, { first });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|