@burdenoff/microfe-billing 2026.601.4 → 2026.601.6

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.
@@ -8,7 +8,7 @@ import { AlertCircle as m, AlertTriangle as h, CheckCircle as g, ChevronDown as
8
8
  import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
9
9
  import { useI18n as ee } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
10
  //#region src/billing/modules/usage/pages/UsagePage.tsx
11
- var j = ({ status: e, className: n = "size-4" }) => {
11
+ var te = ({ status: e, className: n = "size-4" }) => {
12
12
  switch (e) {
13
13
  case "EXHAUSTED": return /* @__PURE__ */ k(D, { className: `${n} ${t.error.icon}` });
14
14
  case "CRITICAL": return /* @__PURE__ */ k(h, { className: `${n} ${t.error.icon}` });
@@ -16,7 +16,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
16
16
  case "HEALTHY": return /* @__PURE__ */ k(g, { className: `${n} ${t.success.icon}` });
17
17
  default: return /* @__PURE__ */ k(x, { className: `${n} ${t.neutral.icon}` });
18
18
  }
19
- }, M = ({ status: e }) => /* @__PURE__ */ k("span", {
19
+ }, j = ({ status: e }) => /* @__PURE__ */ k("span", {
20
20
  className: `px-2 py-0.5 text-xs font-medium rounded ${{
21
21
  EXHAUSTED: `${t.error.bg} ${t.error.text}`,
22
22
  CRITICAL: `${t.error.bg} ${t.error.text}`,
@@ -25,7 +25,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
25
25
  INACTIVE: `${t.neutral.bg} ${t.neutral.text}`
26
26
  }[e]}`,
27
27
  children: e
28
- }), N = ({ percentage: e, status: n }) => {
28
+ }), M = ({ percentage: e, status: n }) => {
29
29
  let r = Math.min(100, Math.max(0, e));
30
30
  return /* @__PURE__ */ k("div", {
31
31
  className: "w-full bg-muted rounded-full h-2.5 overflow-hidden",
@@ -40,7 +40,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
40
40
  style: { width: `${r}%` }
41
41
  })
42
42
  });
43
- }, P = ({ label: e, value: n, icon: r, color: i, isActive: a, onClick: o }) => {
43
+ }, N = ({ label: e, value: n, icon: r, color: i, isActive: a, onClick: o }) => {
44
44
  let s = {
45
45
  blue: t.info,
46
46
  emerald: t.success,
@@ -84,7 +84,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
84
84
  children: n
85
85
  })]
86
86
  });
87
- }, F = ({ quota: e, planLabel: t, onClick: n }) => {
87
+ }, P = ({ quota: e, planLabel: t, onClick: n }) => {
88
88
  let i = e.totalLimit != null && e.totalLimit > 0 && !e.noLimit;
89
89
  return /* @__PURE__ */ A("tr", {
90
90
  onClick: n,
@@ -127,7 +127,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
127
127
  i ? r(e.totalLimit) : "∞",
128
128
  e.unit ? ` ${e.unit}` : ""
129
129
  ]
130
- }), /* @__PURE__ */ k(N, {
130
+ }), /* @__PURE__ */ k(M, {
131
131
  percentage: i ? e.usagePercentage : 0,
132
132
  status: e.status
133
133
  })]
@@ -139,11 +139,11 @@ var j = ({ status: e, className: n = "size-4" }) => {
139
139
  }),
140
140
  /* @__PURE__ */ k("td", {
141
141
  className: "px-4 py-3",
142
- children: /* @__PURE__ */ k(M, { status: e.status })
142
+ children: /* @__PURE__ */ k(j, { status: e.status })
143
143
  })
144
144
  ]
145
145
  });
146
- }, te = ({ quotas: e, planLabel: t, showPlanColumn: n = !0, onQuotaClick: r }) => /* @__PURE__ */ k("div", {
146
+ }, ne = ({ quotas: e, planLabel: t, showPlanColumn: n = !0, onQuotaClick: r }) => /* @__PURE__ */ k("div", {
147
147
  className: "overflow-x-auto rounded-lg border border-border bg-card",
148
148
  children: /* @__PURE__ */ A("table", {
149
149
  className: "w-full text-left",
@@ -171,13 +171,13 @@ var j = ({ status: e, className: n = "size-4" }) => {
171
171
  children: "Status"
172
172
  })
173
173
  ]
174
- }) }), /* @__PURE__ */ k("tbody", { children: e.map((e) => /* @__PURE__ */ k(F, {
174
+ }) }), /* @__PURE__ */ k("tbody", { children: e.map((e) => /* @__PURE__ */ k(P, {
175
175
  quota: e,
176
176
  planLabel: n ? t : void 0,
177
177
  onClick: () => r(e)
178
178
  }, e.name)) })]
179
179
  })
180
- }), ne = ({ subscription: e, onQuotaClick: n, defaultExpanded: r = !0 }) => {
180
+ }), F = ({ subscription: e, onQuotaClick: n, defaultExpanded: r = !0 }) => {
181
181
  let [i, a] = p(r), o = {
182
182
  active: `${t.success.bg} ${t.success.text}`,
183
183
  trialing: `${t.info.bg} ${t.info.text}`,
@@ -219,7 +219,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
219
219
  }),
220
220
  i && e.aggregatedQuotas.length > 0 && /* @__PURE__ */ k("div", {
221
221
  className: "border-t border-border",
222
- children: /* @__PURE__ */ k(te, {
222
+ children: /* @__PURE__ */ k(ne, {
223
223
  quotas: e.aggregatedQuotas,
224
224
  planLabel: e.planName || e.productId || void 0,
225
225
  showPlanColumn: !!(e.planName || e.productId),
@@ -256,7 +256,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
256
256
  className: "sticky top-0 bg-background border-b border-border p-4 flex items-center justify-between",
257
257
  children: [/* @__PURE__ */ A("div", {
258
258
  className: "flex items-center gap-3",
259
- children: [/* @__PURE__ */ k(j, {
259
+ children: [/* @__PURE__ */ k(te, {
260
260
  status: a.status,
261
261
  className: "size-5"
262
262
  }), /* @__PURE__ */ A("div", { children: [
@@ -300,9 +300,9 @@ var j = ({ status: e, className: n = "size-4" }) => {
300
300
  children: [/* @__PURE__ */ k("span", {
301
301
  className: "text-sm text-muted-foreground",
302
302
  children: "Usage"
303
- }), /* @__PURE__ */ k(M, { status: a.status })]
303
+ }), /* @__PURE__ */ k(j, { status: a.status })]
304
304
  }),
305
- /* @__PURE__ */ k(N, {
305
+ /* @__PURE__ */ k(M, {
306
306
  percentage: a.usagePercentage,
307
307
  status: a.status
308
308
  }),
@@ -493,26 +493,24 @@ var j = ({ status: e, className: n = "size-4" }) => {
493
493
  return r === e ? t : r;
494
494
  }, i = a(), [c, l] = p(() => {
495
495
  if (!(typeof window > "u")) return new URLSearchParams(window.location.search).get("billingAccountId") || void 0;
496
- }), [v, y] = p(null), [x, M] = p(""), [N, F] = p("all"), [I, L] = p("active"), ie = d(x), { billingAccounts: R, isLoading: ae } = o(), oe = (R.find((e) => e.isDefault) ?? R[0])?.id, z = c ?? oe, { workspaceId: se, navigate: ce, basePath: le } = e(), ue = u((e) => {
497
- ce(`${le}${e}`);
498
- }, [ce, le]), { dashboard: B, pooledQuotas: V, activeSubscriptions: H, pastSubscriptions: U, isLoading: de, isPastSubscriptionsLoading: W, error: G, refetch: K, loadMorePastSubscriptions: fe, hasMorePastSubscriptions: pe, isLoadingMorePast: me, pastSubscriptionsCount: he } = s({
496
+ }), [v, y] = p(null), [x, j] = p(""), [M, P] = p("all"), [I, L] = p("active"), ie = d(x), { billingAccounts: R, isLoading: ae } = o(), oe = (R.find((e) => e.isDefault) ?? R[0])?.id, z = c ?? oe, { workspaceId: se, navigate: ce } = e(), { dashboard: B, pooledQuotas: V, activeSubscriptions: H, pastSubscriptions: U, isLoading: le, isPastSubscriptionsLoading: W, error: G, refetch: ue, loadMorePastSubscriptions: de, hasMorePastSubscriptions: fe, isLoadingMorePast: pe, pastSubscriptionsCount: K } = s({
499
497
  billingAccountId: z,
500
498
  workspaceId: se,
501
499
  searchQuery: ie || void 0,
502
500
  viewMode: I
503
- }), q = u((e) => N === "all" ? !0 : N === "healthy" ? e.status === "HEALTHY" : N === "warning" ? e.status === "WARNING" : N === "critical" ? e.status === "CRITICAL" || e.status === "EXHAUSTED" : !0, [N]), J = f(() => V.filter(q), [V, q]), Y = f(() => N === "all" ? H : H.map((e) => ({
501
+ }), q = u((e) => M === "all" ? !0 : M === "healthy" ? e.status === "HEALTHY" : M === "warning" ? e.status === "WARNING" : M === "critical" ? e.status === "CRITICAL" || e.status === "EXHAUSTED" : !0, [M]), J = f(() => V.filter(q), [V, q]), Y = f(() => M === "all" ? H : H.map((e) => ({
504
502
  ...e,
505
503
  aggregatedQuotas: e.aggregatedQuotas.filter(q)
506
504
  })).filter((e) => e.aggregatedQuotas.length > 0), [
507
505
  H,
508
- N,
506
+ M,
509
507
  q
510
- ]), X = f(() => N === "all" ? U : U.map((e) => ({
508
+ ]), X = f(() => M === "all" ? U : U.map((e) => ({
511
509
  ...e,
512
510
  aggregatedQuotas: e.aggregatedQuotas.filter(q)
513
511
  })).filter((e) => e.aggregatedQuotas.length > 0), [
514
512
  U,
515
- N,
513
+ M,
516
514
  q
517
515
  ]), Z = f(() => I === "active" ? {
518
516
  active: Y,
@@ -527,10 +525,10 @@ var j = ({ status: e, className: n = "size-4" }) => {
527
525
  I,
528
526
  Y,
529
527
  X
530
- ]), Q = x !== "" || N !== "all", ge = ie !== x, _e = () => {
531
- M(""), F("all");
528
+ ]), Q = x !== "" || M !== "all", me = ie !== x, he = () => {
529
+ j(""), P("all");
532
530
  }, $ = (e) => {
533
- F((t) => t === e ? "all" : e);
531
+ P((t) => t === e ? "all" : e);
534
532
  };
535
533
  if (!i.canViewUsage) return /* @__PURE__ */ k("div", {
536
534
  className: "flex items-center justify-center h-full min-h-[400px]",
@@ -552,7 +550,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
552
550
  ]
553
551
  })
554
552
  });
555
- if ((ae || de) && !B) return /* @__PURE__ */ A("div", {
553
+ if ((ae || le) && !B) return /* @__PURE__ */ A("div", {
556
554
  className: "space-y-6 p-6",
557
555
  children: [
558
556
  /* @__PURE__ */ k("div", { className: "h-8 w-48 bg-muted animate-pulse rounded" }),
@@ -587,14 +585,14 @@ var j = ({ status: e, className: n = "size-4" }) => {
587
585
  }),
588
586
  /* @__PURE__ */ k("button", {
589
587
  type: "button",
590
- onClick: () => K(),
588
+ onClick: () => ue(),
591
589
  className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
592
590
  children: "Try Again"
593
591
  })
594
592
  ]
595
593
  })
596
594
  });
597
- let ve = R.find((e) => e.id === z);
595
+ let ge = R.find((e) => e.id === z);
598
596
  return /* @__PURE__ */ A("div", {
599
597
  className: "space-y-6 p-6",
600
598
  children: [
@@ -618,7 +616,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
618
616
  }, e.id))
619
617
  }), /* @__PURE__ */ k("button", {
620
618
  type: "button",
621
- onClick: () => K(),
619
+ onClick: () => ue(),
622
620
  className: "p-2 border border-border rounded-md hover:bg-muted/50 transition-colors",
623
621
  title: "Refresh",
624
622
  children: /* @__PURE__ */ k(w, { className: "size-4" })
@@ -635,15 +633,15 @@ var j = ({ status: e, className: n = "size-4" }) => {
635
633
  /* @__PURE__ */ k("input", {
636
634
  type: "text",
637
635
  value: x,
638
- onChange: (e) => M(e.target.value),
636
+ onChange: (e) => j(e.target.value),
639
637
  placeholder: `Search ${I === "past" ? "past" : I === "all" ? "all" : "active"} quotas...`,
640
638
  className: "w-full pl-9 pr-3 py-2 text-sm border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
641
639
  }),
642
- (x || ge) && /* @__PURE__ */ k("button", {
640
+ (x || me) && /* @__PURE__ */ k("button", {
643
641
  type: "button",
644
- onClick: () => M(""),
642
+ onClick: () => j(""),
645
643
  className: "absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-muted/50",
646
- children: ge ? /* @__PURE__ */ k(w, { className: "size-3 text-muted-foreground animate-spin" }) : /* @__PURE__ */ k(E, { className: "size-3 text-muted-foreground" })
644
+ children: me ? /* @__PURE__ */ k(w, { className: "size-3 text-muted-foreground animate-spin" }) : /* @__PURE__ */ k(E, { className: "size-3 text-muted-foreground" })
647
645
  })
648
646
  ]
649
647
  }),
@@ -664,56 +662,56 @@ var j = ({ status: e, className: n = "size-4" }) => {
664
662
  }),
665
663
  Q && /* @__PURE__ */ A("button", {
666
664
  type: "button",
667
- onClick: _e,
665
+ onClick: he,
668
666
  className: "flex items-center gap-1.5 px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground border border-border rounded-md hover:bg-muted/50 transition-colors",
669
667
  children: [/* @__PURE__ */ k(E, { className: "size-3" }), "Clear filters"]
670
668
  })
671
669
  ]
672
670
  }),
673
- ve && /* @__PURE__ */ A("div", {
671
+ ge && /* @__PURE__ */ A("div", {
674
672
  className: "text-sm text-muted-foreground",
675
673
  children: [
676
674
  "Viewing usage for",
677
675
  " ",
678
676
  /* @__PURE__ */ k("span", {
679
677
  className: "font-medium text-foreground",
680
- children: ve.name
678
+ children: ge.name
681
679
  })
682
680
  ]
683
681
  }),
684
682
  B && /* @__PURE__ */ A("div", {
685
683
  className: "grid grid-cols-2 sm:grid-cols-4 gap-4",
686
684
  children: [
687
- /* @__PURE__ */ k(P, {
685
+ /* @__PURE__ */ k(N, {
688
686
  label: "Total Quotas",
689
687
  value: B.totalQuotas,
690
688
  icon: /* @__PURE__ */ k(S, { className: "size-5" }),
691
689
  color: "blue",
692
- isActive: N === "all",
693
- onClick: () => F("all")
690
+ isActive: M === "all",
691
+ onClick: () => P("all")
694
692
  }),
695
- /* @__PURE__ */ k(P, {
693
+ /* @__PURE__ */ k(N, {
696
694
  label: "Healthy",
697
695
  value: B.healthyQuotas,
698
696
  icon: /* @__PURE__ */ k(g, { className: "size-5" }),
699
697
  color: "emerald",
700
- isActive: N === "healthy",
698
+ isActive: M === "healthy",
701
699
  onClick: () => $("healthy")
702
700
  }),
703
- /* @__PURE__ */ k(P, {
701
+ /* @__PURE__ */ k(N, {
704
702
  label: "Warning",
705
703
  value: B.warningQuotas,
706
704
  icon: /* @__PURE__ */ k(m, { className: "size-5" }),
707
705
  color: "amber",
708
- isActive: N === "warning",
706
+ isActive: M === "warning",
709
707
  onClick: () => $("warning")
710
708
  }),
711
- /* @__PURE__ */ k(P, {
709
+ /* @__PURE__ */ k(N, {
712
710
  label: "Critical / Exhausted",
713
711
  value: B.criticalQuotas + B.exhaustedQuotas,
714
712
  icon: /* @__PURE__ */ k(h, { className: "size-5" }),
715
713
  color: "red",
716
- isActive: N === "critical",
714
+ isActive: M === "critical",
717
715
  onClick: () => $("critical")
718
716
  })
719
717
  ]
@@ -731,7 +729,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
731
729
  })] })]
732
730
  }), /* @__PURE__ */ k("button", {
733
731
  type: "button",
734
- onClick: () => ue("/support/tickets"),
732
+ onClick: () => ce("/support/tickets"),
735
733
  className: `shrink-0 px-4 py-2 text-sm font-medium rounded-md border transition-colors ${B.criticalQuotas > 0 || B.exhaustedQuotas > 0 ? "bg-destructive text-destructive-foreground border-destructive hover:bg-destructive/90" : "border-status-warning-border text-status-warning-text hover:bg-status-warning-bg-subtle"}`,
736
734
  children: "Request more quota"
737
735
  })]
@@ -750,9 +748,9 @@ var j = ({ status: e, className: n = "size-4" }) => {
750
748
  className: "px-2 py-0.5 bg-primary/10 text-primary rounded text-xs capitalize",
751
749
  children: [I === "active" ? "Active" : I === "past" ? "Past" : "All", " subscriptions"]
752
750
  }),
753
- N !== "all" && /* @__PURE__ */ k("span", {
751
+ M !== "all" && /* @__PURE__ */ k("span", {
754
752
  className: "px-2 py-0.5 bg-muted rounded text-xs capitalize",
755
- children: N === "critical" ? "Critical / Exhausted" : N
753
+ children: M === "critical" ? "Critical / Exhausted" : M
756
754
  }),
757
755
  x && /* @__PURE__ */ A("span", {
758
756
  className: "px-2 py-0.5 bg-muted rounded text-xs",
@@ -769,7 +767,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
769
767
  children: [/* @__PURE__ */ k(C, { className: "size-5 text-primary" }), I === "all" ? "Active Subscription Quotas" : "Subscription Quotas"]
770
768
  }), /* @__PURE__ */ k("div", {
771
769
  className: "space-y-4",
772
- children: Z.active.map((e) => /* @__PURE__ */ k(ne, {
770
+ children: Z.active.map((e) => /* @__PURE__ */ k(F, {
773
771
  subscription: e,
774
772
  onQuotaClick: y,
775
773
  defaultExpanded: !0
@@ -784,30 +782,30 @@ var j = ({ status: e, className: n = "size-4" }) => {
784
782
  className: "text-xs font-normal text-muted-foreground",
785
783
  children: "(Canceled / Expired / Upgraded)"
786
784
  }),
787
- he > 0 && /* @__PURE__ */ A("span", {
785
+ K > 0 && /* @__PURE__ */ A("span", {
788
786
  className: "text-xs font-normal px-2 py-0.5 bg-muted rounded-full",
789
787
  children: [
790
788
  Z.past.length,
791
- pe ? "+" : "",
789
+ fe ? "+" : "",
792
790
  " of ",
793
- he
791
+ K
794
792
  ]
795
793
  })
796
794
  ]
797
795
  }), /* @__PURE__ */ A("div", {
798
796
  className: "space-y-4",
799
- children: [Z.past.map((e) => /* @__PURE__ */ k(ne, {
797
+ children: [Z.past.map((e) => /* @__PURE__ */ k(F, {
800
798
  subscription: e,
801
799
  onQuotaClick: y,
802
800
  defaultExpanded: I === "past"
803
- }, e.id)), pe && /* @__PURE__ */ k("div", {
801
+ }, e.id)), fe && /* @__PURE__ */ k("div", {
804
802
  className: "flex justify-center pt-2",
805
803
  children: /* @__PURE__ */ k("button", {
806
804
  type: "button",
807
- onClick: fe,
808
- disabled: me,
805
+ onClick: de,
806
+ disabled: pe,
809
807
  className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground border border-border rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50",
810
- children: me ? /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(w, { className: "size-4 animate-spin" }), "Loading…"] }) : /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(_, { className: "size-4" }), "Load more past subscriptions"] })
808
+ children: pe ? /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(w, { className: "size-4 animate-spin" }), "Loading…"] }) : /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(_, { className: "size-4" }), "Load more past subscriptions"] })
811
809
  })
812
810
  })]
813
811
  })] }),
@@ -821,7 +819,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
821
819
  children: "(Shared across all products)"
822
820
  })
823
821
  ]
824
- }), /* @__PURE__ */ k(te, {
822
+ }), /* @__PURE__ */ k(ne, {
825
823
  quotas: J,
826
824
  showPlanColumn: !1,
827
825
  onQuotaClick: y
@@ -879,18 +877,18 @@ var j = ({ status: e, className: n = "size-4" }) => {
879
877
  children: [
880
878
  /* @__PURE__ */ k("div", {
881
879
  className: "size-12 rounded-full bg-muted flex items-center justify-center mb-4",
882
- children: N !== "all" && !x ? /* @__PURE__ */ k(j, {
883
- status: N === "healthy" ? "HEALTHY" : N === "warning" ? "WARNING" : "CRITICAL",
880
+ children: M !== "all" && !x ? /* @__PURE__ */ k(te, {
881
+ status: M === "healthy" ? "HEALTHY" : M === "warning" ? "WARNING" : "CRITICAL",
884
882
  className: "size-6"
885
883
  }) : /* @__PURE__ */ k(T, { className: "size-6 text-muted-foreground" })
886
884
  }),
887
885
  /* @__PURE__ */ k("h3", {
888
886
  className: "text-lg font-medium text-foreground",
889
- children: N !== "all" && !x ? `No ${N === "critical" ? "critical or exhausted" : N} quotas` : "No matching quotas"
887
+ children: M !== "all" && !x ? `No ${M === "critical" ? "critical or exhausted" : M} quotas` : "No matching quotas"
890
888
  }),
891
889
  /* @__PURE__ */ k("p", {
892
890
  className: "text-sm text-muted-foreground mt-1 text-center max-w-sm",
893
- children: N !== "all" && !x ? /* @__PURE__ */ A(O, { children: ["All your quotas are in a different status.", B && /* @__PURE__ */ A("span", {
891
+ children: M !== "all" && !x ? /* @__PURE__ */ A(O, { children: ["All your quotas are in a different status.", B && /* @__PURE__ */ A("span", {
894
892
  className: "block mt-1",
895
893
  children: [
896
894
  "You have ",
@@ -907,7 +905,7 @@ var j = ({ status: e, className: n = "size-4" }) => {
907
905
  I === "past" ? "past" : I === "all" ? "any" : "active",
908
906
  " ",
909
907
  "subscriptions.",
910
- I === "active" && !N.includes("all") && /* @__PURE__ */ k("span", {
908
+ I === "active" && !M.includes("all") && /* @__PURE__ */ k("span", {
911
909
  className: "block mt-1",
912
910
  children: "Try switching to “Past” or “All” to search across more subscriptions."
913
911
  })
@@ -917,9 +915,9 @@ var j = ({ status: e, className: n = "size-4" }) => {
917
915
  className: "flex gap-2 mt-4",
918
916
  children: [/* @__PURE__ */ k("button", {
919
917
  type: "button",
920
- onClick: _e,
918
+ onClick: he,
921
919
  className: "px-4 py-2 text-sm font-medium border border-border rounded-md hover:bg-muted/50 transition-colors",
922
- children: N === "all" ? "Clear filters" : "Show all quotas"
920
+ children: M === "all" ? "Clear filters" : "Show all quotas"
923
921
  }), I === "active" && x && /* @__PURE__ */ k("button", {
924
922
  type: "button",
925
923
  onClick: () => L("all"),
@@ -1 +1 @@
1
- {"version":3,"file":"UsagePage.js","names":[],"sources":["../../../../../src/billing/modules/usage/pages/UsagePage.tsx"],"sourcesContent":["/**\n * Usage Module - Enhanced Usage Page\n * Displays usage dashboard with billing account selector, aggregated quotas by subscription,\n * pooled quotas, and lazy-loaded past subscriptions\n */\n\nimport { useState, useMemo, useDeferredValue, useCallback, type FC } from 'react';\nimport {\n ChevronDown,\n ChevronRight,\n AlertTriangle,\n CheckCircle,\n XCircle,\n AlertCircle,\n Layers,\n Package,\n Clock,\n RefreshCw,\n Info,\n ChevronLeft,\n X,\n Search,\n} from 'lucide-react';\nimport {\n useEnhancedUsagePage,\n useBillingAccountsForUsage,\n useQuotaUsageDetails,\n useWorkspaceQuotaHistory,\n type AggregatedQuota,\n type SubscriptionWithAggregatedQuotas,\n} from '../hooks/useUsage';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { formatNumber, formatRelativeTime, formatDateTime } from '../../../shared/utils/format';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport type { QuotaStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface QuotaDetailModalProps {\n quota: AggregatedQuota;\n onClose: () => void;\n}\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\nconst StatusIcon: FC<{ status: QuotaStatus; className?: string }> = ({\n status,\n className = 'size-4',\n}) => {\n switch (status) {\n case 'EXHAUSTED':\n return <XCircle className={`${className} ${statusTokens.error.icon}`} />;\n case 'CRITICAL':\n return <AlertTriangle className={`${className} ${statusTokens.error.icon}`} />;\n case 'WARNING':\n return <AlertCircle className={`${className} ${statusTokens.warning.icon}`} />;\n case 'HEALTHY':\n return <CheckCircle className={`${className} ${statusTokens.success.icon}`} />;\n default:\n return <Info className={`${className} ${statusTokens.neutral.icon}`} />;\n }\n};\n\nconst StatusBadge: FC<{ status: QuotaStatus }> = ({ status }) => {\n const colors: Record<QuotaStatus, string> = {\n EXHAUSTED: `${statusTokens.error.bg} ${statusTokens.error.text}`,\n CRITICAL: `${statusTokens.error.bg} ${statusTokens.error.text}`,\n WARNING: `${statusTokens.warning.bg} ${statusTokens.warning.text}`,\n HEALTHY: `${statusTokens.success.bg} ${statusTokens.success.text}`,\n INACTIVE: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n };\n\n return (\n <span className={`px-2 py-0.5 text-xs font-medium rounded ${colors[status]}`}>{status}</span>\n );\n};\n\nconst ProgressBar: FC<{ percentage: number; status: QuotaStatus }> = ({ percentage, status }) => {\n const clampedPercentage = Math.min(100, Math.max(0, percentage));\n\n const barColors: Record<QuotaStatus, string> = {\n EXHAUSTED: statusTokens.error.dot,\n CRITICAL: statusTokens.error.dot,\n WARNING: statusTokens.warning.dot,\n HEALTHY: statusTokens.success.dot,\n INACTIVE: statusTokens.neutral.dot,\n };\n\n return (\n <div className=\"w-full bg-muted rounded-full h-2.5 overflow-hidden\">\n <div\n className={`h-full rounded-full transition-all duration-300 ${barColors[status]}`}\n style={{ width: `${clampedPercentage}%` }}\n />\n </div>\n );\n};\n\n// ============================================================================\n// Stat Card Component\n// ============================================================================\n\ntype StatusFilter = 'all' | 'healthy' | 'warning' | 'critical';\n\ninterface StatCardProps {\n label: string;\n value: string | number;\n icon: React.ReactNode;\n color: 'blue' | 'emerald' | 'amber' | 'red' | 'gray';\n isActive?: boolean;\n onClick?: () => void;\n}\n\nconst StatCard: FC<StatCardProps> = ({ label, value, icon, color, isActive, onClick }) => {\n // Map color prop to semantic status tokens\n const colorToStatus = {\n blue: statusTokens.info,\n emerald: statusTokens.success,\n amber: statusTokens.warning,\n red: statusTokens.error,\n gray: statusTokens.neutral,\n };\n\n const tokens = colorToStatus[color];\n\n const colors = {\n blue: `${tokens.bg} ${tokens.border}`,\n emerald: `${tokens.bg} ${tokens.border}`,\n amber: `${tokens.bg} ${tokens.border}`,\n red: `${tokens.bg} ${tokens.border}`,\n gray: `${tokens.bg} ${tokens.border}`,\n };\n\n // Active ring colors - using semantic dot color for consistency\n const activeColors = {\n blue: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.info.border.replace('border-', 'ring-')}`,\n emerald: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.success.border.replace('border-', 'ring-')}`,\n amber: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.warning.border.replace('border-', 'ring-')}`,\n red: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.error.border.replace('border-', 'ring-')}`,\n gray: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.neutral.border.replace('border-', 'ring-')}`,\n };\n\n const iconColors = {\n blue: statusTokens.info.icon,\n emerald: statusTokens.success.icon,\n amber: statusTokens.warning.icon,\n red: statusTokens.error.icon,\n gray: statusTokens.neutral.icon,\n };\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={`border rounded-lg p-4 w-full text-left transition-all ${colors[color]} ${\n onClick ? 'cursor-pointer hover:shadow-md' : ''\n } ${isActive ? activeColors[color] : ''}`}\n >\n <div className=\"flex items-center gap-2 mb-2\">\n <span className={iconColors[color]}>{icon}</span>\n <span className=\"text-sm text-muted-foreground\">{label}</span>\n </div>\n <p className=\"text-2xl font-bold text-foreground\">{value}</p>\n </button>\n );\n};\n\n// ============================================================================\n// Quota Table Components\n// ============================================================================\n\ninterface QuotaTableRowProps {\n quota: AggregatedQuota;\n planLabel?: string;\n onClick: () => void;\n}\n\nconst QuotaTableRow: FC<QuotaTableRowProps> = ({ quota, planLabel, onClick }) => {\n const hasLimit = quota.totalLimit != null && quota.totalLimit > 0 && !quota.noLimit;\n\n return (\n <tr\n onClick={onClick}\n className=\"cursor-pointer hover:bg-muted/40 transition-colors border-b border-border last:border-b-0\"\n >\n {/* Quota name */}\n <td className=\"px-4 py-3\">\n <span className=\"text-sm font-medium text-primary hover:underline capitalize\">\n {quota.displayName || quota.name.replace(/_/g, ' ')}\n </span>\n <span className=\"block text-xs font-mono text-muted-foreground mt-0.5\">{quota.name}</span>\n {quota.description && (\n <span className=\"block text-xs text-muted-foreground mt-0.5 max-w-[220px] truncate\">\n {quota.description}\n </span>\n )}\n {quota.quotaCount > 1 && (\n <span className=\"block text-xs text-muted-foreground mt-0.5\">\n {quota.quotaCount} assignments\n </span>\n )}\n </td>\n\n {/* Plan / type */}\n {planLabel !== undefined && (\n <td className=\"px-4 py-3 text-sm text-muted-foreground whitespace-nowrap\">{planLabel}</td>\n )}\n\n {/* Current usage: value + inline progress bar */}\n <td className=\"px-4 py-3\">\n <div className=\"space-y-1.5\">\n <span className=\"text-sm text-foreground tabular-nums\">\n {formatNumber(quota.totalUsed)}&nbsp;/&nbsp;\n {hasLimit ? formatNumber(quota.totalLimit) : '∞'}\n {quota.unit ? ` ${quota.unit}` : ''}\n </span>\n <ProgressBar percentage={hasLimit ? quota.usagePercentage : 0} status={quota.status} />\n </div>\n </td>\n\n {/* Limit */}\n <td className=\"px-4 py-3 text-sm text-muted-foreground tabular-nums whitespace-nowrap\">\n {hasLimit ? formatNumber(quota.totalLimit) : '∞'}\n </td>\n\n {/* Status */}\n <td className=\"px-4 py-3\">\n <StatusBadge status={quota.status} />\n </td>\n </tr>\n );\n};\n\ninterface QuotaTableProps {\n quotas: AggregatedQuota[];\n /** When provided, renders a \"Plan\" column with this value per row */\n planLabel?: string;\n showPlanColumn?: boolean;\n onQuotaClick: (quota: AggregatedQuota) => void;\n}\n\nconst QuotaTable: FC<QuotaTableProps> = ({\n quotas,\n planLabel,\n showPlanColumn = true,\n onQuotaClick,\n}) => (\n <div className=\"overflow-x-auto rounded-lg border border-border bg-card\">\n <table className=\"w-full text-left\">\n <thead>\n <tr className=\"border-b border-border bg-muted/30\">\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Quotas\n </th>\n {showPlanColumn && (\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Plan\n </th>\n )}\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Current Usage\n </th>\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Limit\n </th>\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Status\n </th>\n </tr>\n </thead>\n <tbody>\n {quotas.map((quota) => (\n <QuotaTableRow\n key={quota.name}\n quota={quota}\n planLabel={showPlanColumn ? planLabel : undefined}\n onClick={() => onQuotaClick(quota)}\n />\n ))}\n </tbody>\n </table>\n </div>\n);\n\n// ============================================================================\n// Subscription Section Component\n// ============================================================================\n\ninterface SubscriptionSectionProps {\n subscription: SubscriptionWithAggregatedQuotas;\n onQuotaClick: (quota: AggregatedQuota) => void;\n defaultExpanded?: boolean;\n}\n\nconst SubscriptionSection: FC<SubscriptionSectionProps> = ({\n subscription,\n onQuotaClick,\n defaultExpanded = true,\n}) => {\n const [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\n const statusColors: Record<string, string> = {\n active: `${statusTokens.success.bg} ${statusTokens.success.text}`,\n trialing: `${statusTokens.info.bg} ${statusTokens.info.text}`,\n past_due: `${statusTokens.warning.bg} ${statusTokens.warning.text}`,\n canceled: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n expired: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n upgraded: `${statusTokens.primary.bg} ${statusTokens.primary.text}`,\n };\n\n return (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"w-full flex items-center justify-between p-4 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <Package className=\"size-5 text-primary\" />\n <div className=\"text-left\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-foreground\">Subscription</span>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded capitalize ${statusColors[subscription.status.toLowerCase()] || statusColors.active}`}\n >\n {subscription.status}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {subscription.aggregatedQuotas.length} quota type\n {subscription.aggregatedQuotas.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n {isExpanded ? (\n <ChevronDown className=\"size-5 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"size-5 text-muted-foreground\" />\n )}\n </button>\n\n {isExpanded && subscription.aggregatedQuotas.length > 0 && (\n <div className=\"border-t border-border\">\n <QuotaTable\n quotas={subscription.aggregatedQuotas}\n planLabel={subscription.planName || subscription.productId || undefined}\n showPlanColumn={!!(subscription.planName || subscription.productId)}\n onQuotaClick={onQuotaClick}\n />\n </div>\n )}\n\n {isExpanded && subscription.aggregatedQuotas.length === 0 && (\n <div className=\"border-t border-border p-6 text-center\">\n <p className=\"text-sm text-muted-foreground\">No quotas assigned to this subscription</p>\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Quota Detail Modal Component\n// ============================================================================\n\nconst QuotaDetailModal: FC<QuotaDetailModalProps> = ({ quota, onClose }) => {\n // Cursor stack: [null] = page 1, [null, c1] = page 2, etc.\n const [cursorStack, setCursorStack] = useState<Array<string | null>>([null]);\n const currentCursor = cursorStack[cursorStack.length - 1] ?? null;\n const currentPage = cursorStack.length;\n const { workspaceId } = useBilling();\n\n // Get usage details for the first quota assignment directly from activity-svc\n const firstAssignmentId = quota.quotaAssignments[0]?.id;\n const { usages, hasNextPage, nextCursor, isLoading } = useQuotaUsageDetails(\n firstAssignmentId,\n 10,\n currentCursor,\n workspaceId\n );\n\n // Snapshot history from quota scraper (activity-svc) — shows live value trend\n const { snapshots, isLoading: isSnapshotsLoading } = useWorkspaceQuotaHistory(\n workspaceId,\n quota.name,\n 24\n );\n\n const handleNext = () => {\n if (nextCursor) setCursorStack((prev) => [...prev, nextCursor]);\n };\n\n const handlePrev = () => {\n if (cursorStack.length > 1) setCursorStack((prev) => prev.slice(0, -1));\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={onClose} />\n <div\n className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-auto\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"quota-detail-title\"\n >\n {/* Header */}\n <div className=\"sticky top-0 bg-background border-b border-border p-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <StatusIcon status={quota.status} className=\"size-5\" />\n <div>\n <h2 id=\"quota-detail-title\" className=\"text-lg font-semibold capitalize\">\n {quota.displayName || quota.name.replace(/_/g, ' ')}\n </h2>\n <p className=\"text-xs font-mono text-muted-foreground\">{quota.name}</p>\n {quota.description && (\n <p className=\"text-sm text-muted-foreground mt-0.5\">{quota.description}</p>\n )}\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n {quota.quotaCount} quota assignment{quota.quotaCount !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close quota details\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Summary */}\n <div className=\"p-4 border-b border-border\">\n <div className=\"mb-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-muted-foreground\">Usage</span>\n <StatusBadge status={quota.status} />\n </div>\n <ProgressBar percentage={quota.usagePercentage} status={quota.status} />\n <div className=\"flex items-center justify-between mt-2 text-sm\">\n <span className=\"font-medium\">\n {formatNumber(quota.totalUsed)} /{' '}\n {quota.totalLimit != null && quota.totalLimit > 0\n ? formatNumber(quota.totalLimit)\n : '∞'}\n {quota.unit && ` ${quota.unit}`}\n </span>\n <span className=\"text-muted-foreground\">\n {quota.totalLimit != null && quota.totalLimit > 0\n ? `${Math.round(quota.usagePercentage)}% used`\n : 'Unlimited'}\n </span>\n </div>\n </div>\n\n {/* Quota Assignments Breakdown - sorted by expiry date (earliest first) */}\n {quota.quotaCount > 1 && (\n <div className=\"mt-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">Quota Breakdown</h3>\n <div className=\"border border-border rounded-lg overflow-hidden divide-y divide-border\">\n {[...quota.quotaAssignments]\n .sort((a, b) => {\n // Sort by endtime ascending (earliest expiry first)\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((assignment) => {\n const expiryDate = assignment.endtime ? new Date(assignment.endtime) : null;\n const now = new Date();\n const isExpired = expiryDate && expiryDate < now;\n const daysUntilExpiry = expiryDate\n ? Math.ceil((expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000))\n : null;\n const isExpiringSoon =\n daysUntilExpiry !== null && daysUntilExpiry > 0 && daysUntilExpiry <= 7;\n\n return (\n <div\n key={assignment.id}\n className={`flex items-center justify-between px-3 py-2.5 ${\n isExpired\n ? 'bg-muted/30'\n : isExpiringSoon\n ? statusTokens.warning.bg\n : 'bg-background'\n }`}\n >\n <div className=\"flex items-center gap-2\">\n {isExpiringSoon && !isExpired && (\n <span\n className={`size-2 rounded-full ${statusTokens.warning.dot} animate-pulse`}\n />\n )}\n <div className=\"flex flex-col\">\n <span\n className={`text-sm ${isExpired ? 'text-muted-foreground' : 'text-foreground'}`}\n >\n {expiryDate\n ? expiryDate.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })\n : 'No expiry'}\n </span>\n <span\n className={`text-xs ${\n isExpired\n ? 'text-destructive'\n : isExpiringSoon\n ? statusTokens.warning.text\n : 'text-muted-foreground'\n }`}\n >\n {isExpired\n ? 'Expired'\n : daysUntilExpiry !== null\n ? daysUntilExpiry === 0\n ? 'Expires today'\n : daysUntilExpiry === 1\n ? 'Expires tomorrow'\n : `${daysUntilExpiry} days left`\n : 'Never expires'}\n </span>\n </div>\n </div>\n <div className=\"text-right\">\n <span\n className={`text-sm font-semibold ${isExpired ? 'text-muted-foreground' : 'text-foreground'}`}\n >\n {formatNumber(assignment.used)}\n <span className=\"text-muted-foreground font-normal\">\n {' / '}\n {assignment.limit != null && assignment.limit > 0\n ? formatNumber(assignment.limit)\n : '∞'}\n {assignment.unit && ` ${assignment.unit}`}\n </span>\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n\n {/* Live Value History (scraper snapshots — at most one per hour) */}\n {(isSnapshotsLoading || snapshots.length > 0) && (\n <div className=\"p-4 border-t border-border\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">Live Value History</h3>\n {isSnapshotsLoading ? (\n <div className=\"space-y-1.5\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-8 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n {snapshots.map((snap) => (\n <div\n key={snap.id}\n className=\"flex items-center justify-between px-3 py-2 bg-muted/30 rounded-md\"\n >\n <span className=\"text-xs text-muted-foreground\">\n {formatDateTime(snap.snapshotAt)}\n </span>\n <span className=\"text-sm font-medium text-foreground tabular-nums\">\n {formatNumber(snap.value)}\n {quota.unit && (\n <span className=\"text-xs text-muted-foreground ml-1\">{quota.unit}</span>\n )}\n {!snap.noLimit && snap.limit != null && (\n <span className=\"text-xs text-muted-foreground ml-1\">\n / {formatNumber(snap.limit)}\n </span>\n )}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {/* Usage History */}\n <div className=\"p-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">Recent Usage</h3>\n\n {quota.isCollectorBased ? (\n <p className=\"text-sm text-muted-foreground text-center py-6\">\n Usage is tracked via system metrics, individual event records are not available for\n this quota.\n </p>\n ) : isLoading ? (\n <div className=\"space-y-2\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-12 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n ) : usages.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-6\">No usage records found</p>\n ) : (\n <div className=\"space-y-2\">\n {usages.map((usage) => (\n <div\n key={usage.id}\n className=\"flex items-center justify-between p-3 border border-border rounded-lg\"\n >\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {usage.description || 'Usage recorded'}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {formatRelativeTime(usage.timestamp)} • {formatDateTime(usage.timestamp)}\n </p>\n </div>\n <span className=\"text-sm font-medium text-foreground ml-4\">\n {formatNumber(usage.amount)}\n </span>\n </div>\n ))}\n </div>\n )}\n\n {/* Pagination */}\n {!quota.isCollectorBased && (cursorStack.length > 1 || hasNextPage) && (\n <div className=\"flex items-center justify-center gap-2 mt-4\">\n <button\n type=\"button\"\n onClick={handlePrev}\n disabled={cursorStack.length === 1}\n className=\"px-3 py-1.5 text-sm border border-border rounded-md disabled:opacity-50\"\n aria-label=\"Previous usage page\"\n >\n <ChevronLeft className=\"size-4\" />\n </button>\n <span className=\"text-sm text-muted-foreground\">Page {currentPage}</span>\n <button\n type=\"button\"\n onClick={handleNext}\n disabled={!hasNextPage}\n className=\"px-3 py-1.5 text-sm border border-border rounded-md disabled:opacity-50\"\n aria-label=\"Next usage page\"\n >\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"sticky bottom-0 bg-background border-t border-border p-4\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"w-full py-2 px-4 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Close\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Main Usage Page Component\n// ============================================================================\n\nexport const UsagePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const permissions = useBillingPermissions();\n\n // Helper to get billing account ID from URL (for navigation from Overview page)\n const getInitialBillingAccountId = (): string | undefined => {\n if (typeof window === 'undefined') return undefined;\n const urlParams = new URLSearchParams(window.location.search);\n return urlParams.get('billingAccountId') || undefined;\n };\n\n // State - initialize with URL param if present\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string | undefined>(\n getInitialBillingAccountId\n );\n const [selectedQuota, setSelectedQuota] = useState<AggregatedQuota | null>(null);\n\n // Search and filter state\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');\n\n // View mode: 'active' | 'past' | 'all'\n type ViewMode = 'active' | 'past' | 'all';\n const [viewMode, setViewMode] = useState<ViewMode>('active');\n\n // Debounce search query to avoid too many API calls\n const deferredSearchQuery = useDeferredValue(searchQuery);\n\n // Fetch billing accounts\n const { billingAccounts, isLoading: isLoadingAccounts } = useBillingAccountsForUsage();\n\n // Prefer the default account, fall back to first\n const defaultBillingAccountId = (billingAccounts.find((a) => a.isDefault) ?? billingAccounts[0])\n ?.id;\n // ?? so that an explicit empty string (cleared URL param) still falls back to default\n const effectiveBillingAccountId = selectedBillingAccountId ?? defaultBillingAccountId;\n\n const { workspaceId, navigate, basePath } = useBilling();\n\n const navigateTo = useCallback(\n (path: string) => {\n navigate(`${basePath}${path}`);\n },\n [navigate, basePath]\n );\n\n // Fetch usage data - passes searchQuery and viewMode to backend for server-side filtering\n const {\n dashboard,\n pooledQuotas,\n activeSubscriptions,\n pastSubscriptions,\n isLoading,\n isPastSubscriptionsLoading,\n error,\n refetch,\n // Pagination for past subscriptions\n loadMorePastSubscriptions,\n hasMorePastSubscriptions,\n isLoadingMorePast,\n pastSubscriptionsCount,\n } = useEnhancedUsagePage({\n billingAccountId: effectiveBillingAccountId,\n workspaceId,\n searchQuery: deferredSearchQuery || undefined,\n viewMode, // 'active' | 'past' | 'all'\n });\n\n // Filter quotas by status (search is now handled by backend)\n const filterQuotaByStatus = useCallback(\n (quota: AggregatedQuota): boolean => {\n if (statusFilter === 'all') return true;\n if (statusFilter === 'healthy') return quota.status === 'HEALTHY';\n if (statusFilter === 'warning') return quota.status === 'WARNING';\n if (statusFilter === 'critical')\n return quota.status === 'CRITICAL' || quota.status === 'EXHAUSTED';\n return true;\n },\n [statusFilter]\n );\n\n // Filtered data - only status filter is client-side, search is backend\n const filteredPooledQuotas = useMemo(() => {\n return pooledQuotas.filter(filterQuotaByStatus);\n }, [pooledQuotas, filterQuotaByStatus]);\n\n const filteredActiveSubscriptions = useMemo(() => {\n if (statusFilter === 'all') {\n return activeSubscriptions;\n }\n return activeSubscriptions\n .map((sub) => ({\n ...sub,\n aggregatedQuotas: sub.aggregatedQuotas.filter(filterQuotaByStatus),\n }))\n .filter((sub) => sub.aggregatedQuotas.length > 0);\n }, [activeSubscriptions, statusFilter, filterQuotaByStatus]);\n\n const filteredPastSubscriptions = useMemo(() => {\n if (statusFilter === 'all') {\n return pastSubscriptions;\n }\n return pastSubscriptions\n .map((sub) => ({\n ...sub,\n aggregatedQuotas: sub.aggregatedQuotas.filter(filterQuotaByStatus),\n }))\n .filter((sub) => sub.aggregatedQuotas.length > 0);\n }, [pastSubscriptions, statusFilter, filterQuotaByStatus]);\n\n // Subscriptions to display based on view mode\n const displayedSubscriptions = useMemo(() => {\n if (viewMode === 'active') {\n return { active: filteredActiveSubscriptions, past: [] };\n }\n if (viewMode === 'past') {\n return { active: [], past: filteredPastSubscriptions };\n }\n // 'all' - show both\n return { active: filteredActiveSubscriptions, past: filteredPastSubscriptions };\n }, [viewMode, filteredActiveSubscriptions, filteredPastSubscriptions]);\n\n const hasActiveFilters = searchQuery !== '' || statusFilter !== 'all';\n const isSearching = deferredSearchQuery !== searchQuery; // Show loading indicator while debouncing\n\n const clearFilters = () => {\n setSearchQuery('');\n setStatusFilter('all');\n };\n\n // Toggle status filter (click same to deselect)\n const toggleStatusFilter = (filter: StatusFilter) => {\n setStatusFilter((prev) => (prev === filter ? 'all' : filter));\n };\n\n // Permission check\n if (!permissions.canViewUsage) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <AlertTriangle className=\"size-6 text-muted-foreground\" />\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n You don&apos;t have permission to view usage data.\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if ((isLoadingAccounts || isLoading) && !dashboard) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded-lg\" />\n ))}\n </div>\n <div className=\"h-64 bg-muted animate-pulse rounded-lg\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center\">\n <AlertTriangle className=\"size-6 text-destructive\" />\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Failed to load usage data</h2>\n <p className=\"text-sm text-muted-foreground\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n const selectedAccount = billingAccounts.find((a) => a.id === effectiveBillingAccountId);\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header with Billing Account Selector */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.usage.title', 'Usage')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Monitor your resource consumption and quota usage\n </p>\n </div>\n\n <div className=\"flex items-center gap-3\">\n {/* Billing Account Selector */}\n {billingAccounts.length > 1 && (\n <select\n value={effectiveBillingAccountId || ''}\n onChange={(e) => setSelectedBillingAccountId(e.target.value)}\n className=\"px-3 py-2 text-sm border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary w-full sm:min-w-[200px]\"\n >\n {billingAccounts.map((account) => (\n <option key={account.id} value={account.id}>\n {account.name}\n </option>\n ))}\n </select>\n )}\n\n {/* Refresh Button */}\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"p-2 border border-border rounded-md hover:bg-muted/50 transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Search and Filters */}\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n {/* Search Input */}\n <div className=\"relative flex-1 max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground\" />\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={`Search ${viewMode === 'past' ? 'past' : viewMode === 'all' ? 'all' : 'active'} quotas...`}\n className=\"w-full pl-9 pr-3 py-2 text-sm border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n {(searchQuery || isSearching) && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-muted/50\"\n >\n {isSearching ? (\n <RefreshCw className=\"size-3 text-muted-foreground animate-spin\" />\n ) : (\n <X className=\"size-3 text-muted-foreground\" />\n )}\n </button>\n )}\n </div>\n\n {/* View Mode Tabs */}\n <div className=\"flex items-center bg-muted/50 rounded-lg p-1 border border-border\">\n <button\n type=\"button\"\n onClick={() => setViewMode('active')}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-md transition-all ${\n viewMode === 'active'\n ? 'bg-background text-foreground shadow-sm font-medium'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n <Package className=\"size-3.5\" />\n Active\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('past')}\n disabled={isPastSubscriptionsLoading && viewMode !== 'past'}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-md transition-all ${\n viewMode === 'past'\n ? 'bg-background text-foreground shadow-sm font-medium'\n : 'text-muted-foreground hover:text-foreground'\n } ${isPastSubscriptionsLoading && viewMode !== 'past' ? 'opacity-70' : ''}`}\n >\n {isPastSubscriptionsLoading && viewMode === 'past' ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Clock className=\"size-3.5\" />\n )}\n Past\n </button>\n </div>\n\n {/* Active Filters Indicator */}\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={clearFilters}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n <X className=\"size-3\" />\n Clear filters\n </button>\n )}\n </div>\n\n {/* Billing Account Info */}\n {selectedAccount && (\n <div className=\"text-sm text-muted-foreground\">\n Viewing usage for{' '}\n <span className=\"font-medium text-foreground\">{selectedAccount.name}</span>\n </div>\n )}\n\n {/* Overview Stats - Clickable to filter */}\n {dashboard && (\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <StatCard\n label=\"Total Quotas\"\n value={dashboard.totalQuotas}\n icon={<Layers className=\"size-5\" />}\n color=\"blue\"\n isActive={statusFilter === 'all'}\n onClick={() => setStatusFilter('all')}\n />\n <StatCard\n label=\"Healthy\"\n value={dashboard.healthyQuotas}\n icon={<CheckCircle className=\"size-5\" />}\n color=\"emerald\"\n isActive={statusFilter === 'healthy'}\n onClick={() => toggleStatusFilter('healthy')}\n />\n <StatCard\n label=\"Warning\"\n value={dashboard.warningQuotas}\n icon={<AlertCircle className=\"size-5\" />}\n color=\"amber\"\n isActive={statusFilter === 'warning'}\n onClick={() => toggleStatusFilter('warning')}\n />\n <StatCard\n label=\"Critical / Exhausted\"\n value={dashboard.criticalQuotas + dashboard.exhaustedQuotas}\n icon={<AlertTriangle className=\"size-5\" />}\n color=\"red\"\n isActive={statusFilter === 'critical'}\n onClick={() => toggleStatusFilter('critical')}\n />\n </div>\n )}\n\n {/* Request More Quota Banner — shown when any quotas are warning or critical/exhausted */}\n {dashboard &&\n (dashboard.warningQuotas > 0 ||\n dashboard.criticalQuotas > 0 ||\n dashboard.exhaustedQuotas > 0) && (\n <div\n className={`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between p-4 rounded-lg border ${\n dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? `${statusTokens.error.bg} ${statusTokens.error.border}`\n : `${statusTokens.warning.bg} ${statusTokens.warning.border}`\n }`}\n >\n <div className=\"flex items-center gap-3\">\n {dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0 ? (\n <XCircle className={`size-5 shrink-0 ${statusTokens.error.icon}`} />\n ) : (\n <AlertCircle className={`size-5 shrink-0 ${statusTokens.warning.icon}`} />\n )}\n <div>\n <p\n className={`text-sm font-medium ${\n dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? statusTokens.error.text\n : statusTokens.warning.text\n }`}\n >\n {dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? `${dashboard.criticalQuotas + dashboard.exhaustedQuotas} quota${dashboard.criticalQuotas + dashboard.exhaustedQuotas !== 1 ? 's' : ''} exhausted or critical`\n : `${dashboard.warningQuotas} quota${dashboard.warningQuotas !== 1 ? 's' : ''} approaching the limit`}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n Contact support to request a quota increase for your plan.\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/support/tickets')}\n className={`shrink-0 px-4 py-2 text-sm font-medium rounded-md border transition-colors ${\n dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? 'bg-destructive text-destructive-foreground border-destructive hover:bg-destructive/90'\n : 'border-status-warning-border text-status-warning-text hover:bg-status-warning-bg-subtle'\n }`}\n >\n Request more quota\n </button>\n </div>\n )}\n\n {/* Filter Results Info */}\n {(hasActiveFilters || viewMode !== 'active') && (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground flex-wrap\">\n <span>\n Showing{' '}\n {displayedSubscriptions.active.reduce((acc, s) => acc + s.aggregatedQuotas.length, 0) +\n displayedSubscriptions.past.reduce((acc, s) => acc + s.aggregatedQuotas.length, 0) +\n (viewMode !== 'past' ? filteredPooledQuotas.length : 0)}{' '}\n quotas\n </span>\n <span className=\"px-2 py-0.5 bg-primary/10 text-primary rounded text-xs capitalize\">\n {viewMode === 'active' ? 'Active' : viewMode === 'past' ? 'Past' : 'All'} subscriptions\n </span>\n {statusFilter !== 'all' && (\n <span className=\"px-2 py-0.5 bg-muted rounded text-xs capitalize\">\n {statusFilter === 'critical' ? 'Critical / Exhausted' : statusFilter}\n </span>\n )}\n {searchQuery && (\n <span className=\"px-2 py-0.5 bg-muted rounded text-xs\">\n &ldquo;{searchQuery}&rdquo;\n </span>\n )}\n </div>\n )}\n\n {/* Active Subscriptions Section */}\n {displayedSubscriptions.active.length > 0 && (\n <section>\n <h2 className=\"text-lg font-semibold text-foreground mb-4 flex items-center gap-2\">\n <Package className=\"size-5 text-primary\" />\n {viewMode === 'all' ? 'Active Subscription Quotas' : 'Subscription Quotas'}\n </h2>\n <div className=\"space-y-4\">\n {displayedSubscriptions.active.map((subscription) => (\n <SubscriptionSection\n key={subscription.id}\n subscription={subscription}\n onQuotaClick={setSelectedQuota}\n defaultExpanded={true}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Past Subscriptions Section (when viewMode is 'past' or 'all') */}\n {displayedSubscriptions.past.length > 0 && (\n <section>\n <h2 className=\"text-lg font-semibold text-foreground mb-4 flex items-center gap-2\">\n <Clock className=\"size-5 text-muted-foreground\" />\n Past Subscription Quotas\n <span className=\"text-xs font-normal text-muted-foreground\">\n (Canceled / Expired / Upgraded)\n </span>\n {pastSubscriptionsCount > 0 && (\n <span className=\"text-xs font-normal px-2 py-0.5 bg-muted rounded-full\">\n {displayedSubscriptions.past.length}\n {hasMorePastSubscriptions ? '+' : ''} of {pastSubscriptionsCount}\n </span>\n )}\n </h2>\n <div className=\"space-y-4\">\n {displayedSubscriptions.past.map((subscription) => (\n <SubscriptionSection\n key={subscription.id}\n subscription={subscription}\n onQuotaClick={setSelectedQuota}\n defaultExpanded={viewMode === 'past'}\n />\n ))}\n\n {/* Load More Button */}\n {hasMorePastSubscriptions && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={loadMorePastSubscriptions}\n disabled={isLoadingMorePast}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground border border-border rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50\"\n >\n {isLoadingMorePast ? (\n <>\n <RefreshCw className=\"size-4 animate-spin\" />\n Loading…\n </>\n ) : (\n <>\n <ChevronDown className=\"size-4\" />\n Load more past subscriptions\n </>\n )}\n </button>\n </div>\n )}\n </div>\n </section>\n )}\n\n {/* Pooled Quotas Section - Only show for active/all views (pooled quotas are account-level) */}\n {viewMode !== 'past' && filteredPooledQuotas.length > 0 && (\n <section>\n <h2 className=\"text-lg font-semibold text-foreground mb-4 flex items-center gap-2\">\n <Layers className={`size-5 ${statusTokens.info.icon}`} />\n Pooled Quotas\n <span className=\"text-xs font-normal text-muted-foreground\">\n (Shared across all products)\n </span>\n </h2>\n <QuotaTable\n quotas={filteredPooledQuotas}\n showPlanColumn={false}\n onQuotaClick={setSelectedQuota}\n />\n </section>\n )}\n\n {/* Empty State - No quotas at all (when viewing active) */}\n {viewMode === 'active' &&\n activeSubscriptions.length === 0 &&\n pooledQuotas.length === 0 &&\n !hasActiveFilters && (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <Layers className=\"size-6 text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">No active quotas found</h3>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n Quotas will appear here once you have active subscriptions or pooled allocations.\n </p>\n </div>\n )}\n\n {/* Empty State - No past subscriptions */}\n {viewMode === 'past' &&\n pastSubscriptions.length === 0 &&\n !hasActiveFilters &&\n !isPastSubscriptionsLoading && (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <Clock className=\"size-6 text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">No past subscriptions</h3>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n You don&apos;t have any canceled, expired, or upgraded subscriptions yet.\n </p>\n <button\n type=\"button\"\n onClick={() => setViewMode('active')}\n className=\"mt-4 px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n View active subscriptions\n </button>\n </div>\n )}\n\n {/* Loading State for Past Subscriptions */}\n {viewMode === 'past' && isPastSubscriptionsLoading && (\n <div className=\"space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded-lg\" />\n ))}\n </div>\n )}\n\n {/* Empty State - No results from search/filter */}\n {hasActiveFilters &&\n displayedSubscriptions.active.length === 0 &&\n displayedSubscriptions.past.length === 0 &&\n (viewMode === 'past' || filteredPooledQuotas.length === 0) && (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n {statusFilter !== 'all' && !searchQuery ? (\n <StatusIcon\n status={\n (statusFilter === 'healthy'\n ? 'HEALTHY'\n : statusFilter === 'warning'\n ? 'WARNING'\n : 'CRITICAL') as QuotaStatus\n }\n className=\"size-6\"\n />\n ) : (\n <Search className=\"size-6 text-muted-foreground\" />\n )}\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">\n {statusFilter !== 'all' && !searchQuery\n ? `No ${statusFilter === 'critical' ? 'critical or exhausted' : statusFilter} quotas`\n : 'No matching quotas'}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n {statusFilter !== 'all' && !searchQuery ? (\n <>\n All your quotas are in a different status.\n {dashboard && (\n <span className=\"block mt-1\">\n You have {dashboard.healthyQuotas} healthy, {dashboard.warningQuotas} warning,\n and {dashboard.criticalQuotas + dashboard.exhaustedQuotas} critical quotas.\n </span>\n )}\n </>\n ) : (\n <>\n No quotas match your current search or filter criteria in{' '}\n {viewMode === 'past' ? 'past' : viewMode === 'all' ? 'any' : 'active'}{' '}\n subscriptions.\n {viewMode === 'active' && !statusFilter.includes('all') && (\n <span className=\"block mt-1\">\n Try switching to &ldquo;Past&rdquo; or &ldquo;All&rdquo; to search across more\n subscriptions.\n </span>\n )}\n </>\n )}\n </p>\n <div className=\"flex gap-2 mt-4\">\n <button\n type=\"button\"\n onClick={clearFilters}\n className=\"px-4 py-2 text-sm font-medium border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n {statusFilter !== 'all' ? 'Show all quotas' : 'Clear filters'}\n </button>\n {viewMode === 'active' && searchQuery && (\n <button\n type=\"button\"\n onClick={() => setViewMode('all')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Search all\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Quota Detail Modal */}\n {selectedQuota && (\n <QuotaDetailModal quota={selectedQuota} onClose={() => setSelectedQuota(null)} />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"UsagePage.js","names":[],"sources":["../../../../../src/billing/modules/usage/pages/UsagePage.tsx"],"sourcesContent":["/**\n * Usage Module - Enhanced Usage Page\n * Displays usage dashboard with billing account selector, aggregated quotas by subscription,\n * pooled quotas, and lazy-loaded past subscriptions\n */\n\nimport { useState, useMemo, useDeferredValue, useCallback, type FC } from 'react';\nimport {\n ChevronDown,\n ChevronRight,\n AlertTriangle,\n CheckCircle,\n XCircle,\n AlertCircle,\n Layers,\n Package,\n Clock,\n RefreshCw,\n Info,\n ChevronLeft,\n X,\n Search,\n} from 'lucide-react';\nimport {\n useEnhancedUsagePage,\n useBillingAccountsForUsage,\n useQuotaUsageDetails,\n useWorkspaceQuotaHistory,\n type AggregatedQuota,\n type SubscriptionWithAggregatedQuotas,\n} from '../hooks/useUsage';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { formatNumber, formatRelativeTime, formatDateTime } from '../../../shared/utils/format';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport type { QuotaStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface QuotaDetailModalProps {\n quota: AggregatedQuota;\n onClose: () => void;\n}\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\nconst StatusIcon: FC<{ status: QuotaStatus; className?: string }> = ({\n status,\n className = 'size-4',\n}) => {\n switch (status) {\n case 'EXHAUSTED':\n return <XCircle className={`${className} ${statusTokens.error.icon}`} />;\n case 'CRITICAL':\n return <AlertTriangle className={`${className} ${statusTokens.error.icon}`} />;\n case 'WARNING':\n return <AlertCircle className={`${className} ${statusTokens.warning.icon}`} />;\n case 'HEALTHY':\n return <CheckCircle className={`${className} ${statusTokens.success.icon}`} />;\n default:\n return <Info className={`${className} ${statusTokens.neutral.icon}`} />;\n }\n};\n\nconst StatusBadge: FC<{ status: QuotaStatus }> = ({ status }) => {\n const colors: Record<QuotaStatus, string> = {\n EXHAUSTED: `${statusTokens.error.bg} ${statusTokens.error.text}`,\n CRITICAL: `${statusTokens.error.bg} ${statusTokens.error.text}`,\n WARNING: `${statusTokens.warning.bg} ${statusTokens.warning.text}`,\n HEALTHY: `${statusTokens.success.bg} ${statusTokens.success.text}`,\n INACTIVE: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n };\n\n return (\n <span className={`px-2 py-0.5 text-xs font-medium rounded ${colors[status]}`}>{status}</span>\n );\n};\n\nconst ProgressBar: FC<{ percentage: number; status: QuotaStatus }> = ({ percentage, status }) => {\n const clampedPercentage = Math.min(100, Math.max(0, percentage));\n\n const barColors: Record<QuotaStatus, string> = {\n EXHAUSTED: statusTokens.error.dot,\n CRITICAL: statusTokens.error.dot,\n WARNING: statusTokens.warning.dot,\n HEALTHY: statusTokens.success.dot,\n INACTIVE: statusTokens.neutral.dot,\n };\n\n return (\n <div className=\"w-full bg-muted rounded-full h-2.5 overflow-hidden\">\n <div\n className={`h-full rounded-full transition-all duration-300 ${barColors[status]}`}\n style={{ width: `${clampedPercentage}%` }}\n />\n </div>\n );\n};\n\n// ============================================================================\n// Stat Card Component\n// ============================================================================\n\ntype StatusFilter = 'all' | 'healthy' | 'warning' | 'critical';\n\ninterface StatCardProps {\n label: string;\n value: string | number;\n icon: React.ReactNode;\n color: 'blue' | 'emerald' | 'amber' | 'red' | 'gray';\n isActive?: boolean;\n onClick?: () => void;\n}\n\nconst StatCard: FC<StatCardProps> = ({ label, value, icon, color, isActive, onClick }) => {\n // Map color prop to semantic status tokens\n const colorToStatus = {\n blue: statusTokens.info,\n emerald: statusTokens.success,\n amber: statusTokens.warning,\n red: statusTokens.error,\n gray: statusTokens.neutral,\n };\n\n const tokens = colorToStatus[color];\n\n const colors = {\n blue: `${tokens.bg} ${tokens.border}`,\n emerald: `${tokens.bg} ${tokens.border}`,\n amber: `${tokens.bg} ${tokens.border}`,\n red: `${tokens.bg} ${tokens.border}`,\n gray: `${tokens.bg} ${tokens.border}`,\n };\n\n // Active ring colors - using semantic dot color for consistency\n const activeColors = {\n blue: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.info.border.replace('border-', 'ring-')}`,\n emerald: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.success.border.replace('border-', 'ring-')}`,\n amber: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.warning.border.replace('border-', 'ring-')}`,\n red: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.error.border.replace('border-', 'ring-')}`,\n gray: `ring-2 ring-offset-2 dark:ring-offset-background ${statusTokens.neutral.border.replace('border-', 'ring-')}`,\n };\n\n const iconColors = {\n blue: statusTokens.info.icon,\n emerald: statusTokens.success.icon,\n amber: statusTokens.warning.icon,\n red: statusTokens.error.icon,\n gray: statusTokens.neutral.icon,\n };\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={`border rounded-lg p-4 w-full text-left transition-all ${colors[color]} ${\n onClick ? 'cursor-pointer hover:shadow-md' : ''\n } ${isActive ? activeColors[color] : ''}`}\n >\n <div className=\"flex items-center gap-2 mb-2\">\n <span className={iconColors[color]}>{icon}</span>\n <span className=\"text-sm text-muted-foreground\">{label}</span>\n </div>\n <p className=\"text-2xl font-bold text-foreground\">{value}</p>\n </button>\n );\n};\n\n// ============================================================================\n// Quota Table Components\n// ============================================================================\n\ninterface QuotaTableRowProps {\n quota: AggregatedQuota;\n planLabel?: string;\n onClick: () => void;\n}\n\nconst QuotaTableRow: FC<QuotaTableRowProps> = ({ quota, planLabel, onClick }) => {\n const hasLimit = quota.totalLimit != null && quota.totalLimit > 0 && !quota.noLimit;\n\n return (\n <tr\n onClick={onClick}\n className=\"cursor-pointer hover:bg-muted/40 transition-colors border-b border-border last:border-b-0\"\n >\n {/* Quota name */}\n <td className=\"px-4 py-3\">\n <span className=\"text-sm font-medium text-primary hover:underline capitalize\">\n {quota.displayName || quota.name.replace(/_/g, ' ')}\n </span>\n <span className=\"block text-xs font-mono text-muted-foreground mt-0.5\">{quota.name}</span>\n {quota.description && (\n <span className=\"block text-xs text-muted-foreground mt-0.5 max-w-[220px] truncate\">\n {quota.description}\n </span>\n )}\n {quota.quotaCount > 1 && (\n <span className=\"block text-xs text-muted-foreground mt-0.5\">\n {quota.quotaCount} assignments\n </span>\n )}\n </td>\n\n {/* Plan / type */}\n {planLabel !== undefined && (\n <td className=\"px-4 py-3 text-sm text-muted-foreground whitespace-nowrap\">{planLabel}</td>\n )}\n\n {/* Current usage: value + inline progress bar */}\n <td className=\"px-4 py-3\">\n <div className=\"space-y-1.5\">\n <span className=\"text-sm text-foreground tabular-nums\">\n {formatNumber(quota.totalUsed)}&nbsp;/&nbsp;\n {hasLimit ? formatNumber(quota.totalLimit) : '∞'}\n {quota.unit ? ` ${quota.unit}` : ''}\n </span>\n <ProgressBar percentage={hasLimit ? quota.usagePercentage : 0} status={quota.status} />\n </div>\n </td>\n\n {/* Limit */}\n <td className=\"px-4 py-3 text-sm text-muted-foreground tabular-nums whitespace-nowrap\">\n {hasLimit ? formatNumber(quota.totalLimit) : '∞'}\n </td>\n\n {/* Status */}\n <td className=\"px-4 py-3\">\n <StatusBadge status={quota.status} />\n </td>\n </tr>\n );\n};\n\ninterface QuotaTableProps {\n quotas: AggregatedQuota[];\n /** When provided, renders a \"Plan\" column with this value per row */\n planLabel?: string;\n showPlanColumn?: boolean;\n onQuotaClick: (quota: AggregatedQuota) => void;\n}\n\nconst QuotaTable: FC<QuotaTableProps> = ({\n quotas,\n planLabel,\n showPlanColumn = true,\n onQuotaClick,\n}) => (\n <div className=\"overflow-x-auto rounded-lg border border-border bg-card\">\n <table className=\"w-full text-left\">\n <thead>\n <tr className=\"border-b border-border bg-muted/30\">\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Quotas\n </th>\n {showPlanColumn && (\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Plan\n </th>\n )}\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Current Usage\n </th>\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Limit\n </th>\n <th className=\"px-4 py-2.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Status\n </th>\n </tr>\n </thead>\n <tbody>\n {quotas.map((quota) => (\n <QuotaTableRow\n key={quota.name}\n quota={quota}\n planLabel={showPlanColumn ? planLabel : undefined}\n onClick={() => onQuotaClick(quota)}\n />\n ))}\n </tbody>\n </table>\n </div>\n);\n\n// ============================================================================\n// Subscription Section Component\n// ============================================================================\n\ninterface SubscriptionSectionProps {\n subscription: SubscriptionWithAggregatedQuotas;\n onQuotaClick: (quota: AggregatedQuota) => void;\n defaultExpanded?: boolean;\n}\n\nconst SubscriptionSection: FC<SubscriptionSectionProps> = ({\n subscription,\n onQuotaClick,\n defaultExpanded = true,\n}) => {\n const [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\n const statusColors: Record<string, string> = {\n active: `${statusTokens.success.bg} ${statusTokens.success.text}`,\n trialing: `${statusTokens.info.bg} ${statusTokens.info.text}`,\n past_due: `${statusTokens.warning.bg} ${statusTokens.warning.text}`,\n canceled: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n expired: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n upgraded: `${statusTokens.primary.bg} ${statusTokens.primary.text}`,\n };\n\n return (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"w-full flex items-center justify-between p-4 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <Package className=\"size-5 text-primary\" />\n <div className=\"text-left\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-foreground\">Subscription</span>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded capitalize ${statusColors[subscription.status.toLowerCase()] || statusColors.active}`}\n >\n {subscription.status}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {subscription.aggregatedQuotas.length} quota type\n {subscription.aggregatedQuotas.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n {isExpanded ? (\n <ChevronDown className=\"size-5 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"size-5 text-muted-foreground\" />\n )}\n </button>\n\n {isExpanded && subscription.aggregatedQuotas.length > 0 && (\n <div className=\"border-t border-border\">\n <QuotaTable\n quotas={subscription.aggregatedQuotas}\n planLabel={subscription.planName || subscription.productId || undefined}\n showPlanColumn={!!(subscription.planName || subscription.productId)}\n onQuotaClick={onQuotaClick}\n />\n </div>\n )}\n\n {isExpanded && subscription.aggregatedQuotas.length === 0 && (\n <div className=\"border-t border-border p-6 text-center\">\n <p className=\"text-sm text-muted-foreground\">No quotas assigned to this subscription</p>\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Quota Detail Modal Component\n// ============================================================================\n\nconst QuotaDetailModal: FC<QuotaDetailModalProps> = ({ quota, onClose }) => {\n // Cursor stack: [null] = page 1, [null, c1] = page 2, etc.\n const [cursorStack, setCursorStack] = useState<Array<string | null>>([null]);\n const currentCursor = cursorStack[cursorStack.length - 1] ?? null;\n const currentPage = cursorStack.length;\n const { workspaceId } = useBilling();\n\n // Get usage details for the first quota assignment directly from activity-svc\n const firstAssignmentId = quota.quotaAssignments[0]?.id;\n const { usages, hasNextPage, nextCursor, isLoading } = useQuotaUsageDetails(\n firstAssignmentId,\n 10,\n currentCursor,\n workspaceId\n );\n\n // Snapshot history from quota scraper (activity-svc) — shows live value trend\n const { snapshots, isLoading: isSnapshotsLoading } = useWorkspaceQuotaHistory(\n workspaceId,\n quota.name,\n 24\n );\n\n const handleNext = () => {\n if (nextCursor) setCursorStack((prev) => [...prev, nextCursor]);\n };\n\n const handlePrev = () => {\n if (cursorStack.length > 1) setCursorStack((prev) => prev.slice(0, -1));\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={onClose} />\n <div\n className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-auto\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"quota-detail-title\"\n >\n {/* Header */}\n <div className=\"sticky top-0 bg-background border-b border-border p-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <StatusIcon status={quota.status} className=\"size-5\" />\n <div>\n <h2 id=\"quota-detail-title\" className=\"text-lg font-semibold capitalize\">\n {quota.displayName || quota.name.replace(/_/g, ' ')}\n </h2>\n <p className=\"text-xs font-mono text-muted-foreground\">{quota.name}</p>\n {quota.description && (\n <p className=\"text-sm text-muted-foreground mt-0.5\">{quota.description}</p>\n )}\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n {quota.quotaCount} quota assignment{quota.quotaCount !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close quota details\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Summary */}\n <div className=\"p-4 border-b border-border\">\n <div className=\"mb-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm text-muted-foreground\">Usage</span>\n <StatusBadge status={quota.status} />\n </div>\n <ProgressBar percentage={quota.usagePercentage} status={quota.status} />\n <div className=\"flex items-center justify-between mt-2 text-sm\">\n <span className=\"font-medium\">\n {formatNumber(quota.totalUsed)} /{' '}\n {quota.totalLimit != null && quota.totalLimit > 0\n ? formatNumber(quota.totalLimit)\n : '∞'}\n {quota.unit && ` ${quota.unit}`}\n </span>\n <span className=\"text-muted-foreground\">\n {quota.totalLimit != null && quota.totalLimit > 0\n ? `${Math.round(quota.usagePercentage)}% used`\n : 'Unlimited'}\n </span>\n </div>\n </div>\n\n {/* Quota Assignments Breakdown - sorted by expiry date (earliest first) */}\n {quota.quotaCount > 1 && (\n <div className=\"mt-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">Quota Breakdown</h3>\n <div className=\"border border-border rounded-lg overflow-hidden divide-y divide-border\">\n {[...quota.quotaAssignments]\n .sort((a, b) => {\n // Sort by endtime ascending (earliest expiry first)\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((assignment) => {\n const expiryDate = assignment.endtime ? new Date(assignment.endtime) : null;\n const now = new Date();\n const isExpired = expiryDate && expiryDate < now;\n const daysUntilExpiry = expiryDate\n ? Math.ceil((expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000))\n : null;\n const isExpiringSoon =\n daysUntilExpiry !== null && daysUntilExpiry > 0 && daysUntilExpiry <= 7;\n\n return (\n <div\n key={assignment.id}\n className={`flex items-center justify-between px-3 py-2.5 ${\n isExpired\n ? 'bg-muted/30'\n : isExpiringSoon\n ? statusTokens.warning.bg\n : 'bg-background'\n }`}\n >\n <div className=\"flex items-center gap-2\">\n {isExpiringSoon && !isExpired && (\n <span\n className={`size-2 rounded-full ${statusTokens.warning.dot} animate-pulse`}\n />\n )}\n <div className=\"flex flex-col\">\n <span\n className={`text-sm ${isExpired ? 'text-muted-foreground' : 'text-foreground'}`}\n >\n {expiryDate\n ? expiryDate.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })\n : 'No expiry'}\n </span>\n <span\n className={`text-xs ${\n isExpired\n ? 'text-destructive'\n : isExpiringSoon\n ? statusTokens.warning.text\n : 'text-muted-foreground'\n }`}\n >\n {isExpired\n ? 'Expired'\n : daysUntilExpiry !== null\n ? daysUntilExpiry === 0\n ? 'Expires today'\n : daysUntilExpiry === 1\n ? 'Expires tomorrow'\n : `${daysUntilExpiry} days left`\n : 'Never expires'}\n </span>\n </div>\n </div>\n <div className=\"text-right\">\n <span\n className={`text-sm font-semibold ${isExpired ? 'text-muted-foreground' : 'text-foreground'}`}\n >\n {formatNumber(assignment.used)}\n <span className=\"text-muted-foreground font-normal\">\n {' / '}\n {assignment.limit != null && assignment.limit > 0\n ? formatNumber(assignment.limit)\n : '∞'}\n {assignment.unit && ` ${assignment.unit}`}\n </span>\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n\n {/* Live Value History (scraper snapshots — at most one per hour) */}\n {(isSnapshotsLoading || snapshots.length > 0) && (\n <div className=\"p-4 border-t border-border\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">Live Value History</h3>\n {isSnapshotsLoading ? (\n <div className=\"space-y-1.5\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-8 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n {snapshots.map((snap) => (\n <div\n key={snap.id}\n className=\"flex items-center justify-between px-3 py-2 bg-muted/30 rounded-md\"\n >\n <span className=\"text-xs text-muted-foreground\">\n {formatDateTime(snap.snapshotAt)}\n </span>\n <span className=\"text-sm font-medium text-foreground tabular-nums\">\n {formatNumber(snap.value)}\n {quota.unit && (\n <span className=\"text-xs text-muted-foreground ml-1\">{quota.unit}</span>\n )}\n {!snap.noLimit && snap.limit != null && (\n <span className=\"text-xs text-muted-foreground ml-1\">\n / {formatNumber(snap.limit)}\n </span>\n )}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {/* Usage History */}\n <div className=\"p-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">Recent Usage</h3>\n\n {quota.isCollectorBased ? (\n <p className=\"text-sm text-muted-foreground text-center py-6\">\n Usage is tracked via system metrics, individual event records are not available for\n this quota.\n </p>\n ) : isLoading ? (\n <div className=\"space-y-2\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-12 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n ) : usages.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-6\">No usage records found</p>\n ) : (\n <div className=\"space-y-2\">\n {usages.map((usage) => (\n <div\n key={usage.id}\n className=\"flex items-center justify-between p-3 border border-border rounded-lg\"\n >\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {usage.description || 'Usage recorded'}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {formatRelativeTime(usage.timestamp)} • {formatDateTime(usage.timestamp)}\n </p>\n </div>\n <span className=\"text-sm font-medium text-foreground ml-4\">\n {formatNumber(usage.amount)}\n </span>\n </div>\n ))}\n </div>\n )}\n\n {/* Pagination */}\n {!quota.isCollectorBased && (cursorStack.length > 1 || hasNextPage) && (\n <div className=\"flex items-center justify-center gap-2 mt-4\">\n <button\n type=\"button\"\n onClick={handlePrev}\n disabled={cursorStack.length === 1}\n className=\"px-3 py-1.5 text-sm border border-border rounded-md disabled:opacity-50\"\n aria-label=\"Previous usage page\"\n >\n <ChevronLeft className=\"size-4\" />\n </button>\n <span className=\"text-sm text-muted-foreground\">Page {currentPage}</span>\n <button\n type=\"button\"\n onClick={handleNext}\n disabled={!hasNextPage}\n className=\"px-3 py-1.5 text-sm border border-border rounded-md disabled:opacity-50\"\n aria-label=\"Next usage page\"\n >\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"sticky bottom-0 bg-background border-t border-border p-4\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"w-full py-2 px-4 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Close\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Main Usage Page Component\n// ============================================================================\n\nexport const UsagePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const permissions = useBillingPermissions();\n\n // Helper to get billing account ID from URL (for navigation from Overview page)\n const getInitialBillingAccountId = (): string | undefined => {\n if (typeof window === 'undefined') return undefined;\n const urlParams = new URLSearchParams(window.location.search);\n return urlParams.get('billingAccountId') || undefined;\n };\n\n // State - initialize with URL param if present\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string | undefined>(\n getInitialBillingAccountId\n );\n const [selectedQuota, setSelectedQuota] = useState<AggregatedQuota | null>(null);\n\n // Search and filter state\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');\n\n // View mode: 'active' | 'past' | 'all'\n type ViewMode = 'active' | 'past' | 'all';\n const [viewMode, setViewMode] = useState<ViewMode>('active');\n\n // Debounce search query to avoid too many API calls\n const deferredSearchQuery = useDeferredValue(searchQuery);\n\n // Fetch billing accounts\n const { billingAccounts, isLoading: isLoadingAccounts } = useBillingAccountsForUsage();\n\n // Prefer the default account, fall back to first\n const defaultBillingAccountId = (billingAccounts.find((a) => a.isDefault) ?? billingAccounts[0])\n ?.id;\n // ?? so that an explicit empty string (cleared URL param) still falls back to default\n const effectiveBillingAccountId = selectedBillingAccountId ?? defaultBillingAccountId;\n\n const { workspaceId, navigate } = useBilling();\n\n // Fetch usage data - passes searchQuery and viewMode to backend for server-side filtering\n const {\n dashboard,\n pooledQuotas,\n activeSubscriptions,\n pastSubscriptions,\n isLoading,\n isPastSubscriptionsLoading,\n error,\n refetch,\n // Pagination for past subscriptions\n loadMorePastSubscriptions,\n hasMorePastSubscriptions,\n isLoadingMorePast,\n pastSubscriptionsCount,\n } = useEnhancedUsagePage({\n billingAccountId: effectiveBillingAccountId,\n workspaceId,\n searchQuery: deferredSearchQuery || undefined,\n viewMode, // 'active' | 'past' | 'all'\n });\n\n // Filter quotas by status (search is now handled by backend)\n const filterQuotaByStatus = useCallback(\n (quota: AggregatedQuota): boolean => {\n if (statusFilter === 'all') return true;\n if (statusFilter === 'healthy') return quota.status === 'HEALTHY';\n if (statusFilter === 'warning') return quota.status === 'WARNING';\n if (statusFilter === 'critical')\n return quota.status === 'CRITICAL' || quota.status === 'EXHAUSTED';\n return true;\n },\n [statusFilter]\n );\n\n // Filtered data - only status filter is client-side, search is backend\n const filteredPooledQuotas = useMemo(() => {\n return pooledQuotas.filter(filterQuotaByStatus);\n }, [pooledQuotas, filterQuotaByStatus]);\n\n const filteredActiveSubscriptions = useMemo(() => {\n if (statusFilter === 'all') {\n return activeSubscriptions;\n }\n return activeSubscriptions\n .map((sub) => ({\n ...sub,\n aggregatedQuotas: sub.aggregatedQuotas.filter(filterQuotaByStatus),\n }))\n .filter((sub) => sub.aggregatedQuotas.length > 0);\n }, [activeSubscriptions, statusFilter, filterQuotaByStatus]);\n\n const filteredPastSubscriptions = useMemo(() => {\n if (statusFilter === 'all') {\n return pastSubscriptions;\n }\n return pastSubscriptions\n .map((sub) => ({\n ...sub,\n aggregatedQuotas: sub.aggregatedQuotas.filter(filterQuotaByStatus),\n }))\n .filter((sub) => sub.aggregatedQuotas.length > 0);\n }, [pastSubscriptions, statusFilter, filterQuotaByStatus]);\n\n // Subscriptions to display based on view mode\n const displayedSubscriptions = useMemo(() => {\n if (viewMode === 'active') {\n return { active: filteredActiveSubscriptions, past: [] };\n }\n if (viewMode === 'past') {\n return { active: [], past: filteredPastSubscriptions };\n }\n // 'all' - show both\n return { active: filteredActiveSubscriptions, past: filteredPastSubscriptions };\n }, [viewMode, filteredActiveSubscriptions, filteredPastSubscriptions]);\n\n const hasActiveFilters = searchQuery !== '' || statusFilter !== 'all';\n const isSearching = deferredSearchQuery !== searchQuery; // Show loading indicator while debouncing\n\n const clearFilters = () => {\n setSearchQuery('');\n setStatusFilter('all');\n };\n\n // Toggle status filter (click same to deselect)\n const toggleStatusFilter = (filter: StatusFilter) => {\n setStatusFilter((prev) => (prev === filter ? 'all' : filter));\n };\n\n // Permission check\n if (!permissions.canViewUsage) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <AlertTriangle className=\"size-6 text-muted-foreground\" />\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n You don&apos;t have permission to view usage data.\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if ((isLoadingAccounts || isLoading) && !dashboard) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded-lg\" />\n ))}\n </div>\n <div className=\"h-64 bg-muted animate-pulse rounded-lg\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center\">\n <AlertTriangle className=\"size-6 text-destructive\" />\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Failed to load usage data</h2>\n <p className=\"text-sm text-muted-foreground\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n const selectedAccount = billingAccounts.find((a) => a.id === effectiveBillingAccountId);\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header with Billing Account Selector */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.usage.title', 'Usage')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Monitor your resource consumption and quota usage\n </p>\n </div>\n\n <div className=\"flex items-center gap-3\">\n {/* Billing Account Selector */}\n {billingAccounts.length > 1 && (\n <select\n value={effectiveBillingAccountId || ''}\n onChange={(e) => setSelectedBillingAccountId(e.target.value)}\n className=\"px-3 py-2 text-sm border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary w-full sm:min-w-[200px]\"\n >\n {billingAccounts.map((account) => (\n <option key={account.id} value={account.id}>\n {account.name}\n </option>\n ))}\n </select>\n )}\n\n {/* Refresh Button */}\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"p-2 border border-border rounded-md hover:bg-muted/50 transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw className=\"size-4\" />\n </button>\n </div>\n </div>\n\n {/* Search and Filters */}\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n {/* Search Input */}\n <div className=\"relative flex-1 max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground\" />\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={`Search ${viewMode === 'past' ? 'past' : viewMode === 'all' ? 'all' : 'active'} quotas...`}\n className=\"w-full pl-9 pr-3 py-2 text-sm border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n {(searchQuery || isSearching) && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-muted/50\"\n >\n {isSearching ? (\n <RefreshCw className=\"size-3 text-muted-foreground animate-spin\" />\n ) : (\n <X className=\"size-3 text-muted-foreground\" />\n )}\n </button>\n )}\n </div>\n\n {/* View Mode Tabs */}\n <div className=\"flex items-center bg-muted/50 rounded-lg p-1 border border-border\">\n <button\n type=\"button\"\n onClick={() => setViewMode('active')}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-md transition-all ${\n viewMode === 'active'\n ? 'bg-background text-foreground shadow-sm font-medium'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n <Package className=\"size-3.5\" />\n Active\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('past')}\n disabled={isPastSubscriptionsLoading && viewMode !== 'past'}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-md transition-all ${\n viewMode === 'past'\n ? 'bg-background text-foreground shadow-sm font-medium'\n : 'text-muted-foreground hover:text-foreground'\n } ${isPastSubscriptionsLoading && viewMode !== 'past' ? 'opacity-70' : ''}`}\n >\n {isPastSubscriptionsLoading && viewMode === 'past' ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Clock className=\"size-3.5\" />\n )}\n Past\n </button>\n </div>\n\n {/* Active Filters Indicator */}\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={clearFilters}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n <X className=\"size-3\" />\n Clear filters\n </button>\n )}\n </div>\n\n {/* Billing Account Info */}\n {selectedAccount && (\n <div className=\"text-sm text-muted-foreground\">\n Viewing usage for{' '}\n <span className=\"font-medium text-foreground\">{selectedAccount.name}</span>\n </div>\n )}\n\n {/* Overview Stats - Clickable to filter */}\n {dashboard && (\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <StatCard\n label=\"Total Quotas\"\n value={dashboard.totalQuotas}\n icon={<Layers className=\"size-5\" />}\n color=\"blue\"\n isActive={statusFilter === 'all'}\n onClick={() => setStatusFilter('all')}\n />\n <StatCard\n label=\"Healthy\"\n value={dashboard.healthyQuotas}\n icon={<CheckCircle className=\"size-5\" />}\n color=\"emerald\"\n isActive={statusFilter === 'healthy'}\n onClick={() => toggleStatusFilter('healthy')}\n />\n <StatCard\n label=\"Warning\"\n value={dashboard.warningQuotas}\n icon={<AlertCircle className=\"size-5\" />}\n color=\"amber\"\n isActive={statusFilter === 'warning'}\n onClick={() => toggleStatusFilter('warning')}\n />\n <StatCard\n label=\"Critical / Exhausted\"\n value={dashboard.criticalQuotas + dashboard.exhaustedQuotas}\n icon={<AlertTriangle className=\"size-5\" />}\n color=\"red\"\n isActive={statusFilter === 'critical'}\n onClick={() => toggleStatusFilter('critical')}\n />\n </div>\n )}\n\n {/* Request More Quota Banner — shown when any quotas are warning or critical/exhausted */}\n {dashboard &&\n (dashboard.warningQuotas > 0 ||\n dashboard.criticalQuotas > 0 ||\n dashboard.exhaustedQuotas > 0) && (\n <div\n className={`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between p-4 rounded-lg border ${\n dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? `${statusTokens.error.bg} ${statusTokens.error.border}`\n : `${statusTokens.warning.bg} ${statusTokens.warning.border}`\n }`}\n >\n <div className=\"flex items-center gap-3\">\n {dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0 ? (\n <XCircle className={`size-5 shrink-0 ${statusTokens.error.icon}`} />\n ) : (\n <AlertCircle className={`size-5 shrink-0 ${statusTokens.warning.icon}`} />\n )}\n <div>\n <p\n className={`text-sm font-medium ${\n dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? statusTokens.error.text\n : statusTokens.warning.text\n }`}\n >\n {dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? `${dashboard.criticalQuotas + dashboard.exhaustedQuotas} quota${dashboard.criticalQuotas + dashboard.exhaustedQuotas !== 1 ? 's' : ''} exhausted or critical`\n : `${dashboard.warningQuotas} quota${dashboard.warningQuotas !== 1 ? 's' : ''} approaching the limit`}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n Contact support to request a quota increase for your plan.\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => navigate('/support/tickets')}\n className={`shrink-0 px-4 py-2 text-sm font-medium rounded-md border transition-colors ${\n dashboard.criticalQuotas > 0 || dashboard.exhaustedQuotas > 0\n ? 'bg-destructive text-destructive-foreground border-destructive hover:bg-destructive/90'\n : 'border-status-warning-border text-status-warning-text hover:bg-status-warning-bg-subtle'\n }`}\n >\n Request more quota\n </button>\n </div>\n )}\n\n {/* Filter Results Info */}\n {(hasActiveFilters || viewMode !== 'active') && (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground flex-wrap\">\n <span>\n Showing{' '}\n {displayedSubscriptions.active.reduce((acc, s) => acc + s.aggregatedQuotas.length, 0) +\n displayedSubscriptions.past.reduce((acc, s) => acc + s.aggregatedQuotas.length, 0) +\n (viewMode !== 'past' ? filteredPooledQuotas.length : 0)}{' '}\n quotas\n </span>\n <span className=\"px-2 py-0.5 bg-primary/10 text-primary rounded text-xs capitalize\">\n {viewMode === 'active' ? 'Active' : viewMode === 'past' ? 'Past' : 'All'} subscriptions\n </span>\n {statusFilter !== 'all' && (\n <span className=\"px-2 py-0.5 bg-muted rounded text-xs capitalize\">\n {statusFilter === 'critical' ? 'Critical / Exhausted' : statusFilter}\n </span>\n )}\n {searchQuery && (\n <span className=\"px-2 py-0.5 bg-muted rounded text-xs\">\n &ldquo;{searchQuery}&rdquo;\n </span>\n )}\n </div>\n )}\n\n {/* Active Subscriptions Section */}\n {displayedSubscriptions.active.length > 0 && (\n <section>\n <h2 className=\"text-lg font-semibold text-foreground mb-4 flex items-center gap-2\">\n <Package className=\"size-5 text-primary\" />\n {viewMode === 'all' ? 'Active Subscription Quotas' : 'Subscription Quotas'}\n </h2>\n <div className=\"space-y-4\">\n {displayedSubscriptions.active.map((subscription) => (\n <SubscriptionSection\n key={subscription.id}\n subscription={subscription}\n onQuotaClick={setSelectedQuota}\n defaultExpanded={true}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Past Subscriptions Section (when viewMode is 'past' or 'all') */}\n {displayedSubscriptions.past.length > 0 && (\n <section>\n <h2 className=\"text-lg font-semibold text-foreground mb-4 flex items-center gap-2\">\n <Clock className=\"size-5 text-muted-foreground\" />\n Past Subscription Quotas\n <span className=\"text-xs font-normal text-muted-foreground\">\n (Canceled / Expired / Upgraded)\n </span>\n {pastSubscriptionsCount > 0 && (\n <span className=\"text-xs font-normal px-2 py-0.5 bg-muted rounded-full\">\n {displayedSubscriptions.past.length}\n {hasMorePastSubscriptions ? '+' : ''} of {pastSubscriptionsCount}\n </span>\n )}\n </h2>\n <div className=\"space-y-4\">\n {displayedSubscriptions.past.map((subscription) => (\n <SubscriptionSection\n key={subscription.id}\n subscription={subscription}\n onQuotaClick={setSelectedQuota}\n defaultExpanded={viewMode === 'past'}\n />\n ))}\n\n {/* Load More Button */}\n {hasMorePastSubscriptions && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={loadMorePastSubscriptions}\n disabled={isLoadingMorePast}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground border border-border rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50\"\n >\n {isLoadingMorePast ? (\n <>\n <RefreshCw className=\"size-4 animate-spin\" />\n Loading…\n </>\n ) : (\n <>\n <ChevronDown className=\"size-4\" />\n Load more past subscriptions\n </>\n )}\n </button>\n </div>\n )}\n </div>\n </section>\n )}\n\n {/* Pooled Quotas Section - Only show for active/all views (pooled quotas are account-level) */}\n {viewMode !== 'past' && filteredPooledQuotas.length > 0 && (\n <section>\n <h2 className=\"text-lg font-semibold text-foreground mb-4 flex items-center gap-2\">\n <Layers className={`size-5 ${statusTokens.info.icon}`} />\n Pooled Quotas\n <span className=\"text-xs font-normal text-muted-foreground\">\n (Shared across all products)\n </span>\n </h2>\n <QuotaTable\n quotas={filteredPooledQuotas}\n showPlanColumn={false}\n onQuotaClick={setSelectedQuota}\n />\n </section>\n )}\n\n {/* Empty State - No quotas at all (when viewing active) */}\n {viewMode === 'active' &&\n activeSubscriptions.length === 0 &&\n pooledQuotas.length === 0 &&\n !hasActiveFilters && (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <Layers className=\"size-6 text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">No active quotas found</h3>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n Quotas will appear here once you have active subscriptions or pooled allocations.\n </p>\n </div>\n )}\n\n {/* Empty State - No past subscriptions */}\n {viewMode === 'past' &&\n pastSubscriptions.length === 0 &&\n !hasActiveFilters &&\n !isPastSubscriptionsLoading && (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <Clock className=\"size-6 text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">No past subscriptions</h3>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n You don&apos;t have any canceled, expired, or upgraded subscriptions yet.\n </p>\n <button\n type=\"button\"\n onClick={() => setViewMode('active')}\n className=\"mt-4 px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n View active subscriptions\n </button>\n </div>\n )}\n\n {/* Loading State for Past Subscriptions */}\n {viewMode === 'past' && isPastSubscriptionsLoading && (\n <div className=\"space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded-lg\" />\n ))}\n </div>\n )}\n\n {/* Empty State - No results from search/filter */}\n {hasActiveFilters &&\n displayedSubscriptions.active.length === 0 &&\n displayedSubscriptions.past.length === 0 &&\n (viewMode === 'past' || filteredPooledQuotas.length === 0) && (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n {statusFilter !== 'all' && !searchQuery ? (\n <StatusIcon\n status={\n (statusFilter === 'healthy'\n ? 'HEALTHY'\n : statusFilter === 'warning'\n ? 'WARNING'\n : 'CRITICAL') as QuotaStatus\n }\n className=\"size-6\"\n />\n ) : (\n <Search className=\"size-6 text-muted-foreground\" />\n )}\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">\n {statusFilter !== 'all' && !searchQuery\n ? `No ${statusFilter === 'critical' ? 'critical or exhausted' : statusFilter} quotas`\n : 'No matching quotas'}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n {statusFilter !== 'all' && !searchQuery ? (\n <>\n All your quotas are in a different status.\n {dashboard && (\n <span className=\"block mt-1\">\n You have {dashboard.healthyQuotas} healthy, {dashboard.warningQuotas} warning,\n and {dashboard.criticalQuotas + dashboard.exhaustedQuotas} critical quotas.\n </span>\n )}\n </>\n ) : (\n <>\n No quotas match your current search or filter criteria in{' '}\n {viewMode === 'past' ? 'past' : viewMode === 'all' ? 'any' : 'active'}{' '}\n subscriptions.\n {viewMode === 'active' && !statusFilter.includes('all') && (\n <span className=\"block mt-1\">\n Try switching to &ldquo;Past&rdquo; or &ldquo;All&rdquo; to search across more\n subscriptions.\n </span>\n )}\n </>\n )}\n </p>\n <div className=\"flex gap-2 mt-4\">\n <button\n type=\"button\"\n onClick={clearFilters}\n className=\"px-4 py-2 text-sm font-medium border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n {statusFilter !== 'all' ? 'Show all quotas' : 'Clear filters'}\n </button>\n {viewMode === 'active' && searchQuery && (\n <button\n type=\"button\"\n onClick={() => setViewMode('all')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Search all\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Quota Detail Modal */}\n {selectedQuota && (\n <QuotaDetailModal quota={selectedQuota} onClose={() => setSelectedQuota(null)} />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-billing",
3
- "version": "2026.601.4",
3
+ "version": "2026.601.6",
4
4
  "description": "Billing microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [