@carlonicora/nextjs-jsonapi 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/{BlockNoteEditor-7WGCH2KB.js → BlockNoteEditor-TIMGYKAA.js} +19 -19
  2. package/dist/{BlockNoteEditor-7WGCH2KB.js.map → BlockNoteEditor-TIMGYKAA.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-GWAV5ESO.mjs → BlockNoteEditor-YFJMXJEU.mjs} +4 -4
  4. package/dist/billing/index.js +362 -362
  5. package/dist/billing/index.mjs +3 -3
  6. package/dist/{chunk-GYGMEDVS.js → chunk-7HKJNUW7.js} +755 -746
  7. package/dist/chunk-7HKJNUW7.js.map +1 -0
  8. package/dist/{chunk-3LVSA7IM.mjs → chunk-DJWNNV52.mjs} +2 -2
  9. package/dist/{chunk-LCCRUWWY.mjs → chunk-FS4RVV3K.mjs} +241 -1
  10. package/dist/chunk-FS4RVV3K.mjs.map +1 -0
  11. package/dist/{chunk-M2EGUO2F.mjs → chunk-G4YS52SO.mjs} +16 -7
  12. package/dist/chunk-G4YS52SO.mjs.map +1 -0
  13. package/dist/{chunk-TOKHHZSP.js → chunk-OTEDZ6YW.js} +7 -7
  14. package/dist/{chunk-TOKHHZSP.js.map → chunk-OTEDZ6YW.js.map} +1 -1
  15. package/dist/{chunk-V66AZWPG.js → chunk-RTOC53EZ.js} +246 -6
  16. package/dist/chunk-RTOC53EZ.js.map +1 -0
  17. package/dist/client/index.js +4 -4
  18. package/dist/client/index.mjs +3 -3
  19. package/dist/components/index.d.mts +16 -1
  20. package/dist/components/index.d.ts +16 -1
  21. package/dist/components/index.js +4 -4
  22. package/dist/components/index.mjs +3 -3
  23. package/dist/{tokenusage-admin.module-5wJ_tZTj.d.ts → config-BIjrg5wd.d.ts} +39 -1
  24. package/dist/{tokenusage-admin.module-DQVT4O4j.d.mts → config-BRUjtCd1.d.mts} +39 -1
  25. package/dist/contexts/index.js +4 -4
  26. package/dist/contexts/index.mjs +3 -3
  27. package/dist/core/index.d.mts +5 -2
  28. package/dist/core/index.d.ts +5 -2
  29. package/dist/core/index.js +8 -2
  30. package/dist/core/index.js.map +1 -1
  31. package/dist/core/index.mjs +7 -1
  32. package/dist/features/help/index.js +37 -37
  33. package/dist/features/help/index.mjs +3 -3
  34. package/dist/features/tokenusage/index.d.mts +413 -14
  35. package/dist/features/tokenusage/index.d.ts +413 -14
  36. package/dist/features/tokenusage/index.js +481 -120
  37. package/dist/features/tokenusage/index.js.map +1 -1
  38. package/dist/features/tokenusage/index.mjs +431 -70
  39. package/dist/features/tokenusage/index.mjs.map +1 -1
  40. package/dist/index.d.mts +2 -2
  41. package/dist/index.d.ts +2 -2
  42. package/dist/index.js +9 -3
  43. package/dist/index.js.map +1 -1
  44. package/dist/index.mjs +8 -2
  45. package/dist/server/index.js +3 -3
  46. package/dist/server/index.mjs +1 -1
  47. package/package.json +1 -1
  48. package/src/core/index.ts +10 -0
  49. package/src/core/registry/ModuleRegistry.ts +4 -0
  50. package/src/features/tokenusage/components/TokenUsageAdminContainer.tsx +20 -4
  51. package/src/features/tokenusage/components/TokenUsageAdminTiles.tsx +31 -18
  52. package/src/features/tokenusage/components/TokenUsageBreakdownTable.tsx +13 -13
  53. package/src/features/tokenusage/components/TokenUsageRankedBar.tsx +24 -7
  54. package/src/features/tokenusage/components/TokenUsageReportContainer.tsx +102 -0
  55. package/src/features/tokenusage/components/TokenUsageReportFilterBar.tsx +34 -0
  56. package/src/features/tokenusage/components/TokenUsageReportTiles.tsx +161 -0
  57. package/src/features/tokenusage/components/TokenUsageTimelineChart.tsx +15 -13
  58. package/src/features/tokenusage/components/__tests__/TokenUsageAdminTiles.spec.tsx +4 -2
  59. package/src/features/tokenusage/components/__tests__/TokenUsageRankedBar.spec.tsx +3 -1
  60. package/src/features/tokenusage/contexts/TokenUsageAdminContext.tsx +6 -3
  61. package/src/features/tokenusage/contexts/TokenUsageReportContext.tsx +199 -0
  62. package/src/features/tokenusage/data/TokenUsageReportService.ts +65 -0
  63. package/src/features/tokenusage/data/index.ts +9 -0
  64. package/src/features/tokenusage/data/tokenusage-report-breakdown.interface.ts +12 -0
  65. package/src/features/tokenusage/data/tokenusage-report-breakdown.ts +94 -0
  66. package/src/features/tokenusage/data/tokenusage-report-summary.interface.ts +12 -0
  67. package/src/features/tokenusage/data/tokenusage-report-summary.ts +87 -0
  68. package/src/features/tokenusage/data/tokenusage-report-timeline.interface.ts +13 -0
  69. package/src/features/tokenusage/data/tokenusage-report-timeline.ts +94 -0
  70. package/src/features/tokenusage/data/tokenusage-report.types.ts +51 -0
  71. package/src/features/tokenusage/i18n-keys.ts +28 -0
  72. package/src/features/tokenusage/index.ts +18 -0
  73. package/src/features/tokenusage/lib/config.ts +22 -0
  74. package/src/features/tokenusage/lib/formatters.ts +100 -0
  75. package/src/features/tokenusage/lib/metrics.ts +8 -26
  76. package/src/features/tokenusage/lib/palette.ts +41 -14
  77. package/src/features/tokenusage/tokenusage-admin.module.ts +4 -0
  78. package/src/features/tokenusage/tokenusage.modules.ts +40 -0
  79. package/src/shadcnui/ui/chart.tsx +30 -3
  80. package/dist/chunk-GYGMEDVS.js.map +0 -1
  81. package/dist/chunk-LCCRUWWY.mjs.map +0 -1
  82. package/dist/chunk-M2EGUO2F.mjs.map +0 -1
  83. package/dist/chunk-V66AZWPG.js.map +0 -1
  84. /package/dist/{BlockNoteEditor-GWAV5ESO.mjs.map → BlockNoteEditor-YFJMXJEU.mjs.map} +0 -0
  85. /package/dist/{chunk-3LVSA7IM.mjs.map → chunk-DJWNNV52.mjs.map} +0 -0
@@ -26,8 +26,8 @@
26
26
 
27
27
 
28
28
 
29
- var _chunkGYGMEDVSjs = require('../../chunk-GYGMEDVS.js');
30
- require('../../chunk-TOKHHZSP.js');
29
+ var _chunk7HKJNUW7js = require('../../chunk-7HKJNUW7.js');
30
+ require('../../chunk-OTEDZ6YW.js');
31
31
 
32
32
 
33
33
 
@@ -39,7 +39,13 @@ require('../../chunk-TOKHHZSP.js');
39
39
 
40
40
 
41
41
 
42
- var _chunkV66AZWPGjs = require('../../chunk-V66AZWPG.js');
42
+
43
+
44
+
45
+
46
+
47
+
48
+ var _chunkRTOC53EZjs = require('../../chunk-RTOC53EZ.js');
43
49
  require('../../chunk-LXKSUWAV.js');
44
50
  require('../../chunk-IBS6NI7D.js');
45
51
  require('../../chunk-FPO4DLZN.js');
@@ -64,7 +70,7 @@ function TokenUsageAdminFilterBar({ granularity, companyId, metric, companies, o
64
70
  };
65
71
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-wrap items-center gap-2", children: [
66
72
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
67
- _chunkGYGMEDVSjs.DateRangeSelector,
73
+ _chunk7HKJNUW7js.DateRangeSelector,
68
74
  {
69
75
  onDateChange: (range) => {
70
76
  if (!_optionalChain([range, 'optionalAccess', _ => _.from]) || !_optionalChain([range, 'optionalAccess', _2 => _2.to])) return;
@@ -99,16 +105,16 @@ function TokenUsageAdminFilterBar({ granularity, companyId, metric, companies, o
99
105
  }
100
106
  ),
101
107
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
102
- _chunkGYGMEDVSjs.Select,
108
+ _chunk7HKJNUW7js.Select,
103
109
  {
104
110
  items: companyItems,
105
111
  value: _nullishCoalesce(companyId, () => ( ALL_COMPANIES)),
106
112
  onValueChange: (value) => onChange({ companyId: !value || value === ALL_COMPANIES ? void 0 : value }),
107
113
  children: [
108
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectTrigger, { className: "w-56", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectValue, { placeholder: t("token_usage.admin.all_companies") }) }),
109
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.SelectContent, { children: [
110
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectItem, { value: ALL_COMPANIES, children: t("token_usage.admin.all_companies") }),
111
- companies.map((company) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectItem, { value: company.id, children: company.label }, company.id))
114
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectTrigger, { className: "w-56", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectValue, { placeholder: t("token_usage.admin.all_companies") }) }),
115
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.SelectContent, { children: [
116
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectItem, { value: ALL_COMPANIES, children: t("token_usage.admin.all_companies") }),
117
+ companies.map((company) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectItem, { value: company.id, children: company.label }, company.id))
112
118
  ] })
113
119
  ]
114
120
  }
@@ -131,13 +137,13 @@ function Segmented({
131
137
  children: options.map((option) => {
132
138
  const selected = option.value === value;
133
139
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
134
- _chunkGYGMEDVSjs.Button,
140
+ _chunk7HKJNUW7js.Button,
135
141
  {
136
142
  type: "button",
137
143
  size: "sm",
138
144
  variant: selected ? "default" : "ghost",
139
145
  "aria-pressed": selected,
140
- className: _chunkV66AZWPGjs.cn.call(void 0, !selected && "text-muted-foreground"),
146
+ className: _chunkRTOC53EZjs.cn.call(void 0, !selected && "text-muted-foreground"),
141
147
  onClick: () => onSelect(option.value),
142
148
  children: option.label
143
149
  },
@@ -157,36 +163,36 @@ function withFilters(endpoint, filters) {
157
163
  return endpoint;
158
164
  }
159
165
  _chunk7QVYU63Ejs.__name.call(void 0, withFilters, "withFilters");
160
- var TokenUsageAdminService = class extends _chunkV66AZWPGjs.AbstractService {
166
+ var TokenUsageAdminService = class extends _chunkRTOC53EZjs.AbstractService {
161
167
  static {
162
168
  _chunk7QVYU63Ejs.__name.call(void 0, this, "TokenUsageAdminService");
163
169
  }
164
170
  /** Six rows: {customer, platform, total} × {current, previous}. */
165
171
  static async getSummary(filters) {
166
- const endpoint = withFilters(new (0, _chunkV66AZWPGjs.EndpointCreator)({ endpoint: _chunkV66AZWPGjs.Modules.TokenUsageAdminSummary }), filters);
172
+ const endpoint = withFilters(new (0, _chunkRTOC53EZjs.EndpointCreator)({ endpoint: _chunkRTOC53EZjs.Modules.TokenUsageAdminSummary }), filters);
167
173
  return this.callApi({
168
- type: _chunkV66AZWPGjs.Modules.TokenUsageAdminSummary,
174
+ type: _chunkRTOC53EZjs.Modules.TokenUsageAdminSummary,
169
175
  method: "GET" /* GET */,
170
176
  endpoint: endpoint.generate()
171
177
  });
172
178
  }
173
179
  static async getTimeline(params) {
174
- const endpoint = withFilters(new (0, _chunkV66AZWPGjs.EndpointCreator)({ endpoint: _chunkV66AZWPGjs.Modules.TokenUsageAdminTimeline }), params);
180
+ const endpoint = withFilters(new (0, _chunkRTOC53EZjs.EndpointCreator)({ endpoint: _chunkRTOC53EZjs.Modules.TokenUsageAdminTimeline }), params);
175
181
  endpoint.addAdditionalParam("granularity", params.granularity);
176
182
  endpoint.addAdditionalParam("stackBy", params.stackBy);
177
183
  return this.callApi({
178
- type: _chunkV66AZWPGjs.Modules.TokenUsageAdminTimeline,
184
+ type: _chunkRTOC53EZjs.Modules.TokenUsageAdminTimeline,
179
185
  method: "GET" /* GET */,
180
186
  endpoint: endpoint.generate()
181
187
  });
182
188
  }
183
189
  static async getBreakdown(params) {
184
- const endpoint = withFilters(new (0, _chunkV66AZWPGjs.EndpointCreator)({ endpoint: _chunkV66AZWPGjs.Modules.TokenUsageAdminBreakdown }), params);
190
+ const endpoint = withFilters(new (0, _chunkRTOC53EZjs.EndpointCreator)({ endpoint: _chunkRTOC53EZjs.Modules.TokenUsageAdminBreakdown }), params);
185
191
  endpoint.addAdditionalParam("dimension", params.dimension);
186
192
  endpoint.addAdditionalParam("scope", params.scope);
187
193
  if (params.limit !== void 0) endpoint.addAdditionalParam("limit", String(params.limit));
188
194
  return this.callApi({
189
- type: _chunkV66AZWPGjs.Modules.TokenUsageAdminBreakdown,
195
+ type: _chunkRTOC53EZjs.Modules.TokenUsageAdminBreakdown,
190
196
  method: "GET" /* GET */,
191
197
  endpoint: endpoint.generate()
192
198
  });
@@ -209,10 +215,11 @@ var TokenUsageAdminProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
209
215
  initialCompanyId,
210
216
  initialFrom,
211
217
  initialTo,
212
- topN = DEFAULT_TOP_N
218
+ topN = DEFAULT_TOP_N,
219
+ pageUrl = TOKEN_USAGE_ADMIN_PAGE_URL
213
220
  }) => {
214
221
  const t = _nextintl.useTranslations.call(void 0, );
215
- const generateUrl = _chunkGYGMEDVSjs.usePageUrlGenerator.call(void 0, );
222
+ const generateUrl = _chunk7HKJNUW7js.usePageUrlGenerator.call(void 0, );
216
223
  const [filters, setFilterState] = _react.useState.call(void 0, () => {
217
224
  const range = defaultRange();
218
225
  return {
@@ -281,7 +288,7 @@ var TokenUsageAdminProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
281
288
  const breadcrumb = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => [
282
289
  {
283
290
  name: t("token_usage.admin.title"),
284
- href: generateUrl({ page: TOKEN_USAGE_ADMIN_PAGE_URL })
291
+ href: generateUrl({ page: pageUrl })
285
292
  }
286
293
  ], "breadcrumb");
287
294
  const title = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => ({
@@ -330,7 +337,7 @@ var TokenUsageAdminProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
330
337
  error
331
338
  ]
332
339
  );
333
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SharedProvider, { value: { breadcrumbs: breadcrumb(), title: title() }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageAdminContext.Provider, { value: contextValue, children }) });
340
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SharedProvider, { value: { breadcrumbs: breadcrumb(), title: title() }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageAdminContext.Provider, { value: contextValue, children }) });
334
341
  }, "TokenUsageAdminProvider");
335
342
  var useTokenUsageAdmin = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => {
336
343
  const ctx = _react.useContext.call(void 0, TokenUsageAdminContext);
@@ -348,31 +355,56 @@ var useTokenUsageAdmin = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, ()
348
355
  var _lucidereact = require('lucide-react');
349
356
 
350
357
 
358
+ // src/features/tokenusage/lib/formatters.ts
359
+
360
+
361
+ function createUsageFormatters(locale, currency) {
362
+ const symbol = _nullishCoalesce(_optionalChain([new Intl.NumberFormat(locale, { style: "currency", currency, currencyDisplay: "narrowSymbol" }), 'access', _3 => _3.formatToParts, 'call', _4 => _4(0), 'access', _5 => _5.find, 'call', _6 => _6((part) => part.type === "currency"), 'optionalAccess', _7 => _7.value]), () => ( currency));
363
+ const decimal = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (value, decimals) => value.toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }), "decimal");
364
+ const dayFormat = new Intl.DateTimeFormat(locale, { day: "numeric", month: "short", timeZone: "UTC" });
365
+ const monthFormat = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric", timeZone: "UTC" });
366
+ const compactFormat = new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 1 });
367
+ const collator = new Intl.Collator(locale);
368
+ return {
369
+ decimal,
370
+ metricValue(value, metric) {
371
+ if (metric === "cost") return `${symbol} ${decimal(value, 2)}`;
372
+ if (metric === "credits") return decimal(value, 1);
373
+ return decimal(value, 0);
374
+ },
375
+ currency(value, decimals) {
376
+ return `${symbol} ${decimal(value, decimals)}`;
377
+ },
378
+ percent(value, decimals = 1) {
379
+ return `${decimal(value, decimals)} %`;
380
+ },
381
+ compact(value) {
382
+ return compactFormat.format(value);
383
+ },
384
+ bucketDate(iso, granularity) {
385
+ const date = /* @__PURE__ */ new Date(`${iso}T00:00:00.000Z`);
386
+ return granularity === "month" ? monthFormat.format(date) : dayFormat.format(date);
387
+ },
388
+ compare(a, b) {
389
+ return collator.compare(a, b);
390
+ }
391
+ };
392
+ }
393
+ _chunk7QVYU63Ejs.__name.call(void 0, createUsageFormatters, "createUsageFormatters");
394
+ function useUsageFormatters() {
395
+ const locale = _nextintl.useLocale.call(void 0, );
396
+ const currency = _chunkRTOC53EZjs.getTokenUsageCurrency.call(void 0, );
397
+ return _react.useMemo.call(void 0, () => createUsageFormatters(locale, currency), [locale, currency]);
398
+ }
399
+ _chunk7QVYU63Ejs.__name.call(void 0, useUsageFormatters, "useUsageFormatters");
400
+
351
401
  // src/features/tokenusage/lib/metrics.ts
352
- var LOCALE = "it-IT";
353
402
  function metricValue(row, metric) {
354
403
  if (metric === "cost") return row.cost;
355
404
  if (metric === "credits") return row.credits;
356
405
  return row.tokensIn + row.tokensOut;
357
406
  }
358
407
  _chunk7QVYU63Ejs.__name.call(void 0, metricValue, "metricValue");
359
- function formatDecimal(value, decimals) {
360
- return value.toLocaleString(LOCALE, {
361
- minimumFractionDigits: decimals,
362
- maximumFractionDigits: decimals
363
- });
364
- }
365
- _chunk7QVYU63Ejs.__name.call(void 0, formatDecimal, "formatDecimal");
366
- function formatMetricValue(value, metric) {
367
- if (metric === "cost") return `\u20AC ${formatDecimal(value, 2)}`;
368
- if (metric === "credits") return formatDecimal(value, 2);
369
- return formatDecimal(value, 0);
370
- }
371
- _chunk7QVYU63Ejs.__name.call(void 0, formatMetricValue, "formatMetricValue");
372
- function formatPercent(value, decimals = 1) {
373
- return `${formatDecimal(value, decimals)} %`;
374
- }
375
- _chunk7QVYU63Ejs.__name.call(void 0, formatPercent, "formatPercent");
376
408
  function cacheHitPercentage(cached, tokensIn) {
377
409
  if (!tokensIn) return 0;
378
410
  return cached / tokensIn * 100;
@@ -389,6 +421,7 @@ _chunk7QVYU63Ejs.__name.call(void 0, percentageDelta, "percentageDelta");
389
421
  var ZERO = { cost: 0, credits: 0, tokensIn: 0, tokensOut: 0, cached: 0, calls: 0 };
390
422
  function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
391
423
  const t = _nextintl.useTranslations.call(void 0, );
424
+ const { decimal, metricValue: formatValue, currency, percent } = useUsageFormatters();
392
425
  const rowFor = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (scope, window) => _nullishCoalesce(summary.find((r) => r.scope === scope && r.window === window), () => ( ZERO)), "rowFor");
393
426
  const customerCurrent = rowFor("customer", "current");
394
427
  const customerPrevious = rowFor("customer", "previous");
@@ -399,15 +432,16 @@ function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
399
432
  const averagePerCall = totalCurrent.calls ? totalCurrent.cost / totalCurrent.calls : 0;
400
433
  const cacheHit = cacheHitPercentage(totalCurrent.cached, totalCurrent.tokensIn);
401
434
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-3", children: [
402
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkV66AZWPGjs.cn.call(void 0, "grid gap-3", singleCustomerMode ? "sm:grid-cols-1" : "sm:grid-cols-2"), children: [
435
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkRTOC53EZjs.cn.call(void 0, "grid gap-3", singleCustomerMode ? "sm:grid-cols-1" : "sm:grid-cols-2"), children: [
403
436
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
404
437
  LeadTile,
405
438
  {
406
439
  testId: "tile-customer",
407
440
  label: t("token_usage.admin.customer_spend"),
408
- value: formatMetricValue(metricValue(customerCurrent, metric), metric),
441
+ value: formatValue(metricValue(customerCurrent, metric), metric),
409
442
  delta: percentageDelta(metricValue(customerCurrent, metric), metricValue(customerPrevious, metric)),
410
- previousLabel: t("token_usage.admin.vs_previous")
443
+ previousLabel: t("token_usage.admin.vs_previous"),
444
+ decimal
411
445
  }
412
446
  ),
413
447
  !singleCustomerMode && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -415,9 +449,10 @@ function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
415
449
  {
416
450
  testId: "tile-platform",
417
451
  label: t("token_usage.admin.platform_spend"),
418
- value: formatMetricValue(metricValue(platformCurrent, metric), metric),
452
+ value: formatValue(metricValue(platformCurrent, metric), metric),
419
453
  delta: percentageDelta(metricValue(platformCurrent, metric), metricValue(platformPrevious, metric)),
420
- previousLabel: t("token_usage.admin.vs_previous")
454
+ previousLabel: t("token_usage.admin.vs_previous"),
455
+ decimal
421
456
  }
422
457
  )
423
458
  ] }),
@@ -427,8 +462,9 @@ function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
427
462
  {
428
463
  testId: "tile-total",
429
464
  label: t("token_usage.admin.total_cost"),
430
- value: formatMetricValue(metricValue(totalCurrent, metric), metric),
431
- delta: percentageDelta(metricValue(totalCurrent, metric), metricValue(totalPrevious, metric))
465
+ value: formatValue(metricValue(totalCurrent, metric), metric),
466
+ delta: percentageDelta(metricValue(totalCurrent, metric), metricValue(totalPrevious, metric)),
467
+ decimal
432
468
  }
433
469
  ),
434
470
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -436,7 +472,8 @@ function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
436
472
  {
437
473
  testId: "tile-avg-per-call",
438
474
  label: t("token_usage.admin.avg_per_call"),
439
- value: `\u20AC ${formatDecimal(averagePerCall, 4)}`
475
+ value: currency(averagePerCall, 4),
476
+ decimal
440
477
  }
441
478
  ),
442
479
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -444,7 +481,8 @@ function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
444
481
  {
445
482
  testId: "tile-cache-hit",
446
483
  label: t("token_usage.admin.cache_hit"),
447
- value: formatPercent(cacheHit)
484
+ value: percent(cacheHit),
485
+ decimal
448
486
  }
449
487
  )
450
488
  ] })
@@ -456,13 +494,14 @@ function LeadTile({
456
494
  label,
457
495
  value,
458
496
  delta,
459
- previousLabel
497
+ previousLabel,
498
+ decimal
460
499
  }) {
461
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.Card, { "data-testid": testId, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.CardContent, { className: "grid gap-1", children: [
500
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { "data-testid": testId, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardContent, { className: "grid gap-1", children: [
462
501
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: label }),
463
502
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-primary text-xl font-semibold tabular-nums", children: value }),
464
503
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-1", children: [
465
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Delta, { testId: `${testId}-delta`, delta }),
504
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Delta, { testId: `${testId}-delta`, delta, decimal }),
466
505
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: previousLabel })
467
506
  ] })
468
507
  ] }) });
@@ -472,18 +511,23 @@ function SupportingTile({
472
511
  testId,
473
512
  label,
474
513
  value,
475
- delta
514
+ delta,
515
+ decimal
476
516
  }) {
477
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.Card, { "data-testid": testId, size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.CardContent, { className: "grid gap-1", children: [
517
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { "data-testid": testId, size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardContent, { className: "grid gap-1", children: [
478
518
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: label }),
479
519
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-2", children: [
480
520
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-medium tabular-nums", children: value }),
481
- delta !== void 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Delta, { testId: `${testId}-delta`, delta })
521
+ delta !== void 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Delta, { testId: `${testId}-delta`, delta, decimal })
482
522
  ] })
483
523
  ] }) });
484
524
  }
485
525
  _chunk7QVYU63Ejs.__name.call(void 0, SupportingTile, "SupportingTile");
486
- function Delta({ testId, delta }) {
526
+ function Delta({
527
+ testId,
528
+ delta,
529
+ decimal
530
+ }) {
487
531
  if (delta === void 0) {
488
532
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-testid": testId, className: "text-muted-foreground text-xs", children: "\u2014" });
489
533
  }
@@ -493,13 +537,13 @@ function Delta({ testId, delta }) {
493
537
  "span",
494
538
  {
495
539
  "data-testid": testId,
496
- className: _chunkV66AZWPGjs.cn.call(void 0,
540
+ className: _chunkRTOC53EZjs.cn.call(void 0,
497
541
  "inline-flex items-center gap-0.5 text-xs tabular-nums",
498
542
  increased ? "text-success" : "text-destructive"
499
543
  ),
500
544
  children: [
501
545
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { "aria-hidden": true, className: "size-3" }),
502
- formatDecimal(Math.abs(delta), 0),
546
+ decimal(Math.abs(delta), 0),
503
547
  " %"
504
548
  ]
505
549
  }
@@ -514,6 +558,7 @@ _chunk7QVYU63Ejs.__name.call(void 0, Delta, "Delta");
514
558
 
515
559
  function TokenUsageBreakdownTable({ rows, metric }) {
516
560
  const t = _nextintl.useTranslations.call(void 0, );
561
+ const { decimal, metricValue: formatValue, percent, compare } = useUsageFormatters();
517
562
  const [sort, setSort] = _react.useState.call(void 0, void 0);
518
563
  const showActiveUsers = rows.some((row) => row.activeUsers !== void 0);
519
564
  const total = _react.useMemo.call(void 0, () => rows.reduce((sum, row) => sum + metricValue(row, metric), 0), [rows, metric]);
@@ -540,13 +585,13 @@ function TokenUsageBreakdownTable({ rows, metric }) {
540
585
  const left = sortValue(a, sort.key);
541
586
  const right = sortValue(b, sort.key);
542
587
  if (typeof left === "string" || typeof right === "string") {
543
- return String(left).localeCompare(String(right), "it-IT") * factor;
588
+ return compare(String(left), String(right)) * factor;
544
589
  }
545
590
  return (left - right) * factor;
546
591
  });
547
- }, [rows, sort, metric]);
592
+ }, [rows, sort, metric, compare]);
548
593
  const toggleSort = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (key) => setSort(
549
- (current) => _optionalChain([current, 'optionalAccess', _3 => _3.key]) === key ? { key, direction: current.direction === "desc" ? "asc" : "desc" } : { key, direction: "desc" }
594
+ (current) => _optionalChain([current, 'optionalAccess', _8 => _8.key]) === key ? { key, direction: current.direction === "desc" ? "asc" : "desc" } : { key, direction: "desc" }
550
595
  ), "toggleSort");
551
596
  const columns = [
552
597
  { key: "label", label: t("token_usage.admin.columns.label"), numeric: false },
@@ -566,36 +611,36 @@ function TokenUsageBreakdownTable({ rows, metric }) {
566
611
  // The package's <Table> wraps itself in an `overflow-x-clip` container, which
567
612
  // would swallow the overflow before this scroller ever saw it — hence the
568
613
  // child override. Wide metric tables must scroll, never widen the page.
569
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Table, { children: [
570
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableRow, { children: columns.map((column) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableHead, { className: _chunkV66AZWPGjs.cn.call(void 0, column.numeric && "text-right"), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
614
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Table, { children: [
615
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableRow, { children: columns.map((column) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableHead, { className: _chunkRTOC53EZjs.cn.call(void 0, column.numeric && "text-right"), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
571
616
  "button",
572
617
  {
573
618
  type: "button",
574
619
  onClick: () => toggleSort(column.key),
575
- className: _chunkV66AZWPGjs.cn.call(void 0,
620
+ className: _chunkRTOC53EZjs.cn.call(void 0,
576
621
  "hover:text-primary inline-flex items-center gap-1 text-xs font-medium",
577
622
  column.numeric && "flex-row-reverse"
578
623
  ),
579
624
  children: [
580
625
  column.label,
581
- _optionalChain([sort, 'optionalAccess', _4 => _4.key]) === column.key && (sort.direction === "desc" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ArrowDownIcon, { "aria-hidden": true, className: "size-3" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ArrowUpIcon, { "aria-hidden": true, className: "size-3" }))
626
+ _optionalChain([sort, 'optionalAccess', _9 => _9.key]) === column.key && (sort.direction === "desc" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ArrowDownIcon, { "aria-hidden": true, className: "size-3" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ArrowUpIcon, { "aria-hidden": true, className: "size-3" }))
582
627
  ]
583
628
  }
584
629
  ) }, column.key)) }) }),
585
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableBody, { children: sortedRows.map((row) => {
630
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableBody, { children: sortedRows.map((row) => {
586
631
  const value = metricValue(row, metric);
587
632
  const share = total > 0 ? value / total * 100 : 0;
588
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.TableRow, { "data-testid": `breakdown-row-${row.id}`, children: [
589
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-xs", children: row.id === "other" ? t("token_usage.admin.other") : row.label }),
590
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-muted-foreground text-xs", children: _nullishCoalesce(row.sublabel, () => ( "")) }),
591
- showActiveUsers && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: row.activeUsers === void 0 ? "" : formatDecimal(row.activeUsers, 0) }),
592
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatDecimal(row.calls, 0) }),
593
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatDecimal(row.tokensIn, 0) }),
594
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatDecimal(row.tokensOut, 0) }),
595
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatPercent(cacheHitPercentage(row.cached, row.tokensIn)) }),
596
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatMetricValue(row.cost, "cost") }),
597
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatMetricValue(row.credits, "credits") }),
598
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.TableCell, { className: "text-right text-xs tabular-nums", children: formatPercent(share) })
633
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.TableRow, { "data-testid": `breakdown-row-${row.id}`, children: [
634
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-xs", children: row.id === "other" ? t("token_usage.admin.other") : row.label }),
635
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-muted-foreground text-xs", children: _nullishCoalesce(row.sublabel, () => ( "")) }),
636
+ showActiveUsers && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: row.activeUsers === void 0 ? "" : decimal(row.activeUsers, 0) }),
637
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: decimal(row.calls, 0) }),
638
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: decimal(row.tokensIn, 0) }),
639
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: decimal(row.tokensOut, 0) }),
640
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: percent(cacheHitPercentage(row.cached, row.tokensIn)) }),
641
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: formatValue(row.cost, "cost") }),
642
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: formatValue(row.credits, "credits") }),
643
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.TableCell, { className: "text-right text-xs tabular-nums", children: percent(share) })
599
644
  ] }, row.id);
600
645
  }) })
601
646
  ] }) })
@@ -654,6 +699,7 @@ _chunk7QVYU63Ejs.__name.call(void 0, seriesColor, "seriesColor");
654
699
 
655
700
  function TokenUsageRankedBar({ rows, metric, emptyLabel, labelsAreOperationTypes = false }) {
656
701
  const t = _nextintl.useTranslations.call(void 0, );
702
+ const { metricValue: formatValue, percent } = useUsageFormatters();
657
703
  const renderLabel = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (row) => {
658
704
  if (row.id === "other") return t("token_usage.admin.other");
659
705
  if (!labelsAreOperationTypes) return row.label;
@@ -688,13 +734,13 @@ function TokenUsageRankedBar({ rows, metric, emptyLabel, labelsAreOperationTypes
688
734
  }
689
735
  ) })
690
736
  ] }),
691
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-testid": `ranked-value-${row.id}`, className: "text-right text-xs tabular-nums", children: formatMetricValue(value, metric) }),
737
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-testid": `ranked-value-${row.id}`, className: "text-right text-xs tabular-nums", children: formatValue(value, metric) }),
692
738
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
693
739
  "span",
694
740
  {
695
741
  "data-testid": `ranked-share-${row.id}`,
696
742
  className: "text-muted-foreground w-16 text-right text-xs tabular-nums",
697
- children: formatPercent(share)
743
+ children: percent(share)
698
744
  }
699
745
  )
700
746
  ] }, row.id);
@@ -732,10 +778,9 @@ var inferGranularity = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (buc
732
778
  if (smallestGap >= 7) return "week";
733
779
  return "day";
734
780
  }, "inferGranularity");
735
- var bucketFormatter = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (granularity) => granularity === "month" ? new Intl.DateTimeFormat("it-IT", { month: "short", year: "numeric", timeZone: "UTC" }) : new Intl.DateTimeFormat("it-IT", { day: "numeric", month: "short", timeZone: "UTC" }), "bucketFormatter");
736
- var compactNumber = new Intl.NumberFormat("it-IT", { notation: "compact", maximumFractionDigits: 1 });
737
781
  function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
738
782
  const t = _nextintl.useTranslations.call(void 0, );
783
+ const { compact, bucketDate, metricValue: formatValue } = useUsageFormatters();
739
784
  const { resolvedTheme } = _nextthemes.useTheme.call(void 0, );
740
785
  const mode = resolvedTheme === "dark" ? "dark" : "light";
741
786
  const { chartData, seriesKeys } = _react.useMemo.call(void 0, () => {
@@ -766,7 +811,7 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
766
811
  const seriesLabel = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (series) => {
767
812
  if (series === OTHER_SERIES) {
768
813
  const key = "token_usage.series.other";
769
- return t.has(key) ? t(key) : "Altro";
814
+ return t.has(key) ? t(key) : "Other";
770
815
  }
771
816
  if (stackBy === "company") return series;
772
817
  return operationLabel(
@@ -781,15 +826,14 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
781
826
  );
782
827
  if (chartData.length === 0) {
783
828
  const key = "token_usage.timeline.empty";
784
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: className ? `text-muted-foreground text-sm ${className}` : "text-muted-foreground text-sm", children: t.has(key) ? t(key) : "Nessun dato nel periodo selezionato" });
829
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: className ? `text-muted-foreground text-sm ${className}` : "text-muted-foreground text-sm", children: t.has(key) ? t(key) : "No data in the selected period" });
785
830
  }
786
831
  const granularity = inferGranularity(chartData.map((entry) => entry.bucket));
787
- const formatBucket = bucketFormatter(granularity);
788
832
  const seriesColorFor = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (series, index) => series === OTHER_SERIES ? OTHER_COLOR : seriesColor(index, mode), "seriesColorFor");
789
833
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className, children: [
790
834
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "sr-only", "data-testid": "timeline-data", children: JSON.stringify(chartData) }),
791
835
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "sr-only", "data-testid": "timeline-series", children: JSON.stringify(seriesKeys) }),
792
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.ChartContainer, { config: chartConfig, className: "aspect-auto h-72 w-full", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _recharts.BarChart, { accessibilityLayer: true, data: chartData, margin: { top: 8, right: 8, bottom: 0, left: 0 }, children: [
836
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.ChartContainer, { config: chartConfig, className: "aspect-auto h-72 w-full", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _recharts.BarChart, { accessibilityLayer: true, data: chartData, margin: { top: 8, right: 8, bottom: 0, left: 0 }, children: [
793
837
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _recharts.CartesianGrid, { vertical: false }),
794
838
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
795
839
  _recharts.XAxis,
@@ -799,7 +843,7 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
799
843
  axisLine: false,
800
844
  tickMargin: 8,
801
845
  minTickGap: 16,
802
- tickFormatter: (value) => formatBucket.format(/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`))
846
+ tickFormatter: (value) => bucketDate(value, granularity)
803
847
  }
804
848
  ),
805
849
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -809,21 +853,23 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
809
853
  axisLine: false,
810
854
  tickMargin: 8,
811
855
  width: 48,
812
- tickFormatter: (value) => compactNumber.format(value)
856
+ tickFormatter: (value) => compact(value)
813
857
  }
814
858
  ),
815
859
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
816
- _chunkGYGMEDVSjs.ChartTooltip,
860
+ _chunk7HKJNUW7js.ChartTooltip,
817
861
  {
818
862
  content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
819
- _chunkGYGMEDVSjs.ChartTooltipContent,
863
+ _chunk7HKJNUW7js.ChartTooltipContent,
820
864
  {
821
- labelFormatter: (value) => formatBucket.format(/* @__PURE__ */ new Date(`${String(value)}T00:00:00.000Z`))
865
+ labelFormatter: (value) => bucketDate(String(value), granularity),
866
+ valueFormatter: (value) => formatValue(Number(value), metric),
867
+ hideZeroValues: true
822
868
  }
823
869
  )
824
870
  }
825
871
  ),
826
- seriesKeys.length >= 2 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.ChartLegend, { content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.ChartLegendContent, {}) }) : null,
872
+ seriesKeys.length >= 2 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.ChartLegend, { content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.ChartLegendContent, {}) }) : null,
827
873
  seriesKeys.map((series, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
828
874
  _recharts.Bar,
829
875
  {
@@ -860,6 +906,11 @@ function TokenUsageAdminContainer() {
860
906
  isLoading,
861
907
  error
862
908
  } = useTokenUsageAdmin();
909
+ const platformIsEmpty = _react.useMemo.call(void 0,
910
+ () => summary.filter((row) => row.scope === "platform").every((row) => row.cost === 0 && row.credits === 0 && row.tokensIn === 0 && row.tokensOut === 0),
911
+ [summary]
912
+ );
913
+ const hidePlatform = singleCustomerMode || platformIsEmpty;
863
914
  const stackByItems = _react.useMemo.call(void 0,
864
915
  () => ({
865
916
  scope: t("token_usage.admin.stack.scope"),
@@ -869,19 +920,19 @@ function TokenUsageAdminContainer() {
869
920
  [t]
870
921
  );
871
922
  if (error) {
872
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.Card, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
923
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
873
924
  }
874
- if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true });
925
+ if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.RoundPageContainer, { fullWidth: true, forceHeader: true });
875
926
  const emptyLabel = t("token_usage.admin.no_data");
876
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-4 p-4", children: [
877
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageAdminTiles, { summary, metric: filters.metric, singleCustomerMode }),
878
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Card, { children: [
879
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.CardHeader, { children: [
880
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardTitle, { children: t("token_usage.admin.usage_over_time") }),
881
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardAction, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
927
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-4 p-4", children: [
928
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageAdminTiles, { summary, metric: filters.metric, singleCustomerMode: hidePlatform }),
929
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
930
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: [
931
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.admin.usage_over_time") }),
932
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardAction, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
882
933
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.admin.stack_by") }),
883
934
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
884
- _chunkGYGMEDVSjs.Select,
935
+ _chunk7HKJNUW7js.Select,
885
936
  {
886
937
  items: stackByItems,
887
938
  value: filters.stackBy,
@@ -889,29 +940,29 @@ function TokenUsageAdminContainer() {
889
940
  if (value) setFilters({ stackBy: value });
890
941
  },
891
942
  children: [
892
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectTrigger, { size: "sm", className: "w-36", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectValue, {}) }),
893
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectContent, { children: STACK_BY_VALUES.map((value) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.SelectItem, { value, children: stackByItems[value] }, value)) })
943
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectTrigger, { size: "sm", className: "w-36", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectValue, {}) }),
944
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectContent, { children: STACK_BY_VALUES.map((value) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SelectItem, { value, children: stackByItems[value] }, value)) })
894
945
  ]
895
946
  }
896
947
  )
897
948
  ] }) })
898
949
  ] }),
899
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageTimelineChart, { rows: timeline, metric: filters.metric, stackBy: filters.stackBy }) })
950
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageTimelineChart, { rows: timeline, metric: filters.metric, stackBy: filters.stackBy }) })
900
951
  ] }),
901
952
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-4 md:grid-cols-2", children: [
902
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Card, { children: [
903
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardTitle, { children: t("token_usage.admin.by_company") }) }),
904
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byCompany, metric: filters.metric, emptyLabel }) })
953
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
954
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.admin.by_company") }) }),
955
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byCompany, metric: filters.metric, emptyLabel }) })
905
956
  ] }),
906
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Card, { children: [
907
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardTitle, { children: t("token_usage.admin.by_user") }) }),
908
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byUser, metric: filters.metric, emptyLabel }) })
957
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
958
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.admin.by_user") }) }),
959
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byUser, metric: filters.metric, emptyLabel }) })
909
960
  ] })
910
961
  ] }),
911
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkV66AZWPGjs.cn.call(void 0, "grid gap-4", !singleCustomerMode && "md:grid-cols-2"), children: [
912
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Card, { children: [
913
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardTitle, { children: t("token_usage.admin.customer_by_operation") }) }),
914
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
962
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkRTOC53EZjs.cn.call(void 0, "grid gap-4", !hidePlatform && "md:grid-cols-2"), children: [
963
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
964
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.admin.customer_by_operation") }) }),
965
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
915
966
  TokenUsageRankedBar,
916
967
  {
917
968
  rows: byCustomerOperation,
@@ -921,9 +972,9 @@ function TokenUsageAdminContainer() {
921
972
  }
922
973
  ) })
923
974
  ] }),
924
- !singleCustomerMode && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Card, { children: [
925
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardTitle, { children: t("token_usage.admin.platform_by_operation") }) }),
926
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
975
+ !hidePlatform && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
976
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.admin.platform_by_operation") }) }),
977
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
927
978
  TokenUsageRankedBar,
928
979
  {
929
980
  rows: byOperation,
@@ -934,14 +985,57 @@ function TokenUsageAdminContainer() {
934
985
  ) })
935
986
  ] })
936
987
  ] }),
937
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkGYGMEDVSjs.Card, { children: [
938
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardTitle, { children: t("token_usage.admin.detail") }) }),
939
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGYGMEDVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageBreakdownTable, { rows: byCompany, metric: filters.metric }) })
988
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
989
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.admin.detail") }) }),
990
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageBreakdownTable, { rows: byCompany, metric: filters.metric }) })
940
991
  ] })
941
992
  ] }) });
942
993
  }
943
994
  _chunk7QVYU63Ejs.__name.call(void 0, TokenUsageAdminContainer, "TokenUsageAdminContainer");
944
995
 
996
+ // src/features/tokenusage/data/TokenUsageReportService.ts
997
+ function withFilters2(endpoint, filters) {
998
+ endpoint.addAdditionalParam("from", filters.from);
999
+ endpoint.addAdditionalParam("to", filters.to);
1000
+ return endpoint;
1001
+ }
1002
+ _chunk7QVYU63Ejs.__name.call(void 0, withFilters2, "withFilters");
1003
+ var TokenUsageReportService = class extends _chunkRTOC53EZjs.AbstractService {
1004
+ static {
1005
+ _chunk7QVYU63Ejs.__name.call(void 0, this, "TokenUsageReportService");
1006
+ }
1007
+ /** Two rows: window "current" and "previous". */
1008
+ static async getSummary(filters) {
1009
+ const endpoint = withFilters2(new (0, _chunkRTOC53EZjs.EndpointCreator)({ endpoint: _chunkRTOC53EZjs.Modules.TokenUsageReportSummary }), filters);
1010
+ return this.callApi({
1011
+ type: _chunkRTOC53EZjs.Modules.TokenUsageReportSummary,
1012
+ method: "GET" /* GET */,
1013
+ endpoint: endpoint.generate()
1014
+ });
1015
+ }
1016
+ static async getTimeline(params) {
1017
+ const endpoint = withFilters2(new (0, _chunkRTOC53EZjs.EndpointCreator)({ endpoint: _chunkRTOC53EZjs.Modules.TokenUsageReportTimeline }), params);
1018
+ endpoint.addAdditionalParam("granularity", params.granularity);
1019
+ return this.callApi({
1020
+ type: _chunkRTOC53EZjs.Modules.TokenUsageReportTimeline,
1021
+ method: "GET" /* GET */,
1022
+ endpoint: endpoint.generate()
1023
+ });
1024
+ }
1025
+ static async getBreakdown(params) {
1026
+ const endpoint = withFilters2(new (0, _chunkRTOC53EZjs.EndpointCreator)({ endpoint: _chunkRTOC53EZjs.Modules.TokenUsageReportBreakdown }), params);
1027
+ endpoint.addAdditionalParam("dimension", params.dimension);
1028
+ if (params.targetLabel) endpoint.addAdditionalParam("targetLabel", params.targetLabel);
1029
+ endpoint.addAdditionalParam("metric", params.metric);
1030
+ if (params.limit !== void 0) endpoint.addAdditionalParam("limit", String(params.limit));
1031
+ return this.callApi({
1032
+ type: _chunkRTOC53EZjs.Modules.TokenUsageReportBreakdown,
1033
+ method: "GET" /* GET */,
1034
+ endpoint: endpoint.generate()
1035
+ });
1036
+ }
1037
+ };
1038
+
945
1039
  // src/features/tokenusage/i18n-keys.ts
946
1040
  var TOKEN_USAGE_ADMIN_I18N_KEYS = [
947
1041
  // Page + KPI tiles
@@ -991,6 +1085,273 @@ var TOKEN_USAGE_ADMIN_I18N_KEYS = [
991
1085
  "token_usage.series.other",
992
1086
  "token_usage.timeline.empty"
993
1087
  ];
1088
+ var TOKEN_USAGE_REPORT_I18N_KEYS = [
1089
+ // Page + KPI tiles
1090
+ "token_usage.report.title",
1091
+ "token_usage.report.used_in_period",
1092
+ "token_usage.report.vs_previous",
1093
+ "token_usage.report.monthly_left",
1094
+ "token_usage.report.extra_credits",
1095
+ "token_usage.report.calls",
1096
+ // Panel titles
1097
+ "token_usage.report.usage_over_time",
1098
+ "token_usage.report.by_operation",
1099
+ // Shared states
1100
+ "token_usage.report.no_data"
1101
+ ];
1102
+
1103
+ // src/features/tokenusage/contexts/TokenUsageReportContext.tsx
1104
+
1105
+
1106
+
1107
+ // src/features/tokenusage/components/TokenUsageReportFilterBar.tsx
1108
+
1109
+ function TokenUsageReportFilterBar({ onChange }) {
1110
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-wrap items-center gap-2", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1111
+ _chunk7HKJNUW7js.DateRangeSelector,
1112
+ {
1113
+ onDateChange: (range) => {
1114
+ if (!_optionalChain([range, 'optionalAccess', _10 => _10.from]) || !_optionalChain([range, 'optionalAccess', _11 => _11.to])) return;
1115
+ onChange({ from: range.from.toISOString(), to: range.to.toISOString() });
1116
+ }
1117
+ }
1118
+ ) });
1119
+ }
1120
+ _chunk7QVYU63Ejs.__name.call(void 0, TokenUsageReportFilterBar, "TokenUsageReportFilterBar");
1121
+
1122
+ // src/features/tokenusage/contexts/TokenUsageReportContext.tsx
1123
+
1124
+ var TOKEN_USAGE_REPORT_PAGE_URL = "/tokenusage";
1125
+ var DEFAULT_TOP_N2 = 10;
1126
+ var REPORT_METRIC = "credits";
1127
+ var TokenUsageReportContext = _react.createContext.call(void 0, void 0);
1128
+ function defaultRange2() {
1129
+ const now = /* @__PURE__ */ new Date();
1130
+ const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
1131
+ return { from: start.toISOString(), to: now.toISOString() };
1132
+ }
1133
+ _chunk7QVYU63Ejs.__name.call(void 0, defaultRange2, "defaultRange");
1134
+ var TokenUsageReportProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, ({
1135
+ children,
1136
+ targetLabel,
1137
+ targetPanelTitleKey,
1138
+ initialFrom,
1139
+ initialTo,
1140
+ topN = DEFAULT_TOP_N2,
1141
+ pageUrl = TOKEN_USAGE_REPORT_PAGE_URL
1142
+ }) => {
1143
+ const t = _nextintl.useTranslations.call(void 0, );
1144
+ const generateUrl = _chunk7HKJNUW7js.usePageUrlGenerator.call(void 0, );
1145
+ const [filters, setFilterState] = _react.useState.call(void 0, () => {
1146
+ const range = defaultRange2();
1147
+ return { from: _nullishCoalesce(initialFrom, () => ( range.from)), to: _nullishCoalesce(initialTo, () => ( range.to)) };
1148
+ });
1149
+ const [summary, setSummary] = _react.useState.call(void 0, []);
1150
+ const [timeline, setTimeline] = _react.useState.call(void 0, []);
1151
+ const [byOperation, setByOperation] = _react.useState.call(void 0, []);
1152
+ const [byTarget, setByTarget] = _react.useState.call(void 0, []);
1153
+ const [isLoading, setIsLoading] = _react.useState.call(void 0, true);
1154
+ const [error, setError] = _react.useState.call(void 0, null);
1155
+ const { from, to } = filters;
1156
+ _react.useEffect.call(void 0, () => {
1157
+ let cancelled = false;
1158
+ setIsLoading(true);
1159
+ setError(null);
1160
+ const base = { from, to };
1161
+ Promise.all([
1162
+ TokenUsageReportService.getSummary(base),
1163
+ // granularity is pinned to "day": week/month bucketing buys nothing on the
1164
+ // ranges a single tenant browses.
1165
+ TokenUsageReportService.getTimeline({ ...base, granularity: "day" }),
1166
+ TokenUsageReportService.getBreakdown({ ...base, dimension: "operation", metric: REPORT_METRIC, limit: topN }),
1167
+ // No targetLabel means the host app never opted in, so the request is
1168
+ // skipped entirely rather than issued and discarded.
1169
+ targetLabel ? TokenUsageReportService.getBreakdown({
1170
+ ...base,
1171
+ dimension: "target",
1172
+ targetLabel,
1173
+ metric: REPORT_METRIC,
1174
+ limit: topN
1175
+ }) : Promise.resolve([])
1176
+ ]).then(([nextSummary, nextTimeline, nextByOperation, nextByTarget]) => {
1177
+ if (cancelled) return;
1178
+ setSummary(_nullishCoalesce(nextSummary, () => ( [])));
1179
+ setTimeline(_nullishCoalesce(nextTimeline, () => ( [])));
1180
+ setByOperation(_nullishCoalesce(nextByOperation, () => ( [])));
1181
+ setByTarget(_nullishCoalesce(nextByTarget, () => ( [])));
1182
+ }).catch((err) => {
1183
+ if (cancelled) return;
1184
+ console.error("Failed to load token usage:", err);
1185
+ setError(err instanceof Error ? err.message : String(err));
1186
+ }).finally(() => {
1187
+ if (cancelled) return;
1188
+ setIsLoading(false);
1189
+ });
1190
+ return () => {
1191
+ cancelled = true;
1192
+ };
1193
+ }, [from, to, targetLabel, topN]);
1194
+ const setFilters = _react.useCallback.call(void 0, (next) => {
1195
+ setFilterState((prev) => ({ ...prev, ...next }));
1196
+ }, []);
1197
+ const breadcrumb = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => [
1198
+ { name: t("token_usage.report.title"), href: generateUrl({ page: pageUrl }) }
1199
+ ], "breadcrumb");
1200
+ const title = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => ({
1201
+ type: t("token_usage.report.title"),
1202
+ functions: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageReportFilterBar, { onChange: setFilters }, "tokenUsageReportFilterBar")
1203
+ }), "title");
1204
+ const contextValue = _react.useMemo.call(void 0,
1205
+ () => ({ summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error }),
1206
+ [summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error]
1207
+ );
1208
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.SharedProvider, { value: { breadcrumbs: breadcrumb(), title: title() }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageReportContext.Provider, { value: contextValue, children }) });
1209
+ }, "TokenUsageReportProvider");
1210
+ var useTokenUsageReport = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => {
1211
+ const ctx = _react.useContext.call(void 0, TokenUsageReportContext);
1212
+ if (!ctx) {
1213
+ throw new Error("useTokenUsageReport() called outside <TokenUsageReportProvider>.");
1214
+ }
1215
+ return ctx;
1216
+ }, "useTokenUsageReport");
1217
+
1218
+ // src/features/tokenusage/components/TokenUsageReportContainer.tsx
1219
+
1220
+
1221
+ // src/features/tokenusage/components/TokenUsageReportTiles.tsx
1222
+
1223
+
1224
+
1225
+ var ZERO2 = { cost: 0, credits: 0, tokensIn: 0, tokensOut: 0, cached: 0, calls: 0 };
1226
+ function TokenUsageReportTiles({ summary, metric, balances }) {
1227
+ const t = _nextintl.useTranslations.call(void 0, );
1228
+ const { decimal, metricValue: formatValue } = useUsageFormatters();
1229
+ const rowFor = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (window) => _nullishCoalesce(summary.find((row) => row.window === window), () => ( ZERO2)), "rowFor");
1230
+ const current = rowFor("current");
1231
+ const previous = rowFor("previous");
1232
+ const monthlyPercentage = balances && balances.monthlyCredits > 0 ? balances.availableMonthlyCredits / balances.monthlyCredits * 100 : 0;
1233
+ const monthlyColor = monthlyPercentage > 75 ? "text-success" : monthlyPercentage >= 5 ? "text-warning" : "text-destructive";
1234
+ const whole = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (value) => decimal(Math.max(0, Math.floor(value)), 0), "whole");
1235
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-3", children: [
1236
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { "data-testid": "tile-used", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardContent, { className: "grid gap-1", children: [
1237
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.used_in_period") }),
1238
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-primary text-xl font-semibold tabular-nums", children: formatValue(metricValue(current, metric), metric) }),
1239
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-1", children: [
1240
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1241
+ Delta2,
1242
+ {
1243
+ testId: "tile-used-delta",
1244
+ delta: percentageDelta(metricValue(current, metric), metricValue(previous, metric)),
1245
+ decimal
1246
+ }
1247
+ ),
1248
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.vs_previous") })
1249
+ ] })
1250
+ ] }) }),
1251
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-3 sm:grid-cols-3", children: [
1252
+ balances && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { "data-testid": "tile-monthly", size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardContent, { className: "grid gap-1", children: [
1253
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.monthly_left") }),
1254
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-baseline gap-1", children: [
1255
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-testid": "tile-monthly-value", className: _chunkRTOC53EZjs.cn.call(void 0, "text-sm font-medium tabular-nums", monthlyColor), children: whole(balances.availableMonthlyCredits) }),
1256
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "text-muted-foreground text-xs tabular-nums", children: [
1257
+ "/ ",
1258
+ whole(balances.monthlyCredits)
1259
+ ] })
1260
+ ] })
1261
+ ] }) }),
1262
+ balances && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { "data-testid": "tile-extra", size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardContent, { className: "grid gap-1", children: [
1263
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.extra_credits") }),
1264
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-medium tabular-nums", children: whole(balances.availableExtraCredits) })
1265
+ ] }) }),
1266
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { "data-testid": "tile-calls", size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.CardContent, { className: "grid gap-1", children: [
1267
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.calls") }),
1268
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-medium tabular-nums", children: decimal(current.calls, 0) })
1269
+ ] }) })
1270
+ ] })
1271
+ ] });
1272
+ }
1273
+ _chunk7QVYU63Ejs.__name.call(void 0, TokenUsageReportTiles, "TokenUsageReportTiles");
1274
+ function Delta2({
1275
+ testId,
1276
+ delta,
1277
+ decimal
1278
+ }) {
1279
+ if (delta === void 0) {
1280
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-testid": testId, className: "text-muted-foreground text-xs", children: "\u2014" });
1281
+ }
1282
+ const increased = delta >= 0;
1283
+ const Icon = increased ? _lucidereact.ArrowUpIcon : _lucidereact.ArrowDownIcon;
1284
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
1285
+ "span",
1286
+ {
1287
+ "data-testid": testId,
1288
+ className: _chunkRTOC53EZjs.cn.call(void 0,
1289
+ "inline-flex items-center gap-0.5 text-xs tabular-nums",
1290
+ increased ? "text-warning" : "text-success"
1291
+ ),
1292
+ children: [
1293
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { "aria-hidden": true, className: "size-3" }),
1294
+ decimal(Math.abs(delta), 0),
1295
+ " %"
1296
+ ]
1297
+ }
1298
+ );
1299
+ }
1300
+ _chunk7QVYU63Ejs.__name.call(void 0, Delta2, "Delta");
1301
+
1302
+ // src/features/tokenusage/components/TokenUsageReportContainer.tsx
1303
+
1304
+ function TokenUsageReportContainer({ balances = null }) {
1305
+ const t = _nextintl.useTranslations.call(void 0, );
1306
+ const { summary, timeline, byOperation, byTarget, isLoading, error, targetPanelTitleKey } = useTokenUsageReport();
1307
+ if (error) {
1308
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.Card, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
1309
+ }
1310
+ if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.RoundPageContainer, { fullWidth: true, forceHeader: true });
1311
+ const emptyLabel = t("token_usage.report.no_data");
1312
+ const targetTitle = targetPanelTitleKey && byTarget.length > 0 ? t(targetPanelTitleKey) : void 0;
1313
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-4 p-4", children: [
1314
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageReportTiles, { summary, metric: "credits", balances }),
1315
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
1316
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.report.usage_over_time") }) }),
1317
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageTimelineChart, { rows: timeline, metric: "credits", stackBy: "type" }) })
1318
+ ] }),
1319
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkRTOC53EZjs.cn.call(void 0, "grid gap-4", targetTitle && "md:grid-cols-2"), children: [
1320
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
1321
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: t("token_usage.report.by_operation") }) }),
1322
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1323
+ TokenUsageRankedBar,
1324
+ {
1325
+ rows: byOperation,
1326
+ metric: "credits",
1327
+ emptyLabel,
1328
+ labelsAreOperationTypes: true
1329
+ }
1330
+ ) })
1331
+ ] }),
1332
+ targetTitle && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk7HKJNUW7js.Card, { children: [
1333
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardTitle, { children: targetTitle }) }),
1334
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7HKJNUW7js.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byTarget, metric: "credits", emptyLabel }) })
1335
+ ] })
1336
+ ] })
1337
+ ] }) });
1338
+ }
1339
+ _chunk7QVYU63Ejs.__name.call(void 0, TokenUsageReportContainer, "TokenUsageReportContainer");
1340
+
1341
+
1342
+
1343
+
1344
+
1345
+
1346
+
1347
+
1348
+
1349
+
1350
+
1351
+
1352
+
1353
+
1354
+
994
1355
 
995
1356
 
996
1357
 
@@ -1008,5 +1369,5 @@ var TOKEN_USAGE_ADMIN_I18N_KEYS = [
1008
1369
 
1009
1370
 
1010
1371
 
1011
- exports.TOKEN_USAGE_ADMIN_I18N_KEYS = TOKEN_USAGE_ADMIN_I18N_KEYS; exports.TokenUsageAdminBreakdown = _chunkV66AZWPGjs.TokenUsageAdminBreakdown; exports.TokenUsageAdminBreakdownModule = _chunkV66AZWPGjs.TokenUsageAdminBreakdownModule; exports.TokenUsageAdminContainer = TokenUsageAdminContainer; exports.TokenUsageAdminFilterBar = TokenUsageAdminFilterBar; exports.TokenUsageAdminProvider = TokenUsageAdminProvider; exports.TokenUsageAdminService = TokenUsageAdminService; exports.TokenUsageAdminSummary = _chunkV66AZWPGjs.TokenUsageAdminSummary; exports.TokenUsageAdminSummaryModule = _chunkV66AZWPGjs.TokenUsageAdminSummaryModule; exports.TokenUsageAdminTiles = TokenUsageAdminTiles; exports.TokenUsageAdminTimeline = _chunkV66AZWPGjs.TokenUsageAdminTimeline; exports.TokenUsageAdminTimelineModule = _chunkV66AZWPGjs.TokenUsageAdminTimelineModule; exports.TokenUsageBreakdownTable = TokenUsageBreakdownTable; exports.TokenUsageRankedBar = TokenUsageRankedBar; exports.TokenUsageTimelineChart = TokenUsageTimelineChart; exports.useTokenUsageAdmin = useTokenUsageAdmin;
1372
+ exports.TOKEN_USAGE_ADMIN_I18N_KEYS = TOKEN_USAGE_ADMIN_I18N_KEYS; exports.TOKEN_USAGE_REPORT_I18N_KEYS = TOKEN_USAGE_REPORT_I18N_KEYS; exports.TokenUsageAdminBreakdown = _chunkRTOC53EZjs.TokenUsageAdminBreakdown; exports.TokenUsageAdminBreakdownModule = _chunkRTOC53EZjs.TokenUsageAdminBreakdownModule; exports.TokenUsageAdminContainer = TokenUsageAdminContainer; exports.TokenUsageAdminFilterBar = TokenUsageAdminFilterBar; exports.TokenUsageAdminProvider = TokenUsageAdminProvider; exports.TokenUsageAdminService = TokenUsageAdminService; exports.TokenUsageAdminSummary = _chunkRTOC53EZjs.TokenUsageAdminSummary; exports.TokenUsageAdminSummaryModule = _chunkRTOC53EZjs.TokenUsageAdminSummaryModule; exports.TokenUsageAdminTiles = TokenUsageAdminTiles; exports.TokenUsageAdminTimeline = _chunkRTOC53EZjs.TokenUsageAdminTimeline; exports.TokenUsageAdminTimelineModule = _chunkRTOC53EZjs.TokenUsageAdminTimelineModule; exports.TokenUsageBreakdownTable = TokenUsageBreakdownTable; exports.TokenUsageRankedBar = TokenUsageRankedBar; exports.TokenUsageReportBreakdown = _chunkRTOC53EZjs.TokenUsageReportBreakdown; exports.TokenUsageReportContainer = TokenUsageReportContainer; exports.TokenUsageReportFilterBar = TokenUsageReportFilterBar; exports.TokenUsageReportProvider = TokenUsageReportProvider; exports.TokenUsageReportService = TokenUsageReportService; exports.TokenUsageReportSummary = _chunkRTOC53EZjs.TokenUsageReportSummary; exports.TokenUsageReportTiles = TokenUsageReportTiles; exports.TokenUsageReportTimeline = _chunkRTOC53EZjs.TokenUsageReportTimeline; exports.TokenUsageTimelineChart = TokenUsageTimelineChart; exports.configureTokenUsage = _chunkRTOC53EZjs.configureTokenUsage; exports.createUsageFormatters = createUsageFormatters; exports.getTokenUsageCurrency = _chunkRTOC53EZjs.getTokenUsageCurrency; exports.tokenUsageModules = _chunkRTOC53EZjs.tokenUsageModules; exports.useTokenUsageAdmin = useTokenUsageAdmin; exports.useTokenUsageReport = useTokenUsageReport; exports.useUsageFormatters = useUsageFormatters;
1012
1373
  //# sourceMappingURL=index.js.map