@carlonicora/nextjs-jsonapi 3.4.2 → 3.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-F6TGKMTW.js → BlockNoteEditor-7K2ENAU6.js} +9 -9
- package/dist/{BlockNoteEditor-F6TGKMTW.js.map → BlockNoteEditor-7K2ENAU6.js.map} +1 -1
- package/dist/{BlockNoteEditor-3D5D7YL7.mjs → BlockNoteEditor-MSFNIMZP.mjs} +2 -2
- package/dist/billing/index.js +333 -333
- package/dist/billing/index.mjs +1 -1
- package/dist/{chunk-G5LSXG5K.mjs → chunk-3LF5PPRY.mjs} +30 -19
- package/dist/chunk-3LF5PPRY.mjs.map +1 -0
- package/dist/{chunk-KT4CTWCV.js → chunk-LKFWEYVS.js} +30 -19
- package/dist/chunk-LKFWEYVS.js.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.d.mts +29 -2
- package/dist/components/index.d.ts +29 -2
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/features/help/index.js +31 -31
- package/dist/features/help/index.mjs +1 -1
- package/dist/features/tokenusage/index.js +75 -75
- package/dist/features/tokenusage/index.mjs +1 -1
- package/package.json +1 -1
- package/src/components/containers/RoundPageContainer.tsx +63 -14
- package/src/components/navigations/MobileNavigationBar.tsx +9 -2
- package/dist/chunk-G5LSXG5K.mjs.map +0 -1
- package/dist/chunk-KT4CTWCV.js.map +0 -1
- /package/dist/{BlockNoteEditor-3D5D7YL7.mjs.map → BlockNoteEditor-MSFNIMZP.mjs.map} +0 -0
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
var
|
|
29
|
+
var _chunkLKFWEYVSjs = require('../../chunk-LKFWEYVS.js');
|
|
30
30
|
require('../../chunk-X2MQ3WHX.js');
|
|
31
31
|
|
|
32
32
|
|
|
@@ -70,7 +70,7 @@ function TokenUsageAdminFilterBar({ granularity, companyId, metric, companies, o
|
|
|
70
70
|
};
|
|
71
71
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
72
72
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
73
|
-
|
|
73
|
+
_chunkLKFWEYVSjs.DateRangeSelector,
|
|
74
74
|
{
|
|
75
75
|
onDateChange: (range) => {
|
|
76
76
|
if (!_optionalChain([range, 'optionalAccess', _ => _.from]) || !_optionalChain([range, 'optionalAccess', _2 => _2.to])) return;
|
|
@@ -105,16 +105,16 @@ function TokenUsageAdminFilterBar({ granularity, companyId, metric, companies, o
|
|
|
105
105
|
}
|
|
106
106
|
),
|
|
107
107
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
108
|
-
|
|
108
|
+
_chunkLKFWEYVSjs.Select,
|
|
109
109
|
{
|
|
110
110
|
items: companyItems,
|
|
111
111
|
value: _nullishCoalesce(companyId, () => ( ALL_COMPANIES)),
|
|
112
112
|
onValueChange: (value) => onChange({ companyId: !value || value === ALL_COMPANIES ? void 0 : value }),
|
|
113
113
|
children: [
|
|
114
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
115
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
116
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
117
|
-
companies.map((company) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
114
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectTrigger, { className: "w-56", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectValue, { placeholder: t("token_usage.admin.all_companies") }) }),
|
|
115
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.SelectContent, { children: [
|
|
116
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectItem, { value: ALL_COMPANIES, children: t("token_usage.admin.all_companies") }),
|
|
117
|
+
companies.map((company) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectItem, { value: company.id, children: company.label }, company.id))
|
|
118
118
|
] })
|
|
119
119
|
]
|
|
120
120
|
}
|
|
@@ -137,7 +137,7 @@ function Segmented({
|
|
|
137
137
|
children: options.map((option) => {
|
|
138
138
|
const selected = option.value === value;
|
|
139
139
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
140
|
-
|
|
140
|
+
_chunkLKFWEYVSjs.Button,
|
|
141
141
|
{
|
|
142
142
|
type: "button",
|
|
143
143
|
size: "sm",
|
|
@@ -219,7 +219,7 @@ var TokenUsageAdminProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
|
|
|
219
219
|
pageUrl = TOKEN_USAGE_ADMIN_PAGE_URL
|
|
220
220
|
}) => {
|
|
221
221
|
const t = _nextintl.useTranslations.call(void 0, );
|
|
222
|
-
const generateUrl =
|
|
222
|
+
const generateUrl = _chunkLKFWEYVSjs.usePageUrlGenerator.call(void 0, );
|
|
223
223
|
const [filters, setFilterState] = _react.useState.call(void 0, () => {
|
|
224
224
|
const range = defaultRange();
|
|
225
225
|
return {
|
|
@@ -337,7 +337,7 @@ var TokenUsageAdminProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
|
|
|
337
337
|
error
|
|
338
338
|
]
|
|
339
339
|
);
|
|
340
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
340
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SharedProvider, { value: { breadcrumbs: breadcrumb(), title: title() }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageAdminContext.Provider, { value: contextValue, children }) });
|
|
341
341
|
}, "TokenUsageAdminProvider");
|
|
342
342
|
var useTokenUsageAdmin = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => {
|
|
343
343
|
const ctx = _react.useContext.call(void 0, TokenUsageAdminContext);
|
|
@@ -497,7 +497,7 @@ function LeadTile({
|
|
|
497
497
|
previousLabel,
|
|
498
498
|
decimal
|
|
499
499
|
}) {
|
|
500
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
500
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { "data-testid": testId, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardContent, { className: "grid gap-1", children: [
|
|
501
501
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: label }),
|
|
502
502
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-primary text-xl font-semibold tabular-nums", children: value }),
|
|
503
503
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-1", children: [
|
|
@@ -514,7 +514,7 @@ function SupportingTile({
|
|
|
514
514
|
delta,
|
|
515
515
|
decimal
|
|
516
516
|
}) {
|
|
517
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
517
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { "data-testid": testId, size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardContent, { className: "grid gap-1", children: [
|
|
518
518
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: label }),
|
|
519
519
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-2", children: [
|
|
520
520
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-medium tabular-nums", children: value }),
|
|
@@ -611,8 +611,8 @@ function TokenUsageBreakdownTable({ rows, metric }) {
|
|
|
611
611
|
// The package's <Table> wraps itself in an `overflow-x-clip` container, which
|
|
612
612
|
// would swallow the overflow before this scroller ever saw it — hence the
|
|
613
613
|
// child override. Wide metric tables must scroll, never widen the page.
|
|
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,
|
|
615
|
-
/* @__PURE__ */ _jsxruntime.jsx.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, _chunkLKFWEYVSjs.Table, { children: [
|
|
615
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableRow, { children: columns.map((column) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableHead, { className: _chunk6T6QB6K2js.cn.call(void 0, column.numeric && "text-end"), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
616
616
|
"button",
|
|
617
617
|
{
|
|
618
618
|
type: "button",
|
|
@@ -627,20 +627,20 @@ function TokenUsageBreakdownTable({ rows, metric }) {
|
|
|
627
627
|
]
|
|
628
628
|
}
|
|
629
629
|
) }, column.key)) }) }),
|
|
630
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
630
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableBody, { children: sortedRows.map((row) => {
|
|
631
631
|
const value = metricValue(row, metric);
|
|
632
632
|
const share = total > 0 ? value / total * 100 : 0;
|
|
633
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
634
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
635
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
636
|
-
showActiveUsers && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
637
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
638
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
639
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
640
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
641
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
642
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
643
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
633
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.TableRow, { "data-testid": `breakdown-row-${row.id}`, children: [
|
|
634
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-xs", children: row.id === "other" ? t("token_usage.admin.other") : row.label }),
|
|
635
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-muted-foreground text-xs", children: _nullishCoalesce(row.sublabel, () => ( "")) }),
|
|
636
|
+
showActiveUsers && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: row.activeUsers === void 0 ? "" : decimal(row.activeUsers, 0) }),
|
|
637
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: decimal(row.calls, 0) }),
|
|
638
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: decimal(row.tokensIn, 0) }),
|
|
639
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: decimal(row.tokensOut, 0) }),
|
|
640
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: percent(cacheHitPercentage(row.cached, row.tokensIn)) }),
|
|
641
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: formatValue(row.cost, "cost") }),
|
|
642
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: formatValue(row.credits, "credits") }),
|
|
643
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.TableCell, { className: "text-end text-xs tabular-nums", children: percent(share) })
|
|
644
644
|
] }, row.id);
|
|
645
645
|
}) })
|
|
646
646
|
] }) })
|
|
@@ -838,7 +838,7 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
838
838
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className, dir: "ltr", children: [
|
|
839
839
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "sr-only", "data-testid": "timeline-data", children: JSON.stringify(chartData) }),
|
|
840
840
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "sr-only", "data-testid": "timeline-series", children: JSON.stringify(seriesKeys) }),
|
|
841
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
841
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.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: [
|
|
842
842
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _recharts.CartesianGrid, { vertical: false }),
|
|
843
843
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
844
844
|
_recharts.XAxis,
|
|
@@ -862,10 +862,10 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
862
862
|
}
|
|
863
863
|
),
|
|
864
864
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
865
|
-
|
|
865
|
+
_chunkLKFWEYVSjs.ChartTooltip,
|
|
866
866
|
{
|
|
867
867
|
content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
868
|
-
|
|
868
|
+
_chunkLKFWEYVSjs.ChartTooltipContent,
|
|
869
869
|
{
|
|
870
870
|
labelFormatter: (value) => bucketDate(String(value), granularity),
|
|
871
871
|
valueFormatter: (value) => formatValue(Number(value), metric),
|
|
@@ -874,7 +874,7 @@ function TokenUsageTimelineChart({ rows, metric, stackBy, className }) {
|
|
|
874
874
|
)
|
|
875
875
|
}
|
|
876
876
|
),
|
|
877
|
-
seriesKeys.length >= 2 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
877
|
+
seriesKeys.length >= 2 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.ChartLegend, { content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.ChartLegendContent, {}) }) : null,
|
|
878
878
|
seriesKeys.map((series, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
879
879
|
_recharts.Bar,
|
|
880
880
|
{
|
|
@@ -926,19 +926,19 @@ function TokenUsageAdminContainer() {
|
|
|
926
926
|
[t]
|
|
927
927
|
);
|
|
928
928
|
if (error) {
|
|
929
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
929
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
|
|
930
930
|
}
|
|
931
|
-
if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
931
|
+
if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true });
|
|
932
932
|
const emptyLabel = t("token_usage.admin.no_data");
|
|
933
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
933
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-4 p-4", children: [
|
|
934
934
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageAdminTiles, { summary, metric: filters.metric, singleCustomerMode: hidePlatform }),
|
|
935
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
936
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
937
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
938
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
935
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
936
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: [
|
|
937
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.admin.usage_over_time") }),
|
|
938
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardAction, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
|
|
939
939
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.admin.stack_by") }),
|
|
940
940
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
941
|
-
|
|
941
|
+
_chunkLKFWEYVSjs.Select,
|
|
942
942
|
{
|
|
943
943
|
items: stackByItems,
|
|
944
944
|
value: filters.stackBy,
|
|
@@ -946,29 +946,29 @@ function TokenUsageAdminContainer() {
|
|
|
946
946
|
if (value) setFilters({ stackBy: value });
|
|
947
947
|
},
|
|
948
948
|
children: [
|
|
949
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
950
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
949
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectTrigger, { size: "sm", className: "w-36", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectValue, {}) }),
|
|
950
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectContent, { children: STACK_BY_VALUES.map((value) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SelectItem, { value, children: stackByItems[value] }, value)) })
|
|
951
951
|
]
|
|
952
952
|
}
|
|
953
953
|
)
|
|
954
954
|
] }) })
|
|
955
955
|
] }),
|
|
956
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
956
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageTimelineChart, { rows: timeline, metric: filters.metric, stackBy: filters.stackBy }) })
|
|
957
957
|
] }),
|
|
958
958
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
959
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
960
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
961
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
959
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
960
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.admin.by_company") }) }),
|
|
961
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byCompany, metric: filters.metric, emptyLabel }) })
|
|
962
962
|
] }),
|
|
963
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
964
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
965
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
963
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
964
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.admin.by_user") }) }),
|
|
965
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byUser, metric: filters.metric, emptyLabel }) })
|
|
966
966
|
] })
|
|
967
967
|
] }),
|
|
968
968
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunk6T6QB6K2js.cn.call(void 0, "grid gap-4", !hidePlatform && "md:grid-cols-2"), children: [
|
|
969
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
970
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
971
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
969
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
970
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.admin.customer_by_operation") }) }),
|
|
971
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
972
972
|
TokenUsageRankedBar,
|
|
973
973
|
{
|
|
974
974
|
rows: byCustomerOperation,
|
|
@@ -978,9 +978,9 @@ function TokenUsageAdminContainer() {
|
|
|
978
978
|
}
|
|
979
979
|
) })
|
|
980
980
|
] }),
|
|
981
|
-
!hidePlatform && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
982
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
983
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
981
|
+
!hidePlatform && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
982
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.admin.platform_by_operation") }) }),
|
|
983
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
984
984
|
TokenUsageRankedBar,
|
|
985
985
|
{
|
|
986
986
|
rows: byOperation,
|
|
@@ -991,9 +991,9 @@ function TokenUsageAdminContainer() {
|
|
|
991
991
|
) })
|
|
992
992
|
] })
|
|
993
993
|
] }),
|
|
994
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
995
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
996
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
994
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
995
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.admin.detail") }) }),
|
|
996
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageBreakdownTable, { rows: byCompany, metric: filters.metric }) })
|
|
997
997
|
] })
|
|
998
998
|
] }) });
|
|
999
999
|
}
|
|
@@ -1114,7 +1114,7 @@ var TOKEN_USAGE_REPORT_I18N_KEYS = [
|
|
|
1114
1114
|
|
|
1115
1115
|
function TokenUsageReportFilterBar({ onChange }) {
|
|
1116
1116
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-wrap items-center gap-2", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1117
|
-
|
|
1117
|
+
_chunkLKFWEYVSjs.DateRangeSelector,
|
|
1118
1118
|
{
|
|
1119
1119
|
onDateChange: (range) => {
|
|
1120
1120
|
if (!_optionalChain([range, 'optionalAccess', _10 => _10.from]) || !_optionalChain([range, 'optionalAccess', _11 => _11.to])) return;
|
|
@@ -1147,7 +1147,7 @@ var TokenUsageReportProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
|
|
|
1147
1147
|
pageUrl = TOKEN_USAGE_REPORT_PAGE_URL
|
|
1148
1148
|
}) => {
|
|
1149
1149
|
const t = _nextintl.useTranslations.call(void 0, );
|
|
1150
|
-
const generateUrl =
|
|
1150
|
+
const generateUrl = _chunkLKFWEYVSjs.usePageUrlGenerator.call(void 0, );
|
|
1151
1151
|
const [filters, setFilterState] = _react.useState.call(void 0, () => {
|
|
1152
1152
|
const range = defaultRange2();
|
|
1153
1153
|
return { from: _nullishCoalesce(initialFrom, () => ( range.from)), to: _nullishCoalesce(initialTo, () => ( range.to)) };
|
|
@@ -1211,7 +1211,7 @@ var TokenUsageReportProvider = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void
|
|
|
1211
1211
|
() => ({ summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error }),
|
|
1212
1212
|
[summary, timeline, byOperation, byTarget, targetPanelTitleKey, filters, setFilters, isLoading, error]
|
|
1213
1213
|
);
|
|
1214
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1214
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.SharedProvider, { value: { breadcrumbs: breadcrumb(), title: title() }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageReportContext.Provider, { value: contextValue, children }) });
|
|
1215
1215
|
}, "TokenUsageReportProvider");
|
|
1216
1216
|
var useTokenUsageReport = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => {
|
|
1217
1217
|
const ctx = _react.useContext.call(void 0, TokenUsageReportContext);
|
|
@@ -1239,7 +1239,7 @@ function TokenUsageReportTiles({ summary, metric, balances }) {
|
|
|
1239
1239
|
const monthlyColor = monthlyPercentage > 75 ? "text-success" : monthlyPercentage >= 5 ? "text-warning" : "text-destructive";
|
|
1240
1240
|
const whole = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, (value) => decimal(Math.max(0, Math.floor(value)), 0), "whole");
|
|
1241
1241
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-3", children: [
|
|
1242
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1242
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { "data-testid": "tile-used", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardContent, { className: "grid gap-1", children: [
|
|
1243
1243
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.used_in_period") }),
|
|
1244
1244
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-primary text-xl font-semibold tabular-nums", children: formatValue(metricValue(current, metric), metric) }),
|
|
1245
1245
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-1", children: [
|
|
@@ -1255,7 +1255,7 @@ function TokenUsageReportTiles({ summary, metric, balances }) {
|
|
|
1255
1255
|
] })
|
|
1256
1256
|
] }) }),
|
|
1257
1257
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-3 sm:grid-cols-3", children: [
|
|
1258
|
-
balances && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1258
|
+
balances && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { "data-testid": "tile-monthly", size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardContent, { className: "grid gap-1", children: [
|
|
1259
1259
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.monthly_left") }),
|
|
1260
1260
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-baseline gap-1", children: [
|
|
1261
1261
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-testid": "tile-monthly-value", className: _chunk6T6QB6K2js.cn.call(void 0, "text-sm font-medium tabular-nums", monthlyColor), children: whole(balances.availableMonthlyCredits) }),
|
|
@@ -1265,11 +1265,11 @@ function TokenUsageReportTiles({ summary, metric, balances }) {
|
|
|
1265
1265
|
] })
|
|
1266
1266
|
] })
|
|
1267
1267
|
] }) }),
|
|
1268
|
-
balances && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1268
|
+
balances && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { "data-testid": "tile-extra", size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardContent, { className: "grid gap-1", children: [
|
|
1269
1269
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.extra_credits") }),
|
|
1270
1270
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-medium tabular-nums", children: whole(balances.availableExtraCredits) })
|
|
1271
1271
|
] }) }),
|
|
1272
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1272
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { "data-testid": "tile-calls", size: "sm", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.CardContent, { className: "grid gap-1", children: [
|
|
1273
1273
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground text-xs", children: t("token_usage.report.calls") }),
|
|
1274
1274
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-medium tabular-nums", children: decimal(current.calls, 0) })
|
|
1275
1275
|
] }) })
|
|
@@ -1311,21 +1311,21 @@ function TokenUsageReportContainer({ balances = null }) {
|
|
|
1311
1311
|
const t = _nextintl.useTranslations.call(void 0, );
|
|
1312
1312
|
const { summary, timeline, byOperation, byTarget, isLoading, error, targetPanelTitleKey } = useTokenUsageReport();
|
|
1313
1313
|
if (error) {
|
|
1314
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1314
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.Card, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
|
|
1315
1315
|
}
|
|
1316
|
-
if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1316
|
+
if (isLoading) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true });
|
|
1317
1317
|
const emptyLabel = t("token_usage.report.no_data");
|
|
1318
1318
|
const targetTitle = targetPanelTitleKey && byTarget.length > 0 ? t(targetPanelTitleKey) : void 0;
|
|
1319
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1319
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.RoundPageContainer, { fullWidth: true, forceHeader: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-4 p-4", children: [
|
|
1320
1320
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageReportTiles, { summary, metric: "credits", balances }),
|
|
1321
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1322
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1323
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1321
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
1322
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.report.usage_over_time") }) }),
|
|
1323
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageTimelineChart, { rows: timeline, metric: "credits", stackBy: "type" }) })
|
|
1324
1324
|
] }),
|
|
1325
1325
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunk6T6QB6K2js.cn.call(void 0, "grid gap-4", targetTitle && "md:grid-cols-2"), children: [
|
|
1326
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1327
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1328
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1326
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
1327
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: t("token_usage.report.by_operation") }) }),
|
|
1328
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1329
1329
|
TokenUsageRankedBar,
|
|
1330
1330
|
{
|
|
1331
1331
|
rows: byOperation,
|
|
@@ -1335,9 +1335,9 @@ function TokenUsageReportContainer({ balances = null }) {
|
|
|
1335
1335
|
}
|
|
1336
1336
|
) })
|
|
1337
1337
|
] }),
|
|
1338
|
-
targetTitle && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1339
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1340
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1338
|
+
targetTitle && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkLKFWEYVSjs.Card, { children: [
|
|
1339
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardHeader, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardTitle, { children: targetTitle }) }),
|
|
1340
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLKFWEYVSjs.CardContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TokenUsageRankedBar, { rows: byTarget, metric: "credits", emptyLabel }) })
|
|
1341
1341
|
] })
|
|
1342
1342
|
] })
|
|
1343
1343
|
] }) });
|
package/package.json
CHANGED
|
@@ -93,6 +93,24 @@ type RoundPageContainerProps = {
|
|
|
93
93
|
* navigation — an "info" icon actively misdescribes a conversation list.
|
|
94
94
|
*/
|
|
95
95
|
detailsIcon?: ReactNode;
|
|
96
|
+
/**
|
|
97
|
+
* Which element scrolls the page.
|
|
98
|
+
* - `"document"` (default) — the shell's height only FLOORS at the viewport
|
|
99
|
+
* and grows with its content, so the document itself is the scroller. That
|
|
100
|
+
* is what restores the iOS rubber-band, and with it pull-to-refresh, in an
|
|
101
|
+
* installed PWA.
|
|
102
|
+
* - `"fixed"` — the pre-existing viewport-bound shell: the document cannot
|
|
103
|
+
* scroll and every overflow is owned by an inner pane. Required by pages
|
|
104
|
+
* whose content is itself viewport-bound — a map or canvas sized `h-full`,
|
|
105
|
+
* a kanban board on `100svh`, or any `fillHeight` tab — since those resolve
|
|
106
|
+
* to zero (or overflow the viewport) without a definite height above them.
|
|
107
|
+
*
|
|
108
|
+
* The mode is published as `data-scroll-mode` on `<html>` so an app's global
|
|
109
|
+
* stylesheet can scope root-level rules (`overflow: hidden`,
|
|
110
|
+
* `overscroll-behavior-y: none`) to fixed pages only. Applying those
|
|
111
|
+
* unscoped is what suppressed the document scroll and the bounce everywhere.
|
|
112
|
+
*/
|
|
113
|
+
scroll?: "document" | "fixed";
|
|
96
114
|
};
|
|
97
115
|
|
|
98
116
|
// Rail trigger class: override the horizontal TabsTrigger defaults for a
|
|
@@ -124,6 +142,7 @@ export function RoundPageContainer({
|
|
|
124
142
|
defaultDetailsOpen = false,
|
|
125
143
|
detailsTitle,
|
|
126
144
|
detailsIcon,
|
|
145
|
+
scroll = "document",
|
|
127
146
|
}: RoundPageContainerProps) {
|
|
128
147
|
const headerChildren = useHeaderChildren();
|
|
129
148
|
const headerLeftContent = useHeaderLeftContent();
|
|
@@ -148,6 +167,34 @@ export function RoundPageContainer({
|
|
|
148
167
|
setMounted(true);
|
|
149
168
|
}, []);
|
|
150
169
|
|
|
170
|
+
// Publish the mode for the app stylesheet. Deliberately NO cleanup: on a
|
|
171
|
+
// client navigation the next page's container mounts before this one
|
|
172
|
+
// unmounts, so resetting the attribute here would stomp the incoming page's
|
|
173
|
+
// mode. Only one container renders per page, so the last write always
|
|
174
|
+
// describes the page on screen.
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
document.documentElement.dataset.scrollMode = scroll;
|
|
177
|
+
}, [scroll]);
|
|
178
|
+
|
|
179
|
+
const isFixed = scroll === "fixed";
|
|
180
|
+
|
|
181
|
+
// `scroll="fixed"` reproduces the previous classes exactly — that mode is the
|
|
182
|
+
// old behaviour under a name. In document mode every clip and inner scroller
|
|
183
|
+
// between the shell and the content is dropped so the document is the only
|
|
184
|
+
// scroller; leaving a single `overflow-hidden` in that chain silently
|
|
185
|
+
// truncates the page instead of letting it grow.
|
|
186
|
+
const shellHeight = isFixed
|
|
187
|
+
? `h-[calc(100svh-var(--app-header-h,3rem))]`
|
|
188
|
+
: `min-h-[calc(100svh-var(--app-header-h,3rem))]`;
|
|
189
|
+
const clip = isFixed ? `overflow-hidden` : ``;
|
|
190
|
+
const scrollY = isFixed ? `overflow-y-auto` : ``;
|
|
191
|
+
// The bar is an in-flow sibling of the content (see MobileNavigationBar's own
|
|
192
|
+
// note). In document mode the column is taller than the viewport, so in-flow
|
|
193
|
+
// means it only appears once the user reaches the very bottom — `sticky`
|
|
194
|
+
// pins it to the viewport's bottom edge while keeping it in flow, so it still
|
|
195
|
+
// settles under the card at the end of the page rather than floating over it.
|
|
196
|
+
const mobileBarClass = isFixed ? `` : `sticky bottom-0 z-30`;
|
|
197
|
+
|
|
151
198
|
// Scope the persisted preference PER MODULE. A single global cookie meant
|
|
152
199
|
// collapsing an informational aside on one page silently collapsed a primary
|
|
153
200
|
// navigation panel on another — so a page declaring `defaultDetailsOpen`
|
|
@@ -228,7 +275,8 @@ export function RoundPageContainer({
|
|
|
228
275
|
<main
|
|
229
276
|
data-testid={testId}
|
|
230
277
|
className={cn(
|
|
231
|
-
"flex
|
|
278
|
+
"flex w-full flex-col",
|
|
279
|
+
shellHeight,
|
|
232
280
|
// Deliberately NO bottom safe-area inset, here or in
|
|
233
281
|
// MobileNavigationBar. iOS still reports 34px of bottom inset
|
|
234
282
|
// (measured in an installed PWA), but nothing is drawn in that strip
|
|
@@ -247,7 +295,7 @@ export function RoundPageContainer({
|
|
|
247
295
|
<div className={cn("bg-background flex min-h-0 w-full flex-1", isMobile ? "" : "rounded-lg border p-0")}>
|
|
248
296
|
<div className="flex w-full flex-col" />
|
|
249
297
|
</div>
|
|
250
|
-
<MobileNavigationBar />
|
|
298
|
+
<MobileNavigationBar className={mobileBarClass} />
|
|
251
299
|
</main>
|
|
252
300
|
</>
|
|
253
301
|
);
|
|
@@ -271,7 +319,8 @@ export function RoundPageContainer({
|
|
|
271
319
|
<main
|
|
272
320
|
data-testid={testId}
|
|
273
321
|
className={cn(
|
|
274
|
-
`flex
|
|
322
|
+
`flex w-full flex-col`,
|
|
323
|
+
shellHeight,
|
|
275
324
|
// Deliberately NO bottom safe-area inset, here or in
|
|
276
325
|
// MobileNavigationBar. iOS still reports 34px of bottom inset
|
|
277
326
|
// (measured in an installed PWA), but nothing is drawn in that strip
|
|
@@ -300,7 +349,7 @@ export function RoundPageContainer({
|
|
|
300
349
|
fullWidth={fullWidth}
|
|
301
350
|
/>
|
|
302
351
|
)}
|
|
303
|
-
<div className=
|
|
352
|
+
<div className={cn(`flex w-full`, isFixed && `h-full`, clip)}>
|
|
304
353
|
{layout === "rail" && tabs ? (
|
|
305
354
|
// Rail layout: the vertical-tab navigation is a flush-left
|
|
306
355
|
// sidebar of the card and the content fills the full remaining
|
|
@@ -318,12 +367,15 @@ export function RoundPageContainer({
|
|
|
318
367
|
// the shadcn root's default `data-[orientation=horizontal]:flex-col`
|
|
319
368
|
// to keep the rail and content side by side.
|
|
320
369
|
orientation="horizontal"
|
|
321
|
-
className=
|
|
370
|
+
className={cn(`flex min-w-0 grow data-[orientation=horizontal]:flex-row`, isFixed && `h-full`, clip)}
|
|
322
371
|
>
|
|
323
372
|
{/* Flush-left section rail — md and up */}
|
|
324
373
|
<aside
|
|
325
374
|
data-testid="round-page-rail"
|
|
326
|
-
className=
|
|
375
|
+
className={cn(
|
|
376
|
+
`hidden shrink-0 border-e p-4 md:flex md:w-56 md:flex-col`,
|
|
377
|
+
isFixed && `md:overflow-y-auto`,
|
|
378
|
+
)}
|
|
327
379
|
>
|
|
328
380
|
{/* `w-full` is load-bearing: TabsList's base variant carries
|
|
329
381
|
`w-fit`, which shrinks the rail list to its widest label.
|
|
@@ -359,7 +411,7 @@ export function RoundPageContainer({
|
|
|
359
411
|
</aside>
|
|
360
412
|
|
|
361
413
|
{/* Content — full width, fills the remaining space */}
|
|
362
|
-
<div className=
|
|
414
|
+
<div className={cn(`flex min-w-0 grow flex-col`, clip)}>
|
|
363
415
|
{/* Section Select — below md */}
|
|
364
416
|
<div data-testid="round-page-rail-select" className="p-2 md:hidden">
|
|
365
417
|
<Select
|
|
@@ -392,10 +444,7 @@ export function RoundPageContainer({
|
|
|
392
444
|
</Select>
|
|
393
445
|
</div>
|
|
394
446
|
<div
|
|
395
|
-
className={cn(
|
|
396
|
-
`min-w-0 grow`,
|
|
397
|
-
activeFillHeight ? `flex flex-col overflow-hidden` : `overflow-y-auto p-4`,
|
|
398
|
-
)}
|
|
447
|
+
className={cn(`min-w-0 grow`, activeFillHeight ? cn(`flex flex-col`, clip) : cn(scrollY, `p-4`))}
|
|
399
448
|
>
|
|
400
449
|
{/* Centre and constrain rail content (like the non-rail
|
|
401
450
|
layout). Fill-height tabs keep the full width. */}
|
|
@@ -424,7 +473,7 @@ export function RoundPageContainer({
|
|
|
424
473
|
className={cn(
|
|
425
474
|
`grow`,
|
|
426
475
|
isMobile ? `p-2` : `p-4`,
|
|
427
|
-
activeFillHeight ? `flex flex-col
|
|
476
|
+
activeFillHeight ? cn(`flex flex-col`, clip) : scrollY,
|
|
428
477
|
fullWidth && `p-0`,
|
|
429
478
|
)}
|
|
430
479
|
>
|
|
@@ -479,7 +528,7 @@ export function RoundPageContainer({
|
|
|
479
528
|
className={cn(
|
|
480
529
|
`flex w-full `,
|
|
481
530
|
isMobile ? `` : `px-4`,
|
|
482
|
-
activeFillHeight ? `flex-1 min-h-0` :
|
|
531
|
+
activeFillHeight ? `flex-1 min-h-0` : scrollY,
|
|
483
532
|
)}
|
|
484
533
|
>
|
|
485
534
|
{tabs.map((tab) => (
|
|
@@ -551,7 +600,7 @@ export function RoundPageContainer({
|
|
|
551
600
|
</div>
|
|
552
601
|
))}
|
|
553
602
|
</div>
|
|
554
|
-
<MobileNavigationBar />
|
|
603
|
+
<MobileNavigationBar className={mobileBarClass} />
|
|
555
604
|
</main>
|
|
556
605
|
</>
|
|
557
606
|
);
|
|
@@ -41,8 +41,15 @@ function SlotContent({ item }: { item: MobileNavigationItem }) {
|
|
|
41
41
|
*
|
|
42
42
|
* Rendered as an in-flow flex sibling of the page content (NOT position:fixed),
|
|
43
43
|
* so the content column shrinks to fit and can never be hidden behind the bar.
|
|
44
|
+
*
|
|
45
|
+
* `className` exists for the one thing that in-flow placement cannot cover: a
|
|
46
|
+
* document-scrolled shell (RoundPageContainer's `scroll="document"`), where the
|
|
47
|
+
* column is taller than the viewport and an in-flow bar is only reachable at
|
|
48
|
+
* the very bottom of the page. That caller passes `sticky bottom-0`, which pins
|
|
49
|
+
* the bar without taking it out of flow — so the guarantee above still holds at
|
|
50
|
+
* the end of the page.
|
|
44
51
|
*/
|
|
45
|
-
export function MobileNavigationBar() {
|
|
52
|
+
export function MobileNavigationBar({ className }: { className?: string } = {}) {
|
|
46
53
|
const items = useMobileNavigationItems();
|
|
47
54
|
const isMobile = useIsMobile();
|
|
48
55
|
const pathname = usePathname();
|
|
@@ -63,7 +70,7 @@ export function MobileNavigationBar() {
|
|
|
63
70
|
<nav
|
|
64
71
|
aria-label="Primary"
|
|
65
72
|
data-testid="mobile-navigation-bar"
|
|
66
|
-
className="bg-sidebar flex w-full shrink-0 flex-row items-stretch rounded-lg border"
|
|
73
|
+
className={cn("bg-sidebar flex w-full shrink-0 flex-row items-stretch rounded-lg border", className)}
|
|
67
74
|
>
|
|
68
75
|
{items.map((item) => {
|
|
69
76
|
const active = item.href ? isActiveHref(pathname, item.href) : false;
|