@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.
- package/dist/{BlockNoteEditor-7WGCH2KB.js → BlockNoteEditor-TIMGYKAA.js} +19 -19
- package/dist/{BlockNoteEditor-7WGCH2KB.js.map → BlockNoteEditor-TIMGYKAA.js.map} +1 -1
- package/dist/{BlockNoteEditor-GWAV5ESO.mjs → BlockNoteEditor-YFJMXJEU.mjs} +4 -4
- package/dist/billing/index.js +362 -362
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-GYGMEDVS.js → chunk-7HKJNUW7.js} +755 -746
- package/dist/chunk-7HKJNUW7.js.map +1 -0
- package/dist/{chunk-3LVSA7IM.mjs → chunk-DJWNNV52.mjs} +2 -2
- package/dist/{chunk-LCCRUWWY.mjs → chunk-FS4RVV3K.mjs} +241 -1
- package/dist/chunk-FS4RVV3K.mjs.map +1 -0
- package/dist/{chunk-M2EGUO2F.mjs → chunk-G4YS52SO.mjs} +16 -7
- package/dist/chunk-G4YS52SO.mjs.map +1 -0
- package/dist/{chunk-TOKHHZSP.js → chunk-OTEDZ6YW.js} +7 -7
- package/dist/{chunk-TOKHHZSP.js.map → chunk-OTEDZ6YW.js.map} +1 -1
- package/dist/{chunk-V66AZWPG.js → chunk-RTOC53EZ.js} +246 -6
- package/dist/chunk-RTOC53EZ.js.map +1 -0
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +16 -1
- package/dist/components/index.d.ts +16 -1
- package/dist/components/index.js +4 -4
- package/dist/components/index.mjs +3 -3
- package/dist/{tokenusage-admin.module-5wJ_tZTj.d.ts → config-BIjrg5wd.d.ts} +39 -1
- package/dist/{tokenusage-admin.module-DQVT4O4j.d.mts → config-BRUjtCd1.d.mts} +39 -1
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +5 -2
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.js +8 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +7 -1
- package/dist/features/help/index.js +37 -37
- package/dist/features/help/index.mjs +3 -3
- package/dist/features/tokenusage/index.d.mts +413 -14
- package/dist/features/tokenusage/index.d.ts +413 -14
- package/dist/features/tokenusage/index.js +481 -120
- package/dist/features/tokenusage/index.js.map +1 -1
- package/dist/features/tokenusage/index.mjs +431 -70
- package/dist/features/tokenusage/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/package.json +1 -1
- package/src/core/index.ts +10 -0
- package/src/core/registry/ModuleRegistry.ts +4 -0
- package/src/features/tokenusage/components/TokenUsageAdminContainer.tsx +20 -4
- package/src/features/tokenusage/components/TokenUsageAdminTiles.tsx +31 -18
- package/src/features/tokenusage/components/TokenUsageBreakdownTable.tsx +13 -13
- package/src/features/tokenusage/components/TokenUsageRankedBar.tsx +24 -7
- package/src/features/tokenusage/components/TokenUsageReportContainer.tsx +102 -0
- package/src/features/tokenusage/components/TokenUsageReportFilterBar.tsx +34 -0
- package/src/features/tokenusage/components/TokenUsageReportTiles.tsx +161 -0
- package/src/features/tokenusage/components/TokenUsageTimelineChart.tsx +15 -13
- package/src/features/tokenusage/components/__tests__/TokenUsageAdminTiles.spec.tsx +4 -2
- package/src/features/tokenusage/components/__tests__/TokenUsageRankedBar.spec.tsx +3 -1
- package/src/features/tokenusage/contexts/TokenUsageAdminContext.tsx +6 -3
- package/src/features/tokenusage/contexts/TokenUsageReportContext.tsx +199 -0
- package/src/features/tokenusage/data/TokenUsageReportService.ts +65 -0
- package/src/features/tokenusage/data/index.ts +9 -0
- package/src/features/tokenusage/data/tokenusage-report-breakdown.interface.ts +12 -0
- package/src/features/tokenusage/data/tokenusage-report-breakdown.ts +94 -0
- package/src/features/tokenusage/data/tokenusage-report-summary.interface.ts +12 -0
- package/src/features/tokenusage/data/tokenusage-report-summary.ts +87 -0
- package/src/features/tokenusage/data/tokenusage-report-timeline.interface.ts +13 -0
- package/src/features/tokenusage/data/tokenusage-report-timeline.ts +94 -0
- package/src/features/tokenusage/data/tokenusage-report.types.ts +51 -0
- package/src/features/tokenusage/i18n-keys.ts +28 -0
- package/src/features/tokenusage/index.ts +18 -0
- package/src/features/tokenusage/lib/config.ts +22 -0
- package/src/features/tokenusage/lib/formatters.ts +100 -0
- package/src/features/tokenusage/lib/metrics.ts +8 -26
- package/src/features/tokenusage/lib/palette.ts +41 -14
- package/src/features/tokenusage/tokenusage-admin.module.ts +4 -0
- package/src/features/tokenusage/tokenusage.modules.ts +40 -0
- package/src/shadcnui/ui/chart.tsx +30 -3
- package/dist/chunk-GYGMEDVS.js.map +0 -1
- package/dist/chunk-LCCRUWWY.mjs.map +0 -1
- package/dist/chunk-M2EGUO2F.mjs.map +0 -1
- package/dist/chunk-V66AZWPG.js.map +0 -1
- /package/dist/{BlockNoteEditor-GWAV5ESO.mjs.map → BlockNoteEditor-YFJMXJEU.mjs.map} +0 -0
- /package/dist/{chunk-3LVSA7IM.mjs.map → chunk-DJWNNV52.mjs.map} +0 -0
|
@@ -26,8 +26,8 @@ import {
|
|
|
26
26
|
TableHeader,
|
|
27
27
|
TableRow,
|
|
28
28
|
usePageUrlGenerator
|
|
29
|
-
} from "../../chunk-
|
|
30
|
-
import "../../chunk-
|
|
29
|
+
} from "../../chunk-G4YS52SO.mjs";
|
|
30
|
+
import "../../chunk-DJWNNV52.mjs";
|
|
31
31
|
import {
|
|
32
32
|
AbstractService,
|
|
33
33
|
EndpointCreator,
|
|
@@ -38,8 +38,14 @@ import {
|
|
|
38
38
|
TokenUsageAdminSummaryModule,
|
|
39
39
|
TokenUsageAdminTimeline,
|
|
40
40
|
TokenUsageAdminTimelineModule,
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
TokenUsageReportBreakdown,
|
|
42
|
+
TokenUsageReportSummary,
|
|
43
|
+
TokenUsageReportTimeline,
|
|
44
|
+
cn,
|
|
45
|
+
configureTokenUsage,
|
|
46
|
+
getTokenUsageCurrency,
|
|
47
|
+
tokenUsageModules
|
|
48
|
+
} from "../../chunk-FS4RVV3K.mjs";
|
|
43
49
|
import "../../chunk-AUXK7QSA.mjs";
|
|
44
50
|
import "../../chunk-C7C7VY4F.mjs";
|
|
45
51
|
import "../../chunk-E5YA5Z5X.mjs";
|
|
@@ -209,7 +215,8 @@ var TokenUsageAdminProvider = /* @__PURE__ */ __name(({
|
|
|
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 = useTranslations2();
|
|
215
222
|
const generateUrl = usePageUrlGenerator();
|
|
@@ -281,7 +288,7 @@ var TokenUsageAdminProvider = /* @__PURE__ */ __name(({
|
|
|
281
288
|
const breadcrumb = /* @__PURE__ */ __name(() => [
|
|
282
289
|
{
|
|
283
290
|
name: t("token_usage.admin.title"),
|
|
284
|
-
href: generateUrl({ page:
|
|
291
|
+
href: generateUrl({ page: pageUrl })
|
|
285
292
|
}
|
|
286
293
|
], "breadcrumb");
|
|
287
294
|
const title = /* @__PURE__ */ __name(() => ({
|
|
@@ -342,37 +349,62 @@ var useTokenUsageAdmin = /* @__PURE__ */ __name(() => {
|
|
|
342
349
|
|
|
343
350
|
// src/features/tokenusage/components/TokenUsageAdminContainer.tsx
|
|
344
351
|
import { useTranslations as useTranslations7 } from "next-intl";
|
|
345
|
-
import { useMemo as
|
|
352
|
+
import { useMemo as useMemo5 } from "react";
|
|
346
353
|
|
|
347
354
|
// src/features/tokenusage/components/TokenUsageAdminTiles.tsx
|
|
348
355
|
import { ArrowDownIcon, ArrowUpIcon } from "lucide-react";
|
|
349
356
|
import { useTranslations as useTranslations3 } from "next-intl";
|
|
350
357
|
|
|
358
|
+
// src/features/tokenusage/lib/formatters.ts
|
|
359
|
+
import { useLocale } from "next-intl";
|
|
360
|
+
import { useMemo as useMemo2 } from "react";
|
|
361
|
+
function createUsageFormatters(locale, currency) {
|
|
362
|
+
const symbol = new Intl.NumberFormat(locale, { style: "currency", currency, currencyDisplay: "narrowSymbol" }).formatToParts(0).find((part) => part.type === "currency")?.value ?? currency;
|
|
363
|
+
const decimal = /* @__PURE__ */ __name((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
|
+
__name(createUsageFormatters, "createUsageFormatters");
|
|
394
|
+
function useUsageFormatters() {
|
|
395
|
+
const locale = useLocale();
|
|
396
|
+
const currency = getTokenUsageCurrency();
|
|
397
|
+
return useMemo2(() => createUsageFormatters(locale, currency), [locale, currency]);
|
|
398
|
+
}
|
|
399
|
+
__name(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
|
__name(metricValue, "metricValue");
|
|
359
|
-
function formatDecimal(value, decimals) {
|
|
360
|
-
return value.toLocaleString(LOCALE, {
|
|
361
|
-
minimumFractionDigits: decimals,
|
|
362
|
-
maximumFractionDigits: decimals
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
__name(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
|
-
__name(formatMetricValue, "formatMetricValue");
|
|
372
|
-
function formatPercent(value, decimals = 1) {
|
|
373
|
-
return `${formatDecimal(value, decimals)} %`;
|
|
374
|
-
}
|
|
375
|
-
__name(formatPercent, "formatPercent");
|
|
376
408
|
function cacheHitPercentage(cached, tokensIn) {
|
|
377
409
|
if (!tokensIn) return 0;
|
|
378
410
|
return cached / tokensIn * 100;
|
|
@@ -389,6 +421,7 @@ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
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 = useTranslations3();
|
|
424
|
+
const { decimal, metricValue: formatValue, currency, percent } = useUsageFormatters();
|
|
392
425
|
const rowFor = /* @__PURE__ */ __name((scope, window) => 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");
|
|
@@ -405,9 +438,10 @@ function TokenUsageAdminTiles({ summary, metric, singleCustomerMode }) {
|
|
|
405
438
|
{
|
|
406
439
|
testId: "tile-customer",
|
|
407
440
|
label: t("token_usage.admin.customer_spend"),
|
|
408
|
-
value:
|
|
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__ */ jsx3(
|
|
@@ -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:
|
|
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:
|
|
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__ */ jsx3(
|
|
@@ -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:
|
|
475
|
+
value: currency(averagePerCall, 4),
|
|
476
|
+
decimal
|
|
440
477
|
}
|
|
441
478
|
),
|
|
442
479
|
/* @__PURE__ */ jsx3(
|
|
@@ -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:
|
|
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
500
|
return /* @__PURE__ */ jsx3(Card, { "data-testid": testId, children: /* @__PURE__ */ jsxs2(CardContent, { className: "grid gap-1", children: [
|
|
462
501
|
/* @__PURE__ */ jsx3("span", { className: "text-muted-foreground text-xs", children: label }),
|
|
463
502
|
/* @__PURE__ */ jsx3("span", { className: "text-primary text-xl font-semibold tabular-nums", children: value }),
|
|
464
503
|
/* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-1", children: [
|
|
465
|
-
/* @__PURE__ */ jsx3(Delta, { testId: `${testId}-delta`, delta }),
|
|
504
|
+
/* @__PURE__ */ jsx3(Delta, { testId: `${testId}-delta`, delta, decimal }),
|
|
466
505
|
/* @__PURE__ */ jsx3("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
517
|
return /* @__PURE__ */ jsx3(Card, { "data-testid": testId, size: "sm", children: /* @__PURE__ */ jsxs2(CardContent, { className: "grid gap-1", children: [
|
|
478
518
|
/* @__PURE__ */ jsx3("span", { className: "text-muted-foreground text-xs", children: label }),
|
|
479
519
|
/* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-2", children: [
|
|
480
520
|
/* @__PURE__ */ jsx3("span", { className: "text-sm font-medium tabular-nums", children: value }),
|
|
481
|
-
delta !== void 0 && /* @__PURE__ */ jsx3(Delta, { testId: `${testId}-delta`, delta })
|
|
521
|
+
delta !== void 0 && /* @__PURE__ */ jsx3(Delta, { testId: `${testId}-delta`, delta, decimal })
|
|
482
522
|
] })
|
|
483
523
|
] }) });
|
|
484
524
|
}
|
|
485
525
|
__name(SupportingTile, "SupportingTile");
|
|
486
|
-
function Delta({
|
|
526
|
+
function Delta({
|
|
527
|
+
testId,
|
|
528
|
+
delta,
|
|
529
|
+
decimal
|
|
530
|
+
}) {
|
|
487
531
|
if (delta === void 0) {
|
|
488
532
|
return /* @__PURE__ */ jsx3("span", { "data-testid": testId, className: "text-muted-foreground text-xs", children: "\u2014" });
|
|
489
533
|
}
|
|
@@ -499,7 +543,7 @@ function Delta({ testId, delta }) {
|
|
|
499
543
|
),
|
|
500
544
|
children: [
|
|
501
545
|
/* @__PURE__ */ jsx3(Icon, { "aria-hidden": true, className: "size-3" }),
|
|
502
|
-
|
|
546
|
+
decimal(Math.abs(delta), 0),
|
|
503
547
|
" %"
|
|
504
548
|
]
|
|
505
549
|
}
|
|
@@ -510,13 +554,14 @@ __name(Delta, "Delta");
|
|
|
510
554
|
// src/features/tokenusage/components/TokenUsageBreakdownTable.tsx
|
|
511
555
|
import { ArrowDownIcon as ArrowDownIcon2, ArrowUpIcon as ArrowUpIcon2 } from "lucide-react";
|
|
512
556
|
import { useTranslations as useTranslations4 } from "next-intl";
|
|
513
|
-
import { useMemo as
|
|
557
|
+
import { useMemo as useMemo3, useState as useState2 } from "react";
|
|
514
558
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
515
559
|
function TokenUsageBreakdownTable({ rows, metric }) {
|
|
516
560
|
const t = useTranslations4();
|
|
561
|
+
const { decimal, metricValue: formatValue, percent, compare } = useUsageFormatters();
|
|
517
562
|
const [sort, setSort] = useState2(void 0);
|
|
518
563
|
const showActiveUsers = rows.some((row) => row.activeUsers !== void 0);
|
|
519
|
-
const total =
|
|
564
|
+
const total = useMemo3(() => rows.reduce((sum, row) => sum + metricValue(row, metric), 0), [rows, metric]);
|
|
520
565
|
const sortValue = /* @__PURE__ */ __name((row, key) => {
|
|
521
566
|
switch (key) {
|
|
522
567
|
case "label":
|
|
@@ -533,18 +578,18 @@ function TokenUsageBreakdownTable({ rows, metric }) {
|
|
|
533
578
|
return row[key];
|
|
534
579
|
}
|
|
535
580
|
}, "sortValue");
|
|
536
|
-
const sortedRows =
|
|
581
|
+
const sortedRows = useMemo3(() => {
|
|
537
582
|
if (!sort) return rows;
|
|
538
583
|
const factor = sort.direction === "desc" ? -1 : 1;
|
|
539
584
|
return [...rows].sort((a, b) => {
|
|
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)
|
|
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__ */ __name((key) => setSort(
|
|
549
594
|
(current) => current?.key === key ? { key, direction: current.direction === "desc" ? "asc" : "desc" } : { key, direction: "desc" }
|
|
550
595
|
), "toggleSort");
|
|
@@ -588,14 +633,14 @@ function TokenUsageBreakdownTable({ rows, metric }) {
|
|
|
588
633
|
return /* @__PURE__ */ jsxs3(TableRow, { "data-testid": `breakdown-row-${row.id}`, children: [
|
|
589
634
|
/* @__PURE__ */ jsx4(TableCell, { className: "text-xs", children: row.id === "other" ? t("token_usage.admin.other") : row.label }),
|
|
590
635
|
/* @__PURE__ */ jsx4(TableCell, { className: "text-muted-foreground text-xs", children: row.sublabel ?? "" }),
|
|
591
|
-
showActiveUsers && /* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: row.activeUsers === void 0 ? "" :
|
|
592
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
593
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
594
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
595
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
596
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
597
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
598
|
-
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children:
|
|
636
|
+
showActiveUsers && /* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: row.activeUsers === void 0 ? "" : decimal(row.activeUsers, 0) }),
|
|
637
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: decimal(row.calls, 0) }),
|
|
638
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: decimal(row.tokensIn, 0) }),
|
|
639
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: decimal(row.tokensOut, 0) }),
|
|
640
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: percent(cacheHitPercentage(row.cached, row.tokensIn)) }),
|
|
641
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: formatValue(row.cost, "cost") }),
|
|
642
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: formatValue(row.credits, "credits") }),
|
|
643
|
+
/* @__PURE__ */ jsx4(TableCell, { className: "text-right text-xs tabular-nums", children: percent(share) })
|
|
599
644
|
] }, row.id);
|
|
600
645
|
}) })
|
|
601
646
|
] }) })
|
|
@@ -654,6 +699,7 @@ __name(seriesColor, "seriesColor");
|
|
|
654
699
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
655
700
|
function TokenUsageRankedBar({ rows, metric, emptyLabel, labelsAreOperationTypes = false }) {
|
|
656
701
|
const t = useTranslations5();
|
|
702
|
+
const { metricValue: formatValue, percent } = useUsageFormatters();
|
|
657
703
|
const renderLabel = /* @__PURE__ */ __name((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__ */ jsx5("span", { "data-testid": `ranked-value-${row.id}`, className: "text-right text-xs tabular-nums", children:
|
|
737
|
+
/* @__PURE__ */ jsx5("span", { "data-testid": `ranked-value-${row.id}`, className: "text-right text-xs tabular-nums", children: formatValue(value, metric) }),
|
|
692
738
|
/* @__PURE__ */ jsx5(
|
|
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:
|
|
743
|
+
children: percent(share)
|
|
698
744
|
}
|
|
699
745
|
)
|
|
700
746
|
] }, row.id);
|
|
@@ -705,7 +751,7 @@ __name(TokenUsageRankedBar, "TokenUsageRankedBar");
|
|
|
705
751
|
// src/features/tokenusage/components/TokenUsageTimelineChart.tsx
|
|
706
752
|
import { useTranslations as useTranslations6 } from "next-intl";
|
|
707
753
|
import { useTheme } from "next-themes";
|
|
708
|
-
import { useMemo as
|
|
754
|
+
import { useMemo as useMemo4 } from "react";
|
|
709
755
|
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
|
710
756
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
711
757
|
var OTHER_SERIES = "other";
|
|
@@ -732,13 +778,12 @@ var inferGranularity = /* @__PURE__ */ __name((buckets) => {
|
|
|
732
778
|
if (smallestGap >= 7) return "week";
|
|
733
779
|
return "day";
|
|
734
780
|
}, "inferGranularity");
|
|
735
|
-
var bucketFormatter = /* @__PURE__ */ __name((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 = useTranslations6();
|
|
783
|
+
const { compact, bucketDate, metricValue: formatValue } = useUsageFormatters();
|
|
739
784
|
const { resolvedTheme } = useTheme();
|
|
740
785
|
const mode = resolvedTheme === "dark" ? "dark" : "light";
|
|
741
|
-
const { chartData, seriesKeys } =
|
|
786
|
+
const { chartData, seriesKeys } = useMemo4(() => {
|
|
742
787
|
if (rows.length === 0) return { chartData: [], seriesKeys: [] };
|
|
743
788
|
const totals = /* @__PURE__ */ new Map();
|
|
744
789
|
for (const row of rows) totals.set(row.series, (totals.get(row.series) ?? 0) + metricValue2(row, metric));
|
|
@@ -766,7 +811,7 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
766
811
|
const seriesLabel = /* @__PURE__ */ __name((series) => {
|
|
767
812
|
if (series === OTHER_SERIES) {
|
|
768
813
|
const key = "token_usage.series.other";
|
|
769
|
-
return t.has(key) ? t(key) : "
|
|
814
|
+
return t.has(key) ? t(key) : "Other";
|
|
770
815
|
}
|
|
771
816
|
if (stackBy === "company") return series;
|
|
772
817
|
return operationLabel(
|
|
@@ -775,16 +820,15 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
775
820
|
(key) => t.has(key)
|
|
776
821
|
);
|
|
777
822
|
}, "seriesLabel");
|
|
778
|
-
const chartConfig =
|
|
823
|
+
const chartConfig = useMemo4(
|
|
779
824
|
() => Object.fromEntries(seriesKeys.map((series) => [series, { label: seriesLabel(series) }])),
|
|
780
825
|
[seriesKeys, stackBy]
|
|
781
826
|
);
|
|
782
827
|
if (chartData.length === 0) {
|
|
783
828
|
const key = "token_usage.timeline.empty";
|
|
784
|
-
return /* @__PURE__ */ jsx6("p", { className: className ? `text-muted-foreground text-sm ${className}` : "text-muted-foreground text-sm", children: t.has(key) ? t(key) : "
|
|
829
|
+
return /* @__PURE__ */ jsx6("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__ */ __name((series, index) => series === OTHER_SERIES ? OTHER_COLOR : seriesColor(index, mode), "seriesColorFor");
|
|
789
833
|
return /* @__PURE__ */ jsxs5("div", { className, children: [
|
|
790
834
|
/* @__PURE__ */ jsx6("span", { className: "sr-only", "data-testid": "timeline-data", children: JSON.stringify(chartData) }),
|
|
@@ -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) =>
|
|
846
|
+
tickFormatter: (value) => bucketDate(value, granularity)
|
|
803
847
|
}
|
|
804
848
|
),
|
|
805
849
|
/* @__PURE__ */ jsx6(
|
|
@@ -809,7 +853,7 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
809
853
|
axisLine: false,
|
|
810
854
|
tickMargin: 8,
|
|
811
855
|
width: 48,
|
|
812
|
-
tickFormatter: (value) =>
|
|
856
|
+
tickFormatter: (value) => compact(value)
|
|
813
857
|
}
|
|
814
858
|
),
|
|
815
859
|
/* @__PURE__ */ jsx6(
|
|
@@ -818,7 +862,9 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
818
862
|
content: /* @__PURE__ */ jsx6(
|
|
819
863
|
ChartTooltipContent,
|
|
820
864
|
{
|
|
821
|
-
labelFormatter: (value) =>
|
|
865
|
+
labelFormatter: (value) => bucketDate(String(value), granularity),
|
|
866
|
+
valueFormatter: (value) => formatValue(Number(value), metric),
|
|
867
|
+
hideZeroValues: true
|
|
822
868
|
}
|
|
823
869
|
)
|
|
824
870
|
}
|
|
@@ -860,7 +906,12 @@ function TokenUsageAdminContainer() {
|
|
|
860
906
|
isLoading,
|
|
861
907
|
error
|
|
862
908
|
} = useTokenUsageAdmin();
|
|
863
|
-
const
|
|
909
|
+
const platformIsEmpty = useMemo5(
|
|
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;
|
|
914
|
+
const stackByItems = useMemo5(
|
|
864
915
|
() => ({
|
|
865
916
|
scope: t("token_usage.admin.stack.scope"),
|
|
866
917
|
type: t("token_usage.admin.stack.type"),
|
|
@@ -874,7 +925,7 @@ function TokenUsageAdminContainer() {
|
|
|
874
925
|
if (isLoading) return /* @__PURE__ */ jsx7(RoundPageContainer, { fullWidth: true, forceHeader: true });
|
|
875
926
|
const emptyLabel = t("token_usage.admin.no_data");
|
|
876
927
|
return /* @__PURE__ */ jsx7(RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-4 p-4", children: [
|
|
877
|
-
/* @__PURE__ */ jsx7(TokenUsageAdminTiles, { summary, metric: filters.metric, singleCustomerMode }),
|
|
928
|
+
/* @__PURE__ */ jsx7(TokenUsageAdminTiles, { summary, metric: filters.metric, singleCustomerMode: hidePlatform }),
|
|
878
929
|
/* @__PURE__ */ jsxs6(Card, { children: [
|
|
879
930
|
/* @__PURE__ */ jsxs6(CardHeader, { children: [
|
|
880
931
|
/* @__PURE__ */ jsx7(CardTitle, { children: t("token_usage.admin.usage_over_time") }),
|
|
@@ -908,7 +959,7 @@ function TokenUsageAdminContainer() {
|
|
|
908
959
|
/* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7(TokenUsageRankedBar, { rows: byUser, metric: filters.metric, emptyLabel }) })
|
|
909
960
|
] })
|
|
910
961
|
] }),
|
|
911
|
-
/* @__PURE__ */ jsxs6("div", { className: cn("grid gap-4", !
|
|
962
|
+
/* @__PURE__ */ jsxs6("div", { className: cn("grid gap-4", !hidePlatform && "md:grid-cols-2"), children: [
|
|
912
963
|
/* @__PURE__ */ jsxs6(Card, { children: [
|
|
913
964
|
/* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { children: t("token_usage.admin.customer_by_operation") }) }),
|
|
914
965
|
/* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7(
|
|
@@ -921,7 +972,7 @@ function TokenUsageAdminContainer() {
|
|
|
921
972
|
}
|
|
922
973
|
) })
|
|
923
974
|
] }),
|
|
924
|
-
!
|
|
975
|
+
!hidePlatform && /* @__PURE__ */ jsxs6(Card, { children: [
|
|
925
976
|
/* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { children: t("token_usage.admin.platform_by_operation") }) }),
|
|
926
977
|
/* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7(
|
|
927
978
|
TokenUsageRankedBar,
|
|
@@ -942,6 +993,49 @@ function TokenUsageAdminContainer() {
|
|
|
942
993
|
}
|
|
943
994
|
__name(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
|
+
__name(withFilters2, "withFilters");
|
|
1003
|
+
var TokenUsageReportService = class extends AbstractService {
|
|
1004
|
+
static {
|
|
1005
|
+
__name(this, "TokenUsageReportService");
|
|
1006
|
+
}
|
|
1007
|
+
/** Two rows: window "current" and "previous". */
|
|
1008
|
+
static async getSummary(filters) {
|
|
1009
|
+
const endpoint = withFilters2(new EndpointCreator({ endpoint: Modules.TokenUsageReportSummary }), filters);
|
|
1010
|
+
return this.callApi({
|
|
1011
|
+
type: Modules.TokenUsageReportSummary,
|
|
1012
|
+
method: "GET" /* GET */,
|
|
1013
|
+
endpoint: endpoint.generate()
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
static async getTimeline(params) {
|
|
1017
|
+
const endpoint = withFilters2(new EndpointCreator({ endpoint: Modules.TokenUsageReportTimeline }), params);
|
|
1018
|
+
endpoint.addAdditionalParam("granularity", params.granularity);
|
|
1019
|
+
return this.callApi({
|
|
1020
|
+
type: Modules.TokenUsageReportTimeline,
|
|
1021
|
+
method: "GET" /* GET */,
|
|
1022
|
+
endpoint: endpoint.generate()
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
static async getBreakdown(params) {
|
|
1026
|
+
const endpoint = withFilters2(new EndpointCreator({ endpoint: 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: 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,8 +1085,261 @@ 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
|
+
import { useTranslations as useTranslations8 } from "next-intl";
|
|
1105
|
+
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect2, useMemo as useMemo6, useState as useState3 } from "react";
|
|
1106
|
+
|
|
1107
|
+
// src/features/tokenusage/components/TokenUsageReportFilterBar.tsx
|
|
1108
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1109
|
+
function TokenUsageReportFilterBar({ onChange }) {
|
|
1110
|
+
return /* @__PURE__ */ jsx8("div", { className: "flex flex-wrap items-center gap-2", children: /* @__PURE__ */ jsx8(
|
|
1111
|
+
DateRangeSelector,
|
|
1112
|
+
{
|
|
1113
|
+
onDateChange: (range) => {
|
|
1114
|
+
if (!range?.from || !range?.to) return;
|
|
1115
|
+
onChange({ from: range.from.toISOString(), to: range.to.toISOString() });
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
) });
|
|
1119
|
+
}
|
|
1120
|
+
__name(TokenUsageReportFilterBar, "TokenUsageReportFilterBar");
|
|
1121
|
+
|
|
1122
|
+
// src/features/tokenusage/contexts/TokenUsageReportContext.tsx
|
|
1123
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1124
|
+
var TOKEN_USAGE_REPORT_PAGE_URL = "/tokenusage";
|
|
1125
|
+
var DEFAULT_TOP_N2 = 10;
|
|
1126
|
+
var REPORT_METRIC = "credits";
|
|
1127
|
+
var TokenUsageReportContext = createContext2(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
|
+
__name(defaultRange2, "defaultRange");
|
|
1134
|
+
var TokenUsageReportProvider = /* @__PURE__ */ __name(({
|
|
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 = useTranslations8();
|
|
1144
|
+
const generateUrl = usePageUrlGenerator();
|
|
1145
|
+
const [filters, setFilterState] = useState3(() => {
|
|
1146
|
+
const range = defaultRange2();
|
|
1147
|
+
return { from: initialFrom ?? range.from, to: initialTo ?? range.to };
|
|
1148
|
+
});
|
|
1149
|
+
const [summary, setSummary] = useState3([]);
|
|
1150
|
+
const [timeline, setTimeline] = useState3([]);
|
|
1151
|
+
const [byOperation, setByOperation] = useState3([]);
|
|
1152
|
+
const [byTarget, setByTarget] = useState3([]);
|
|
1153
|
+
const [isLoading, setIsLoading] = useState3(true);
|
|
1154
|
+
const [error, setError] = useState3(null);
|
|
1155
|
+
const { from, to } = filters;
|
|
1156
|
+
useEffect2(() => {
|
|
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(nextSummary ?? []);
|
|
1179
|
+
setTimeline(nextTimeline ?? []);
|
|
1180
|
+
setByOperation(nextByOperation ?? []);
|
|
1181
|
+
setByTarget(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 = useCallback2((next) => {
|
|
1195
|
+
setFilterState((prev) => ({ ...prev, ...next }));
|
|
1196
|
+
}, []);
|
|
1197
|
+
const breadcrumb = /* @__PURE__ */ __name(() => [
|
|
1198
|
+
{ name: t("token_usage.report.title"), href: generateUrl({ page: pageUrl }) }
|
|
1199
|
+
], "breadcrumb");
|
|
1200
|
+
const title = /* @__PURE__ */ __name(() => ({
|
|
1201
|
+
type: t("token_usage.report.title"),
|
|
1202
|
+
functions: /* @__PURE__ */ jsx9(TokenUsageReportFilterBar, { onChange: setFilters }, "tokenUsageReportFilterBar")
|
|
1203
|
+
}), "title");
|
|
1204
|
+
const contextValue = useMemo6(
|
|
1205
|
+
() => ({ summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error }),
|
|
1206
|
+
[summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error]
|
|
1207
|
+
);
|
|
1208
|
+
return /* @__PURE__ */ jsx9(SharedProvider, { value: { breadcrumbs: breadcrumb(), title: title() }, children: /* @__PURE__ */ jsx9(TokenUsageReportContext.Provider, { value: contextValue, children }) });
|
|
1209
|
+
}, "TokenUsageReportProvider");
|
|
1210
|
+
var useTokenUsageReport = /* @__PURE__ */ __name(() => {
|
|
1211
|
+
const ctx = useContext2(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
|
+
import { useTranslations as useTranslations10 } from "next-intl";
|
|
1220
|
+
|
|
1221
|
+
// src/features/tokenusage/components/TokenUsageReportTiles.tsx
|
|
1222
|
+
import { ArrowDownIcon as ArrowDownIcon3, ArrowUpIcon as ArrowUpIcon3 } from "lucide-react";
|
|
1223
|
+
import { useTranslations as useTranslations9 } from "next-intl";
|
|
1224
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1225
|
+
var ZERO2 = { cost: 0, credits: 0, tokensIn: 0, tokensOut: 0, cached: 0, calls: 0 };
|
|
1226
|
+
function TokenUsageReportTiles({ summary, metric, balances }) {
|
|
1227
|
+
const t = useTranslations9();
|
|
1228
|
+
const { decimal, metricValue: formatValue } = useUsageFormatters();
|
|
1229
|
+
const rowFor = /* @__PURE__ */ __name((window) => 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__ */ __name((value) => decimal(Math.max(0, Math.floor(value)), 0), "whole");
|
|
1235
|
+
return /* @__PURE__ */ jsxs7("div", { className: "grid gap-3", children: [
|
|
1236
|
+
/* @__PURE__ */ jsx10(Card, { "data-testid": "tile-used", children: /* @__PURE__ */ jsxs7(CardContent, { className: "grid gap-1", children: [
|
|
1237
|
+
/* @__PURE__ */ jsx10("span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.used_in_period") }),
|
|
1238
|
+
/* @__PURE__ */ jsx10("span", { className: "text-primary text-xl font-semibold tabular-nums", children: formatValue(metricValue(current, metric), metric) }),
|
|
1239
|
+
/* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-1", children: [
|
|
1240
|
+
/* @__PURE__ */ jsx10(
|
|
1241
|
+
Delta2,
|
|
1242
|
+
{
|
|
1243
|
+
testId: "tile-used-delta",
|
|
1244
|
+
delta: percentageDelta(metricValue(current, metric), metricValue(previous, metric)),
|
|
1245
|
+
decimal
|
|
1246
|
+
}
|
|
1247
|
+
),
|
|
1248
|
+
/* @__PURE__ */ jsx10("span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.vs_previous") })
|
|
1249
|
+
] })
|
|
1250
|
+
] }) }),
|
|
1251
|
+
/* @__PURE__ */ jsxs7("div", { className: "grid gap-3 sm:grid-cols-3", children: [
|
|
1252
|
+
balances && /* @__PURE__ */ jsx10(Card, { "data-testid": "tile-monthly", size: "sm", children: /* @__PURE__ */ jsxs7(CardContent, { className: "grid gap-1", children: [
|
|
1253
|
+
/* @__PURE__ */ jsx10("span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.monthly_left") }),
|
|
1254
|
+
/* @__PURE__ */ jsxs7("span", { className: "flex items-baseline gap-1", children: [
|
|
1255
|
+
/* @__PURE__ */ jsx10("span", { "data-testid": "tile-monthly-value", className: cn("text-sm font-medium tabular-nums", monthlyColor), children: whole(balances.availableMonthlyCredits) }),
|
|
1256
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground text-xs tabular-nums", children: [
|
|
1257
|
+
"/ ",
|
|
1258
|
+
whole(balances.monthlyCredits)
|
|
1259
|
+
] })
|
|
1260
|
+
] })
|
|
1261
|
+
] }) }),
|
|
1262
|
+
balances && /* @__PURE__ */ jsx10(Card, { "data-testid": "tile-extra", size: "sm", children: /* @__PURE__ */ jsxs7(CardContent, { className: "grid gap-1", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx10("span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.extra_credits") }),
|
|
1264
|
+
/* @__PURE__ */ jsx10("span", { className: "text-sm font-medium tabular-nums", children: whole(balances.availableExtraCredits) })
|
|
1265
|
+
] }) }),
|
|
1266
|
+
/* @__PURE__ */ jsx10(Card, { "data-testid": "tile-calls", size: "sm", children: /* @__PURE__ */ jsxs7(CardContent, { className: "grid gap-1", children: [
|
|
1267
|
+
/* @__PURE__ */ jsx10("span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.calls") }),
|
|
1268
|
+
/* @__PURE__ */ jsx10("span", { className: "text-sm font-medium tabular-nums", children: decimal(current.calls, 0) })
|
|
1269
|
+
] }) })
|
|
1270
|
+
] })
|
|
1271
|
+
] });
|
|
1272
|
+
}
|
|
1273
|
+
__name(TokenUsageReportTiles, "TokenUsageReportTiles");
|
|
1274
|
+
function Delta2({
|
|
1275
|
+
testId,
|
|
1276
|
+
delta,
|
|
1277
|
+
decimal
|
|
1278
|
+
}) {
|
|
1279
|
+
if (delta === void 0) {
|
|
1280
|
+
return /* @__PURE__ */ jsx10("span", { "data-testid": testId, className: "text-muted-foreground text-xs", children: "\u2014" });
|
|
1281
|
+
}
|
|
1282
|
+
const increased = delta >= 0;
|
|
1283
|
+
const Icon = increased ? ArrowUpIcon3 : ArrowDownIcon3;
|
|
1284
|
+
return /* @__PURE__ */ jsxs7(
|
|
1285
|
+
"span",
|
|
1286
|
+
{
|
|
1287
|
+
"data-testid": testId,
|
|
1288
|
+
className: cn(
|
|
1289
|
+
"inline-flex items-center gap-0.5 text-xs tabular-nums",
|
|
1290
|
+
increased ? "text-warning" : "text-success"
|
|
1291
|
+
),
|
|
1292
|
+
children: [
|
|
1293
|
+
/* @__PURE__ */ jsx10(Icon, { "aria-hidden": true, className: "size-3" }),
|
|
1294
|
+
decimal(Math.abs(delta), 0),
|
|
1295
|
+
" %"
|
|
1296
|
+
]
|
|
1297
|
+
}
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
__name(Delta2, "Delta");
|
|
1301
|
+
|
|
1302
|
+
// src/features/tokenusage/components/TokenUsageReportContainer.tsx
|
|
1303
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1304
|
+
function TokenUsageReportContainer({ balances = null }) {
|
|
1305
|
+
const t = useTranslations10();
|
|
1306
|
+
const { summary, timeline, byOperation, byTarget, isLoading, error, targetPanelTitleKey } = useTokenUsageReport();
|
|
1307
|
+
if (error) {
|
|
1308
|
+
return /* @__PURE__ */ jsx11(RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsx11("div", { className: "p-4", children: /* @__PURE__ */ jsx11(Card, { children: /* @__PURE__ */ jsx11(CardContent, { children: /* @__PURE__ */ jsx11("p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
|
|
1309
|
+
}
|
|
1310
|
+
if (isLoading) return /* @__PURE__ */ jsx11(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__ */ jsx11(RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-4 p-4", children: [
|
|
1314
|
+
/* @__PURE__ */ jsx11(TokenUsageReportTiles, { summary, metric: "credits", balances }),
|
|
1315
|
+
/* @__PURE__ */ jsxs8(Card, { children: [
|
|
1316
|
+
/* @__PURE__ */ jsx11(CardHeader, { children: /* @__PURE__ */ jsx11(CardTitle, { children: t("token_usage.report.usage_over_time") }) }),
|
|
1317
|
+
/* @__PURE__ */ jsx11(CardContent, { children: /* @__PURE__ */ jsx11(TokenUsageTimelineChart, { rows: timeline, metric: "credits", stackBy: "type" }) })
|
|
1318
|
+
] }),
|
|
1319
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("grid gap-4", targetTitle && "md:grid-cols-2"), children: [
|
|
1320
|
+
/* @__PURE__ */ jsxs8(Card, { children: [
|
|
1321
|
+
/* @__PURE__ */ jsx11(CardHeader, { children: /* @__PURE__ */ jsx11(CardTitle, { children: t("token_usage.report.by_operation") }) }),
|
|
1322
|
+
/* @__PURE__ */ jsx11(CardContent, { children: /* @__PURE__ */ jsx11(
|
|
1323
|
+
TokenUsageRankedBar,
|
|
1324
|
+
{
|
|
1325
|
+
rows: byOperation,
|
|
1326
|
+
metric: "credits",
|
|
1327
|
+
emptyLabel,
|
|
1328
|
+
labelsAreOperationTypes: true
|
|
1329
|
+
}
|
|
1330
|
+
) })
|
|
1331
|
+
] }),
|
|
1332
|
+
targetTitle && /* @__PURE__ */ jsxs8(Card, { children: [
|
|
1333
|
+
/* @__PURE__ */ jsx11(CardHeader, { children: /* @__PURE__ */ jsx11(CardTitle, { children: targetTitle }) }),
|
|
1334
|
+
/* @__PURE__ */ jsx11(CardContent, { children: /* @__PURE__ */ jsx11(TokenUsageRankedBar, { rows: byTarget, metric: "credits", emptyLabel }) })
|
|
1335
|
+
] })
|
|
1336
|
+
] })
|
|
1337
|
+
] }) });
|
|
1338
|
+
}
|
|
1339
|
+
__name(TokenUsageReportContainer, "TokenUsageReportContainer");
|
|
994
1340
|
export {
|
|
995
1341
|
TOKEN_USAGE_ADMIN_I18N_KEYS,
|
|
1342
|
+
TOKEN_USAGE_REPORT_I18N_KEYS,
|
|
996
1343
|
TokenUsageAdminBreakdown,
|
|
997
1344
|
TokenUsageAdminBreakdownModule,
|
|
998
1345
|
TokenUsageAdminContainer,
|
|
@@ -1006,7 +1353,21 @@ export {
|
|
|
1006
1353
|
TokenUsageAdminTimelineModule,
|
|
1007
1354
|
TokenUsageBreakdownTable,
|
|
1008
1355
|
TokenUsageRankedBar,
|
|
1356
|
+
TokenUsageReportBreakdown,
|
|
1357
|
+
TokenUsageReportContainer,
|
|
1358
|
+
TokenUsageReportFilterBar,
|
|
1359
|
+
TokenUsageReportProvider,
|
|
1360
|
+
TokenUsageReportService,
|
|
1361
|
+
TokenUsageReportSummary,
|
|
1362
|
+
TokenUsageReportTiles,
|
|
1363
|
+
TokenUsageReportTimeline,
|
|
1009
1364
|
TokenUsageTimelineChart,
|
|
1010
|
-
|
|
1365
|
+
configureTokenUsage,
|
|
1366
|
+
createUsageFormatters,
|
|
1367
|
+
getTokenUsageCurrency,
|
|
1368
|
+
tokenUsageModules,
|
|
1369
|
+
useTokenUsageAdmin,
|
|
1370
|
+
useTokenUsageReport,
|
|
1371
|
+
useUsageFormatters
|
|
1011
1372
|
};
|
|
1012
1373
|
//# sourceMappingURL=index.mjs.map
|