figmanage 1.2.2 → 1.2.3
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.
|
@@ -459,14 +459,15 @@ export async function quarterlyDesignOpsReport(config, params) {
|
|
|
459
459
|
params: { plan_parent_id: orgId, plan_type: 'organization' },
|
|
460
460
|
}),
|
|
461
461
|
]);
|
|
462
|
-
// Paginate all members (cursor-based
|
|
462
|
+
// Paginate all members (cursor-based)
|
|
463
463
|
const errors = [];
|
|
464
464
|
const allMembers = [];
|
|
465
465
|
let cursor;
|
|
466
|
-
|
|
466
|
+
let membersComplete = true;
|
|
467
|
+
const maxPages = 200; // safety cap: 200 * 50 = 10,000 members
|
|
467
468
|
for (let page = 0; page < maxPages; page++) {
|
|
468
469
|
try {
|
|
469
|
-
const params = { page_size:
|
|
470
|
+
const params = { page_size: 50 };
|
|
470
471
|
if (cursor)
|
|
471
472
|
params.cursor = cursor;
|
|
472
473
|
const res = await api.get(`/api/v2/orgs/${orgId}/org_users`, { params });
|
|
@@ -476,14 +477,19 @@ export async function quarterlyDesignOpsReport(config, params) {
|
|
|
476
477
|
break;
|
|
477
478
|
allMembers.push(...batch);
|
|
478
479
|
cursor = Array.isArray(meta.cursor) ? meta.cursor[0] : meta.cursor;
|
|
479
|
-
if (!cursor || batch.length <
|
|
480
|
+
if (!cursor || batch.length < 50)
|
|
480
481
|
break;
|
|
481
482
|
}
|
|
482
483
|
catch (e) {
|
|
483
|
-
|
|
484
|
+
membersComplete = false;
|
|
485
|
+
errors.push(`members: pagination stopped at page ${page + 1} of ${maxPages}, fetched ${allMembers.length} members (${e.response?.status || e.message})`);
|
|
484
486
|
break;
|
|
485
487
|
}
|
|
486
488
|
}
|
|
489
|
+
if (cursor && allMembers.length >= maxPages * 50) {
|
|
490
|
+
membersComplete = false;
|
|
491
|
+
errors.push(`members: hit ${maxPages}-page safety cap at ${allMembers.length} members, org may have more`);
|
|
492
|
+
}
|
|
487
493
|
// Process teams
|
|
488
494
|
const teamsRaw = teamsResult.status === 'fulfilled'
|
|
489
495
|
? (teamsResult.value.data?.meta || teamsResult.value.data || [])
|
|
@@ -612,6 +618,7 @@ export async function quarterlyDesignOpsReport(config, params) {
|
|
|
612
618
|
total_teams: teams.length,
|
|
613
619
|
total_members: allMembers.length,
|
|
614
620
|
total_paid_seats: totalPaid,
|
|
621
|
+
members_complete: membersComplete,
|
|
615
622
|
},
|
|
616
623
|
seat_utilization: {
|
|
617
624
|
active_paid: activePaid,
|
|
@@ -47,17 +47,7 @@ export declare function seatOptimization(config: AuthConfig, params: {
|
|
|
47
47
|
org_id?: string;
|
|
48
48
|
days_inactive: number;
|
|
49
49
|
include_cost: boolean;
|
|
50
|
-
}): Promise<
|
|
51
|
-
summary: {
|
|
52
|
-
total_paid: number;
|
|
53
|
-
inactive_paid: number;
|
|
54
|
-
monthly_waste_cents: any;
|
|
55
|
-
annual_savings_cents: number;
|
|
56
|
-
};
|
|
57
|
-
seat_breakdown: any;
|
|
58
|
-
inactive_users: any[];
|
|
59
|
-
recommendations: string[];
|
|
60
|
-
}>;
|
|
50
|
+
}): Promise<Record<string, any>>;
|
|
61
51
|
export declare function permissionAudit(config: AuthConfig, params: {
|
|
62
52
|
scope_type: 'project' | 'team';
|
|
63
53
|
scope_id: string;
|
|
@@ -242,23 +242,36 @@ export async function seatOptimization(config, params) {
|
|
|
242
242
|
const api = internalClient(config);
|
|
243
243
|
const cutoff = Date.now() - days_inactive * 86400000;
|
|
244
244
|
const paidKeys = new Set(['expert', 'developer', 'collaborator']);
|
|
245
|
-
// Paginate org members (cursor-based
|
|
245
|
+
// Paginate org members (cursor-based)
|
|
246
246
|
const allMembers = [];
|
|
247
|
-
const
|
|
247
|
+
const warnings = [];
|
|
248
|
+
let membersComplete = true;
|
|
249
|
+
const MAX_PAGES = 200; // safety cap: 200 * 50 = 10,000 members
|
|
248
250
|
let cursor;
|
|
249
251
|
for (let page = 0; page < MAX_PAGES; page++) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
252
|
+
try {
|
|
253
|
+
const params = { page_size: 50 };
|
|
254
|
+
if (cursor)
|
|
255
|
+
params.cursor = cursor;
|
|
256
|
+
const res = await api.get(`/api/v2/orgs/${orgId}/org_users`, { params });
|
|
257
|
+
const meta = res.data?.meta || {};
|
|
258
|
+
const members = meta.users || [];
|
|
259
|
+
if (!Array.isArray(members) || members.length === 0)
|
|
260
|
+
break;
|
|
261
|
+
allMembers.push(...members);
|
|
262
|
+
cursor = Array.isArray(meta.cursor) ? meta.cursor[0] : meta.cursor;
|
|
263
|
+
if (!cursor || members.length < 50)
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
membersComplete = false;
|
|
268
|
+
warnings.push(`members: pagination stopped at page ${page + 1}, fetched ${allMembers.length} members (${e.response?.status || e.message})`);
|
|
261
269
|
break;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (cursor && allMembers.length >= MAX_PAGES * 50) {
|
|
273
|
+
membersComplete = false;
|
|
274
|
+
warnings.push(`members: hit ${MAX_PAGES}-page safety cap at ${allMembers.length} members, org may have more`);
|
|
262
275
|
}
|
|
263
276
|
// Fetch seat breakdown and optionally contract rates in parallel
|
|
264
277
|
const parallelCalls = [
|
|
@@ -317,17 +330,21 @@ export async function seatOptimization(config, params) {
|
|
|
317
330
|
if (monthlyWasteCents > 0) {
|
|
318
331
|
recommendations.push(`Potential monthly savings: $${(monthlyWasteCents / 100).toFixed(2)} ($${((monthlyWasteCents * 12) / 100).toFixed(2)}/yr).`);
|
|
319
332
|
}
|
|
320
|
-
|
|
333
|
+
const result = {
|
|
321
334
|
summary: {
|
|
322
335
|
total_paid: totalPaid,
|
|
323
336
|
inactive_paid: inactiveUsers.length,
|
|
324
337
|
monthly_waste_cents: monthlyWasteCents,
|
|
325
338
|
annual_savings_cents: monthlyWasteCents * 12,
|
|
339
|
+
members_complete: membersComplete,
|
|
326
340
|
},
|
|
327
341
|
seat_breakdown: seats,
|
|
328
342
|
inactive_users: inactiveUsers,
|
|
329
343
|
recommendations,
|
|
330
344
|
};
|
|
345
|
+
if (warnings.length > 0)
|
|
346
|
+
result.warnings = warnings;
|
|
347
|
+
return result;
|
|
331
348
|
}
|
|
332
349
|
// -- permission_audit --
|
|
333
350
|
export async function permissionAudit(config, params) {
|
package/package.json
CHANGED