@burdenoff/microfe-billing 2026.519.1 → 2026.520.1

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.
@@ -2,9 +2,72 @@ import { AnalyticsPeriod as e, QuotaStatus as t } from "../../../shared/types/gr
2
2
  import { useGetBillingAccountQuotasSummaryForUsageQuery as n, useGetBillingAccountsForUsageQuery as r, useGetPastSubscriptionsWithQuotasQuery as i, useGetSubscriptionsWithQuotasQuery as a, useGetUsageDashboardQuery as o } from "../../../../generated/global-operations.js";
3
3
  import { useGetUsageRecordsQuery as s } from "../../../../generated/wspace-operations.js";
4
4
  import { useCallback as c, useEffect as l, useMemo as u, useState as d } from "react";
5
- import { useGatewayClient as f } from "@burdenoff/fe-libs/shared/graphql";
5
+ import { gql as f, useGatewayClient as p, useQuery as m } from "@burdenoff/fe-libs/shared/graphql";
6
6
  //#region src/billing/modules/usage/hooks/useUsage.ts
7
- function p({ billingAccountId: t, period: n = e.LAST_30_DAYS }) {
7
+ var h = f`
8
+ query WorkspaceQuotaStatuses($workspaceId: ID!) {
9
+ workspaceQuotaStatuses(workspaceId: $workspaceId) {
10
+ workspaceId
11
+ quotaName
12
+ productId
13
+ currentValue
14
+ limit
15
+ noLimit
16
+ scrapedAt
17
+ }
18
+ }
19
+ `, g = f`
20
+ query WorkspaceQuotaHistory($workspaceId: ID!, $quotaName: String!, $limit: Int) {
21
+ workspaceQuotaHistory(workspaceId: $workspaceId, quotaName: $quotaName, limit: $limit) {
22
+ id
23
+ workspaceId
24
+ quotaName
25
+ productId
26
+ value
27
+ limit
28
+ noLimit
29
+ snapshotAt
30
+ }
31
+ }
32
+ `;
33
+ function _(e) {
34
+ let { data: t, loading: n, error: r } = m(h, {
35
+ client: p("workspace"),
36
+ variables: { workspaceId: e },
37
+ skip: !e,
38
+ fetchPolicy: "cache-and-network"
39
+ }), { statuses: i, statusMap: a } = u(() => {
40
+ let e = t?.workspaceQuotaStatuses ?? [];
41
+ return {
42
+ statuses: e,
43
+ statusMap: new Map(e.map((e) => [e.quotaName, e]))
44
+ };
45
+ }, [t]);
46
+ return {
47
+ statuses: i,
48
+ statusMap: a,
49
+ isLoading: n,
50
+ error: r ?? null
51
+ };
52
+ }
53
+ function v(e, t, n = 48) {
54
+ let { data: r, loading: i, error: a } = m(g, {
55
+ client: p("workspace"),
56
+ variables: {
57
+ workspaceId: e,
58
+ quotaName: t,
59
+ limit: n
60
+ },
61
+ skip: !e || !t,
62
+ fetchPolicy: "cache-and-network"
63
+ });
64
+ return {
65
+ snapshots: r?.workspaceQuotaHistory ?? [],
66
+ isLoading: i,
67
+ error: a ?? null
68
+ };
69
+ }
70
+ function y({ billingAccountId: t, period: n = e.LAST_30_DAYS }) {
8
71
  let { data: r, loading: i, error: a, refetch: s } = o({
9
72
  variables: {
10
73
  billingAccountId: t,
@@ -20,10 +83,38 @@ function p({ billingAccountId: t, period: n = e.LAST_30_DAYS }) {
20
83
  refetch: s
21
84
  };
22
85
  }
23
- function m(e, n) {
24
- return n <= 0 ? t.EXHAUSTED : e >= 90 ? t.CRITICAL : e >= 75 ? t.WARNING : t.HEALTHY;
86
+ function b(e, n, r = !1) {
87
+ return r ? t.HEALTHY : n <= 0 ? t.EXHAUSTED : e >= 90 ? t.CRITICAL : e >= 75 ? t.WARNING : t.HEALTHY;
25
88
  }
26
- function h(e) {
89
+ var x = {
90
+ "agents-registered": "vibecontrols.agents.count",
91
+ "vibes-created": "vibecontrols.vibes.count",
92
+ "catalogs-created": "vibecontrols.catalogs.count",
93
+ "tunnels-created": "vibecontrols.tunnels.count",
94
+ "sessions-started": "vibecontrols.sessions.starts"
95
+ };
96
+ function S(e, t) {
97
+ return t.size === 0 ? e : e.map((e) => {
98
+ let n = x[e.name] ?? e.name, r = t.get(n);
99
+ if (!r) return e;
100
+ let i = r.currentValue, { totalLimit: a, noLimit: o } = e, s = o || a === 0 ? 0 : i / a * 100, c = o ? 0 : Math.max(0, a - i), l = i, u = [...e.quotaAssignments].sort((e, t) => (e.endtime ? new Date(e.endtime).getTime() : Infinity) - (t.endtime ? new Date(t.endtime).getTime() : Infinity)).map((e) => {
101
+ let t = Math.min(l, e.limit || 0);
102
+ return l = Math.max(0, l - t), {
103
+ ...e,
104
+ used: t
105
+ };
106
+ });
107
+ return {
108
+ ...e,
109
+ totalUsed: i,
110
+ usagePercentage: s,
111
+ isCollectorBased: !0,
112
+ status: b(s, c, o),
113
+ quotaAssignments: u
114
+ };
115
+ });
116
+ }
117
+ function C(e) {
27
118
  let t = /* @__PURE__ */ new Map();
28
119
  for (let n of e) {
29
120
  let e = t.get(n.name) || [];
@@ -31,7 +122,7 @@ function h(e) {
31
122
  }
32
123
  let n = [];
33
124
  for (let [e, r] of t) {
34
- let t = r.reduce((e, t) => e + (t.limit || 0), 0), i = r.reduce((e, t) => e + (t.used || 0), 0), a = r.reduce((e, t) => e + (t.limitLeft || 0), 0), o = t === 0 && r.every((e) => e.noLimit || (e.limit || 0) === 0), s = o || t === 0 ? 0 : i / t * 100, c = m(s, a), l = r[0]?.reusable || !1, u = r[0]?.unit || "", d = r[0]?.context, f = typeof d?.displayName == "string" ? d.displayName : void 0, p = typeof d?.description == "string" ? d.description : void 0;
125
+ let t = r.reduce((e, t) => e + (t.limit || 0), 0), i = r.reduce((e, t) => e + (t.used || 0), 0), a = r.reduce((e, t) => e + (t.limitLeft || 0), 0), o = t === 0 && r.every((e) => e.noLimit || (e.limit || 0) === 0), s = o || t === 0 ? 0 : i / t * 100, c = b(s, a, o), l = r[0]?.reusable || !1, u = r[0]?.unit || "", d = r[0]?.context, f = typeof d?.displayName == "string" ? d.displayName : void 0, p = typeof d?.description == "string" ? d.description : void 0;
35
126
  n.push({
36
127
  name: e,
37
128
  displayName: f,
@@ -43,6 +134,7 @@ function h(e) {
43
134
  status: c,
44
135
  quotaCount: r.length,
45
136
  noLimit: o,
137
+ isCollectorBased: !1,
46
138
  reusable: l,
47
139
  quotaAssignments: r
48
140
  });
@@ -59,7 +151,7 @@ function h(e) {
59
151
  return n === 0 ? t.usagePercentage - e.usagePercentage : n;
60
152
  });
61
153
  }
62
- function g() {
154
+ function w() {
63
155
  let { data: e, loading: t, error: n } = r({ fetchPolicy: "cache-and-network" });
64
156
  return {
65
157
  billingAccounts: e?.getBillingAccountsByOrg ?? [],
@@ -67,7 +159,7 @@ function g() {
67
159
  error: n ?? null
68
160
  };
69
161
  }
70
- function _(e, t) {
162
+ function T(e, t) {
71
163
  let { data: r, loading: i, error: a, refetch: o } = n({
72
164
  variables: {
73
165
  billingAccountId: e,
@@ -80,32 +172,32 @@ function _(e, t) {
80
172
  pooledQuotas: u(() => {
81
173
  if (!r?.getBillingAccountQuotasSummary) return [];
82
174
  let e = r.getBillingAccountQuotasSummary;
83
- return h(e.pooledQuotas || []);
175
+ return C(e.pooledQuotas || []);
84
176
  }, [r]),
85
177
  isLoading: i,
86
178
  error: a ?? null,
87
179
  refetch: o
88
180
  };
89
181
  }
90
- var v = 10;
91
- function y(e, t = {}) {
92
- let { searchQuery: n, viewMode: r = "active" } = t, o = r === "active" || r === "all", s = r === "past" || r === "all", { data: f, loading: p, error: m, refetch: g } = a({
182
+ var E = 10;
183
+ function D(e, t = {}) {
184
+ let { searchQuery: n, viewMode: r = "active" } = t, o = r === "active" || r === "all", s = r === "past" || r === "all", { data: f, loading: p, error: m, refetch: h } = a({
93
185
  variables: {
94
186
  billingAccountId: e,
95
187
  searchQuery: n || null
96
188
  },
97
189
  skip: !e || !o,
98
190
  fetchPolicy: "cache-and-network"
99
- }), { data: _, loading: y, refetch: b, fetchMore: x } = i({
191
+ }), { data: g, loading: _, refetch: v, fetchMore: y } = i({
100
192
  variables: {
101
193
  billingAccountId: e,
102
194
  searchQuery: n || null,
103
195
  page: 1,
104
- pageSize: v
196
+ pageSize: E
105
197
  },
106
198
  skip: !e || !s,
107
199
  fetchPolicy: "cache-and-network"
108
- }), [S, C] = d(!1), [w, T] = d(1), E = c((e) => {
200
+ }), [b, x] = d(!1), [S, w] = d(1), T = c((e) => {
109
201
  let t = (e.quotas || []).map((t) => {
110
202
  let n = t.limits?.value || 0, r = t.currentUsageSum || 0, i = Math.max(0, n - r), a = n > 0 ? r / n * 100 : 0, o = t.limits?.unit || "";
111
203
  return {
@@ -130,13 +222,13 @@ function y(e, t = {}) {
130
222
  id: e.id,
131
223
  status: e.status,
132
224
  createdAt: e.createdAt,
133
- aggregatedQuotas: h(t)
225
+ aggregatedQuotas: C(t)
134
226
  };
135
- }, []), D = u(() => f?.getSubscriptionUsageOverview ? f.getSubscriptionUsageOverview.map(E) : [], [f, E]), O = u(() => _?.getSubscriptionUsageOverview ? (_.getSubscriptionUsageOverview.subscriptions || _.getSubscriptionUsageOverview).map(E) : [], [_, E]), k = u(() => {
136
- let e = _?.getSubscriptionUsageOverview;
227
+ }, []), D = u(() => f?.getSubscriptionUsageOverview ? f.getSubscriptionUsageOverview.map(T) : [], [f, T]), O = u(() => g?.getSubscriptionUsageOverview ? (g.getSubscriptionUsageOverview.subscriptions || g.getSubscriptionUsageOverview).map(T) : [], [g, T]), k = u(() => {
228
+ let e = g?.getSubscriptionUsageOverview;
137
229
  return e ? e.totalCount === void 0 ? {
138
230
  totalCount: O.length,
139
- hasMore: O.length >= v * w
231
+ hasMore: O.length >= E * S
140
232
  } : {
141
233
  totalCount: e.totalCount,
142
234
  hasMore: O.length < e.totalCount
@@ -145,61 +237,61 @@ function y(e, t = {}) {
145
237
  hasMore: !1
146
238
  };
147
239
  }, [
148
- _,
240
+ g,
149
241
  O.length,
150
- w
242
+ S
151
243
  ]);
152
244
  l(() => {
153
- T(1);
245
+ w(1);
154
246
  }, [n, r]);
155
247
  let A = c(async () => {
156
- if (!x || S || !k.hasMore) return;
157
- C(!0);
158
- let e = w + 1;
248
+ if (!y || b || !k.hasMore) return;
249
+ x(!0);
250
+ let e = S + 1;
159
251
  try {
160
- await x({
252
+ await y({
161
253
  variables: {
162
254
  page: e,
163
- pageSize: v
255
+ pageSize: E
164
256
  },
165
257
  updateQuery: (e, { fetchMoreResult: t }) => {
166
258
  if (!t) return e;
167
259
  let n = e?.getSubscriptionUsageOverview ?? [], r = t?.getSubscriptionUsageOverview ?? [];
168
260
  return { getSubscriptionUsageOverview: [...n, ...r] };
169
261
  }
170
- }), T(e);
262
+ }), w(e);
171
263
  } catch (e) {
172
264
  console.error("Failed to load more past subscriptions:", e);
173
265
  } finally {
174
- C(!1);
266
+ x(!1);
175
267
  }
176
268
  }, [
177
- x,
178
- S,
269
+ y,
270
+ b,
179
271
  k.hasMore,
180
- w
272
+ S
181
273
  ]), j = c(() => {
182
- T(1), o && g(), s && b();
274
+ w(1), o && h(), s && v();
183
275
  }, [
184
276
  o,
185
277
  s,
186
- g,
187
- b
278
+ h,
279
+ v
188
280
  ]);
189
281
  return {
190
282
  activeSubscriptions: D,
191
283
  pastSubscriptions: O,
192
284
  isLoading: o ? p : !1,
193
- isPastSubscriptionsLoading: s ? y : !1,
285
+ isPastSubscriptionsLoading: s ? _ : !1,
194
286
  error: m ?? null,
195
287
  refetch: j,
196
288
  loadMorePastSubscriptions: A,
197
289
  hasMorePastSubscriptions: k.hasMore,
198
- isLoadingMorePast: S,
290
+ isLoadingMorePast: b,
199
291
  pastSubscriptionsCount: k.totalCount
200
292
  };
201
293
  }
202
- function b(e, t, n, r) {
294
+ function O(e, t, n, r) {
203
295
  return {
204
296
  filters: {
205
297
  quotaAssignmentId: e,
@@ -211,7 +303,7 @@ function b(e, t, n, r) {
211
303
  }
212
304
  };
213
305
  }
214
- function x(e) {
306
+ function k(e) {
215
307
  return {
216
308
  id: e.id,
217
309
  amount: e.amount,
@@ -223,15 +315,15 @@ function x(e) {
223
315
  lastUpdated: e.createdAt
224
316
  };
225
317
  }
226
- function S(e, t = 20, n = null, r) {
318
+ function A(e, t = 20, n = null, r) {
227
319
  let { data: i, loading: a, error: o, refetch: c } = s({
228
- client: f("workspace"),
229
- variables: b(e, t, n, r),
320
+ client: p("workspace"),
321
+ variables: O(e, t, n, r),
230
322
  skip: !e || !r,
231
323
  fetchPolicy: "cache-and-network"
232
324
  });
233
325
  return {
234
- usages: (i?.getUsageRecords?.items ?? []).map(x),
326
+ usages: (i?.getUsageRecords?.items ?? []).map(k),
235
327
  hasNextPage: i?.getUsageRecords?.hasNextPage ?? !1,
236
328
  nextCursor: i?.getUsageRecords?.nextCursor ?? null,
237
329
  isLoading: a,
@@ -239,36 +331,42 @@ function S(e, t = 20, n = null, r) {
239
331
  refetch: c
240
332
  };
241
333
  }
242
- function C({ billingAccountId: t, period: n = e.LAST_30_DAYS, searchQuery: r, viewMode: i = "active" }) {
243
- let a = p({
334
+ function j({ billingAccountId: t, workspaceId: n, period: r = e.LAST_30_DAYS, searchQuery: i, viewMode: a = "active" }) {
335
+ let o = y({
244
336
  billingAccountId: t,
245
- period: n
246
- }), o = _(t), s = y(t, {
247
- searchQuery: r,
248
- viewMode: i
249
- }), l = c(() => {
250
- a.refetch(), o.refetch(), s.refetch();
337
+ period: r
338
+ }), s = T(t), { statusMap: l } = _(n), d = D(t, {
339
+ searchQuery: i,
340
+ viewMode: a
341
+ }), f = u(() => S(s.pooledQuotas, l), [s.pooledQuotas, l]), p = u(() => d.activeSubscriptions.map((e) => ({
342
+ ...e,
343
+ aggregatedQuotas: S(e.aggregatedQuotas, l)
344
+ })), [d.activeSubscriptions, l]), m = u(() => d.pastSubscriptions.map((e) => ({
345
+ ...e,
346
+ aggregatedQuotas: S(e.aggregatedQuotas, l)
347
+ })), [d.pastSubscriptions, l]), h = c(() => {
348
+ o.refetch(), s.refetch(), d.refetch();
251
349
  }, [
252
- a,
253
350
  o,
254
- s
351
+ s,
352
+ d
255
353
  ]);
256
354
  return {
257
- dashboard: a.dashboard,
258
- pooledQuotas: o.pooledQuotas,
259
- activeSubscriptions: s.activeSubscriptions,
260
- pastSubscriptions: s.pastSubscriptions,
261
- isLoading: a.isLoading || o.isLoading || s.isLoading,
262
- isPastSubscriptionsLoading: s.isPastSubscriptionsLoading,
263
- error: a.error || o.error || s.error,
264
- refetch: l,
265
- loadMorePastSubscriptions: s.loadMorePastSubscriptions,
266
- hasMorePastSubscriptions: s.hasMorePastSubscriptions,
267
- isLoadingMorePast: s.isLoadingMorePast,
268
- pastSubscriptionsCount: s.pastSubscriptionsCount
355
+ dashboard: o.dashboard,
356
+ pooledQuotas: f,
357
+ activeSubscriptions: p,
358
+ pastSubscriptions: m,
359
+ isLoading: o.isLoading || s.isLoading || d.isLoading,
360
+ isPastSubscriptionsLoading: d.isPastSubscriptionsLoading,
361
+ error: o.error || s.error || d.error,
362
+ refetch: h,
363
+ loadMorePastSubscriptions: d.loadMorePastSubscriptions,
364
+ hasMorePastSubscriptions: d.hasMorePastSubscriptions,
365
+ isLoadingMorePast: d.isLoadingMorePast,
366
+ pastSubscriptionsCount: d.pastSubscriptionsCount
269
367
  };
270
368
  }
271
369
  //#endregion
272
- export { _ as useBillingAccountQuotasSummary, g as useBillingAccountsForUsage, C as useEnhancedUsagePage, S as useQuotaUsageDetails, y as useSubscriptionsWithQuotas, p as useUsageDashboard };
370
+ export { T as useBillingAccountQuotasSummary, w as useBillingAccountsForUsage, j as useEnhancedUsagePage, A as useQuotaUsageDetails, D as useSubscriptionsWithQuotas, y as useUsageDashboard, v as useWorkspaceQuotaHistory };
273
371
 
274
372
  //# sourceMappingURL=useUsage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useUsage.js","names":[],"sources":["../../../../../src/billing/modules/usage/hooks/useUsage.ts"],"sourcesContent":["/**\n * Usage Module - Hooks\n * Custom hooks for usage data fetching and mutations\n */\n\nimport { useMemo, useCallback, useState, useEffect } from 'react';\nimport {\n useGetUsageDashboardQuery,\n useGetBillingAccountQuotasSummaryForUsageQuery,\n useGetSubscriptionsWithQuotasQuery,\n useGetPastSubscriptionsWithQuotasQuery,\n useGetBillingAccountsForUsageQuery,\n useGetQuotaAnalyticsForUsageQuery,\n type GetPastSubscriptionsWithQuotasQuery,\n} from '../../../../generated/global-operations';\nimport {\n useGetUsageRecordsQuery,\n type GetUsageRecordsQuery,\n type GetUsageRecordsQueryVariables,\n} from '../../../../generated/wspace-operations';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { AnalyticsPeriod, QuotaStatus } from '../../../shared/types';\nimport type { UsageDashboard } from '../../../shared/types';\n\n// ============================================================================\n// Dashboard Hook\n// ============================================================================\n\ninterface UseUsageDashboardParams {\n billingAccountId?: string;\n period?: AnalyticsPeriod;\n}\n\ninterface UseUsageDashboardResult {\n dashboard: UsageDashboard | null;\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport function useUsageDashboard({\n billingAccountId,\n period = AnalyticsPeriod.LAST_30_DAYS,\n}: UseUsageDashboardParams): UseUsageDashboardResult {\n const { data, loading, error, refetch } = useGetUsageDashboardQuery({\n variables: { billingAccountId: billingAccountId!, period },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n dashboard: data?.getUsageDashboard ?? null,\n isLoading: loading,\n error: error ?? null,\n refetch,\n };\n}\n\n// ============================================================================\n// Enhanced Usage Page Hooks\n// ============================================================================\n\n/**\n * Quota assignment from the API\n */\ninterface QuotaAssignmentSummary {\n id: string;\n name: string;\n unit: string;\n quotaType: 'POOLED' | 'PRODUCT_SPECIFIC';\n reusable: boolean;\n limit: number;\n limitLeft: number;\n used: number;\n usagePercentage: number;\n noLimit: boolean;\n subscriptionId?: string;\n context?: Record<string, unknown> | null;\n recentUsages: Array<{\n id: string;\n amount: number;\n description: string;\n timestamp: string;\n tags?: Record<string, unknown>;\n }>;\n createdAt: string;\n endtime: string;\n}\n\n/**\n * Aggregated quota for display (multiple quota assignments with same name combined)\n */\nexport interface AggregatedQuota {\n name: string;\n displayName?: string;\n description?: string;\n unit: string;\n totalLimit: number;\n totalUsed: number;\n usagePercentage: number;\n status: QuotaStatus;\n quotaCount: number;\n noLimit: boolean;\n reusable: boolean;\n quotaAssignments: QuotaAssignmentSummary[];\n}\n\n/**\n * Subscription with aggregated quotas\n */\nexport interface SubscriptionWithAggregatedQuotas {\n id: string;\n status: string;\n planName?: string;\n productId?: string;\n createdAt: string;\n aggregatedQuotas: AggregatedQuota[];\n}\n\n/**\n * Helper function to calculate quota status based on usage percentage\n */\nfunction calculateQuotaStatus(usagePercentage: number, limitLeft: number): QuotaStatus {\n if (limitLeft <= 0) return QuotaStatus.EXHAUSTED;\n if (usagePercentage >= 90) return QuotaStatus.CRITICAL;\n if (usagePercentage >= 75) return QuotaStatus.WARNING;\n return QuotaStatus.HEALTHY;\n}\n\n/**\n * Helper function to aggregate quotas by name\n */\nfunction aggregateQuotasByName(quotas: QuotaAssignmentSummary[]): AggregatedQuota[] {\n const grouped = new Map<string, QuotaAssignmentSummary[]>();\n\n for (const quota of quotas) {\n const existing = grouped.get(quota.name) || [];\n existing.push(quota);\n grouped.set(quota.name, existing);\n }\n\n const aggregated: AggregatedQuota[] = [];\n\n for (const [name, assignments] of grouped) {\n // Sum up limits and usage first\n const totalLimit = assignments.reduce((sum, q) => sum + (q.limit || 0), 0);\n const totalUsed = assignments.reduce((sum, q) => sum + (q.used || 0), 0);\n const totalLimitLeft = assignments.reduce((sum, q) => sum + (q.limitLeft || 0), 0);\n\n // Check if there's actually no limit (ignore noLimit flag if there's a numeric limit)\n // Only treat as unlimited if totalLimit is 0 or all assignments explicitly have noLimit AND no numeric limit\n const hasNoLimit =\n totalLimit === 0 && assignments.every((q) => q.noLimit || (q.limit || 0) === 0);\n\n // Calculate percentage\n const usagePercentage = hasNoLimit || totalLimit === 0 ? 0 : (totalUsed / totalLimit) * 100;\n\n // Get status\n const status = calculateQuotaStatus(usagePercentage, totalLimitLeft);\n\n // Get reusable, unit, and display info from first assignment\n const reusable = assignments[0]?.reusable || false;\n const unit = assignments[0]?.unit || '';\n const ctx = assignments[0]?.context;\n const displayName = typeof ctx?.displayName === 'string' ? ctx.displayName : undefined;\n const description = typeof ctx?.description === 'string' ? ctx.description : undefined;\n\n aggregated.push({\n name,\n displayName,\n description,\n unit,\n totalLimit: hasNoLimit ? 0 : totalLimit,\n totalUsed,\n usagePercentage,\n status,\n quotaCount: assignments.length,\n noLimit: hasNoLimit,\n reusable,\n quotaAssignments: assignments,\n });\n }\n\n // Sort by status priority (critical first) then by usage percentage\n const statusPriority: Record<QuotaStatus, number> = {\n EXHAUSTED: 0,\n CRITICAL: 1,\n WARNING: 2,\n HEALTHY: 3,\n INACTIVE: 4,\n };\n\n return aggregated.sort((a, b) => {\n const priorityDiff = statusPriority[a.status] - statusPriority[b.status];\n if (priorityDiff !== 0) return priorityDiff;\n return b.usagePercentage - a.usagePercentage;\n });\n}\n\n/**\n * Hook to get billing accounts for the selector\n */\ninterface UseBillingAccountsForUsageResult {\n billingAccounts: Array<{\n id: string;\n name: string;\n email: string;\n currency: string;\n creditAmount: number;\n isActive: boolean;\n isDefault: boolean;\n }>;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport function useBillingAccountsForUsage(): UseBillingAccountsForUsageResult {\n const { data, loading, error } = useGetBillingAccountsForUsageQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n billingAccounts: data?.getBillingAccountsByOrg ?? [],\n isLoading: loading,\n error: error ?? null,\n };\n}\n\n/**\n * Hook to get billing account quotas summary with aggregation\n * Only returns pooled quotas - subscription quotas come from useSubscriptionsWithQuotas\n */\ninterface UseBillingAccountQuotasSummaryResult {\n pooledQuotas: AggregatedQuota[];\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport function useBillingAccountQuotasSummary(\n billingAccountId: string | undefined,\n productId?: string\n): UseBillingAccountQuotasSummaryResult {\n const { data, loading, error, refetch } = useGetBillingAccountQuotasSummaryForUsageQuery({\n variables: { billingAccountId: billingAccountId!, productId },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const pooledQuotas = useMemo(() => {\n if (!data?.getBillingAccountQuotasSummary) {\n return [];\n }\n\n const summary = data.getBillingAccountQuotasSummary;\n return aggregateQuotasByName(summary.pooledQuotas || []);\n }, [data]);\n\n return {\n pooledQuotas,\n isLoading: loading,\n error: error ?? null,\n refetch,\n };\n}\n\n/**\n * Subscription from the API\n */\ninterface SubscriptionQuota {\n id: string;\n name: string;\n limits: { type: string; value: number; unit?: string; originalValue?: number };\n context?: Record<string, unknown> | null;\n currentUsageSum: number;\n usageCount: number;\n createdAt: string;\n endtime: string;\n}\n\ninterface SubscriptionFromAPI {\n id: string;\n status: string;\n createdAt: string;\n quotas: SubscriptionQuota[];\n}\n\n/**\n * Hook to get subscriptions with their quotas grouped by status\n * Uses lazy loading for past subscriptions and supports backend search\n */\ntype ViewMode = 'active' | 'past' | 'all';\n\nconst PAST_SUBSCRIPTIONS_PAGE_SIZE = 10;\n\ninterface UseSubscriptionsWithQuotasOptions {\n searchQuery?: string;\n viewMode?: ViewMode;\n}\n\ninterface UseSubscriptionsWithQuotasResult {\n activeSubscriptions: SubscriptionWithAggregatedQuotas[];\n pastSubscriptions: SubscriptionWithAggregatedQuotas[];\n isLoading: boolean;\n isPastSubscriptionsLoading: boolean;\n error: Error | null;\n refetch: () => void;\n // Pagination for past subscriptions\n loadMorePastSubscriptions: () => void;\n hasMorePastSubscriptions: boolean;\n isLoadingMorePast: boolean;\n pastSubscriptionsCount: number;\n}\n\nexport function useSubscriptionsWithQuotas(\n billingAccountId: string | undefined,\n options: UseSubscriptionsWithQuotasOptions = {}\n): UseSubscriptionsWithQuotasResult {\n const { searchQuery, viewMode = 'active' } = options;\n\n // Determine which subscriptions to fetch based on viewMode\n const shouldFetchActive = viewMode === 'active' || viewMode === 'all';\n const shouldFetchPast = viewMode === 'past' || viewMode === 'all';\n\n // Query for active subscriptions\n const { data, loading, error, refetch } = useGetSubscriptionsWithQuotasQuery({\n variables: {\n billingAccountId: billingAccountId!,\n searchQuery: searchQuery || null,\n },\n skip: !billingAccountId || !shouldFetchActive,\n fetchPolicy: 'cache-and-network',\n });\n\n // Query for past subscriptions with pagination - only when viewMode is 'past' or 'all'\n const {\n data: pastData,\n loading: pastLoading,\n refetch: refetchPast,\n fetchMore: fetchMorePast,\n } = useGetPastSubscriptionsWithQuotasQuery({\n variables: {\n billingAccountId: billingAccountId!,\n searchQuery: searchQuery || null,\n page: 1,\n pageSize: PAST_SUBSCRIPTIONS_PAGE_SIZE,\n },\n skip: !billingAccountId || !shouldFetchPast,\n fetchPolicy: 'cache-and-network',\n });\n\n // Track if we're loading more past subscriptions\n const [isLoadingMorePast, setIsLoadingMorePast] = useState(false);\n const [currentPastPage, setCurrentPastPage] = useState(1);\n\n // Helper function to process subscription data\n const processSubscription = useCallback(\n (sub: SubscriptionFromAPI): SubscriptionWithAggregatedQuotas => {\n const quotaSummaries: QuotaAssignmentSummary[] = (sub.quotas || []).map((q) => {\n const limit = q.limits?.value || 0;\n const used = q.currentUsageSum || 0;\n const limitLeft = Math.max(0, limit - used);\n const usagePercentage = limit > 0 ? (used / limit) * 100 : 0;\n const unit = q.limits?.unit || '';\n\n return {\n id: q.id,\n name: q.name,\n unit,\n quotaType: 'PRODUCT_SPECIFIC' as const,\n reusable: false,\n limit,\n limitLeft,\n used,\n usagePercentage,\n noLimit: q.limits?.type !== 'number' && q.limits?.type !== 'numeric',\n subscriptionId: sub.id,\n context: q.context,\n recentUsages: [],\n createdAt: q.createdAt,\n endtime: q.endtime,\n };\n });\n\n return {\n id: sub.id,\n status: sub.status,\n createdAt: sub.createdAt,\n aggregatedQuotas: aggregateQuotasByName(quotaSummaries),\n };\n },\n []\n );\n\n // Process active subscriptions\n const activeSubscriptions = useMemo(() => {\n if (!data?.getSubscriptionUsageOverview) {\n return [];\n }\n\n const subscriptions: SubscriptionFromAPI[] = data.getSubscriptionUsageOverview;\n return subscriptions.map(processSubscription);\n }, [data, processSubscription]);\n\n // Process past subscriptions\n const pastSubscriptions = useMemo(() => {\n if (!pastData?.getSubscriptionUsageOverview) {\n return [];\n }\n\n const subscriptions: SubscriptionFromAPI[] =\n pastData.getSubscriptionUsageOverview.subscriptions || pastData.getSubscriptionUsageOverview;\n return subscriptions.map(processSubscription);\n }, [pastData, processSubscription]);\n\n // Get pagination info for past subscriptions\n const pastSubscriptionsPagination = useMemo(() => {\n const overview = pastData?.getSubscriptionUsageOverview;\n if (!overview) {\n return { totalCount: 0, hasMore: false };\n }\n // If it's the new paginated format\n if (overview.totalCount !== undefined) {\n return {\n totalCount: overview.totalCount,\n hasMore: pastSubscriptions.length < overview.totalCount,\n };\n }\n // Legacy format - assume there might be more if we got a full page\n return {\n totalCount: pastSubscriptions.length,\n hasMore: pastSubscriptions.length >= PAST_SUBSCRIPTIONS_PAGE_SIZE * currentPastPage,\n };\n }, [pastData, pastSubscriptions.length, currentPastPage]);\n\n // Reset page when search query or viewMode changes\n useEffect(() => {\n setCurrentPastPage(1);\n }, [searchQuery, viewMode]);\n\n // Load more past subscriptions\n const loadMorePastSubscriptions = useCallback(async () => {\n if (!fetchMorePast || isLoadingMorePast || !pastSubscriptionsPagination.hasMore) return;\n\n setIsLoadingMorePast(true);\n const nextPage = currentPastPage + 1;\n\n try {\n await fetchMorePast({\n variables: {\n page: nextPage,\n pageSize: PAST_SUBSCRIPTIONS_PAGE_SIZE,\n },\n updateQuery: (\n prev: GetPastSubscriptionsWithQuotasQuery,\n { fetchMoreResult }: { fetchMoreResult: GetPastSubscriptionsWithQuotasQuery }\n ) => {\n if (!fetchMoreResult) return prev;\n\n const prevSubs = prev?.getSubscriptionUsageOverview ?? [];\n const newSubs = fetchMoreResult?.getSubscriptionUsageOverview ?? [];\n\n return {\n getSubscriptionUsageOverview: [...prevSubs, ...newSubs],\n };\n },\n });\n setCurrentPastPage(nextPage);\n } catch (err) {\n console.error('Failed to load more past subscriptions:', err);\n } finally {\n setIsLoadingMorePast(false);\n }\n }, [fetchMorePast, isLoadingMorePast, pastSubscriptionsPagination.hasMore, currentPastPage]);\n\n const combinedRefetch = useCallback(() => {\n setCurrentPastPage(1);\n if (shouldFetchActive) refetch();\n if (shouldFetchPast) refetchPast();\n }, [shouldFetchActive, shouldFetchPast, refetch, refetchPast]);\n\n return {\n activeSubscriptions,\n pastSubscriptions,\n isLoading: shouldFetchActive ? loading : false,\n isPastSubscriptionsLoading: shouldFetchPast ? pastLoading : false,\n error: error ?? null,\n refetch: combinedRefetch,\n // Pagination for past subscriptions\n loadMorePastSubscriptions,\n hasMorePastSubscriptions: pastSubscriptionsPagination.hasMore,\n isLoadingMorePast,\n pastSubscriptionsCount: pastSubscriptionsPagination.totalCount,\n };\n}\n\n/**\n * Hook to get quota usage details for drill-down using cursor-based pagination.\n *\n * The activity-svc API is cursor-based (limit / cursor → items / hasNextPage / nextCursor).\n * Callers should maintain a cursor stack:\n * - push nextCursor to go forward\n * - pop to go back\n * - pass null as cursor for the first page\n */\nexport interface QuotaUsageDetail {\n id: string;\n amount: number;\n description: string;\n tags?: Record<string, unknown>;\n productId?: string;\n userId?: string;\n timestamp: string;\n lastUpdated?: string;\n}\n\ninterface UseQuotaUsageDetailsResult {\n usages: QuotaUsageDetail[];\n hasNextPage: boolean;\n nextCursor: string | null;\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\ntype UsageRecord = GetUsageRecordsQuery['getUsageRecords']['items'][number];\n\nexport function buildUsageRecordsVariables(\n quotaAssignmentId: string | undefined,\n pageSize: number,\n cursor: string | null,\n workspaceId?: string\n): GetUsageRecordsQueryVariables {\n return {\n filters: {\n quotaAssignmentId,\n workspaceId,\n },\n pagination: {\n limit: pageSize,\n ...(cursor ? { cursor } : {}),\n },\n };\n}\n\nexport function mapUsageRecordToQuotaUsageDetail(record: UsageRecord): QuotaUsageDetail {\n return {\n id: record.id,\n amount: record.amount,\n description: record.description ?? '',\n tags: (record.metadata as Record<string, unknown>) ?? {},\n productId: record.productId,\n userId: record.userId ?? undefined,\n timestamp: record.createdAt,\n lastUpdated: record.createdAt,\n };\n}\n\nexport function useQuotaUsageDetails(\n quotaAssignmentId: string | undefined,\n pageSize: number = 20,\n cursor: string | null = null,\n workspaceId?: string\n): UseQuotaUsageDetailsResult {\n // Query workspace gateway directly — usage records live in wspace-activity-svc.\n // Uses the dedicated workspace Apollo client so it doesn't conflict with the\n // shell's global Apollo client used by all other billing queries.\n const wsClient = useGatewayClient('workspace');\n const { data, loading, error, refetch } = useGetUsageRecordsQuery({\n client: wsClient,\n variables: buildUsageRecordsVariables(quotaAssignmentId, pageSize, cursor, workspaceId),\n skip: !quotaAssignmentId || !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const items: GetUsageRecordsQuery['getUsageRecords']['items'] =\n data?.getUsageRecords?.items ?? [];\n\n return {\n usages: items.map(mapUsageRecordToQuotaUsageDetail),\n hasNextPage: data?.getUsageRecords?.hasNextPage ?? false,\n nextCursor: data?.getUsageRecords?.nextCursor ?? null,\n isLoading: loading,\n error: error ?? null,\n refetch,\n };\n}\n\n/**\n * Hook to get quota analytics\n */\ninterface UseQuotaAnalyticsResult {\n analytics: {\n dailyUsage: Array<{ date: string; usage: number; count: number }>;\n usageByType: Array<{ resourceType: string; usage: number; count: number; percentage: number }>;\n trends?: {\n growthRate?: number;\n predictedUsage?: number;\n averageDailyUsage?: number;\n };\n peakUsage?: {\n maxDailyUsage: number;\n peakDate: string;\n peakHour?: number;\n };\n } | null;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport function useQuotaAnalytics(\n quotaAssignmentId: string | undefined,\n period: AnalyticsPeriod = AnalyticsPeriod.LAST_30_DAYS\n): UseQuotaAnalyticsResult {\n const { data, loading, error } = useGetQuotaAnalyticsForUsageQuery({\n variables: { quotaAssignmentId: quotaAssignmentId!, period },\n skip: !quotaAssignmentId,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n analytics: data?.getQuotaAnalytics ?? null,\n isLoading: loading,\n error: error ?? null,\n };\n}\n\n/**\n * Combined hook for the enhanced usage page\n */\ninterface UseEnhancedUsagePageParams {\n billingAccountId: string | undefined;\n period?: AnalyticsPeriod;\n searchQuery?: string;\n viewMode?: ViewMode;\n}\n\ninterface UseEnhancedUsagePageResult {\n // Dashboard overview\n dashboard: UsageDashboard | null;\n\n // Pooled quotas (subscription quotas come from subscriptions directly)\n pooledQuotas: AggregatedQuota[];\n\n // Subscriptions with their quotas\n activeSubscriptions: SubscriptionWithAggregatedQuotas[];\n pastSubscriptions: SubscriptionWithAggregatedQuotas[];\n\n // Loading states\n isLoading: boolean;\n isPastSubscriptionsLoading: boolean;\n error: Error | null;\n\n // Actions\n refetch: () => void;\n\n // Pagination for past subscriptions\n loadMorePastSubscriptions: () => void;\n hasMorePastSubscriptions: boolean;\n isLoadingMorePast: boolean;\n pastSubscriptionsCount: number;\n}\n\nexport function useEnhancedUsagePage({\n billingAccountId,\n period = AnalyticsPeriod.LAST_30_DAYS,\n searchQuery,\n viewMode = 'active',\n}: UseEnhancedUsagePageParams): UseEnhancedUsagePageResult {\n // Dashboard for overview stats\n const dashboardResult = useUsageDashboard({\n billingAccountId,\n period,\n });\n\n // Quotas summary with aggregation\n const quotasSummaryResult = useBillingAccountQuotasSummary(billingAccountId);\n\n // Subscriptions with quotas - passes searchQuery and viewMode to backend\n const subscriptionsResult = useSubscriptionsWithQuotas(billingAccountId, {\n searchQuery,\n viewMode,\n });\n\n const refetch = useCallback(() => {\n dashboardResult.refetch();\n quotasSummaryResult.refetch();\n subscriptionsResult.refetch();\n }, [dashboardResult, quotasSummaryResult, subscriptionsResult]);\n\n return {\n dashboard: dashboardResult.dashboard,\n pooledQuotas: quotasSummaryResult.pooledQuotas,\n activeSubscriptions: subscriptionsResult.activeSubscriptions,\n pastSubscriptions: subscriptionsResult.pastSubscriptions,\n isLoading:\n dashboardResult.isLoading || quotasSummaryResult.isLoading || subscriptionsResult.isLoading,\n isPastSubscriptionsLoading: subscriptionsResult.isPastSubscriptionsLoading,\n error: dashboardResult.error || quotasSummaryResult.error || subscriptionsResult.error,\n refetch,\n // Pagination for past subscriptions\n loadMorePastSubscriptions: subscriptionsResult.loadMorePastSubscriptions,\n hasMorePastSubscriptions: subscriptionsResult.hasMorePastSubscriptions,\n isLoadingMorePast: subscriptionsResult.isLoadingMorePast,\n pastSubscriptionsCount: subscriptionsResult.pastSubscriptionsCount,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useUsage.js","names":[],"sources":["../../../../../src/billing/modules/usage/hooks/useUsage.ts"],"sourcesContent":["/**\n * Usage Module - Hooks\n * Custom hooks for usage data fetching and mutations\n */\n\nimport { useMemo, useCallback, useState, useEffect } from 'react';\nimport {\n useGetUsageDashboardQuery,\n useGetBillingAccountQuotasSummaryForUsageQuery,\n useGetSubscriptionsWithQuotasQuery,\n useGetPastSubscriptionsWithQuotasQuery,\n useGetBillingAccountsForUsageQuery,\n useGetQuotaAnalyticsForUsageQuery,\n type GetPastSubscriptionsWithQuotasQuery,\n} from '../../../../generated/global-operations';\nimport {\n useGetUsageRecordsQuery,\n type GetUsageRecordsQuery,\n type GetUsageRecordsQueryVariables,\n} from '../../../../generated/wspace-operations';\nimport { useGatewayClient, useQuery, gql } from '@burdenoff/fe-libs/shared/graphql';\nimport { AnalyticsPeriod, QuotaStatus } from '../../../shared/types';\nimport type { UsageDashboard } from '../../../shared/types';\n\n// ============================================================================\n// Workspace Quota Status — live values from activity-svc\n// ============================================================================\n\nconst WORKSPACE_QUOTA_STATUSES_QUERY = gql`\n query WorkspaceQuotaStatuses($workspaceId: ID!) {\n workspaceQuotaStatuses(workspaceId: $workspaceId) {\n workspaceId\n quotaName\n productId\n currentValue\n limit\n noLimit\n scrapedAt\n }\n }\n`;\n\nconst WORKSPACE_QUOTA_HISTORY_QUERY = gql`\n query WorkspaceQuotaHistory($workspaceId: ID!, $quotaName: String!, $limit: Int) {\n workspaceQuotaHistory(workspaceId: $workspaceId, quotaName: $quotaName, limit: $limit) {\n id\n workspaceId\n quotaName\n productId\n value\n limit\n noLimit\n snapshotAt\n }\n }\n`;\n\nexport interface WorkspaceQuotaStatusItem {\n workspaceId: string;\n quotaName: string;\n productId: string;\n currentValue: number;\n limit: number | null;\n noLimit: boolean;\n scrapedAt: string;\n}\n\nexport interface WorkspaceQuotaSnapshotItem {\n id: string;\n workspaceId: string;\n quotaName: string;\n productId: string;\n value: number;\n limit: number | null;\n noLimit: boolean;\n snapshotAt: string;\n}\n\ninterface UseWorkspaceQuotaStatusesResult {\n statuses: WorkspaceQuotaStatusItem[];\n statusMap: Map<string, WorkspaceQuotaStatusItem>;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport function useWorkspaceQuotaStatuses(workspaceId?: string): UseWorkspaceQuotaStatusesResult {\n const wsClient = useGatewayClient('workspace');\n const { data, loading, error } = useQuery<{ workspaceQuotaStatuses: WorkspaceQuotaStatusItem[] }>(\n WORKSPACE_QUOTA_STATUSES_QUERY,\n {\n client: wsClient,\n variables: { workspaceId },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n }\n );\n\n const { statuses, statusMap } = useMemo(() => {\n const list = data?.workspaceQuotaStatuses ?? [];\n return { statuses: list, statusMap: new Map(list.map((s) => [s.quotaName, s])) };\n }, [data]);\n\n return { statuses, statusMap, isLoading: loading, error: error ?? null };\n}\n\ninterface UseWorkspaceQuotaHistoryResult {\n snapshots: WorkspaceQuotaSnapshotItem[];\n isLoading: boolean;\n error: Error | null;\n}\n\nexport function useWorkspaceQuotaHistory(\n workspaceId?: string,\n quotaName?: string,\n limit: number = 48\n): UseWorkspaceQuotaHistoryResult {\n const wsClient = useGatewayClient('workspace');\n const { data, loading, error } = useQuery<{\n workspaceQuotaHistory: WorkspaceQuotaSnapshotItem[];\n }>(WORKSPACE_QUOTA_HISTORY_QUERY, {\n client: wsClient,\n variables: { workspaceId, quotaName, limit },\n skip: !workspaceId || !quotaName,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n snapshots: data?.workspaceQuotaHistory ?? [],\n isLoading: loading,\n error: error ?? null,\n };\n}\n\n// ============================================================================\n// Dashboard Hook\n// ============================================================================\n\ninterface UseUsageDashboardParams {\n billingAccountId?: string;\n period?: AnalyticsPeriod;\n}\n\ninterface UseUsageDashboardResult {\n dashboard: UsageDashboard | null;\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport function useUsageDashboard({\n billingAccountId,\n period = AnalyticsPeriod.LAST_30_DAYS,\n}: UseUsageDashboardParams): UseUsageDashboardResult {\n const { data, loading, error, refetch } = useGetUsageDashboardQuery({\n variables: { billingAccountId: billingAccountId!, period },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n dashboard: data?.getUsageDashboard ?? null,\n isLoading: loading,\n error: error ?? null,\n refetch,\n };\n}\n\n// ============================================================================\n// Enhanced Usage Page Hooks\n// ============================================================================\n\n/**\n * Quota assignment from the API\n */\ninterface QuotaAssignmentSummary {\n id: string;\n name: string;\n unit: string;\n quotaType: 'POOLED' | 'PRODUCT_SPECIFIC';\n reusable: boolean;\n limit: number;\n limitLeft: number;\n used: number;\n usagePercentage: number;\n noLimit: boolean;\n subscriptionId?: string;\n context?: Record<string, unknown> | null;\n recentUsages: Array<{\n id: string;\n amount: number;\n description: string;\n timestamp: string;\n tags?: Record<string, unknown>;\n }>;\n createdAt: string;\n endtime: string;\n}\n\n/**\n * Aggregated quota for display (multiple quota assignments with same name combined)\n */\nexport interface AggregatedQuota {\n name: string;\n displayName?: string;\n description?: string;\n unit: string;\n totalLimit: number;\n totalUsed: number;\n usagePercentage: number;\n status: QuotaStatus;\n quotaCount: number;\n noLimit: boolean;\n reusable: boolean;\n /** True when usage comes from the activity-svc scraper, not billing recordUsage events. */\n isCollectorBased: boolean;\n quotaAssignments: QuotaAssignmentSummary[];\n}\n\n/**\n * Subscription with aggregated quotas\n */\nexport interface SubscriptionWithAggregatedQuotas {\n id: string;\n status: string;\n planName?: string;\n productId?: string;\n createdAt: string;\n aggregatedQuotas: AggregatedQuota[];\n}\n\n/**\n * Helper function to calculate quota status based on usage percentage\n */\nfunction calculateQuotaStatus(\n usagePercentage: number,\n limitLeft: number,\n noLimit = false\n): QuotaStatus {\n if (noLimit) return QuotaStatus.HEALTHY;\n if (limitLeft <= 0) return QuotaStatus.EXHAUSTED;\n if (usagePercentage >= 90) return QuotaStatus.CRITICAL;\n if (usagePercentage >= 75) return QuotaStatus.WARNING;\n return QuotaStatus.HEALTHY;\n}\n\n// Maps billing quota names to their corresponding scraper metric names in workspace_quota_statuses.\n// Scraper keys use dot-notation product prefix; billing keys use kebab-case.\nconst BILLING_TO_SCRAPER_METRIC: Record<string, string> = {\n 'agents-registered': 'vibecontrols.agents.count',\n 'vibes-created': 'vibecontrols.vibes.count',\n 'catalogs-created': 'vibecontrols.catalogs.count',\n 'tunnels-created': 'vibecontrols.tunnels.count',\n 'sessions-started': 'vibecontrols.sessions.starts',\n};\n\n/**\n * Merges live collector values from activity-svc into a list of aggregated quotas.\n * Overrides `totalUsed` (and derived fields) when a live value exists, and distributes\n * the live total across individual assignments (earliest-expiry first) so the\n * Quota Breakdown shows real per-assignment consumption instead of always-0.\n */\nfunction mergeLiveValues(\n aggregated: AggregatedQuota[],\n statusMap: Map<string, WorkspaceQuotaStatusItem>\n): AggregatedQuota[] {\n if (statusMap.size === 0) return aggregated;\n return aggregated.map((q) => {\n const scraperKey = BILLING_TO_SCRAPER_METRIC[q.name] ?? q.name;\n const live = statusMap.get(scraperKey);\n if (!live) return q;\n\n const totalUsed = live.currentValue;\n const { totalLimit, noLimit } = q;\n const usagePercentage = noLimit || totalLimit === 0 ? 0 : (totalUsed / totalLimit) * 100;\n const limitLeft = noLimit ? 0 : Math.max(0, totalLimit - totalUsed);\n\n // Distribute live total into assignments: fill earliest-expiry first, overflow to next.\n let remaining = totalUsed;\n const updatedAssignments = [...q.quotaAssignments]\n .sort((a, b) => {\n const dateA = a.endtime ? new Date(a.endtime).getTime() : Infinity;\n const dateB = b.endtime ? new Date(b.endtime).getTime() : Infinity;\n return dateA - dateB;\n })\n .map((a) => {\n const assignmentUsed = Math.min(remaining, a.limit || 0);\n remaining = Math.max(0, remaining - assignmentUsed);\n return { ...a, used: assignmentUsed };\n });\n\n return {\n ...q,\n totalUsed,\n usagePercentage,\n isCollectorBased: true,\n status: calculateQuotaStatus(usagePercentage, limitLeft, noLimit),\n quotaAssignments: updatedAssignments,\n };\n });\n}\n\n/**\n * Helper function to aggregate quotas by name\n */\nfunction aggregateQuotasByName(quotas: QuotaAssignmentSummary[]): AggregatedQuota[] {\n const grouped = new Map<string, QuotaAssignmentSummary[]>();\n\n for (const quota of quotas) {\n const existing = grouped.get(quota.name) || [];\n existing.push(quota);\n grouped.set(quota.name, existing);\n }\n\n const aggregated: AggregatedQuota[] = [];\n\n for (const [name, assignments] of grouped) {\n // Sum up limits and usage first\n const totalLimit = assignments.reduce((sum, q) => sum + (q.limit || 0), 0);\n const totalUsed = assignments.reduce((sum, q) => sum + (q.used || 0), 0);\n const totalLimitLeft = assignments.reduce((sum, q) => sum + (q.limitLeft || 0), 0);\n\n // Check if there's actually no limit (ignore noLimit flag if there's a numeric limit)\n // Only treat as unlimited if totalLimit is 0 or all assignments explicitly have noLimit AND no numeric limit\n const hasNoLimit =\n totalLimit === 0 && assignments.every((q) => q.noLimit || (q.limit || 0) === 0);\n\n // Calculate percentage\n const usagePercentage = hasNoLimit || totalLimit === 0 ? 0 : (totalUsed / totalLimit) * 100;\n\n // Get status\n const status = calculateQuotaStatus(usagePercentage, totalLimitLeft, hasNoLimit);\n\n // Get reusable, unit, and display info from first assignment\n const reusable = assignments[0]?.reusable || false;\n const unit = assignments[0]?.unit || '';\n const ctx = assignments[0]?.context;\n const displayName = typeof ctx?.displayName === 'string' ? ctx.displayName : undefined;\n const description = typeof ctx?.description === 'string' ? ctx.description : undefined;\n\n aggregated.push({\n name,\n displayName,\n description,\n unit,\n totalLimit: hasNoLimit ? 0 : totalLimit,\n totalUsed,\n usagePercentage,\n status,\n quotaCount: assignments.length,\n noLimit: hasNoLimit,\n isCollectorBased: false,\n reusable,\n quotaAssignments: assignments,\n });\n }\n\n // Sort by status priority (critical first) then by usage percentage\n const statusPriority: Record<QuotaStatus, number> = {\n EXHAUSTED: 0,\n CRITICAL: 1,\n WARNING: 2,\n HEALTHY: 3,\n INACTIVE: 4,\n };\n\n return aggregated.sort((a, b) => {\n const priorityDiff = statusPriority[a.status] - statusPriority[b.status];\n if (priorityDiff !== 0) return priorityDiff;\n return b.usagePercentage - a.usagePercentage;\n });\n}\n\n/**\n * Hook to get billing accounts for the selector\n */\ninterface UseBillingAccountsForUsageResult {\n billingAccounts: Array<{\n id: string;\n name: string;\n email: string;\n currency: string;\n creditAmount: number;\n isActive: boolean;\n isDefault: boolean;\n }>;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport function useBillingAccountsForUsage(): UseBillingAccountsForUsageResult {\n const { data, loading, error } = useGetBillingAccountsForUsageQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n billingAccounts: data?.getBillingAccountsByOrg ?? [],\n isLoading: loading,\n error: error ?? null,\n };\n}\n\n/**\n * Hook to get billing account quotas summary with aggregation\n * Only returns pooled quotas - subscription quotas come from useSubscriptionsWithQuotas\n */\ninterface UseBillingAccountQuotasSummaryResult {\n pooledQuotas: AggregatedQuota[];\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport function useBillingAccountQuotasSummary(\n billingAccountId: string | undefined,\n productId?: string\n): UseBillingAccountQuotasSummaryResult {\n const { data, loading, error, refetch } = useGetBillingAccountQuotasSummaryForUsageQuery({\n variables: { billingAccountId: billingAccountId!, productId },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const pooledQuotas = useMemo(() => {\n if (!data?.getBillingAccountQuotasSummary) {\n return [];\n }\n\n const summary = data.getBillingAccountQuotasSummary;\n return aggregateQuotasByName(summary.pooledQuotas || []);\n }, [data]);\n\n return {\n pooledQuotas,\n isLoading: loading,\n error: error ?? null,\n refetch,\n };\n}\n\n/**\n * Subscription from the API\n */\ninterface SubscriptionQuota {\n id: string;\n name: string;\n limits: { type: string; value: number; unit?: string; originalValue?: number };\n context?: Record<string, unknown> | null;\n currentUsageSum: number;\n usageCount: number;\n createdAt: string;\n endtime: string;\n}\n\ninterface SubscriptionFromAPI {\n id: string;\n status: string;\n createdAt: string;\n quotas: SubscriptionQuota[];\n}\n\n/**\n * Hook to get subscriptions with their quotas grouped by status\n * Uses lazy loading for past subscriptions and supports backend search\n */\ntype ViewMode = 'active' | 'past' | 'all';\n\nconst PAST_SUBSCRIPTIONS_PAGE_SIZE = 10;\n\ninterface UseSubscriptionsWithQuotasOptions {\n searchQuery?: string;\n viewMode?: ViewMode;\n}\n\ninterface UseSubscriptionsWithQuotasResult {\n activeSubscriptions: SubscriptionWithAggregatedQuotas[];\n pastSubscriptions: SubscriptionWithAggregatedQuotas[];\n isLoading: boolean;\n isPastSubscriptionsLoading: boolean;\n error: Error | null;\n refetch: () => void;\n // Pagination for past subscriptions\n loadMorePastSubscriptions: () => void;\n hasMorePastSubscriptions: boolean;\n isLoadingMorePast: boolean;\n pastSubscriptionsCount: number;\n}\n\nexport function useSubscriptionsWithQuotas(\n billingAccountId: string | undefined,\n options: UseSubscriptionsWithQuotasOptions = {}\n): UseSubscriptionsWithQuotasResult {\n const { searchQuery, viewMode = 'active' } = options;\n\n // Determine which subscriptions to fetch based on viewMode\n const shouldFetchActive = viewMode === 'active' || viewMode === 'all';\n const shouldFetchPast = viewMode === 'past' || viewMode === 'all';\n\n // Query for active subscriptions\n const { data, loading, error, refetch } = useGetSubscriptionsWithQuotasQuery({\n variables: {\n billingAccountId: billingAccountId!,\n searchQuery: searchQuery || null,\n },\n skip: !billingAccountId || !shouldFetchActive,\n fetchPolicy: 'cache-and-network',\n });\n\n // Query for past subscriptions with pagination - only when viewMode is 'past' or 'all'\n const {\n data: pastData,\n loading: pastLoading,\n refetch: refetchPast,\n fetchMore: fetchMorePast,\n } = useGetPastSubscriptionsWithQuotasQuery({\n variables: {\n billingAccountId: billingAccountId!,\n searchQuery: searchQuery || null,\n page: 1,\n pageSize: PAST_SUBSCRIPTIONS_PAGE_SIZE,\n },\n skip: !billingAccountId || !shouldFetchPast,\n fetchPolicy: 'cache-and-network',\n });\n\n // Track if we're loading more past subscriptions\n const [isLoadingMorePast, setIsLoadingMorePast] = useState(false);\n const [currentPastPage, setCurrentPastPage] = useState(1);\n\n // Helper function to process subscription data\n const processSubscription = useCallback(\n (sub: SubscriptionFromAPI): SubscriptionWithAggregatedQuotas => {\n const quotaSummaries: QuotaAssignmentSummary[] = (sub.quotas || []).map((q) => {\n const limit = q.limits?.value || 0;\n const used = q.currentUsageSum || 0;\n const limitLeft = Math.max(0, limit - used);\n const usagePercentage = limit > 0 ? (used / limit) * 100 : 0;\n const unit = q.limits?.unit || '';\n\n return {\n id: q.id,\n name: q.name,\n unit,\n quotaType: 'PRODUCT_SPECIFIC' as const,\n reusable: false,\n limit,\n limitLeft,\n used,\n usagePercentage,\n noLimit: q.limits?.type !== 'number' && q.limits?.type !== 'numeric',\n subscriptionId: sub.id,\n context: q.context,\n recentUsages: [],\n createdAt: q.createdAt,\n endtime: q.endtime,\n };\n });\n\n return {\n id: sub.id,\n status: sub.status,\n createdAt: sub.createdAt,\n aggregatedQuotas: aggregateQuotasByName(quotaSummaries),\n };\n },\n []\n );\n\n // Process active subscriptions\n const activeSubscriptions = useMemo(() => {\n if (!data?.getSubscriptionUsageOverview) {\n return [];\n }\n\n const subscriptions: SubscriptionFromAPI[] = data.getSubscriptionUsageOverview;\n return subscriptions.map(processSubscription);\n }, [data, processSubscription]);\n\n // Process past subscriptions\n const pastSubscriptions = useMemo(() => {\n if (!pastData?.getSubscriptionUsageOverview) {\n return [];\n }\n\n const subscriptions: SubscriptionFromAPI[] =\n pastData.getSubscriptionUsageOverview.subscriptions || pastData.getSubscriptionUsageOverview;\n return subscriptions.map(processSubscription);\n }, [pastData, processSubscription]);\n\n // Get pagination info for past subscriptions\n const pastSubscriptionsPagination = useMemo(() => {\n const overview = pastData?.getSubscriptionUsageOverview;\n if (!overview) {\n return { totalCount: 0, hasMore: false };\n }\n // If it's the new paginated format\n if (overview.totalCount !== undefined) {\n return {\n totalCount: overview.totalCount,\n hasMore: pastSubscriptions.length < overview.totalCount,\n };\n }\n // Legacy format - assume there might be more if we got a full page\n return {\n totalCount: pastSubscriptions.length,\n hasMore: pastSubscriptions.length >= PAST_SUBSCRIPTIONS_PAGE_SIZE * currentPastPage,\n };\n }, [pastData, pastSubscriptions.length, currentPastPage]);\n\n // Reset page when search query or viewMode changes\n useEffect(() => {\n setCurrentPastPage(1);\n }, [searchQuery, viewMode]);\n\n // Load more past subscriptions\n const loadMorePastSubscriptions = useCallback(async () => {\n if (!fetchMorePast || isLoadingMorePast || !pastSubscriptionsPagination.hasMore) return;\n\n setIsLoadingMorePast(true);\n const nextPage = currentPastPage + 1;\n\n try {\n await fetchMorePast({\n variables: {\n page: nextPage,\n pageSize: PAST_SUBSCRIPTIONS_PAGE_SIZE,\n },\n updateQuery: (\n prev: GetPastSubscriptionsWithQuotasQuery,\n { fetchMoreResult }: { fetchMoreResult: GetPastSubscriptionsWithQuotasQuery }\n ) => {\n if (!fetchMoreResult) return prev;\n\n const prevSubs = prev?.getSubscriptionUsageOverview ?? [];\n const newSubs = fetchMoreResult?.getSubscriptionUsageOverview ?? [];\n\n return {\n getSubscriptionUsageOverview: [...prevSubs, ...newSubs],\n };\n },\n });\n setCurrentPastPage(nextPage);\n } catch (err) {\n console.error('Failed to load more past subscriptions:', err);\n } finally {\n setIsLoadingMorePast(false);\n }\n }, [fetchMorePast, isLoadingMorePast, pastSubscriptionsPagination.hasMore, currentPastPage]);\n\n const combinedRefetch = useCallback(() => {\n setCurrentPastPage(1);\n if (shouldFetchActive) refetch();\n if (shouldFetchPast) refetchPast();\n }, [shouldFetchActive, shouldFetchPast, refetch, refetchPast]);\n\n return {\n activeSubscriptions,\n pastSubscriptions,\n isLoading: shouldFetchActive ? loading : false,\n isPastSubscriptionsLoading: shouldFetchPast ? pastLoading : false,\n error: error ?? null,\n refetch: combinedRefetch,\n // Pagination for past subscriptions\n loadMorePastSubscriptions,\n hasMorePastSubscriptions: pastSubscriptionsPagination.hasMore,\n isLoadingMorePast,\n pastSubscriptionsCount: pastSubscriptionsPagination.totalCount,\n };\n}\n\n/**\n * Hook to get quota usage details for drill-down using cursor-based pagination.\n *\n * The activity-svc API is cursor-based (limit / cursor → items / hasNextPage / nextCursor).\n * Callers should maintain a cursor stack:\n * - push nextCursor to go forward\n * - pop to go back\n * - pass null as cursor for the first page\n */\nexport interface QuotaUsageDetail {\n id: string;\n amount: number;\n description: string;\n tags?: Record<string, unknown>;\n productId?: string;\n userId?: string;\n timestamp: string;\n lastUpdated?: string;\n}\n\ninterface UseQuotaUsageDetailsResult {\n usages: QuotaUsageDetail[];\n hasNextPage: boolean;\n nextCursor: string | null;\n isLoading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\ntype UsageRecord = GetUsageRecordsQuery['getUsageRecords']['items'][number];\n\nexport function buildUsageRecordsVariables(\n quotaAssignmentId: string | undefined,\n pageSize: number,\n cursor: string | null,\n workspaceId?: string\n): GetUsageRecordsQueryVariables {\n return {\n filters: {\n quotaAssignmentId,\n workspaceId,\n },\n pagination: {\n limit: pageSize,\n ...(cursor ? { cursor } : {}),\n },\n };\n}\n\nexport function mapUsageRecordToQuotaUsageDetail(record: UsageRecord): QuotaUsageDetail {\n return {\n id: record.id,\n amount: record.amount,\n description: record.description ?? '',\n tags: (record.metadata as Record<string, unknown>) ?? {},\n productId: record.productId,\n userId: record.userId ?? undefined,\n timestamp: record.createdAt,\n lastUpdated: record.createdAt,\n };\n}\n\nexport function useQuotaUsageDetails(\n quotaAssignmentId: string | undefined,\n pageSize: number = 20,\n cursor: string | null = null,\n workspaceId?: string\n): UseQuotaUsageDetailsResult {\n // Query workspace gateway directly — usage records live in wspace-activity-svc.\n // Uses the dedicated workspace Apollo client so it doesn't conflict with the\n // shell's global Apollo client used by all other billing queries.\n const wsClient = useGatewayClient('workspace');\n const { data, loading, error, refetch } = useGetUsageRecordsQuery({\n client: wsClient,\n variables: buildUsageRecordsVariables(quotaAssignmentId, pageSize, cursor, workspaceId),\n skip: !quotaAssignmentId || !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const items: GetUsageRecordsQuery['getUsageRecords']['items'] =\n data?.getUsageRecords?.items ?? [];\n\n return {\n usages: items.map(mapUsageRecordToQuotaUsageDetail),\n hasNextPage: data?.getUsageRecords?.hasNextPage ?? false,\n nextCursor: data?.getUsageRecords?.nextCursor ?? null,\n isLoading: loading,\n error: error ?? null,\n refetch,\n };\n}\n\n/**\n * Hook to get quota analytics\n */\ninterface UseQuotaAnalyticsResult {\n analytics: {\n dailyUsage: Array<{ date: string; usage: number; count: number }>;\n usageByType: Array<{ resourceType: string; usage: number; count: number; percentage: number }>;\n trends?: {\n growthRate?: number;\n predictedUsage?: number;\n averageDailyUsage?: number;\n };\n peakUsage?: {\n maxDailyUsage: number;\n peakDate: string;\n peakHour?: number;\n };\n } | null;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport function useQuotaAnalytics(\n quotaAssignmentId: string | undefined,\n period: AnalyticsPeriod = AnalyticsPeriod.LAST_30_DAYS\n): UseQuotaAnalyticsResult {\n const { data, loading, error } = useGetQuotaAnalyticsForUsageQuery({\n variables: { quotaAssignmentId: quotaAssignmentId!, period },\n skip: !quotaAssignmentId,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n analytics: data?.getQuotaAnalytics ?? null,\n isLoading: loading,\n error: error ?? null,\n };\n}\n\n/**\n * Combined hook for the enhanced usage page\n */\ninterface UseEnhancedUsagePageParams {\n billingAccountId: string | undefined;\n workspaceId?: string;\n period?: AnalyticsPeriod;\n searchQuery?: string;\n viewMode?: ViewMode;\n}\n\ninterface UseEnhancedUsagePageResult {\n // Dashboard overview\n dashboard: UsageDashboard | null;\n\n // Pooled quotas (subscription quotas come from subscriptions directly)\n pooledQuotas: AggregatedQuota[];\n\n // Subscriptions with their quotas\n activeSubscriptions: SubscriptionWithAggregatedQuotas[];\n pastSubscriptions: SubscriptionWithAggregatedQuotas[];\n\n // Loading states\n isLoading: boolean;\n isPastSubscriptionsLoading: boolean;\n error: Error | null;\n\n // Actions\n refetch: () => void;\n\n // Pagination for past subscriptions\n loadMorePastSubscriptions: () => void;\n hasMorePastSubscriptions: boolean;\n isLoadingMorePast: boolean;\n pastSubscriptionsCount: number;\n}\n\nexport function useEnhancedUsagePage({\n billingAccountId,\n workspaceId,\n period = AnalyticsPeriod.LAST_30_DAYS,\n searchQuery,\n viewMode = 'active',\n}: UseEnhancedUsagePageParams): UseEnhancedUsagePageResult {\n // Dashboard for overview stats\n const dashboardResult = useUsageDashboard({\n billingAccountId,\n period,\n });\n\n // Quotas summary with aggregation\n const quotasSummaryResult = useBillingAccountQuotasSummary(billingAccountId);\n\n // Live collector values from activity-svc (source of truth for count/concurrent quotas)\n const { statusMap } = useWorkspaceQuotaStatuses(workspaceId);\n\n // Subscriptions with quotas - passes searchQuery and viewMode to backend\n const subscriptionsResult = useSubscriptionsWithQuotas(billingAccountId, {\n searchQuery,\n viewMode,\n });\n\n // Merge live values into both pooled quotas and subscription quotas\n const pooledQuotas = useMemo(\n () => mergeLiveValues(quotasSummaryResult.pooledQuotas, statusMap),\n [quotasSummaryResult.pooledQuotas, statusMap]\n );\n\n const activeSubscriptions = useMemo(\n () =>\n subscriptionsResult.activeSubscriptions.map((sub) => ({\n ...sub,\n aggregatedQuotas: mergeLiveValues(sub.aggregatedQuotas, statusMap),\n })),\n [subscriptionsResult.activeSubscriptions, statusMap]\n );\n\n const pastSubscriptions = useMemo(\n () =>\n subscriptionsResult.pastSubscriptions.map((sub) => ({\n ...sub,\n aggregatedQuotas: mergeLiveValues(sub.aggregatedQuotas, statusMap),\n })),\n [subscriptionsResult.pastSubscriptions, statusMap]\n );\n\n const refetch = useCallback(() => {\n dashboardResult.refetch();\n quotasSummaryResult.refetch();\n subscriptionsResult.refetch();\n }, [dashboardResult, quotasSummaryResult, subscriptionsResult]);\n\n return {\n dashboard: dashboardResult.dashboard,\n pooledQuotas,\n activeSubscriptions,\n pastSubscriptions,\n isLoading:\n dashboardResult.isLoading || quotasSummaryResult.isLoading || subscriptionsResult.isLoading,\n isPastSubscriptionsLoading: subscriptionsResult.isPastSubscriptionsLoading,\n error: dashboardResult.error || quotasSummaryResult.error || subscriptionsResult.error,\n refetch,\n // Pagination for past subscriptions\n loadMorePastSubscriptions: subscriptionsResult.loadMorePastSubscriptions,\n hasMorePastSubscriptions: subscriptionsResult.hasMorePastSubscriptions,\n isLoadingMorePast: subscriptionsResult.isLoadingMorePast,\n pastSubscriptionsCount: subscriptionsResult.pastSubscriptionsCount,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"UsagePage.d.ts","sourceRoot":"","sources":["../../../../../src/billing/modules/usage/pages/UsagePage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAoD,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAwmBlF,eAAO,MAAM,SAAS,EAAE,EAmkBvB,CAAC"}
1
+ {"version":3,"file":"UsagePage.d.ts","sourceRoot":"","sources":["../../../../../src/billing/modules/usage/pages/UsagePage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAoD,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AA2pBlF,eAAO,MAAM,SAAS,EAAE,EAskBvB,CAAC"}