@gitscrum-studio/mcp-server 1.0.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/LICENSE +21 -0
- package/README.md +250 -0
- package/dist/auth/DeviceAuthenticator.d.ts +51 -0
- package/dist/auth/DeviceAuthenticator.d.ts.map +1 -0
- package/dist/auth/DeviceAuthenticator.js +89 -0
- package/dist/auth/DeviceAuthenticator.js.map +1 -0
- package/dist/auth/RateLimiter.d.ts +58 -0
- package/dist/auth/RateLimiter.d.ts.map +1 -0
- package/dist/auth/RateLimiter.js +181 -0
- package/dist/auth/RateLimiter.js.map +1 -0
- package/dist/auth/TokenManager.d.ts +62 -0
- package/dist/auth/TokenManager.d.ts.map +1 -0
- package/dist/auth/TokenManager.js +164 -0
- package/dist/auth/TokenManager.js.map +1 -0
- package/dist/client/GitScrumClient.d.ts +1002 -0
- package/dist/client/GitScrumClient.d.ts.map +1 -0
- package/dist/client/GitScrumClient.js +1835 -0
- package/dist/client/GitScrumClient.js.map +1 -0
- package/dist/context/ActiveContext.d.ts +79 -0
- package/dist/context/ActiveContext.d.ts.map +1 -0
- package/dist/context/ActiveContext.js +151 -0
- package/dist/context/ActiveContext.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/activity.d.ts +12 -0
- package/dist/tools/activity.d.ts.map +1 -0
- package/dist/tools/activity.js +106 -0
- package/dist/tools/activity.js.map +1 -0
- package/dist/tools/analytics.d.ts +11 -0
- package/dist/tools/analytics.d.ts.map +1 -0
- package/dist/tools/analytics.js +117 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/auth.d.ts +17 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +173 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/budget.d.ts +12 -0
- package/dist/tools/budget.d.ts.map +1 -0
- package/dist/tools/budget.js +109 -0
- package/dist/tools/budget.js.map +1 -0
- package/dist/tools/clientflow.d.ts +12 -0
- package/dist/tools/clientflow.d.ts.map +1 -0
- package/dist/tools/clientflow.js +476 -0
- package/dist/tools/clientflow.js.map +1 -0
- package/dist/tools/comments.d.ts +11 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +99 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/discussions.d.ts +12 -0
- package/dist/tools/discussions.d.ts.map +1 -0
- package/dist/tools/discussions.js +200 -0
- package/dist/tools/discussions.js.map +1 -0
- package/dist/tools/epics.d.ts +11 -0
- package/dist/tools/epics.d.ts.map +1 -0
- package/dist/tools/epics.js +122 -0
- package/dist/tools/epics.js.map +1 -0
- package/dist/tools/labels.d.ts +11 -0
- package/dist/tools/labels.d.ts.map +1 -0
- package/dist/tools/labels.js +138 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/notes.d.ts +11 -0
- package/dist/tools/notes.d.ts.map +1 -0
- package/dist/tools/notes.js +201 -0
- package/dist/tools/notes.js.map +1 -0
- package/dist/tools/projects.d.ts +12 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +272 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/search.d.ts +17 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +56 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/shared/actionHandler.d.ts +67 -0
- package/dist/tools/shared/actionHandler.d.ts.map +1 -0
- package/dist/tools/shared/actionHandler.js +98 -0
- package/dist/tools/shared/actionHandler.js.map +1 -0
- package/dist/tools/shared/initModules.d.ts +6 -0
- package/dist/tools/shared/initModules.d.ts.map +1 -0
- package/dist/tools/shared/initModules.js +166 -0
- package/dist/tools/shared/initModules.js.map +1 -0
- package/dist/tools/shared/toolRegistry.d.ts +45 -0
- package/dist/tools/shared/toolRegistry.d.ts.map +1 -0
- package/dist/tools/shared/toolRegistry.js +59 -0
- package/dist/tools/shared/toolRegistry.js.map +1 -0
- package/dist/tools/sprints.d.ts +11 -0
- package/dist/tools/sprints.d.ts.map +1 -0
- package/dist/tools/sprints.js +271 -0
- package/dist/tools/sprints.js.map +1 -0
- package/dist/tools/standup.d.ts +11 -0
- package/dist/tools/standup.d.ts.map +1 -0
- package/dist/tools/standup.js +89 -0
- package/dist/tools/standup.js.map +1 -0
- package/dist/tools/taskTypes.d.ts +11 -0
- package/dist/tools/taskTypes.d.ts.map +1 -0
- package/dist/tools/taskTypes.js +132 -0
- package/dist/tools/taskTypes.js.map +1 -0
- package/dist/tools/tasks.d.ts +12 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +470 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/timeTracking.d.ts +11 -0
- package/dist/tools/timeTracking.d.ts.map +1 -0
- package/dist/tools/timeTracking.js +174 -0
- package/dist/tools/timeTracking.js.map +1 -0
- package/dist/tools/userStories.d.ts +11 -0
- package/dist/tools/userStories.d.ts.map +1 -0
- package/dist/tools/userStories.js +166 -0
- package/dist/tools/userStories.js.map +1 -0
- package/dist/tools/wiki.d.ts +11 -0
- package/dist/tools/wiki.d.ts.map +1 -0
- package/dist/tools/wiki.js +154 -0
- package/dist/tools/wiki.js.map +1 -0
- package/dist/tools/workflows.d.ts +11 -0
- package/dist/tools/workflows.d.ts.map +1 -0
- package/dist/tools/workflows.js +144 -0
- package/dist/tools/workflows.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,1835 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitScrum API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for communicating with the GitScrum REST API.
|
|
5
|
+
* Provides type-safe methods for all GitScrum resources including
|
|
6
|
+
* tasks, projects, sprints, time tracking, and ClientFlow CRM.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - JWT authentication with secure token storage
|
|
10
|
+
* - Comprehensive error handling with user-friendly messages
|
|
11
|
+
* - Rate limiting and retry logic
|
|
12
|
+
* - Full TypeScript support
|
|
13
|
+
*
|
|
14
|
+
* @module @gitscrum-studio/mcp-server/client
|
|
15
|
+
* @author GitScrum <hello@gitscrum.com>
|
|
16
|
+
* @license MIT
|
|
17
|
+
*/
|
|
18
|
+
import { TokenManager } from "../auth/TokenManager.js";
|
|
19
|
+
export class GitScrumClient {
|
|
20
|
+
baseUrl;
|
|
21
|
+
token;
|
|
22
|
+
tokenManager;
|
|
23
|
+
constructor() {
|
|
24
|
+
this.baseUrl = process.env.GITSCRUM_API_URL || "https://services.gitscrum.com";
|
|
25
|
+
this.tokenManager = new TokenManager();
|
|
26
|
+
// Try to get token from environment or saved file
|
|
27
|
+
this.token = this.tokenManager.getToken() || "";
|
|
28
|
+
if (!this.token) {
|
|
29
|
+
console.error("Warning: No authentication token found. Use auth_login tool to authenticate.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set the authentication token
|
|
34
|
+
*/
|
|
35
|
+
setToken(token) {
|
|
36
|
+
this.token = token;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if client is authenticated
|
|
40
|
+
*/
|
|
41
|
+
isAuthenticated() {
|
|
42
|
+
return !!this.token;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Logout and invalidate token
|
|
46
|
+
*/
|
|
47
|
+
async logout() {
|
|
48
|
+
if (this.token) {
|
|
49
|
+
try {
|
|
50
|
+
await this.post("auth/logout", {});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Ignore errors during logout
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
this.token = "";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Make an authenticated request to the GitScrum API
|
|
60
|
+
*/
|
|
61
|
+
async request(endpoint, options = {}) {
|
|
62
|
+
const url = `${this.baseUrl}/${endpoint.replace(/^\//, "")}`;
|
|
63
|
+
const response = await fetch(url, {
|
|
64
|
+
...options,
|
|
65
|
+
headers: {
|
|
66
|
+
Authorization: `Bearer ${this.token}`,
|
|
67
|
+
Accept: "application/json",
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"X-Client-Type": "mcp",
|
|
70
|
+
"X-Client-Source": "mcp-server",
|
|
71
|
+
...options.headers,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const errorData = (await response.json().catch(() => ({})));
|
|
76
|
+
const errorMessage = errorData.message || `API Error: ${response.status}`;
|
|
77
|
+
// Handle 400 Bad Request - validation or invalid input
|
|
78
|
+
if (response.status === 400) {
|
|
79
|
+
throw new Error(`Invalid request: ${errorMessage}`);
|
|
80
|
+
}
|
|
81
|
+
// Handle 401 Unauthorized - authentication issue
|
|
82
|
+
if (response.status === 401) {
|
|
83
|
+
throw new Error("Session expired. Authentication required. Use login to reconnect.");
|
|
84
|
+
}
|
|
85
|
+
// Handle 403 Forbidden
|
|
86
|
+
if (response.status === 403) {
|
|
87
|
+
throw new Error(`Access denied: ${errorMessage}`);
|
|
88
|
+
}
|
|
89
|
+
// Handle 404 Not Found
|
|
90
|
+
if (response.status === 404) {
|
|
91
|
+
throw new Error("Resource not found or was deleted.");
|
|
92
|
+
}
|
|
93
|
+
// Handle 409 Conflict - resource conflict (e.g., duplicate, pending operation)
|
|
94
|
+
if (response.status === 409) {
|
|
95
|
+
throw new Error(`Conflict: ${errorMessage}`);
|
|
96
|
+
}
|
|
97
|
+
// Handle 422 Unprocessable Entity - validation errors
|
|
98
|
+
if (response.status === 422) {
|
|
99
|
+
throw new Error(JSON.stringify({
|
|
100
|
+
error: "validation_failed",
|
|
101
|
+
message: errorMessage,
|
|
102
|
+
validation_errors: errorData.errors
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
// Handle 429 Too Many Requests - MCP rate limit exceeded
|
|
106
|
+
if (response.status === 429) {
|
|
107
|
+
throw new Error(JSON.stringify({
|
|
108
|
+
error: "rate_limit_exceeded",
|
|
109
|
+
limit: response.headers.get("X-MCP-RateLimit-Limit"),
|
|
110
|
+
remaining: response.headers.get("X-MCP-RateLimit-Remaining"),
|
|
111
|
+
reset: response.headers.get("X-MCP-RateLimit-Reset"),
|
|
112
|
+
upgrade_url: "https://gitscrum.com/pricing"
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
// Handle 500+ Server Errors
|
|
116
|
+
if (response.status >= 500) {
|
|
117
|
+
throw new Error(`Server error: ${errorMessage}`);
|
|
118
|
+
}
|
|
119
|
+
throw new Error(errorMessage);
|
|
120
|
+
}
|
|
121
|
+
return (await response.json());
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* GET request helper
|
|
125
|
+
*/
|
|
126
|
+
async get(endpoint, params) {
|
|
127
|
+
let url = endpoint;
|
|
128
|
+
if (params) {
|
|
129
|
+
const searchParams = new URLSearchParams();
|
|
130
|
+
for (const [key, value] of Object.entries(params)) {
|
|
131
|
+
if (value !== undefined && value !== null) {
|
|
132
|
+
searchParams.append(key, String(value));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const queryString = searchParams.toString();
|
|
136
|
+
if (queryString) {
|
|
137
|
+
url += `?${queryString}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return this.request(url, { method: "GET" });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* POST request helper
|
|
144
|
+
*/
|
|
145
|
+
async post(endpoint, body) {
|
|
146
|
+
return this.request(endpoint, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* PUT request helper
|
|
153
|
+
*/
|
|
154
|
+
async put(endpoint, body) {
|
|
155
|
+
return this.request(endpoint, {
|
|
156
|
+
method: "PUT",
|
|
157
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* DELETE request helper
|
|
162
|
+
*/
|
|
163
|
+
async delete(endpoint) {
|
|
164
|
+
return this.request(endpoint, { method: "DELETE" });
|
|
165
|
+
}
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// AUTH / USER
|
|
168
|
+
// ============================================================================
|
|
169
|
+
/**
|
|
170
|
+
* Get current authenticated user
|
|
171
|
+
*/
|
|
172
|
+
async getMe() {
|
|
173
|
+
const response = await this.post("auth/me");
|
|
174
|
+
return response.data;
|
|
175
|
+
}
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// WORKSPACES (Companies)
|
|
178
|
+
// ============================================================================
|
|
179
|
+
/**
|
|
180
|
+
* Get all workspaces the user has access to (with pagination metadata)
|
|
181
|
+
* Uses the new /workspaces endpoint with proper pagination
|
|
182
|
+
*/
|
|
183
|
+
async getWorkspaces(options) {
|
|
184
|
+
const params = {};
|
|
185
|
+
if (options?.perPage)
|
|
186
|
+
params.per_page = options.perPage;
|
|
187
|
+
if (options?.page)
|
|
188
|
+
params.page = options.page;
|
|
189
|
+
if (options?.search)
|
|
190
|
+
params.search = options.search;
|
|
191
|
+
const response = await this.get("workspaces", params);
|
|
192
|
+
return response;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Find workspace by name and return slug
|
|
196
|
+
* Searches workspaces by name and returns the first match with its slug
|
|
197
|
+
*/
|
|
198
|
+
async findWorkspaceByName(name) {
|
|
199
|
+
const response = await this.getWorkspaces({ search: name, perPage: 5 });
|
|
200
|
+
const workspaces = response.data;
|
|
201
|
+
if (!workspaces || workspaces.length === 0) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
// Return exact match if found, otherwise first result
|
|
205
|
+
const exactMatch = workspaces.find(w => w.name.toLowerCase() === name.toLowerCase());
|
|
206
|
+
return exactMatch || workspaces[0];
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get workspace details
|
|
210
|
+
*/
|
|
211
|
+
async getWorkspace(slug) {
|
|
212
|
+
const response = await this.get(`workspaces/${slug}`);
|
|
213
|
+
return response.data;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get workspace statistics
|
|
217
|
+
*/
|
|
218
|
+
async getWorkspaceStats(companySlug) {
|
|
219
|
+
const response = await this.get(`workspaces/${companySlug}/stats`);
|
|
220
|
+
return response.data;
|
|
221
|
+
}
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// PROJECTS
|
|
224
|
+
// ============================================================================
|
|
225
|
+
/**
|
|
226
|
+
* Create a new project in a workspace
|
|
227
|
+
*/
|
|
228
|
+
async createProject(companySlug, data) {
|
|
229
|
+
const response = await this.post(`projects?company_slug=${companySlug}`, data);
|
|
230
|
+
return {
|
|
231
|
+
project_slug: response.data.slug,
|
|
232
|
+
name: response.data.name,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get all projects in a workspace (with pagination metadata)
|
|
237
|
+
*/
|
|
238
|
+
async getProjects(companySlug, options) {
|
|
239
|
+
const params = {
|
|
240
|
+
company_slug: companySlug,
|
|
241
|
+
per_page: options?.perPage ?? 100,
|
|
242
|
+
};
|
|
243
|
+
if (options?.status) {
|
|
244
|
+
params.status = options.status;
|
|
245
|
+
}
|
|
246
|
+
const response = await this.get("projects", params);
|
|
247
|
+
return response;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Find project by name within a workspace and return slug info
|
|
251
|
+
* Uses search endpoint to find project by name
|
|
252
|
+
*/
|
|
253
|
+
async findProjectByName(name, companySlug) {
|
|
254
|
+
const results = await this.search(name, {
|
|
255
|
+
company_slug: companySlug,
|
|
256
|
+
categories: 'projects',
|
|
257
|
+
limit: 5,
|
|
258
|
+
});
|
|
259
|
+
const projects = results?.projects?.items;
|
|
260
|
+
if (!projects || projects.length === 0) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
// Parse route to extract slugs: "/{company_slug}/projects/{project_slug}"
|
|
264
|
+
const parseRoute = (route) => {
|
|
265
|
+
const match = route.match(/^\/([^/]+)\/projects\/([^/]+)$/);
|
|
266
|
+
if (match) {
|
|
267
|
+
return { company_slug: match[1], project_slug: match[2] };
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
};
|
|
271
|
+
// Find exact match first
|
|
272
|
+
const exactMatch = projects.find(p => p.title.toLowerCase() === name.toLowerCase());
|
|
273
|
+
const project = exactMatch || projects[0];
|
|
274
|
+
const slugs = parseRoute(project.route);
|
|
275
|
+
if (!slugs) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
project_slug: slugs.project_slug,
|
|
280
|
+
company_slug: slugs.company_slug,
|
|
281
|
+
name: project.title,
|
|
282
|
+
workspace_slug: companySlug || slugs.company_slug,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get project details
|
|
287
|
+
*/
|
|
288
|
+
async getProject(projectSlug, companySlug) {
|
|
289
|
+
const response = await this.get(`projects/${projectSlug}`, {
|
|
290
|
+
company_slug: companySlug,
|
|
291
|
+
});
|
|
292
|
+
return response.data;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get project workflows/statuses (Kanban columns)
|
|
296
|
+
*/
|
|
297
|
+
async getProjectWorkflows(projectSlug, companySlug) {
|
|
298
|
+
const response = await this.get("project-templates/workflow", {
|
|
299
|
+
project_slug: projectSlug,
|
|
300
|
+
company_slug: companySlug,
|
|
301
|
+
});
|
|
302
|
+
return response.data;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Create a new workflow/column in a project Kanban board
|
|
306
|
+
*/
|
|
307
|
+
async createWorkflow(projectSlug, companySlug, data) {
|
|
308
|
+
const response = await this.post(`projects-workflows/?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
309
|
+
return response.data;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Update an existing workflow/column
|
|
313
|
+
*/
|
|
314
|
+
async updateWorkflow(workflowId, companySlug, projectSlug, data) {
|
|
315
|
+
await this.put(`projects-workflows/${workflowId}/?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Get project task types
|
|
319
|
+
*/
|
|
320
|
+
async getProjectTypes(projectSlug, companySlug) {
|
|
321
|
+
const response = await this.get("project-templates/type", {
|
|
322
|
+
project_slug: projectSlug,
|
|
323
|
+
company_slug: companySlug,
|
|
324
|
+
});
|
|
325
|
+
return response.data;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get project efforts/priorities
|
|
329
|
+
*/
|
|
330
|
+
async getProjectEfforts(projectSlug, companySlug) {
|
|
331
|
+
const response = await this.get("project-templates/effort", {
|
|
332
|
+
project_slug: projectSlug,
|
|
333
|
+
company_slug: companySlug,
|
|
334
|
+
});
|
|
335
|
+
return response.data;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get project labels
|
|
339
|
+
*/
|
|
340
|
+
async getProjectLabels(projectSlug, companySlug) {
|
|
341
|
+
const response = await this.get("task-labels", {
|
|
342
|
+
project_slug: projectSlug,
|
|
343
|
+
company_slug: companySlug,
|
|
344
|
+
});
|
|
345
|
+
return response.data;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Get project members/assignees
|
|
349
|
+
* Returns usernames that can be assigned to tasks
|
|
350
|
+
*/
|
|
351
|
+
async getProjectMembers(projectSlug, companySlug) {
|
|
352
|
+
// Returns direct array of assignees with uuid, name, username, avatar
|
|
353
|
+
const response = await this.get(`project-members/${projectSlug}/assignees`, {
|
|
354
|
+
company_slug: companySlug,
|
|
355
|
+
});
|
|
356
|
+
return response;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get project statistics
|
|
360
|
+
*/
|
|
361
|
+
async getProjectStats(projectSlug, companySlug) {
|
|
362
|
+
const response = await this.get(`projects/${projectSlug}/stats`, {
|
|
363
|
+
company_slug: companySlug,
|
|
364
|
+
});
|
|
365
|
+
return response.data;
|
|
366
|
+
}
|
|
367
|
+
// ============================================================================
|
|
368
|
+
// TASKS
|
|
369
|
+
// ============================================================================
|
|
370
|
+
/**
|
|
371
|
+
* Get all tasks assigned to current user across all workspaces (with pagination metadata)
|
|
372
|
+
*/
|
|
373
|
+
async getMyTasks(perPage = 100) {
|
|
374
|
+
const response = await this.get("tasks/all-workspaces", {
|
|
375
|
+
per_page: perPage,
|
|
376
|
+
});
|
|
377
|
+
return response;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get user notifications (mentions, assignments, updates)
|
|
381
|
+
*/
|
|
382
|
+
async getNotifications() {
|
|
383
|
+
const response = await this.get("feeds/notifications");
|
|
384
|
+
return response.data || [];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get unread notification count
|
|
388
|
+
*/
|
|
389
|
+
async getNotificationCount() {
|
|
390
|
+
const response = await this.get("feeds/notifications/count");
|
|
391
|
+
return response.data || 0;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Get today's tasks for current user (with pagination metadata)
|
|
395
|
+
*/
|
|
396
|
+
async getTodayTasks(limit = 50) {
|
|
397
|
+
const response = await this.get("tasks/my-today", {
|
|
398
|
+
limit,
|
|
399
|
+
});
|
|
400
|
+
return response;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get task details by UUID
|
|
404
|
+
*/
|
|
405
|
+
async getTask(uuid) {
|
|
406
|
+
const response = await this.get(`tasks/${uuid}`);
|
|
407
|
+
return response.data;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get tasks in a project (with pagination metadata)
|
|
411
|
+
*/
|
|
412
|
+
async getProjectTasks(projectSlug, companySlug, perPage = 100) {
|
|
413
|
+
const response = await this.get("tasks", {
|
|
414
|
+
project_slug: projectSlug,
|
|
415
|
+
company_slug: companySlug,
|
|
416
|
+
per_page: perPage,
|
|
417
|
+
});
|
|
418
|
+
return response;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Advanced task search with comprehensive filters
|
|
422
|
+
* Supports filtering by workflow (status/column), labels, types, efforts, sprints, user stories,
|
|
423
|
+
* date ranges, assignees, flags (blocker, bug, unassigned), and text search.
|
|
424
|
+
*/
|
|
425
|
+
async searchTasks(projectSlug, companySlug, filters = {}) {
|
|
426
|
+
const params = {
|
|
427
|
+
project_slug: projectSlug,
|
|
428
|
+
company_slug: companySlug,
|
|
429
|
+
per_page: filters.per_page || 50,
|
|
430
|
+
};
|
|
431
|
+
if (filters.page)
|
|
432
|
+
params.page = filters.page;
|
|
433
|
+
if (filters.title)
|
|
434
|
+
params.title = filters.title;
|
|
435
|
+
if (filters.description)
|
|
436
|
+
params.description = filters.description;
|
|
437
|
+
if (filters.number)
|
|
438
|
+
params.number = filters.number;
|
|
439
|
+
if (filters.status)
|
|
440
|
+
params.status = filters.status;
|
|
441
|
+
if (filters.users)
|
|
442
|
+
params.users = filters.users;
|
|
443
|
+
if (filters.start_date)
|
|
444
|
+
params.start_date = filters.start_date;
|
|
445
|
+
if (filters.due_date)
|
|
446
|
+
params.due_date = filters.due_date;
|
|
447
|
+
if (filters.created_at)
|
|
448
|
+
params.created_at = filters.created_at;
|
|
449
|
+
if (filters.closed_at)
|
|
450
|
+
params.closed_at = filters.closed_at;
|
|
451
|
+
if (filters.is_blocker)
|
|
452
|
+
params.is_blocker = 1;
|
|
453
|
+
if (filters.is_bug)
|
|
454
|
+
params.is_bug = 1;
|
|
455
|
+
if (filters.unassigned)
|
|
456
|
+
params.unassigned = 1;
|
|
457
|
+
if (filters.is_archived)
|
|
458
|
+
params.is_archived = 1;
|
|
459
|
+
if (filters.workflow)
|
|
460
|
+
params.workflows = filters.workflow;
|
|
461
|
+
if (filters.labels)
|
|
462
|
+
params.labels = filters.labels;
|
|
463
|
+
if (filters.type)
|
|
464
|
+
params.types = filters.type;
|
|
465
|
+
if (filters.effort)
|
|
466
|
+
params.efforts = filters.effort;
|
|
467
|
+
if (filters.sprint)
|
|
468
|
+
params.sprints = filters.sprint;
|
|
469
|
+
if (filters.user_story)
|
|
470
|
+
params.user_stories = filters.user_story;
|
|
471
|
+
return this.get("tasks", params);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Create a new task
|
|
475
|
+
*
|
|
476
|
+
* @param data - Task creation data
|
|
477
|
+
* @param data.title - Task title (required)
|
|
478
|
+
* @param data.project_slug - Project slug (required)
|
|
479
|
+
* @param data.company_slug - Workspace slug (required)
|
|
480
|
+
* @param data.description - Task description
|
|
481
|
+
* @param data.workflow_id - Workflow/status ID (use project_get_workflows to discover)
|
|
482
|
+
* @param data.effort_id - Effort/priority ID (use project_get_efforts to discover)
|
|
483
|
+
* @param data.type_id - Task type ID (use project_get_types to discover)
|
|
484
|
+
* @param data.usernames - Array of usernames to assign (use project_get_members to discover)
|
|
485
|
+
* @param data.label_ids - Array of label IDs (use project_get_labels to discover)
|
|
486
|
+
* @param data.board_id - Board UUID (from project boards)
|
|
487
|
+
* @param data.due_date - Due date (YYYY-MM-DD)
|
|
488
|
+
* @param data.start_date - Start date (YYYY-MM-DD)
|
|
489
|
+
* @param data.estimated_minutes - Time estimate in minutes
|
|
490
|
+
* @param data.sprint_slug - Sprint slug to add task to
|
|
491
|
+
* @param data.user_story_slug - User story slug
|
|
492
|
+
* @param data.parent_id - Parent task UUID for subtasks
|
|
493
|
+
*/
|
|
494
|
+
async createTask(data) {
|
|
495
|
+
const response = await this.post("tasks", data);
|
|
496
|
+
return response.data;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Update a task
|
|
500
|
+
*/
|
|
501
|
+
async updateTask(uuid, data) {
|
|
502
|
+
const response = await this.put(`tasks/${uuid}`, data);
|
|
503
|
+
return response.data;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Complete a task
|
|
507
|
+
*/
|
|
508
|
+
async completeTask(uuid) {
|
|
509
|
+
const response = await this.put(`tasks/${uuid}/complete`, {});
|
|
510
|
+
return response;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Get sub-tasks
|
|
514
|
+
*/
|
|
515
|
+
async getSubTasks(taskUuid) {
|
|
516
|
+
const response = await this.get(`tasks/${taskUuid}/sub-tasks`);
|
|
517
|
+
return response.data;
|
|
518
|
+
}
|
|
519
|
+
// ============================================================================
|
|
520
|
+
// SPRINTS
|
|
521
|
+
// ============================================================================
|
|
522
|
+
/**
|
|
523
|
+
* Get sprints in a project (with pagination metadata)
|
|
524
|
+
*/
|
|
525
|
+
async getSprints(projectSlug, companySlug) {
|
|
526
|
+
const response = await this.get("sprints", {
|
|
527
|
+
project_slug: projectSlug,
|
|
528
|
+
company_slug: companySlug,
|
|
529
|
+
});
|
|
530
|
+
return response;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Get all sprints across workspaces (with pagination metadata)
|
|
534
|
+
*/
|
|
535
|
+
async getAllSprints() {
|
|
536
|
+
const response = await this.get("sprints/all-workspaces");
|
|
537
|
+
return response;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Get sprint details
|
|
541
|
+
*/
|
|
542
|
+
async getSprint(slug, projectSlug, companySlug) {
|
|
543
|
+
const response = await this.get(`sprints/${slug}`, {
|
|
544
|
+
project_slug: projectSlug,
|
|
545
|
+
company_slug: companySlug,
|
|
546
|
+
});
|
|
547
|
+
return response.data;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Get sprint KPIs
|
|
551
|
+
*/
|
|
552
|
+
async getSprintKPIs(slug, projectSlug, companySlug) {
|
|
553
|
+
const response = await this.get(`sprints/${slug}/kpis`, {
|
|
554
|
+
project_slug: projectSlug,
|
|
555
|
+
company_slug: companySlug,
|
|
556
|
+
});
|
|
557
|
+
return response.data;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Create a new sprint
|
|
561
|
+
*/
|
|
562
|
+
async createSprint(projectSlug, companySlug, data) {
|
|
563
|
+
const response = await this.post(`sprints?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
564
|
+
return response.data;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Update an existing sprint
|
|
568
|
+
*/
|
|
569
|
+
async updateSprint(slug, projectSlug, companySlug, data) {
|
|
570
|
+
await this.put(`sprints/${slug}?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
571
|
+
}
|
|
572
|
+
// ============================================================================
|
|
573
|
+
// TIME TRACKING
|
|
574
|
+
// ============================================================================
|
|
575
|
+
/**
|
|
576
|
+
* Get active timer
|
|
577
|
+
*/
|
|
578
|
+
async getActiveTimer(companySlug) {
|
|
579
|
+
const params = {};
|
|
580
|
+
if (companySlug)
|
|
581
|
+
params.company_slug = companySlug;
|
|
582
|
+
const response = await this.get("time-trackings/active", params);
|
|
583
|
+
return response.data;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Start timer for a task
|
|
587
|
+
*/
|
|
588
|
+
async startTimer(taskUuid, description) {
|
|
589
|
+
const response = await this.post("time-trackings", {
|
|
590
|
+
task_uuid: taskUuid,
|
|
591
|
+
work_description: description,
|
|
592
|
+
});
|
|
593
|
+
return response.data;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Stop timer
|
|
597
|
+
*
|
|
598
|
+
* @param timeTrackingId - ID of the time tracker to stop
|
|
599
|
+
*/
|
|
600
|
+
async stopTimer(timeTrackingId) {
|
|
601
|
+
const response = await this.put(`time-trackings/${timeTrackingId}`, {
|
|
602
|
+
end: new Date().toISOString(),
|
|
603
|
+
});
|
|
604
|
+
return response.data;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Get time logs for a project
|
|
608
|
+
*/
|
|
609
|
+
async getTimeLogs(projectSlug, companySlug) {
|
|
610
|
+
const response = await this.get("time-trackings", {
|
|
611
|
+
project_slug: projectSlug,
|
|
612
|
+
company_slug: companySlug,
|
|
613
|
+
});
|
|
614
|
+
return response.data;
|
|
615
|
+
}
|
|
616
|
+
// ============================================================================
|
|
617
|
+
// USER STORIES
|
|
618
|
+
// ============================================================================
|
|
619
|
+
/**
|
|
620
|
+
* Get user stories in a project (with pagination metadata)
|
|
621
|
+
*/
|
|
622
|
+
async getUserStories(projectSlug, companySlug) {
|
|
623
|
+
const response = await this.get("user-stories", {
|
|
624
|
+
project_slug: projectSlug,
|
|
625
|
+
company_slug: companySlug,
|
|
626
|
+
});
|
|
627
|
+
return response;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Get user story details
|
|
631
|
+
*/
|
|
632
|
+
async getUserStory(slug, projectSlug, companySlug) {
|
|
633
|
+
const response = await this.get(`user-stories/${slug}`, {
|
|
634
|
+
project_slug: projectSlug,
|
|
635
|
+
company_slug: companySlug,
|
|
636
|
+
});
|
|
637
|
+
return response.data;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Create user story
|
|
641
|
+
*/
|
|
642
|
+
async createUserStory(data) {
|
|
643
|
+
const response = await this.post("user-stories", data);
|
|
644
|
+
return response.data;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Update user story
|
|
648
|
+
*/
|
|
649
|
+
async updateUserStory(slug, data) {
|
|
650
|
+
const response = await this.put(`user-stories/${slug}`, data);
|
|
651
|
+
return response;
|
|
652
|
+
}
|
|
653
|
+
// ============================================================================
|
|
654
|
+
// WIKI
|
|
655
|
+
// ============================================================================
|
|
656
|
+
/**
|
|
657
|
+
* Get wiki pages in a project
|
|
658
|
+
*/
|
|
659
|
+
async getWikiPages(projectSlug, companySlug) {
|
|
660
|
+
const response = await this.get("wiki/pages", {
|
|
661
|
+
project_slug: projectSlug,
|
|
662
|
+
company_slug: companySlug,
|
|
663
|
+
});
|
|
664
|
+
return response.data;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Get wiki page content
|
|
668
|
+
*/
|
|
669
|
+
async getWikiPage(uuid, projectSlug, companySlug) {
|
|
670
|
+
const response = await this.get(`wiki/pages/${uuid}`, {
|
|
671
|
+
project_slug: projectSlug,
|
|
672
|
+
company_slug: companySlug,
|
|
673
|
+
});
|
|
674
|
+
return response.data;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Create wiki page
|
|
678
|
+
*/
|
|
679
|
+
async createWikiPage(data) {
|
|
680
|
+
const response = await this.post("wiki/pages", data);
|
|
681
|
+
return response.data;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Update wiki page
|
|
685
|
+
*/
|
|
686
|
+
async updateWikiPage(uuid, projectSlug, companySlug, data) {
|
|
687
|
+
const response = await this.put(`wiki/pages/${uuid}`, {
|
|
688
|
+
...data,
|
|
689
|
+
project_slug: projectSlug,
|
|
690
|
+
company_slug: companySlug,
|
|
691
|
+
});
|
|
692
|
+
return response.data;
|
|
693
|
+
}
|
|
694
|
+
// ============================================================================
|
|
695
|
+
// SEARCH
|
|
696
|
+
// ============================================================================
|
|
697
|
+
/**
|
|
698
|
+
* Global search across all entities
|
|
699
|
+
*
|
|
700
|
+
* @param query - Search query string (min 2 chars)
|
|
701
|
+
* @param options - Search options
|
|
702
|
+
* @param options.company_slug - Optional workspace to search in (searches all if omitted)
|
|
703
|
+
* @param options.categories - Comma-separated categories: tasks,projects,user_stories,sprints,wiki,notes
|
|
704
|
+
* @param options.limit - Max results per category (default 5)
|
|
705
|
+
*/
|
|
706
|
+
async search(query, options) {
|
|
707
|
+
const params = {
|
|
708
|
+
q: query,
|
|
709
|
+
};
|
|
710
|
+
if (options?.company_slug) {
|
|
711
|
+
params.company_slug = options.company_slug;
|
|
712
|
+
}
|
|
713
|
+
if (options?.categories) {
|
|
714
|
+
params.categories = options.categories;
|
|
715
|
+
}
|
|
716
|
+
if (options?.limit) {
|
|
717
|
+
params.limit = options.limit;
|
|
718
|
+
}
|
|
719
|
+
const response = await this.get("search", params);
|
|
720
|
+
return response.data;
|
|
721
|
+
}
|
|
722
|
+
// ============================================================================
|
|
723
|
+
// NOTEVAULT - NOTES
|
|
724
|
+
// ============================================================================
|
|
725
|
+
/**
|
|
726
|
+
* Get all notes for the current user
|
|
727
|
+
*/
|
|
728
|
+
async getNotes(companySlug, options) {
|
|
729
|
+
const params = {};
|
|
730
|
+
if (companySlug)
|
|
731
|
+
params.company_slug = companySlug;
|
|
732
|
+
if (options?.folder_uuid)
|
|
733
|
+
params.folder_uuid = options.folder_uuid;
|
|
734
|
+
if (options?.color)
|
|
735
|
+
params.color = options.color;
|
|
736
|
+
if (options?.search)
|
|
737
|
+
params.search = options.search;
|
|
738
|
+
const response = await this.get("notes", params);
|
|
739
|
+
return response.data;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Get a specific note by UUID
|
|
743
|
+
*/
|
|
744
|
+
async getNote(uuid, companySlug) {
|
|
745
|
+
const params = {};
|
|
746
|
+
if (companySlug)
|
|
747
|
+
params.company_slug = companySlug;
|
|
748
|
+
const response = await this.get(`notes/${uuid}`, params);
|
|
749
|
+
return response.data;
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Create a new note
|
|
753
|
+
*/
|
|
754
|
+
async createNote(data) {
|
|
755
|
+
const response = await this.post("notes", data);
|
|
756
|
+
return response.data;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Update an existing note
|
|
760
|
+
*/
|
|
761
|
+
async updateNote(uuid, data) {
|
|
762
|
+
const response = await this.put(`notes/${uuid}`, data);
|
|
763
|
+
return response.data;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Toggle note sharing (enable/disable public link)
|
|
767
|
+
*/
|
|
768
|
+
async toggleNoteShare(uuid) {
|
|
769
|
+
const response = await this.put(`notes/${uuid}/share/toggle`, {});
|
|
770
|
+
return response.data;
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Get note revision history
|
|
774
|
+
*/
|
|
775
|
+
async getNoteRevisions(uuid, companySlug) {
|
|
776
|
+
const response = await this.get(`notes/${uuid}/revisions`, {
|
|
777
|
+
company_slug: companySlug,
|
|
778
|
+
});
|
|
779
|
+
return response.data;
|
|
780
|
+
}
|
|
781
|
+
// ============================================================================
|
|
782
|
+
// NOTEVAULT - FOLDERS
|
|
783
|
+
// ============================================================================
|
|
784
|
+
/**
|
|
785
|
+
* Get all note folders
|
|
786
|
+
*/
|
|
787
|
+
async getNoteFolders(companySlug) {
|
|
788
|
+
const response = await this.get("note-folders", {
|
|
789
|
+
company_slug: companySlug,
|
|
790
|
+
});
|
|
791
|
+
return response.data;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Create a new note folder
|
|
795
|
+
*/
|
|
796
|
+
async createNoteFolder(data) {
|
|
797
|
+
const response = await this.post("note-folders", data);
|
|
798
|
+
return response.data;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Update a note folder
|
|
802
|
+
*/
|
|
803
|
+
async updateNoteFolder(uuid, data) {
|
|
804
|
+
const response = await this.put(`note-folders/${uuid}`, data);
|
|
805
|
+
return response.data;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Move a note to a folder
|
|
809
|
+
*/
|
|
810
|
+
async moveNoteToFolder(noteUuid, folderUuid) {
|
|
811
|
+
const response = await this.post("note-folders/move-note", {
|
|
812
|
+
note_uuid: noteUuid,
|
|
813
|
+
folder_uuid: folderUuid,
|
|
814
|
+
});
|
|
815
|
+
return response;
|
|
816
|
+
}
|
|
817
|
+
// ============================================================================
|
|
818
|
+
// CLIENTFLOW - CLIENTS
|
|
819
|
+
// ============================================================================
|
|
820
|
+
/**
|
|
821
|
+
* Get all clients
|
|
822
|
+
*/
|
|
823
|
+
async getClients(companySlug) {
|
|
824
|
+
const response = await this.get("contact-companies/clients", {
|
|
825
|
+
company_slug: companySlug,
|
|
826
|
+
});
|
|
827
|
+
return response.data;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Get client details
|
|
831
|
+
*/
|
|
832
|
+
async getClient(uuid, companySlug) {
|
|
833
|
+
const response = await this.get(`contact-companies/${uuid}`, {
|
|
834
|
+
company_slug: companySlug,
|
|
835
|
+
});
|
|
836
|
+
return response.data;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Get client statistics
|
|
840
|
+
*/
|
|
841
|
+
async getClientStats(uuid, companySlug) {
|
|
842
|
+
const response = await this.get(`contact-companies/${uuid}/stats`, {
|
|
843
|
+
company_slug: companySlug,
|
|
844
|
+
});
|
|
845
|
+
return response.data;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Create a new client
|
|
849
|
+
*/
|
|
850
|
+
async createClient(data) {
|
|
851
|
+
const response = await this.post("contact-companies", data);
|
|
852
|
+
return response.data;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Update a client
|
|
856
|
+
*/
|
|
857
|
+
async updateClient(uuid, data) {
|
|
858
|
+
const response = await this.put(`contact-companies/${uuid}`, data);
|
|
859
|
+
return response.data;
|
|
860
|
+
}
|
|
861
|
+
// ============================================================================
|
|
862
|
+
// CLIENTFLOW - INVOICES
|
|
863
|
+
// ============================================================================
|
|
864
|
+
/**
|
|
865
|
+
* Get all invoices
|
|
866
|
+
*/
|
|
867
|
+
async getInvoices(companySlug, options) {
|
|
868
|
+
const response = await this.get("company-invoices", {
|
|
869
|
+
company_slug: companySlug,
|
|
870
|
+
...options,
|
|
871
|
+
});
|
|
872
|
+
return response.data;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Get invoice details
|
|
876
|
+
*/
|
|
877
|
+
async getInvoice(uuid, companySlug) {
|
|
878
|
+
const response = await this.get(`company-invoices/${uuid}`, {
|
|
879
|
+
company_slug: companySlug,
|
|
880
|
+
});
|
|
881
|
+
return response.data;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Get invoice statistics
|
|
885
|
+
*/
|
|
886
|
+
async getInvoiceStats(companySlug) {
|
|
887
|
+
const response = await this.get("company-invoices/stats", {
|
|
888
|
+
company_slug: companySlug,
|
|
889
|
+
});
|
|
890
|
+
return response.data;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Create an invoice
|
|
894
|
+
*/
|
|
895
|
+
async createInvoice(data) {
|
|
896
|
+
const response = await this.post("company-invoices", data);
|
|
897
|
+
return response.data;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Update an invoice
|
|
901
|
+
*/
|
|
902
|
+
async updateInvoice(uuid, data) {
|
|
903
|
+
const response = await this.put(`company-invoices/${uuid}`, data);
|
|
904
|
+
return response.data;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Issue/publish an invoice
|
|
908
|
+
*/
|
|
909
|
+
async issueInvoice(uuid) {
|
|
910
|
+
const response = await this.post(`company-invoices/${uuid}/issue`, {});
|
|
911
|
+
return response.data;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Send invoice to client
|
|
915
|
+
*/
|
|
916
|
+
async sendInvoice(uuid) {
|
|
917
|
+
const response = await this.post(`company-invoices/${uuid}/send`, {});
|
|
918
|
+
return response.data;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Mark invoice as paid
|
|
922
|
+
*/
|
|
923
|
+
async markInvoicePaid(uuid) {
|
|
924
|
+
const response = await this.post(`company-invoices/${uuid}/paid`, {});
|
|
925
|
+
return response.data;
|
|
926
|
+
}
|
|
927
|
+
// ============================================================================
|
|
928
|
+
// CLIENTFLOW - PROPOSALS
|
|
929
|
+
// ============================================================================
|
|
930
|
+
/**
|
|
931
|
+
* Get all proposals
|
|
932
|
+
*/
|
|
933
|
+
async getProposals(companySlug, options) {
|
|
934
|
+
const response = await this.get("proposals", {
|
|
935
|
+
company_slug: companySlug,
|
|
936
|
+
...options,
|
|
937
|
+
});
|
|
938
|
+
return response.data;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Get proposal details
|
|
942
|
+
*/
|
|
943
|
+
async getProposal(uuid, companySlug) {
|
|
944
|
+
const response = await this.get(`proposals/${uuid}`, {
|
|
945
|
+
company_slug: companySlug,
|
|
946
|
+
});
|
|
947
|
+
return response.data;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Get proposal statistics
|
|
951
|
+
*/
|
|
952
|
+
async getProposalStats(companySlug) {
|
|
953
|
+
const response = await this.get("proposals/stats", {
|
|
954
|
+
company_slug: companySlug,
|
|
955
|
+
});
|
|
956
|
+
return response.data;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Create a proposal
|
|
960
|
+
*/
|
|
961
|
+
async createProposal(data) {
|
|
962
|
+
const response = await this.post("proposals", data);
|
|
963
|
+
return response.data;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Update a proposal
|
|
967
|
+
*/
|
|
968
|
+
async updateProposal(uuid, data) {
|
|
969
|
+
const response = await this.put(`proposals/${uuid}`, data);
|
|
970
|
+
return response.data;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Send proposal to client
|
|
974
|
+
*/
|
|
975
|
+
async sendProposal(uuid) {
|
|
976
|
+
const response = await this.post(`proposals/${uuid}/send`, {});
|
|
977
|
+
return response.data;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Approve a proposal
|
|
981
|
+
*/
|
|
982
|
+
async approveProposal(uuid) {
|
|
983
|
+
const response = await this.post(`proposals/${uuid}/approve`, {});
|
|
984
|
+
return response.data;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Reject a proposal
|
|
988
|
+
*/
|
|
989
|
+
async rejectProposal(uuid, reason) {
|
|
990
|
+
const response = await this.post(`proposals/${uuid}/reject`, { reason });
|
|
991
|
+
return response.data;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Convert proposal to project
|
|
995
|
+
*/
|
|
996
|
+
async convertProposalToProject(uuid) {
|
|
997
|
+
const response = await this.post(`proposals/${uuid}/convert-to-project`, {});
|
|
998
|
+
return response.data;
|
|
999
|
+
}
|
|
1000
|
+
// ============================================================================
|
|
1001
|
+
// CLIENTFLOW - DASHBOARD
|
|
1002
|
+
// ============================================================================
|
|
1003
|
+
/**
|
|
1004
|
+
* Get ClientFlow dashboard overview
|
|
1005
|
+
*/
|
|
1006
|
+
async getClientFlowOverview(companySlug) {
|
|
1007
|
+
const response = await this.get("client-flow/dashboard/overview", {
|
|
1008
|
+
company_slug: companySlug,
|
|
1009
|
+
});
|
|
1010
|
+
return response.data;
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Get revenue pipeline
|
|
1014
|
+
*/
|
|
1015
|
+
async getRevenuePipeline(companySlug) {
|
|
1016
|
+
const response = await this.get("client-flow/dashboard/revenue-pipeline", {
|
|
1017
|
+
company_slug: companySlug,
|
|
1018
|
+
});
|
|
1019
|
+
return response.data;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Get clients at risk
|
|
1023
|
+
*/
|
|
1024
|
+
async getClientsAtRisk(companySlug) {
|
|
1025
|
+
const response = await this.get("client-flow/dashboard/clients-at-risk", {
|
|
1026
|
+
company_slug: companySlug,
|
|
1027
|
+
});
|
|
1028
|
+
return response.data;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Get pending approvals
|
|
1032
|
+
*/
|
|
1033
|
+
async getPendingApprovals(companySlug) {
|
|
1034
|
+
const response = await this.get("client-flow/dashboard/pending-approvals", {
|
|
1035
|
+
company_slug: companySlug,
|
|
1036
|
+
});
|
|
1037
|
+
return response.data;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Get projects health
|
|
1041
|
+
*/
|
|
1042
|
+
async getProjectsHealth(companySlug) {
|
|
1043
|
+
const response = await this.get("client-flow/dashboard/projects-health", {
|
|
1044
|
+
company_slug: companySlug,
|
|
1045
|
+
});
|
|
1046
|
+
return response.data;
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Get actionable insights
|
|
1050
|
+
*/
|
|
1051
|
+
async getActionableInsights(companySlug) {
|
|
1052
|
+
const response = await this.get("client-flow/dashboard/insights", {
|
|
1053
|
+
company_slug: companySlug,
|
|
1054
|
+
});
|
|
1055
|
+
return response.data;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Get client leaderboard
|
|
1059
|
+
*/
|
|
1060
|
+
async getClientLeaderboard(companySlug) {
|
|
1061
|
+
const response = await this.get("client-flow/dashboard/leaderboard", {
|
|
1062
|
+
company_slug: companySlug,
|
|
1063
|
+
});
|
|
1064
|
+
return response.data;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Get analytics
|
|
1068
|
+
*/
|
|
1069
|
+
async getClientFlowAnalytics(companySlug) {
|
|
1070
|
+
const response = await this.get("client-flow/dashboard/analytics", {
|
|
1071
|
+
company_slug: companySlug,
|
|
1072
|
+
});
|
|
1073
|
+
return response.data;
|
|
1074
|
+
}
|
|
1075
|
+
// ============================================================================
|
|
1076
|
+
// CLIENTFLOW - CROSS-WORKSPACE (all workspaces owned by the user)
|
|
1077
|
+
// ============================================================================
|
|
1078
|
+
/**
|
|
1079
|
+
* Get invoices overview across all workspaces
|
|
1080
|
+
*/
|
|
1081
|
+
async getCrossWorkspaceInvoices(perPage, page) {
|
|
1082
|
+
const params = {};
|
|
1083
|
+
if (perPage)
|
|
1084
|
+
params.per_page = perPage;
|
|
1085
|
+
if (page)
|
|
1086
|
+
params.page = page;
|
|
1087
|
+
const response = await this.get("client-flow/all-workspaces/invoices", params);
|
|
1088
|
+
return response;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Get proposals overview across all workspaces
|
|
1092
|
+
*/
|
|
1093
|
+
async getCrossWorkspaceProposals(perPage, page) {
|
|
1094
|
+
const params = {};
|
|
1095
|
+
if (perPage)
|
|
1096
|
+
params.per_page = perPage;
|
|
1097
|
+
if (page)
|
|
1098
|
+
params.page = page;
|
|
1099
|
+
const response = await this.get("client-flow/all-workspaces/proposals", params);
|
|
1100
|
+
return response;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Get clients overview across all workspaces
|
|
1104
|
+
*/
|
|
1105
|
+
async getCrossWorkspaceClients(perPage, page) {
|
|
1106
|
+
const params = {};
|
|
1107
|
+
if (perPage)
|
|
1108
|
+
params.per_page = perPage;
|
|
1109
|
+
if (page)
|
|
1110
|
+
params.page = page;
|
|
1111
|
+
const response = await this.get("client-flow/all-workspaces/clients", params);
|
|
1112
|
+
return response;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Get change requests overview across all workspaces
|
|
1116
|
+
*/
|
|
1117
|
+
async getCrossWorkspaceChangeRequests(perPage, page) {
|
|
1118
|
+
const params = {};
|
|
1119
|
+
if (perPage)
|
|
1120
|
+
params.per_page = perPage;
|
|
1121
|
+
if (page)
|
|
1122
|
+
params.page = page;
|
|
1123
|
+
const response = await this.get("client-flow/all-workspaces/change-requests", params);
|
|
1124
|
+
return response;
|
|
1125
|
+
}
|
|
1126
|
+
// ============================================
|
|
1127
|
+
// LABEL MANAGEMENT
|
|
1128
|
+
// ============================================
|
|
1129
|
+
/**
|
|
1130
|
+
* Get all labels in a workspace
|
|
1131
|
+
*/
|
|
1132
|
+
async getWorkspaceLabels(companySlug) {
|
|
1133
|
+
const response = await this.get("projects-labels", {
|
|
1134
|
+
company_slug: companySlug,
|
|
1135
|
+
});
|
|
1136
|
+
return response.data;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Create a new label in the workspace
|
|
1140
|
+
*/
|
|
1141
|
+
async createLabel(companySlug, data) {
|
|
1142
|
+
const response = await this.post(`projects-labels?company_slug=${companySlug}`, data);
|
|
1143
|
+
return response.data;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Update a label
|
|
1147
|
+
*/
|
|
1148
|
+
async updateLabel(labelSlug, companySlug, data) {
|
|
1149
|
+
await this.put(`projects-labels/${labelSlug}?company_slug=${companySlug}`, data);
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Attach a label to a project
|
|
1153
|
+
*/
|
|
1154
|
+
async attachLabelToProject(labelSlug, projectSlug, companySlug) {
|
|
1155
|
+
await this.post(`projects-labels/${labelSlug}/attach?company_slug=${companySlug}&project_slug=${projectSlug}`, {
|
|
1156
|
+
slug: labelSlug,
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Detach a label from a project
|
|
1161
|
+
*/
|
|
1162
|
+
async detachLabelFromProject(labelSlug, projectSlug, companySlug) {
|
|
1163
|
+
await this.delete(`projects-labels/${labelSlug}/detach?company_slug=${companySlug}&project_slug=${projectSlug}`);
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Toggle a label on a task
|
|
1167
|
+
*/
|
|
1168
|
+
async toggleLabelOnTask(taskUuid, labelSlug, projectSlug, companySlug) {
|
|
1169
|
+
const response = await this.post(`task-labels/${labelSlug}/toggle?company_slug=${companySlug}&project_slug=${projectSlug}&task_uuid=${taskUuid}`, {});
|
|
1170
|
+
return response;
|
|
1171
|
+
}
|
|
1172
|
+
// ============================================
|
|
1173
|
+
// TASK TYPE MANAGEMENT
|
|
1174
|
+
// ============================================
|
|
1175
|
+
/**
|
|
1176
|
+
* Create a new task type
|
|
1177
|
+
*/
|
|
1178
|
+
async createTaskType(projectSlug, companySlug, data) {
|
|
1179
|
+
const response = await this.post(`project-templates/type?company_slug=${companySlug}&project_slug=${projectSlug}`, {
|
|
1180
|
+
...data,
|
|
1181
|
+
type: "issues",
|
|
1182
|
+
});
|
|
1183
|
+
return response;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Update a task type
|
|
1187
|
+
*/
|
|
1188
|
+
async updateTaskType(typeId, projectSlug, companySlug, data) {
|
|
1189
|
+
await this.put(`project-templates/type/${typeId}?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Assign a type to a task
|
|
1193
|
+
*/
|
|
1194
|
+
async assignTypeToTask(taskUuid, typeId, projectSlug, companySlug) {
|
|
1195
|
+
const response = await this.put(`tasks/${taskUuid}?company_slug=${companySlug}&project_slug=${projectSlug}`, {
|
|
1196
|
+
config_issue_type_id: typeId,
|
|
1197
|
+
});
|
|
1198
|
+
return response.data;
|
|
1199
|
+
}
|
|
1200
|
+
// ============================================================================
|
|
1201
|
+
// STANDUP / DAILY
|
|
1202
|
+
// ============================================================================
|
|
1203
|
+
/**
|
|
1204
|
+
* Get team standup summary - completed yesterday, in progress, blocked, time tracked
|
|
1205
|
+
*/
|
|
1206
|
+
async getStandupSummary(companySlug, projectSlug) {
|
|
1207
|
+
const params = { company_slug: companySlug };
|
|
1208
|
+
if (projectSlug)
|
|
1209
|
+
params.project_slug = projectSlug;
|
|
1210
|
+
const response = await this.get("companies/standup/summary", params);
|
|
1211
|
+
return response.data;
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Get tasks completed on a specific date (yesterday by default)
|
|
1215
|
+
*/
|
|
1216
|
+
async getStandupCompletedYesterday(companySlug, projectSlug, date) {
|
|
1217
|
+
const params = { company_slug: companySlug };
|
|
1218
|
+
if (projectSlug)
|
|
1219
|
+
params.project_slug = projectSlug;
|
|
1220
|
+
if (date)
|
|
1221
|
+
params.date = date;
|
|
1222
|
+
const response = await this.get("companies/standup/completed-yesterday", params);
|
|
1223
|
+
return response.data;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Get active blockers - tasks that are currently blocked
|
|
1227
|
+
*/
|
|
1228
|
+
async getStandupBlockers(companySlug, projectSlug) {
|
|
1229
|
+
const params = { company_slug: companySlug };
|
|
1230
|
+
if (projectSlug)
|
|
1231
|
+
params.project_slug = projectSlug;
|
|
1232
|
+
const response = await this.get("companies/standup/blockers", params);
|
|
1233
|
+
return response.data;
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Get team status - current status of each team member with their active tasks
|
|
1237
|
+
*/
|
|
1238
|
+
async getStandupTeamStatus(companySlug, projectSlug) {
|
|
1239
|
+
const params = { company_slug: companySlug };
|
|
1240
|
+
if (projectSlug)
|
|
1241
|
+
params.project_slug = projectSlug;
|
|
1242
|
+
const response = await this.get("companies/standup/team-status", params);
|
|
1243
|
+
return response.data;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Get stuck tasks - tasks in progress for longer than expected
|
|
1247
|
+
*/
|
|
1248
|
+
async getStandupStuckTasks(companySlug, projectSlug) {
|
|
1249
|
+
const params = { company_slug: companySlug };
|
|
1250
|
+
if (projectSlug)
|
|
1251
|
+
params.project_slug = projectSlug;
|
|
1252
|
+
const response = await this.get("companies/standup/stuck-tasks", params);
|
|
1253
|
+
return response.data;
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Get weekly digest - summary of the week's activity
|
|
1257
|
+
*/
|
|
1258
|
+
async getStandupWeeklyDigest(companySlug, projectSlug) {
|
|
1259
|
+
const params = { company_slug: companySlug };
|
|
1260
|
+
if (projectSlug)
|
|
1261
|
+
params.project_slug = projectSlug;
|
|
1262
|
+
const response = await this.get("companies/standup/weekly-digest", params);
|
|
1263
|
+
return response.data;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Get contributors stats - team member contributions over time
|
|
1267
|
+
*/
|
|
1268
|
+
async getStandupContributors(companySlug, projectSlug, period) {
|
|
1269
|
+
const params = { company_slug: companySlug };
|
|
1270
|
+
if (projectSlug)
|
|
1271
|
+
params.project_slug = projectSlug;
|
|
1272
|
+
if (period)
|
|
1273
|
+
params.period = period;
|
|
1274
|
+
const response = await this.get("companies/standup/contributors", params);
|
|
1275
|
+
return response.data;
|
|
1276
|
+
}
|
|
1277
|
+
// ============================================================================
|
|
1278
|
+
// COMMENTS
|
|
1279
|
+
// ============================================================================
|
|
1280
|
+
/**
|
|
1281
|
+
* Get comments for a task
|
|
1282
|
+
*/
|
|
1283
|
+
async getTaskComments(taskUuid, companySlug, projectSlug) {
|
|
1284
|
+
const response = await this.get("comments", {
|
|
1285
|
+
commentable_id: taskUuid,
|
|
1286
|
+
commentable_type: "issues",
|
|
1287
|
+
company_slug: companySlug,
|
|
1288
|
+
project_slug: projectSlug,
|
|
1289
|
+
});
|
|
1290
|
+
return response.data;
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Add a comment to a task
|
|
1294
|
+
*/
|
|
1295
|
+
async addTaskComment(taskUuid, text, companySlug, projectSlug) {
|
|
1296
|
+
const response = await this.post(`comments?company_slug=${companySlug}&project_slug=${projectSlug}`, {
|
|
1297
|
+
commentable_id: taskUuid,
|
|
1298
|
+
commentable_type: "issues",
|
|
1299
|
+
comment_text: text,
|
|
1300
|
+
});
|
|
1301
|
+
return response.data;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Update a comment
|
|
1305
|
+
*/
|
|
1306
|
+
async updateComment(commentId, text) {
|
|
1307
|
+
await this.put(`comments/${commentId}`, { comment_text: text });
|
|
1308
|
+
}
|
|
1309
|
+
// ============================================================================
|
|
1310
|
+
// ANALYTICS / MANAGER DASHBOARD
|
|
1311
|
+
// ============================================================================
|
|
1312
|
+
/**
|
|
1313
|
+
* Get manager pulse - real-time workspace health metrics
|
|
1314
|
+
*/
|
|
1315
|
+
async getManagerPulse(companySlug, view, period) {
|
|
1316
|
+
const params = { company_slug: companySlug };
|
|
1317
|
+
if (view)
|
|
1318
|
+
params.view = view;
|
|
1319
|
+
if (period)
|
|
1320
|
+
params.period = period;
|
|
1321
|
+
const response = await this.get("companies/manager-dashboard/pulse", params);
|
|
1322
|
+
return response.data;
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Get manager risks - risk detection and analysis
|
|
1326
|
+
*/
|
|
1327
|
+
async getManagerRisks(companySlug, filter, severity) {
|
|
1328
|
+
const params = { company_slug: companySlug };
|
|
1329
|
+
if (filter)
|
|
1330
|
+
params.filter = filter;
|
|
1331
|
+
if (severity)
|
|
1332
|
+
params.severity = severity;
|
|
1333
|
+
const response = await this.get("companies/manager-dashboard/risks", params);
|
|
1334
|
+
return response.data;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Get cumulative flow report - daily snapshot of tasks by status
|
|
1338
|
+
*/
|
|
1339
|
+
async getReportsCumulativeFlow(companySlug, days) {
|
|
1340
|
+
const params = { company_slug: companySlug };
|
|
1341
|
+
if (days)
|
|
1342
|
+
params.days = days;
|
|
1343
|
+
const response = await this.get("companies/reports/cumulative-flow", params);
|
|
1344
|
+
return response.data;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Get project age report - project age vs completion percentage
|
|
1348
|
+
*/
|
|
1349
|
+
async getReportsProjectAge(companySlug) {
|
|
1350
|
+
const response = await this.get("companies/reports/project-age", {
|
|
1351
|
+
company_slug: companySlug,
|
|
1352
|
+
});
|
|
1353
|
+
return response.data;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Get weekly activity report - activity by project over last 5 weeks
|
|
1357
|
+
*/
|
|
1358
|
+
async getReportsWeeklyActivity(companySlug) {
|
|
1359
|
+
const response = await this.get("companies/reports/weekly-activity", {
|
|
1360
|
+
company_slug: companySlug,
|
|
1361
|
+
});
|
|
1362
|
+
return response.data;
|
|
1363
|
+
}
|
|
1364
|
+
// ============================================================================
|
|
1365
|
+
// USER STORIES - CROSS-WORKSPACE
|
|
1366
|
+
// ============================================================================
|
|
1367
|
+
/**
|
|
1368
|
+
* Get all user stories across all workspaces
|
|
1369
|
+
*/
|
|
1370
|
+
async getAllWorkspacesUserStories(perPage, page) {
|
|
1371
|
+
const params = {};
|
|
1372
|
+
if (perPage)
|
|
1373
|
+
params.per_page = perPage;
|
|
1374
|
+
if (page)
|
|
1375
|
+
params.page = page;
|
|
1376
|
+
const response = await this.get("user-stories/all-workspaces", params);
|
|
1377
|
+
return response;
|
|
1378
|
+
}
|
|
1379
|
+
// ============================================================================
|
|
1380
|
+
// SPRINT - STATS / REPORTS / PROGRESS / METRICS
|
|
1381
|
+
// ============================================================================
|
|
1382
|
+
/**
|
|
1383
|
+
* Get sprint statistics
|
|
1384
|
+
*/
|
|
1385
|
+
async getSprintStats(slug, projectSlug, companySlug) {
|
|
1386
|
+
const response = await this.get(`sprints/${slug}/stats`, {
|
|
1387
|
+
project_slug: projectSlug,
|
|
1388
|
+
company_slug: companySlug,
|
|
1389
|
+
});
|
|
1390
|
+
return response.data;
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Get sprint reports (burndown, burnup, performance, etc.)
|
|
1394
|
+
*/
|
|
1395
|
+
async getSprintReports(slug, projectSlug, companySlug, options) {
|
|
1396
|
+
const params = {
|
|
1397
|
+
project_slug: projectSlug,
|
|
1398
|
+
company_slug: companySlug,
|
|
1399
|
+
};
|
|
1400
|
+
if (options?.resource)
|
|
1401
|
+
params.resource = options.resource;
|
|
1402
|
+
if (options?.report_task)
|
|
1403
|
+
params.report_task = options.report_task;
|
|
1404
|
+
const response = await this.get(`sprints/${slug}/reports`, params);
|
|
1405
|
+
return response.data;
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Get sprint progress
|
|
1409
|
+
*/
|
|
1410
|
+
async getSprintProgress(slug, projectSlug, companySlug) {
|
|
1411
|
+
const response = await this.get(`sprints/${slug}/progress`, {
|
|
1412
|
+
project_slug: projectSlug,
|
|
1413
|
+
company_slug: companySlug,
|
|
1414
|
+
});
|
|
1415
|
+
return response.data;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Get sprint metrics
|
|
1419
|
+
*/
|
|
1420
|
+
async getSprintMetrics(slug, projectSlug, companySlug) {
|
|
1421
|
+
const response = await this.get(`sprints/${slug}/metrics`, {
|
|
1422
|
+
project_slug: projectSlug,
|
|
1423
|
+
company_slug: companySlug,
|
|
1424
|
+
});
|
|
1425
|
+
return response.data;
|
|
1426
|
+
}
|
|
1427
|
+
// ============================================================================
|
|
1428
|
+
// TASK - BY CODE / DUPLICATE / MOVE
|
|
1429
|
+
// ============================================================================
|
|
1430
|
+
/**
|
|
1431
|
+
* Get task by code (e.g., PROJ-123)
|
|
1432
|
+
*/
|
|
1433
|
+
async getTaskByCode(taskCode, projectSlug, companySlug) {
|
|
1434
|
+
const response = await this.get(`tasks/by-code/${taskCode}`, {
|
|
1435
|
+
project_slug: projectSlug,
|
|
1436
|
+
company_slug: companySlug,
|
|
1437
|
+
});
|
|
1438
|
+
return response.data;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Duplicate a task
|
|
1442
|
+
*/
|
|
1443
|
+
async duplicateTask(taskUuid, projectSlug, companySlug, workflowId) {
|
|
1444
|
+
const body = {};
|
|
1445
|
+
if (workflowId)
|
|
1446
|
+
body.config_workflow_id = workflowId;
|
|
1447
|
+
const response = await this.post(`tasks/${taskUuid}/duplicate?company_slug=${companySlug}&project_slug=${projectSlug}`, body);
|
|
1448
|
+
return response.data;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Move a task to another project
|
|
1452
|
+
*/
|
|
1453
|
+
async moveTask(taskUuid, projectSlug, companySlug, newProjectSlug, newWorkflowId) {
|
|
1454
|
+
const response = await this.post(`tasks/${taskUuid}/move?company_slug=${companySlug}&project_slug=${projectSlug}`, { new_project_slug: newProjectSlug, new_workflow_id: newWorkflowId });
|
|
1455
|
+
return response;
|
|
1456
|
+
}
|
|
1457
|
+
// ============================================================================
|
|
1458
|
+
// WIKI - SEARCH
|
|
1459
|
+
// ============================================================================
|
|
1460
|
+
/**
|
|
1461
|
+
* Search wiki pages
|
|
1462
|
+
*/
|
|
1463
|
+
async searchWikiPages(projectSlug, companySlug, query, limit) {
|
|
1464
|
+
const params = {
|
|
1465
|
+
project_slug: projectSlug,
|
|
1466
|
+
company_slug: companySlug,
|
|
1467
|
+
q: query,
|
|
1468
|
+
};
|
|
1469
|
+
if (limit)
|
|
1470
|
+
params.limit = limit;
|
|
1471
|
+
const response = await this.get("wiki/pages/search", params);
|
|
1472
|
+
return response.data;
|
|
1473
|
+
}
|
|
1474
|
+
// ============================================================================
|
|
1475
|
+
// TIME TRACKING - ANALYTICS
|
|
1476
|
+
// ============================================================================
|
|
1477
|
+
/**
|
|
1478
|
+
* Get time tracking analytics
|
|
1479
|
+
*/
|
|
1480
|
+
async getTimeTrackingAnalytics(companySlug, options) {
|
|
1481
|
+
const params = { company_slug: companySlug };
|
|
1482
|
+
if (options?.project_slug)
|
|
1483
|
+
params.project_slug = options.project_slug;
|
|
1484
|
+
if (options?.period)
|
|
1485
|
+
params.period = options.period;
|
|
1486
|
+
if (options?.start)
|
|
1487
|
+
params.start = options.start;
|
|
1488
|
+
if (options?.end)
|
|
1489
|
+
params.end = options.end;
|
|
1490
|
+
if (options?.users)
|
|
1491
|
+
params.users = options.users;
|
|
1492
|
+
const response = await this.get("time-trackings/analytics", params);
|
|
1493
|
+
return response.data;
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Get time tracking team stats
|
|
1497
|
+
*/
|
|
1498
|
+
async getTimeTrackingTeam(companySlug, options) {
|
|
1499
|
+
const params = { company_slug: companySlug };
|
|
1500
|
+
if (options?.project_slug)
|
|
1501
|
+
params.project_slug = options.project_slug;
|
|
1502
|
+
if (options?.period)
|
|
1503
|
+
params.period = options.period;
|
|
1504
|
+
const response = await this.get("time-trackings/team", params);
|
|
1505
|
+
return response.data;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Get time tracking reports
|
|
1509
|
+
*/
|
|
1510
|
+
async getTimeTrackingReports(companySlug, options) {
|
|
1511
|
+
const params = { company_slug: companySlug };
|
|
1512
|
+
if (options?.project_slug)
|
|
1513
|
+
params.project_slug = options.project_slug;
|
|
1514
|
+
if (options?.period)
|
|
1515
|
+
params.period = options.period;
|
|
1516
|
+
if (options?.report_type)
|
|
1517
|
+
params.report_type = options.report_type;
|
|
1518
|
+
if (options?.hourly_rate)
|
|
1519
|
+
params.hourly_rate = options.hourly_rate;
|
|
1520
|
+
const response = await this.get("time-trackings/reports", params);
|
|
1521
|
+
return response.data;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Get time tracking productivity
|
|
1525
|
+
*/
|
|
1526
|
+
async getTimeTrackingProductivity(companySlug, options) {
|
|
1527
|
+
const params = { company_slug: companySlug };
|
|
1528
|
+
if (options?.project_slug)
|
|
1529
|
+
params.project_slug = options.project_slug;
|
|
1530
|
+
if (options?.period)
|
|
1531
|
+
params.period = options.period;
|
|
1532
|
+
const response = await this.get("time-trackings/productivity", params);
|
|
1533
|
+
return response.data;
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Get time tracking timeline
|
|
1537
|
+
*/
|
|
1538
|
+
async getTimeTrackingTimeline(companySlug, options) {
|
|
1539
|
+
const params = { company_slug: companySlug };
|
|
1540
|
+
if (options?.project_slug)
|
|
1541
|
+
params.project_slug = options.project_slug;
|
|
1542
|
+
if (options?.period)
|
|
1543
|
+
params.period = options.period;
|
|
1544
|
+
const response = await this.get("time-trackings/timeline", params);
|
|
1545
|
+
return response.data;
|
|
1546
|
+
}
|
|
1547
|
+
// ============================================================================
|
|
1548
|
+
// MANAGER DASHBOARD - ADDITIONAL REPORTS
|
|
1549
|
+
// ============================================================================
|
|
1550
|
+
/**
|
|
1551
|
+
* Get manager dashboard overview
|
|
1552
|
+
*/
|
|
1553
|
+
async getManagerOverview(companySlug) {
|
|
1554
|
+
const response = await this.get("companies/manager-dashboard/overview", {
|
|
1555
|
+
company_slug: companySlug,
|
|
1556
|
+
});
|
|
1557
|
+
return response.data;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Get manager dashboard health
|
|
1561
|
+
*/
|
|
1562
|
+
async getManagerHealth(companySlug) {
|
|
1563
|
+
const response = await this.get("companies/manager-dashboard/health", {
|
|
1564
|
+
company_slug: companySlug,
|
|
1565
|
+
});
|
|
1566
|
+
return response.data;
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Get manager dashboard blockers
|
|
1570
|
+
*/
|
|
1571
|
+
async getManagerBlockers(companySlug) {
|
|
1572
|
+
const response = await this.get("companies/manager-dashboard/blockers", {
|
|
1573
|
+
company_slug: companySlug,
|
|
1574
|
+
});
|
|
1575
|
+
return response.data;
|
|
1576
|
+
}
|
|
1577
|
+
/**
|
|
1578
|
+
* Get manager command center
|
|
1579
|
+
*/
|
|
1580
|
+
async getManagerCommandCenter(companySlug) {
|
|
1581
|
+
const response = await this.get("companies/manager-dashboard/command-center", {
|
|
1582
|
+
company_slug: companySlug,
|
|
1583
|
+
});
|
|
1584
|
+
return response.data;
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Get manager dashboard time entries
|
|
1588
|
+
*/
|
|
1589
|
+
async getManagerTimeEntries(companySlug, filter) {
|
|
1590
|
+
const params = { company_slug: companySlug };
|
|
1591
|
+
if (filter)
|
|
1592
|
+
params.filter = filter;
|
|
1593
|
+
const response = await this.get("companies/manager-dashboard/time-entries", params);
|
|
1594
|
+
return response.data;
|
|
1595
|
+
}
|
|
1596
|
+
// ============================================================================
|
|
1597
|
+
// DISCUSSIONS
|
|
1598
|
+
// ============================================================================
|
|
1599
|
+
/**
|
|
1600
|
+
* Get all discussions across all workspaces
|
|
1601
|
+
*/
|
|
1602
|
+
async getAllDiscussions() {
|
|
1603
|
+
const response = await this.get("discussions/all");
|
|
1604
|
+
return response.data;
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Get global unread count across all workspaces
|
|
1608
|
+
*/
|
|
1609
|
+
async getDiscussionGlobalUnreadCount() {
|
|
1610
|
+
const response = await this.get("discussions/global-unread-count");
|
|
1611
|
+
return response.data;
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
* Get channels in a project
|
|
1615
|
+
*/
|
|
1616
|
+
async getDiscussionChannels(projectSlug, companySlug, includeArchived) {
|
|
1617
|
+
const params = {
|
|
1618
|
+
project_slug: projectSlug,
|
|
1619
|
+
company_slug: companySlug,
|
|
1620
|
+
};
|
|
1621
|
+
if (includeArchived)
|
|
1622
|
+
params.include_archived = includeArchived;
|
|
1623
|
+
const response = await this.get("discussions/channels", params);
|
|
1624
|
+
return response.data;
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Get a single channel
|
|
1628
|
+
*/
|
|
1629
|
+
async getDiscussionChannel(uuid) {
|
|
1630
|
+
const response = await this.get(`discussions/channels/${uuid}`);
|
|
1631
|
+
return response.data;
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Create a discussion channel
|
|
1635
|
+
*/
|
|
1636
|
+
async createDiscussionChannel(data) {
|
|
1637
|
+
const response = await this.post("discussions/channels", data);
|
|
1638
|
+
return response.data;
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Update a discussion channel
|
|
1642
|
+
*/
|
|
1643
|
+
async updateDiscussionChannel(uuid, data) {
|
|
1644
|
+
const response = await this.put(`discussions/channels/${uuid}`, data);
|
|
1645
|
+
return response.data;
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Get messages in a channel (cursor-based pagination)
|
|
1649
|
+
*/
|
|
1650
|
+
async getDiscussionMessages(channelUuid, options) {
|
|
1651
|
+
const params = {};
|
|
1652
|
+
if (options?.before_id)
|
|
1653
|
+
params.before_id = options.before_id;
|
|
1654
|
+
if (options?.after_id)
|
|
1655
|
+
params.after_id = options.after_id;
|
|
1656
|
+
if (options?.limit)
|
|
1657
|
+
params.limit = options.limit;
|
|
1658
|
+
const response = await this.get(`discussions/channels/${channelUuid}/messages`, params);
|
|
1659
|
+
return response;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Send a message in a channel
|
|
1663
|
+
*/
|
|
1664
|
+
async sendDiscussionMessage(channelUuid, data) {
|
|
1665
|
+
const response = await this.post(`discussions/channels/${channelUuid}/messages`, data);
|
|
1666
|
+
return response.data;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Search messages in a channel
|
|
1670
|
+
*/
|
|
1671
|
+
async searchDiscussionMessages(channelUuid, query, limit) {
|
|
1672
|
+
const params = { q: query };
|
|
1673
|
+
if (limit)
|
|
1674
|
+
params.limit = limit;
|
|
1675
|
+
const response = await this.get(`discussions/channels/${channelUuid}/search`, params);
|
|
1676
|
+
return response.data;
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Get unread count for a project
|
|
1680
|
+
*/
|
|
1681
|
+
async getDiscussionUnreadCount(projectSlug, companySlug) {
|
|
1682
|
+
const response = await this.get("discussions/unread-count", {
|
|
1683
|
+
project_slug: projectSlug,
|
|
1684
|
+
company_slug: companySlug,
|
|
1685
|
+
});
|
|
1686
|
+
return response.data;
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Mark a channel as read
|
|
1690
|
+
*/
|
|
1691
|
+
async markDiscussionChannelRead(channelUuid) {
|
|
1692
|
+
const response = await this.post(`discussions/channels/${channelUuid}/read`, {});
|
|
1693
|
+
return response;
|
|
1694
|
+
}
|
|
1695
|
+
// ============================================================================
|
|
1696
|
+
// ACTIVITY FEED
|
|
1697
|
+
// ============================================================================
|
|
1698
|
+
/**
|
|
1699
|
+
* Get user's activity feed
|
|
1700
|
+
*/
|
|
1701
|
+
async getActivityFeed() {
|
|
1702
|
+
const response = await this.get("feeds");
|
|
1703
|
+
return response;
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Get activity feed for a specific user
|
|
1707
|
+
*/
|
|
1708
|
+
async getActivityFeedByUser(username) {
|
|
1709
|
+
const response = await this.get(`feeds/user/${username}`);
|
|
1710
|
+
return response;
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Get notification feed
|
|
1714
|
+
*/
|
|
1715
|
+
async getNotificationFeed() {
|
|
1716
|
+
const response = await this.get("feeds/notifications");
|
|
1717
|
+
return response;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Get activities by context (company, project, sprint, etc.)
|
|
1721
|
+
*/
|
|
1722
|
+
async getActivities(options) {
|
|
1723
|
+
const params = {};
|
|
1724
|
+
if (options.from_context)
|
|
1725
|
+
params.from_context = options.from_context;
|
|
1726
|
+
if (options.company_slug)
|
|
1727
|
+
params.company_slug = options.company_slug;
|
|
1728
|
+
if (options.project_slug)
|
|
1729
|
+
params.project_slug = options.project_slug;
|
|
1730
|
+
if (options.sprint_slug)
|
|
1731
|
+
params.sprint_slug = options.sprint_slug;
|
|
1732
|
+
if (options.uuid)
|
|
1733
|
+
params.uuid = options.uuid;
|
|
1734
|
+
const response = await this.get("activities", params);
|
|
1735
|
+
return response.data;
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Get workflow history for a task
|
|
1739
|
+
*/
|
|
1740
|
+
async getTaskWorkflowHistory(taskUuid) {
|
|
1741
|
+
const response = await this.get(`activities/task/${taskUuid}/workflow`);
|
|
1742
|
+
return response.data;
|
|
1743
|
+
}
|
|
1744
|
+
// ============================================================================
|
|
1745
|
+
// BUDGET
|
|
1746
|
+
// ============================================================================
|
|
1747
|
+
/**
|
|
1748
|
+
* Get projects at budget risk
|
|
1749
|
+
*/
|
|
1750
|
+
async getBudgetProjectsAtRisk(companySlug) {
|
|
1751
|
+
const response = await this.get("budget/projects-at-risk", {
|
|
1752
|
+
company_slug: companySlug,
|
|
1753
|
+
});
|
|
1754
|
+
return response.data;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Get project budget overview
|
|
1758
|
+
*/
|
|
1759
|
+
async getBudgetOverview(projectUuid, options) {
|
|
1760
|
+
const params = {};
|
|
1761
|
+
if (options?.start_date)
|
|
1762
|
+
params.start_date = options.start_date;
|
|
1763
|
+
if (options?.end_date)
|
|
1764
|
+
params.end_date = options.end_date;
|
|
1765
|
+
if (options?.user_ids)
|
|
1766
|
+
params.user_ids = options.user_ids;
|
|
1767
|
+
if (options?.status)
|
|
1768
|
+
params.status = options.status;
|
|
1769
|
+
const response = await this.get(`projects/${projectUuid}/budget/overview`, params);
|
|
1770
|
+
return response.data;
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Get project budget consumption
|
|
1774
|
+
*/
|
|
1775
|
+
async getBudgetConsumption(projectUuid) {
|
|
1776
|
+
const response = await this.get(`projects/${projectUuid}/budget/consumption`);
|
|
1777
|
+
return response.data;
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Get project budget burn-down
|
|
1781
|
+
*/
|
|
1782
|
+
async getBudgetBurnDown(projectUuid, options) {
|
|
1783
|
+
const params = {};
|
|
1784
|
+
if (options?.start_date)
|
|
1785
|
+
params.start_date = options.start_date;
|
|
1786
|
+
if (options?.end_date)
|
|
1787
|
+
params.end_date = options.end_date;
|
|
1788
|
+
const response = await this.get(`projects/${projectUuid}/budget/burn-down`, params);
|
|
1789
|
+
return response.data;
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Get budget alerts for a project
|
|
1793
|
+
*/
|
|
1794
|
+
async getBudgetAlerts(projectUuid) {
|
|
1795
|
+
const response = await this.get(`projects/${projectUuid}/budget/alerts`);
|
|
1796
|
+
return response.data;
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Get budget events for a project
|
|
1800
|
+
*/
|
|
1801
|
+
async getBudgetEvents(projectUuid, limit) {
|
|
1802
|
+
const params = {};
|
|
1803
|
+
if (limit)
|
|
1804
|
+
params.limit = limit;
|
|
1805
|
+
const response = await this.get(`projects/${projectUuid}/budget/events`, params);
|
|
1806
|
+
return response.data;
|
|
1807
|
+
}
|
|
1808
|
+
// ============================================================================
|
|
1809
|
+
// EPICS
|
|
1810
|
+
// ============================================================================
|
|
1811
|
+
/**
|
|
1812
|
+
* Get all epics in a project
|
|
1813
|
+
*/
|
|
1814
|
+
async getEpics(projectSlug, companySlug) {
|
|
1815
|
+
const response = await this.get("user-story-epics", {
|
|
1816
|
+
project_slug: projectSlug,
|
|
1817
|
+
company_slug: companySlug,
|
|
1818
|
+
});
|
|
1819
|
+
return response.data;
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Create a new epic
|
|
1823
|
+
*/
|
|
1824
|
+
async createEpic(projectSlug, companySlug, data) {
|
|
1825
|
+
const response = await this.post(`user-story-epics?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
1826
|
+
return response;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Update an epic
|
|
1830
|
+
*/
|
|
1831
|
+
async updateEpic(epicUuid, projectSlug, companySlug, data) {
|
|
1832
|
+
await this.put(`user-story-epics/${epicUuid}?company_slug=${companySlug}&project_slug=${projectSlug}`, data);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
//# sourceMappingURL=GitScrumClient.js.map
|