@streamoid/settings 0.14.0 → 0.15.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/index.d.mts +207 -5
- package/dist/index.d.ts +207 -5
- package/dist/index.js +532 -66
- package/dist/index.mjs +533 -66
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7676,6 +7676,7 @@ import {
|
|
|
7676
7676
|
useEffect as useEffect18,
|
|
7677
7677
|
useCallback as useCallback8,
|
|
7678
7678
|
useMemo as useMemo4,
|
|
7679
|
+
useReducer,
|
|
7679
7680
|
useRef as useRef10
|
|
7680
7681
|
} from "react";
|
|
7681
7682
|
import {
|
|
@@ -7826,7 +7827,10 @@ function formatDateRange(date) {
|
|
|
7826
7827
|
function ExportDateModal({
|
|
7827
7828
|
title,
|
|
7828
7829
|
onClose,
|
|
7829
|
-
onExport
|
|
7830
|
+
onExport,
|
|
7831
|
+
busy = false,
|
|
7832
|
+
busyText,
|
|
7833
|
+
notice = null
|
|
7830
7834
|
}) {
|
|
7831
7835
|
useOverlayOpen(true, onClose);
|
|
7832
7836
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -7913,6 +7917,7 @@ function ExportDateModal({
|
|
|
7913
7917
|
[rangeStart, rangeEnd, isFuture]
|
|
7914
7918
|
);
|
|
7915
7919
|
const handleExport = () => {
|
|
7920
|
+
if (busy) return;
|
|
7916
7921
|
if (rangeStart && rangeEnd) onExport(rangeStart, rangeEnd);
|
|
7917
7922
|
};
|
|
7918
7923
|
const getCellVisuals = (cell, idx) => {
|
|
@@ -8134,24 +8139,42 @@ function ExportDateModal({
|
|
|
8134
8139
|
borderTop: "1px solid var(--alias-border-subtle)"
|
|
8135
8140
|
},
|
|
8136
8141
|
children: [
|
|
8137
|
-
/* @__PURE__ */
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8142
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex-1 min-w-0 flex flex-col", children: [
|
|
8143
|
+
/* @__PURE__ */ jsx21(
|
|
8144
|
+
"p",
|
|
8145
|
+
{
|
|
8146
|
+
className: "min-w-0 truncate",
|
|
8147
|
+
style: {
|
|
8148
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
8149
|
+
fontSize: "var(--font-size-font-size-md)",
|
|
8150
|
+
fontWeight: 400,
|
|
8151
|
+
lineHeight: "var(--line-height-line-height-md)",
|
|
8152
|
+
color: "var(--alias-text---icons-primary)"
|
|
8153
|
+
},
|
|
8154
|
+
"aria-live": "polite",
|
|
8155
|
+
children: busy && busyText ? busyText : rangeStart && rangeEnd ? `${formatDateRange(rangeStart)} - ${formatDateRange(rangeEnd)}` : rangeStart ? `${formatDateRange(rangeStart)} - Select end date` : "Select date range"
|
|
8156
|
+
}
|
|
8157
|
+
),
|
|
8158
|
+
notice && !busy && /* @__PURE__ */ jsx21(
|
|
8159
|
+
"p",
|
|
8160
|
+
{
|
|
8161
|
+
role: "alert",
|
|
8162
|
+
style: {
|
|
8163
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
8164
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
8165
|
+
fontWeight: 400,
|
|
8166
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
8167
|
+
color: "var(--alias-text---icons-error)"
|
|
8168
|
+
},
|
|
8169
|
+
children: notice
|
|
8170
|
+
}
|
|
8171
|
+
)
|
|
8172
|
+
] }),
|
|
8151
8173
|
/* @__PURE__ */ jsx21(
|
|
8152
8174
|
"div",
|
|
8153
8175
|
{
|
|
8154
|
-
className: `flex items-center justify-center shrink-0 ${rangeStart && rangeEnd ? "cursor-pointer" : "opacity-50 cursor-not-allowed"}`,
|
|
8176
|
+
className: `flex items-center justify-center shrink-0 ${rangeStart && rangeEnd && !busy ? "cursor-pointer" : "opacity-50 cursor-not-allowed"}`,
|
|
8177
|
+
"aria-disabled": busy || !(rangeStart && rangeEnd),
|
|
8155
8178
|
style: {
|
|
8156
8179
|
backgroundColor: "var(--alias-text---icons-primary)",
|
|
8157
8180
|
borderRadius: "var(--radius-xl)",
|
|
@@ -8169,7 +8192,7 @@ function ExportDateModal({
|
|
|
8169
8192
|
lineHeight: "var(--line-height-line-height-sm)",
|
|
8170
8193
|
color: "var(--alias-text---icons-inverse)"
|
|
8171
8194
|
},
|
|
8172
|
-
children: "Export"
|
|
8195
|
+
children: busy ? "Exporting\u2026" : "Export"
|
|
8173
8196
|
}
|
|
8174
8197
|
)
|
|
8175
8198
|
}
|
|
@@ -10588,6 +10611,196 @@ async function loadCurrentPlan(fetchCurrentPlan) {
|
|
|
10588
10611
|
return { subscription, entitlement, paid: hasPaidFeatures({ entitlement, subscription }) };
|
|
10589
10612
|
}
|
|
10590
10613
|
|
|
10614
|
+
// src/usage-logs.ts
|
|
10615
|
+
var USAGE_LOGS_PAGE_SIZE = 50;
|
|
10616
|
+
var USAGE_LOGS_EXPORT_BATCH_SIZE = 100;
|
|
10617
|
+
var USAGE_LOGS_EXPORT_MAX_ROWS = 2e4;
|
|
10618
|
+
var USAGE_LOGS_EXPORT_EXTRA_REQUESTS = 10;
|
|
10619
|
+
function rowsOf(res) {
|
|
10620
|
+
return Array.isArray(res?.data) ? res.data : [];
|
|
10621
|
+
}
|
|
10622
|
+
function sliceUsageLogsPage(all, page, pageSize = USAGE_LOGS_PAGE_SIZE) {
|
|
10623
|
+
const total = all.length;
|
|
10624
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
10625
|
+
const safePage = Math.min(Math.max(1, Math.floor(page) || 1), totalPages);
|
|
10626
|
+
return {
|
|
10627
|
+
rows: all.slice((safePage - 1) * pageSize, safePage * pageSize),
|
|
10628
|
+
page: safePage,
|
|
10629
|
+
totalPages,
|
|
10630
|
+
total,
|
|
10631
|
+
unpaged: all
|
|
10632
|
+
};
|
|
10633
|
+
}
|
|
10634
|
+
function usageLogsPage(res, page, pageSize = USAGE_LOGS_PAGE_SIZE) {
|
|
10635
|
+
const data = rowsOf(res);
|
|
10636
|
+
const pagination = res?.pagination;
|
|
10637
|
+
if (!pagination || !Number.isFinite(pagination.total)) {
|
|
10638
|
+
return sliceUsageLogsPage(data, page, pageSize);
|
|
10639
|
+
}
|
|
10640
|
+
const total = Math.max(0, pagination.total);
|
|
10641
|
+
const limit = pagination.limit > 0 ? pagination.limit : pageSize;
|
|
10642
|
+
const totalPages = Math.max(
|
|
10643
|
+
1,
|
|
10644
|
+
pagination.totalPages > 0 ? pagination.totalPages : Math.ceil(total / limit)
|
|
10645
|
+
);
|
|
10646
|
+
return {
|
|
10647
|
+
rows: data.slice(0, pageSize),
|
|
10648
|
+
page: pagination.page > 0 ? pagination.page : page,
|
|
10649
|
+
totalPages,
|
|
10650
|
+
total,
|
|
10651
|
+
unpaged: null
|
|
10652
|
+
};
|
|
10653
|
+
}
|
|
10654
|
+
var EMPTY_USAGE_LOGS_PAGE = {
|
|
10655
|
+
rows: [],
|
|
10656
|
+
page: 1,
|
|
10657
|
+
totalPages: 1,
|
|
10658
|
+
total: 0,
|
|
10659
|
+
unpaged: null
|
|
10660
|
+
};
|
|
10661
|
+
var INITIAL_USAGE_LOGS_STATE = {
|
|
10662
|
+
logs: EMPTY_USAGE_LOGS_PAGE,
|
|
10663
|
+
requestedPage: 1,
|
|
10664
|
+
loading: false,
|
|
10665
|
+
error: null
|
|
10666
|
+
};
|
|
10667
|
+
function usageLogsReducer(state, action) {
|
|
10668
|
+
switch (action.type) {
|
|
10669
|
+
case "request":
|
|
10670
|
+
return { ...state, requestedPage: action.page, loading: true, error: null };
|
|
10671
|
+
case "loaded":
|
|
10672
|
+
return {
|
|
10673
|
+
logs: action.logs,
|
|
10674
|
+
requestedPage: action.logs.page,
|
|
10675
|
+
loading: false,
|
|
10676
|
+
error: null
|
|
10677
|
+
};
|
|
10678
|
+
case "failed":
|
|
10679
|
+
return { ...state, loading: false, error: action.error };
|
|
10680
|
+
case "turn": {
|
|
10681
|
+
if (!state.logs.unpaged || state.error) return state;
|
|
10682
|
+
const logs = sliceUsageLogsPage(state.logs.unpaged, action.page);
|
|
10683
|
+
return { ...state, logs, requestedPage: logs.page };
|
|
10684
|
+
}
|
|
10685
|
+
case "reset":
|
|
10686
|
+
return INITIAL_USAGE_LOGS_STATE;
|
|
10687
|
+
}
|
|
10688
|
+
}
|
|
10689
|
+
function usageLogsSpentByOthers(logs, workspaceId) {
|
|
10690
|
+
return (logs.unpaged ?? logs.rows).some(
|
|
10691
|
+
(log) => usedByWorkspaceKey(log, workspaceId) !== null
|
|
10692
|
+
);
|
|
10693
|
+
}
|
|
10694
|
+
function createUsedByNamesLoader(save) {
|
|
10695
|
+
let savedFor = null;
|
|
10696
|
+
let inFlight = null;
|
|
10697
|
+
return {
|
|
10698
|
+
async load(workspaceId, fetchCreditPool) {
|
|
10699
|
+
if (savedFor === workspaceId || inFlight?.workspaceId === workspaceId) {
|
|
10700
|
+
return;
|
|
10701
|
+
}
|
|
10702
|
+
const lookup = { workspaceId };
|
|
10703
|
+
inFlight = lookup;
|
|
10704
|
+
try {
|
|
10705
|
+
const pool = await fetchCreditPool();
|
|
10706
|
+
if (inFlight !== lookup) return;
|
|
10707
|
+
if (save(workspaceId, usedByNamesFrom(pool))) savedFor = workspaceId;
|
|
10708
|
+
} catch {
|
|
10709
|
+
} finally {
|
|
10710
|
+
if (inFlight === lookup) inFlight = null;
|
|
10711
|
+
}
|
|
10712
|
+
},
|
|
10713
|
+
reset() {
|
|
10714
|
+
savedFor = null;
|
|
10715
|
+
inFlight = null;
|
|
10716
|
+
}
|
|
10717
|
+
};
|
|
10718
|
+
}
|
|
10719
|
+
function usageLogsExportFileName(start, end) {
|
|
10720
|
+
return `usage_logs_${toLocalDateInput(start)}_${toLocalDateInput(end)}.csv`;
|
|
10721
|
+
}
|
|
10722
|
+
function usageLogsDateRange(start, end) {
|
|
10723
|
+
const from = new Date(start);
|
|
10724
|
+
from.setHours(0, 0, 0, 0);
|
|
10725
|
+
const to = new Date(end);
|
|
10726
|
+
to.setHours(23, 59, 59, 999);
|
|
10727
|
+
return { startDate: from.toISOString(), endDate: to.toISOString() };
|
|
10728
|
+
}
|
|
10729
|
+
async function fetchUsageLogsInRange(fetchUsageLogs, range, {
|
|
10730
|
+
batchSize = USAGE_LOGS_EXPORT_BATCH_SIZE,
|
|
10731
|
+
maxRows = USAGE_LOGS_EXPORT_MAX_ROWS,
|
|
10732
|
+
maxRequests,
|
|
10733
|
+
onProgress,
|
|
10734
|
+
signal
|
|
10735
|
+
} = {}) {
|
|
10736
|
+
const first = await fetchUsageLogs({ ...range, page: 1, limit: batchSize });
|
|
10737
|
+
if (signal?.aborted) return { status: "cancelled" };
|
|
10738
|
+
if (!first?.pagination || !Number.isFinite(first.pagination.total)) {
|
|
10739
|
+
const from = Date.parse(range.startDate);
|
|
10740
|
+
const to = Date.parse(range.endDate);
|
|
10741
|
+
const rows2 = rowsOf(first).filter((row) => {
|
|
10742
|
+
const at = Date.parse(row.createdAt);
|
|
10743
|
+
return at >= from && at <= to;
|
|
10744
|
+
});
|
|
10745
|
+
if (rows2.length > maxRows) return { status: "too-large", total: rows2.length };
|
|
10746
|
+
onProgress?.(rows2.length, rows2.length);
|
|
10747
|
+
return { status: "ok", rows: rows2, total: rows2.length };
|
|
10748
|
+
}
|
|
10749
|
+
let total = Math.max(0, first.pagination.total);
|
|
10750
|
+
if (total > maxRows) return { status: "too-large", total };
|
|
10751
|
+
const limit = first.pagination.limit > 0 ? first.pagination.limit : batchSize;
|
|
10752
|
+
const requestCap = maxRequests ?? Math.ceil(maxRows / limit) + USAGE_LOGS_EXPORT_EXTRA_REQUESTS;
|
|
10753
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10754
|
+
const rows = [];
|
|
10755
|
+
const add = (batch) => {
|
|
10756
|
+
for (const row of batch) {
|
|
10757
|
+
if (rows.length >= maxRows) return;
|
|
10758
|
+
if (seen.has(row._id)) continue;
|
|
10759
|
+
seen.add(row._id);
|
|
10760
|
+
rows.push(row);
|
|
10761
|
+
}
|
|
10762
|
+
};
|
|
10763
|
+
const hasNextAfter = (pagination, page) => typeof pagination?.hasNextPage === "boolean" ? pagination.hasNextPage : page * limit < total;
|
|
10764
|
+
add(rowsOf(first));
|
|
10765
|
+
onProgress?.(rows.length, total);
|
|
10766
|
+
let hasNext = hasNextAfter(first.pagination, 1);
|
|
10767
|
+
let requests = 1;
|
|
10768
|
+
for (let page = 2; hasNext && rows.length < total; page++) {
|
|
10769
|
+
if (requests >= requestCap) {
|
|
10770
|
+
return { status: "incomplete", fetched: rows.length, total };
|
|
10771
|
+
}
|
|
10772
|
+
if (signal?.aborted) return { status: "cancelled" };
|
|
10773
|
+
const res = await fetchUsageLogs({ ...range, page, limit });
|
|
10774
|
+
requests++;
|
|
10775
|
+
if (signal?.aborted) return { status: "cancelled" };
|
|
10776
|
+
if (res?.pagination && Number.isFinite(res.pagination.total)) {
|
|
10777
|
+
total = Math.max(0, res.pagination.total);
|
|
10778
|
+
if (total > maxRows) return { status: "too-large", total };
|
|
10779
|
+
}
|
|
10780
|
+
const batch = rowsOf(res);
|
|
10781
|
+
if (batch.length === 0) break;
|
|
10782
|
+
add(batch);
|
|
10783
|
+
onProgress?.(rows.length, total);
|
|
10784
|
+
hasNext = hasNextAfter(res?.pagination, page);
|
|
10785
|
+
}
|
|
10786
|
+
return { status: "ok", rows, total };
|
|
10787
|
+
}
|
|
10788
|
+
function usageLogsCsvRows(logs, userMap, formatDate5) {
|
|
10789
|
+
return [
|
|
10790
|
+
["Date", "Type", "Credits", "Service", "User ID"],
|
|
10791
|
+
...logs.map((log) => [
|
|
10792
|
+
formatDate5(log.createdAt),
|
|
10793
|
+
log.type,
|
|
10794
|
+
log.amount.toLocaleString("en-US"),
|
|
10795
|
+
log.service?.serviceGroupName ?? log.meta?.info ?? "",
|
|
10796
|
+
log.triggeredByUserId != null ? userMap[log.triggeredByUserId] ?? log.triggeredByUserId : ""
|
|
10797
|
+
])
|
|
10798
|
+
];
|
|
10799
|
+
}
|
|
10800
|
+
function toCsv(rows) {
|
|
10801
|
+
return rows.map((r) => r.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(",")).join("\n");
|
|
10802
|
+
}
|
|
10803
|
+
|
|
10591
10804
|
// src/billing-content.tsx
|
|
10592
10805
|
import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
10593
10806
|
var DEFAULT_TOPUP_SERVICE = "catalogix";
|
|
@@ -11473,6 +11686,8 @@ function UsageLogsTable({
|
|
|
11473
11686
|
logs,
|
|
11474
11687
|
userMap,
|
|
11475
11688
|
loading,
|
|
11689
|
+
error = null,
|
|
11690
|
+
onRetry,
|
|
11476
11691
|
currentWorkspaceId = null,
|
|
11477
11692
|
usedByNames = {}
|
|
11478
11693
|
}) {
|
|
@@ -11503,6 +11718,50 @@ function UsageLogsTable({
|
|
|
11503
11718
|
}
|
|
11504
11719
|
);
|
|
11505
11720
|
}
|
|
11721
|
+
if (error) {
|
|
11722
|
+
return /* @__PURE__ */ jsxs26(
|
|
11723
|
+
"div",
|
|
11724
|
+
{
|
|
11725
|
+
role: "alert",
|
|
11726
|
+
className: "flex flex-col items-center justify-center w-full",
|
|
11727
|
+
style: {
|
|
11728
|
+
backgroundColor: "var(--alias-surface-base)",
|
|
11729
|
+
borderRadius: "var(--radius-3xl)",
|
|
11730
|
+
border: "1px solid var(--alias-border-subtle)",
|
|
11731
|
+
padding: "var(--spacing-6xl)",
|
|
11732
|
+
gap: "var(--spacing-md)"
|
|
11733
|
+
},
|
|
11734
|
+
children: [
|
|
11735
|
+
/* @__PURE__ */ jsxs26(
|
|
11736
|
+
"span",
|
|
11737
|
+
{
|
|
11738
|
+
style: {
|
|
11739
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
11740
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
11741
|
+
fontWeight: 400,
|
|
11742
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
11743
|
+
color: "var(--alias-text---icons-muted)",
|
|
11744
|
+
textAlign: "center"
|
|
11745
|
+
},
|
|
11746
|
+
children: [
|
|
11747
|
+
"Couldn't load usage logs. ",
|
|
11748
|
+
error
|
|
11749
|
+
]
|
|
11750
|
+
}
|
|
11751
|
+
),
|
|
11752
|
+
onRetry && /* @__PURE__ */ jsx29(
|
|
11753
|
+
ScButton14,
|
|
11754
|
+
{
|
|
11755
|
+
text: "Try again",
|
|
11756
|
+
variant: "tertiary",
|
|
11757
|
+
size: "sm",
|
|
11758
|
+
onClick: onRetry
|
|
11759
|
+
}
|
|
11760
|
+
)
|
|
11761
|
+
]
|
|
11762
|
+
}
|
|
11763
|
+
);
|
|
11764
|
+
}
|
|
11506
11765
|
return /* @__PURE__ */ jsxs26(
|
|
11507
11766
|
"div",
|
|
11508
11767
|
{
|
|
@@ -11678,6 +11937,71 @@ function UsageLogsTable({
|
|
|
11678
11937
|
}
|
|
11679
11938
|
);
|
|
11680
11939
|
}
|
|
11940
|
+
function UsageLogsPager({
|
|
11941
|
+
page,
|
|
11942
|
+
totalPages,
|
|
11943
|
+
total,
|
|
11944
|
+
loading = false,
|
|
11945
|
+
onPageChange
|
|
11946
|
+
}) {
|
|
11947
|
+
if (total <= 0) return null;
|
|
11948
|
+
const canPrevious = !loading && page > 1;
|
|
11949
|
+
const canNext = !loading && page < totalPages;
|
|
11950
|
+
return /* @__PURE__ */ jsxs26(
|
|
11951
|
+
"div",
|
|
11952
|
+
{
|
|
11953
|
+
className: "flex items-center justify-between shrink-0 w-full",
|
|
11954
|
+
style: { gap: "var(--spacing-md)" },
|
|
11955
|
+
children: [
|
|
11956
|
+
/* @__PURE__ */ jsxs26(
|
|
11957
|
+
"span",
|
|
11958
|
+
{
|
|
11959
|
+
style: {
|
|
11960
|
+
color: "var(--alias-text---icons-muted)",
|
|
11961
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
11962
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
11963
|
+
lineHeight: "var(--line-height-line-height-sm)"
|
|
11964
|
+
},
|
|
11965
|
+
children: [
|
|
11966
|
+
"Page ",
|
|
11967
|
+
page.toLocaleString("en-US"),
|
|
11968
|
+
" of",
|
|
11969
|
+
" ",
|
|
11970
|
+
totalPages.toLocaleString("en-US"),
|
|
11971
|
+
" \xB7",
|
|
11972
|
+
" ",
|
|
11973
|
+
total.toLocaleString("en-US"),
|
|
11974
|
+
" ",
|
|
11975
|
+
total === 1 ? "log" : "logs"
|
|
11976
|
+
]
|
|
11977
|
+
}
|
|
11978
|
+
),
|
|
11979
|
+
totalPages > 1 && /* @__PURE__ */ jsxs26("div", { className: "flex items-center", style: { gap: "var(--spacing-md)" }, children: [
|
|
11980
|
+
/* @__PURE__ */ jsx29(
|
|
11981
|
+
ScButton14,
|
|
11982
|
+
{
|
|
11983
|
+
text: "Previous",
|
|
11984
|
+
variant: "tertiary",
|
|
11985
|
+
size: "sm",
|
|
11986
|
+
state: canPrevious ? "default" : "disabled",
|
|
11987
|
+
onClick: canPrevious ? () => onPageChange(page - 1) : void 0
|
|
11988
|
+
}
|
|
11989
|
+
),
|
|
11990
|
+
/* @__PURE__ */ jsx29(
|
|
11991
|
+
ScButton14,
|
|
11992
|
+
{
|
|
11993
|
+
text: "Next",
|
|
11994
|
+
variant: "tertiary",
|
|
11995
|
+
size: "sm",
|
|
11996
|
+
state: canNext ? "default" : "disabled",
|
|
11997
|
+
onClick: canNext ? () => onPageChange(page + 1) : void 0
|
|
11998
|
+
}
|
|
11999
|
+
)
|
|
12000
|
+
] })
|
|
12001
|
+
]
|
|
12002
|
+
}
|
|
12003
|
+
);
|
|
12004
|
+
}
|
|
11681
12005
|
function BackToBilling({ onClick }) {
|
|
11682
12006
|
return /* @__PURE__ */ jsxs26(
|
|
11683
12007
|
"div",
|
|
@@ -11757,6 +12081,9 @@ function BillingContent({
|
|
|
11757
12081
|
else setShowSharedCreditsInternal(next);
|
|
11758
12082
|
};
|
|
11759
12083
|
const [showExportModal, setShowExportModal] = useState21(false);
|
|
12084
|
+
const [exportProgress, setExportProgress] = useState21(null);
|
|
12085
|
+
const [exportNotice, setExportNotice] = useState21(null);
|
|
12086
|
+
const exportAbortRef = useRef10(null);
|
|
11760
12087
|
const [showBuyCreditsModal, setShowBuyCreditsModal] = useState21(false);
|
|
11761
12088
|
const [subscription, setSubscription] = useState21(null);
|
|
11762
12089
|
const [entitlement, setEntitlement] = useState21(
|
|
@@ -11768,7 +12095,24 @@ function BillingContent({
|
|
|
11768
12095
|
const [products, setProducts] = useState21([]);
|
|
11769
12096
|
const [invoices, setInvoices] = useState21([]);
|
|
11770
12097
|
const [totalCredits, setTotalCredits] = useState21(null);
|
|
11771
|
-
const [
|
|
12098
|
+
const [usageLogsState, dispatchUsageLogs] = useReducer(
|
|
12099
|
+
usageLogsReducer,
|
|
12100
|
+
INITIAL_USAGE_LOGS_STATE
|
|
12101
|
+
);
|
|
12102
|
+
const {
|
|
12103
|
+
logs: usageLogs,
|
|
12104
|
+
loading: loadingUsageLogs,
|
|
12105
|
+
error: usageLogsError
|
|
12106
|
+
} = usageLogsState;
|
|
12107
|
+
const usageLogsRequestRef = useRef10(0);
|
|
12108
|
+
const workspaceIdRef = useRef10(workspaceId);
|
|
12109
|
+
workspaceIdRef.current = workspaceId;
|
|
12110
|
+
const loadUsedByNamesRef = useRef10(null);
|
|
12111
|
+
loadUsedByNamesRef.current ??= createUsedByNamesLoader((forWorkspace, names) => {
|
|
12112
|
+
if (forWorkspace !== workspaceIdRef.current) return false;
|
|
12113
|
+
setUsedByNames(names);
|
|
12114
|
+
return true;
|
|
12115
|
+
});
|
|
11772
12116
|
const [userMap, setUserMap] = useState21({});
|
|
11773
12117
|
const [customPlanCredits, setCustomPlanCredits] = useState21(
|
|
11774
12118
|
null
|
|
@@ -11852,8 +12196,68 @@ function BillingContent({
|
|
|
11852
12196
|
} catch {
|
|
11853
12197
|
}
|
|
11854
12198
|
}, [api]);
|
|
12199
|
+
const ensureUsedByNames = useCallback8(
|
|
12200
|
+
(logs) => {
|
|
12201
|
+
const fetchCreditPool = api.fetchCreditPool;
|
|
12202
|
+
if (!fetchCreditPool || !workspaceId || !usageLogsSpentByOthers(logs, workspaceId)) {
|
|
12203
|
+
return;
|
|
12204
|
+
}
|
|
12205
|
+
void loadUsedByNamesRef.current?.load(
|
|
12206
|
+
workspaceId,
|
|
12207
|
+
() => fetchCreditPool.call(api)
|
|
12208
|
+
);
|
|
12209
|
+
},
|
|
12210
|
+
[api, workspaceId]
|
|
12211
|
+
);
|
|
12212
|
+
const loadUsageLogsPage = useCallback8(
|
|
12213
|
+
async (page) => {
|
|
12214
|
+
const request = ++usageLogsRequestRef.current;
|
|
12215
|
+
if (!api.fetchUsageLogs) {
|
|
12216
|
+
dispatchUsageLogs({ type: "loaded", logs: EMPTY_USAGE_LOGS_PAGE });
|
|
12217
|
+
return;
|
|
12218
|
+
}
|
|
12219
|
+
dispatchUsageLogs({ type: "request", page });
|
|
12220
|
+
try {
|
|
12221
|
+
const res = await api.fetchUsageLogs({
|
|
12222
|
+
page,
|
|
12223
|
+
limit: USAGE_LOGS_PAGE_SIZE
|
|
12224
|
+
});
|
|
12225
|
+
if (request !== usageLogsRequestRef.current) return;
|
|
12226
|
+
const next = usageLogsPage(res, page);
|
|
12227
|
+
dispatchUsageLogs({ type: "loaded", logs: next });
|
|
12228
|
+
ensureUsedByNames(next);
|
|
12229
|
+
} catch (err) {
|
|
12230
|
+
if (request !== usageLogsRequestRef.current) return;
|
|
12231
|
+
dispatchUsageLogs({
|
|
12232
|
+
type: "failed",
|
|
12233
|
+
error: errorMessage(err, { method: "GET" })
|
|
12234
|
+
});
|
|
12235
|
+
}
|
|
12236
|
+
},
|
|
12237
|
+
[api, ensureUsedByNames]
|
|
12238
|
+
);
|
|
12239
|
+
const goToUsageLogsPage = useCallback8(
|
|
12240
|
+
(page) => {
|
|
12241
|
+
if (usageLogs.unpaged && !usageLogsError) {
|
|
12242
|
+
dispatchUsageLogs({ type: "turn", page });
|
|
12243
|
+
ensureUsedByNames(usageLogs);
|
|
12244
|
+
return;
|
|
12245
|
+
}
|
|
12246
|
+
void loadUsageLogsPage(page);
|
|
12247
|
+
},
|
|
12248
|
+
[usageLogs, usageLogsError, loadUsageLogsPage, ensureUsedByNames]
|
|
12249
|
+
);
|
|
12250
|
+
useEffect18(() => {
|
|
12251
|
+
if (!loadingUsageLogs && !usageLogsError && usageLogs.unpaged === null && usageLogs.rows.length === 0 && usageLogs.total > 0 && usageLogs.page > usageLogs.totalPages) {
|
|
12252
|
+
void loadUsageLogsPage(usageLogs.totalPages);
|
|
12253
|
+
}
|
|
12254
|
+
}, [usageLogs, loadingUsageLogs, usageLogsError, loadUsageLogsPage]);
|
|
11855
12255
|
const fetchTableData = useCallback8(
|
|
11856
12256
|
async (tab) => {
|
|
12257
|
+
if (tab === "usage") {
|
|
12258
|
+
await loadUsageLogsPage(1);
|
|
12259
|
+
return;
|
|
12260
|
+
}
|
|
11857
12261
|
setLoadingTable(true);
|
|
11858
12262
|
try {
|
|
11859
12263
|
if (tab === "invoice") {
|
|
@@ -11865,25 +12269,14 @@ function BillingContent({
|
|
|
11865
12269
|
(item) => item.expiresAt && item.expiresAt.split("T")[0] > today
|
|
11866
12270
|
).reduce((total, item) => total + item.amount, 0)
|
|
11867
12271
|
);
|
|
11868
|
-
} else if (api.fetchUsageLogs) {
|
|
11869
|
-
const res = await api.fetchUsageLogs();
|
|
11870
|
-
const logs = res.data?.length ? res.data : [];
|
|
11871
|
-
setUsageLogs(logs);
|
|
11872
|
-
if (api.fetchCreditPool && logs.some((log) => usedByWorkspaceKey(log, workspaceId))) {
|
|
11873
|
-
const pool = await api.fetchCreditPool().catch(() => null);
|
|
11874
|
-
setUsedByNames(usedByNamesFrom(pool));
|
|
11875
|
-
}
|
|
11876
|
-
} else {
|
|
11877
|
-
setUsageLogs([]);
|
|
11878
12272
|
}
|
|
11879
12273
|
} catch {
|
|
11880
|
-
|
|
11881
|
-
else setUsageLogs([]);
|
|
12274
|
+
setInvoices([]);
|
|
11882
12275
|
} finally {
|
|
11883
12276
|
setLoadingTable(false);
|
|
11884
12277
|
}
|
|
11885
12278
|
},
|
|
11886
|
-
[api,
|
|
12279
|
+
[api, loadUsageLogsPage]
|
|
11887
12280
|
);
|
|
11888
12281
|
const handlePlanAction = useCallback8(
|
|
11889
12282
|
async ({ productId, priceId, planName: planName2 }) => {
|
|
@@ -11990,6 +12383,10 @@ function BillingContent({
|
|
|
11990
12383
|
setProducts([]);
|
|
11991
12384
|
setTotalCredits(null);
|
|
11992
12385
|
setCustomPlanCredits(null);
|
|
12386
|
+
usageLogsRequestRef.current++;
|
|
12387
|
+
dispatchUsageLogs({ type: "reset" });
|
|
12388
|
+
loadUsedByNamesRef.current?.reset();
|
|
12389
|
+
setUsedByNames({});
|
|
11993
12390
|
}, [workspaceId]);
|
|
11994
12391
|
useEffect18(() => {
|
|
11995
12392
|
setInitialFetchTriggered(true);
|
|
@@ -12092,35 +12489,74 @@ function BillingContent({
|
|
|
12092
12489
|
toast.success(`Opened ${successCount} invoice PDF(s).`);
|
|
12093
12490
|
}
|
|
12094
12491
|
} else {
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12492
|
+
await exportUsageLogs(startDate, endDate);
|
|
12493
|
+
}
|
|
12494
|
+
};
|
|
12495
|
+
const closeExportModal = () => {
|
|
12496
|
+
exportAbortRef.current?.abort();
|
|
12497
|
+
exportAbortRef.current = null;
|
|
12498
|
+
setExportProgress(null);
|
|
12499
|
+
setExportNotice(null);
|
|
12500
|
+
setShowExportModal(false);
|
|
12501
|
+
};
|
|
12502
|
+
useEffect18(() => () => exportAbortRef.current?.abort(), []);
|
|
12503
|
+
const exportUsageLogs = async (startDate, endDate) => {
|
|
12504
|
+
if (!api.fetchUsageLogs) {
|
|
12505
|
+
toast.info?.("Usage logs are not available in this workspace.");
|
|
12506
|
+
closeExportModal();
|
|
12507
|
+
return;
|
|
12508
|
+
}
|
|
12509
|
+
exportAbortRef.current?.abort();
|
|
12510
|
+
const controller = new AbortController();
|
|
12511
|
+
exportAbortRef.current = controller;
|
|
12512
|
+
setExportNotice(null);
|
|
12513
|
+
setExportProgress({ fetched: 0, total: 0 });
|
|
12514
|
+
const range = usageLogsDateRange(startDate, endDate);
|
|
12515
|
+
try {
|
|
12516
|
+
const result = await fetchUsageLogsInRange(api.fetchUsageLogs, range, {
|
|
12517
|
+
signal: controller.signal,
|
|
12518
|
+
onProgress: (fetched, total) => {
|
|
12519
|
+
if (!controller.signal.aborted) setExportProgress({ fetched, total });
|
|
12520
|
+
}
|
|
12098
12521
|
});
|
|
12099
|
-
if (
|
|
12522
|
+
if (result.status === "cancelled" || controller.signal.aborted) return;
|
|
12523
|
+
if (result.status === "too-large") {
|
|
12524
|
+
setExportNotice(
|
|
12525
|
+
`${result.total.toLocaleString("en-US")} logs in this range; an export holds up to ${USAGE_LOGS_EXPORT_MAX_ROWS.toLocaleString("en-US")}. Choose a shorter range.`
|
|
12526
|
+
);
|
|
12527
|
+
return;
|
|
12528
|
+
}
|
|
12529
|
+
if (result.status === "incomplete") {
|
|
12530
|
+
setExportNotice(
|
|
12531
|
+
"Usage logs kept changing while exporting, so some were missed. Try again."
|
|
12532
|
+
);
|
|
12533
|
+
return;
|
|
12534
|
+
}
|
|
12535
|
+
if (result.rows.length === 0) {
|
|
12100
12536
|
toast.info?.("No usage logs found in the selected date range.");
|
|
12101
|
-
|
|
12537
|
+
closeExportModal();
|
|
12102
12538
|
return;
|
|
12103
12539
|
}
|
|
12104
|
-
const headers = ["Date", "Type", "Credits", "Service", "User ID"];
|
|
12105
|
-
const rows = filtered.map((log) => [
|
|
12106
|
-
formatDate3(log.createdAt),
|
|
12107
|
-
log.type,
|
|
12108
|
-
log.amount.toLocaleString("en-US"),
|
|
12109
|
-
log.service?.serviceGroupName ?? log.meta?.info ?? "",
|
|
12110
|
-
log.triggeredByUserId != null ? userMap[log.triggeredByUserId] ?? log.triggeredByUserId : ""
|
|
12111
|
-
]);
|
|
12112
12540
|
downloadCsv(
|
|
12113
|
-
|
|
12114
|
-
|
|
12541
|
+
usageLogsCsvRows(result.rows, userMap, formatDate3),
|
|
12542
|
+
usageLogsExportFileName(startDate, endDate)
|
|
12115
12543
|
);
|
|
12116
|
-
toast.success(`Exported ${
|
|
12117
|
-
|
|
12544
|
+
toast.success(`Exported ${result.rows.length.toLocaleString("en-US")} log(s).`);
|
|
12545
|
+
closeExportModal();
|
|
12546
|
+
} catch (err) {
|
|
12547
|
+
if (controller.signal.aborted) return;
|
|
12548
|
+
setExportNotice(
|
|
12549
|
+
`Couldn't export usage logs. ${errorMessage(err, { method: "GET" })}`
|
|
12550
|
+
);
|
|
12551
|
+
} finally {
|
|
12552
|
+
if (exportAbortRef.current === controller) {
|
|
12553
|
+
exportAbortRef.current = null;
|
|
12554
|
+
setExportProgress(null);
|
|
12555
|
+
}
|
|
12118
12556
|
}
|
|
12119
12557
|
};
|
|
12120
12558
|
function downloadCsv(rows, filename) {
|
|
12121
|
-
const csv = rows
|
|
12122
|
-
(r) => r.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(",")
|
|
12123
|
-
).join("\n");
|
|
12559
|
+
const csv = toCsv(rows);
|
|
12124
12560
|
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
12125
12561
|
const url = URL.createObjectURL(blob);
|
|
12126
12562
|
const a = document.createElement("a");
|
|
@@ -12131,9 +12567,6 @@ function BillingContent({
|
|
|
12131
12567
|
document.body.removeChild(a);
|
|
12132
12568
|
URL.revokeObjectURL(url);
|
|
12133
12569
|
}
|
|
12134
|
-
function fmtFilename(d) {
|
|
12135
|
-
return d.toISOString().slice(0, 10);
|
|
12136
|
-
}
|
|
12137
12570
|
async function handleBuyCredits(credits2, price) {
|
|
12138
12571
|
if (!workspaceId) {
|
|
12139
12572
|
toast.error("Missing workspace details for credit top-up");
|
|
@@ -12586,22 +13019,39 @@ function BillingContent({
|
|
|
12586
13019
|
loading: loadingTable,
|
|
12587
13020
|
workspaceCountry: workspace.current?.country
|
|
12588
13021
|
}
|
|
12589
|
-
) : /* @__PURE__ */
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
13022
|
+
) : /* @__PURE__ */ jsxs26(Fragment11, { children: [
|
|
13023
|
+
/* @__PURE__ */ jsx29(
|
|
13024
|
+
UsageLogsTable,
|
|
13025
|
+
{
|
|
13026
|
+
logs: usageLogs.rows,
|
|
13027
|
+
userMap,
|
|
13028
|
+
loading: loadingUsageLogs,
|
|
13029
|
+
error: usageLogsError,
|
|
13030
|
+
onRetry: () => void loadUsageLogsPage(usageLogsState.requestedPage),
|
|
13031
|
+
currentWorkspaceId: workspaceId,
|
|
13032
|
+
usedByNames
|
|
13033
|
+
}
|
|
13034
|
+
),
|
|
13035
|
+
!usageLogsError && /* @__PURE__ */ jsx29(
|
|
13036
|
+
UsageLogsPager,
|
|
13037
|
+
{
|
|
13038
|
+
page: usageLogs.page,
|
|
13039
|
+
totalPages: usageLogs.totalPages,
|
|
13040
|
+
total: usageLogs.total,
|
|
13041
|
+
loading: loadingUsageLogs,
|
|
13042
|
+
onPageChange: goToUsageLogsPage
|
|
13043
|
+
}
|
|
13044
|
+
)
|
|
13045
|
+
] }),
|
|
12599
13046
|
showExportModal && /* @__PURE__ */ jsx29(
|
|
12600
13047
|
ExportDateModal,
|
|
12601
13048
|
{
|
|
12602
13049
|
title: activeTab === "invoice" ? "Export invoices" : "Export log",
|
|
12603
|
-
onClose:
|
|
12604
|
-
onExport: handleExport
|
|
13050
|
+
onClose: closeExportModal,
|
|
13051
|
+
onExport: handleExport,
|
|
13052
|
+
busy: exportProgress !== null,
|
|
13053
|
+
busyText: exportProgress && exportProgress.total > 0 ? `Fetched ${exportProgress.fetched.toLocaleString("en-US")} of ${exportProgress.total.toLocaleString("en-US")} logs\u2026` : "Fetching logs\u2026",
|
|
13054
|
+
notice: exportNotice
|
|
12605
13055
|
}
|
|
12606
13056
|
),
|
|
12607
13057
|
showBuyCreditsModal && /* @__PURE__ */ jsx29(
|
|
@@ -16972,7 +17422,9 @@ export {
|
|
|
16972
17422
|
CreditPoolMemberModal,
|
|
16973
17423
|
DEFAULT_PERMISSIONS,
|
|
16974
17424
|
DEFAULT_USER_LANGUAGE,
|
|
17425
|
+
EMPTY_USAGE_LOGS_PAGE,
|
|
16975
17426
|
ExportDateModal,
|
|
17427
|
+
INITIAL_USAGE_LOGS_STATE,
|
|
16976
17428
|
InviteUpdateModal,
|
|
16977
17429
|
MemberCreditLimitModal,
|
|
16978
17430
|
MemberListFrame,
|
|
@@ -17005,7 +17457,12 @@ export {
|
|
|
17005
17457
|
TeamsContent,
|
|
17006
17458
|
TokensContent,
|
|
17007
17459
|
URL_TO_TAB,
|
|
17460
|
+
USAGE_LOGS_EXPORT_BATCH_SIZE,
|
|
17461
|
+
USAGE_LOGS_EXPORT_EXTRA_REQUESTS,
|
|
17462
|
+
USAGE_LOGS_EXPORT_MAX_ROWS,
|
|
17463
|
+
USAGE_LOGS_PAGE_SIZE,
|
|
17008
17464
|
USER_LANGUAGES,
|
|
17465
|
+
UsageLogsPager,
|
|
17009
17466
|
WORKSPACE_LIST_PAGE_SIZE,
|
|
17010
17467
|
WorkspaceGeneralTab,
|
|
17011
17468
|
WorkspaceListTab,
|
|
@@ -17017,6 +17474,7 @@ export {
|
|
|
17017
17474
|
cleanCreditLimitInput,
|
|
17018
17475
|
cleanPoolValueInput,
|
|
17019
17476
|
countLabel,
|
|
17477
|
+
createUsedByNamesLoader,
|
|
17020
17478
|
creditCycleUnit,
|
|
17021
17479
|
creditsMeter,
|
|
17022
17480
|
deriveWorkspaceInvitePermission,
|
|
@@ -17026,6 +17484,7 @@ export {
|
|
|
17026
17484
|
entitlementFromBody,
|
|
17027
17485
|
entitlementFromFailure,
|
|
17028
17486
|
expiryChoiceFor,
|
|
17487
|
+
fetchUsageLogsInRange,
|
|
17029
17488
|
findBillingError,
|
|
17030
17489
|
findPoolError,
|
|
17031
17490
|
formatCreditLimit,
|
|
@@ -17060,8 +17519,16 @@ export {
|
|
|
17060
17519
|
settingsReturnTarget,
|
|
17061
17520
|
sharedPoolBannerText,
|
|
17062
17521
|
sharedPoolEndsText,
|
|
17522
|
+
sliceUsageLogsPage,
|
|
17063
17523
|
sponsoredCreditsText,
|
|
17524
|
+
toCsv,
|
|
17064
17525
|
toLocalDateInput,
|
|
17526
|
+
usageLogsCsvRows,
|
|
17527
|
+
usageLogsDateRange,
|
|
17528
|
+
usageLogsExportFileName,
|
|
17529
|
+
usageLogsPage,
|
|
17530
|
+
usageLogsReducer,
|
|
17531
|
+
usageLogsSpentByOthers,
|
|
17065
17532
|
useBillingEntitlement,
|
|
17066
17533
|
useSettingsAdapters,
|
|
17067
17534
|
useWorkspaceInvitePermission,
|