@streamoid/settings 0.13.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 +312 -7
- package/dist/index.d.ts +312 -7
- package/dist/index.js +700 -75
- package/dist/index.mjs +699 -73
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,7 +34,9 @@ __export(index_exports, {
|
|
|
34
34
|
CreditPoolMemberModal: () => CreditPoolMemberModal,
|
|
35
35
|
DEFAULT_PERMISSIONS: () => DEFAULT_PERMISSIONS,
|
|
36
36
|
DEFAULT_USER_LANGUAGE: () => DEFAULT_USER_LANGUAGE,
|
|
37
|
+
EMPTY_USAGE_LOGS_PAGE: () => EMPTY_USAGE_LOGS_PAGE,
|
|
37
38
|
ExportDateModal: () => ExportDateModal,
|
|
39
|
+
INITIAL_USAGE_LOGS_STATE: () => INITIAL_USAGE_LOGS_STATE,
|
|
38
40
|
InviteUpdateModal: () => InviteUpdateModal,
|
|
39
41
|
MemberCreditLimitModal: () => MemberCreditLimitModal,
|
|
40
42
|
MemberListFrame: () => MemberListFrame,
|
|
@@ -67,7 +69,12 @@ __export(index_exports, {
|
|
|
67
69
|
TeamsContent: () => TeamsContent,
|
|
68
70
|
TokensContent: () => TokensContent,
|
|
69
71
|
URL_TO_TAB: () => URL_TO_TAB,
|
|
72
|
+
USAGE_LOGS_EXPORT_BATCH_SIZE: () => USAGE_LOGS_EXPORT_BATCH_SIZE,
|
|
73
|
+
USAGE_LOGS_EXPORT_EXTRA_REQUESTS: () => USAGE_LOGS_EXPORT_EXTRA_REQUESTS,
|
|
74
|
+
USAGE_LOGS_EXPORT_MAX_ROWS: () => USAGE_LOGS_EXPORT_MAX_ROWS,
|
|
75
|
+
USAGE_LOGS_PAGE_SIZE: () => USAGE_LOGS_PAGE_SIZE,
|
|
70
76
|
USER_LANGUAGES: () => USER_LANGUAGES,
|
|
77
|
+
UsageLogsPager: () => UsageLogsPager,
|
|
71
78
|
WORKSPACE_LIST_PAGE_SIZE: () => WORKSPACE_LIST_PAGE_SIZE,
|
|
72
79
|
WorkspaceGeneralTab: () => WorkspaceGeneralTab,
|
|
73
80
|
WorkspaceListTab: () => WorkspaceListTab,
|
|
@@ -79,13 +86,17 @@ __export(index_exports, {
|
|
|
79
86
|
cleanCreditLimitInput: () => cleanCreditLimitInput,
|
|
80
87
|
cleanPoolValueInput: () => cleanPoolValueInput,
|
|
81
88
|
countLabel: () => countLabel,
|
|
89
|
+
createUsedByNamesLoader: () => createUsedByNamesLoader,
|
|
82
90
|
creditCycleUnit: () => creditCycleUnit,
|
|
83
91
|
creditsMeter: () => creditsMeter,
|
|
84
92
|
deriveWorkspaceInvitePermission: () => deriveWorkspaceInvitePermission,
|
|
85
93
|
describeAllowance: () => describeAllowance,
|
|
86
94
|
describeDefaultLimit: () => describeDefaultLimit,
|
|
87
95
|
describeMemberLimit: () => describeMemberLimit,
|
|
96
|
+
entitlementFromBody: () => entitlementFromBody,
|
|
97
|
+
entitlementFromFailure: () => entitlementFromFailure,
|
|
88
98
|
expiryChoiceFor: () => expiryChoiceFor,
|
|
99
|
+
fetchUsageLogsInRange: () => fetchUsageLogsInRange,
|
|
89
100
|
findBillingError: () => findBillingError,
|
|
90
101
|
findPoolError: () => findPoolError,
|
|
91
102
|
formatCreditLimit: () => formatCreditLimit,
|
|
@@ -93,16 +104,21 @@ __export(index_exports, {
|
|
|
93
104
|
formatPoolEndsAt: () => formatPoolEndsAt,
|
|
94
105
|
formatPoolSplit: () => formatPoolSplit,
|
|
95
106
|
groupWorkspaceList: () => groupWorkspaceList,
|
|
107
|
+
hasPaidFeatures: () => hasPaidFeatures,
|
|
96
108
|
isLimitBelowUsage: () => isLimitBelowUsage,
|
|
109
|
+
isPaidPlan: () => isPaidPlan,
|
|
110
|
+
isSponsoredEntitlement: () => isSponsoredEntitlement,
|
|
97
111
|
joinMembersWithLimits: () => joinMembersWithLimits,
|
|
98
112
|
lastUsedLabel: () => lastUsedLabel,
|
|
99
113
|
limitBelowUsageUsed: () => limitBelowUsageUsed,
|
|
100
114
|
limitSourceLabel: () => limitSourceLabel,
|
|
115
|
+
loadCurrentPlan: () => loadCurrentPlan,
|
|
101
116
|
loadWorkspaceOrganization: () => loadWorkspaceOrganization,
|
|
102
117
|
memberRoleUpdate: () => memberRoleUpdate,
|
|
103
118
|
minPoolEndDate: () => minPoolEndDate,
|
|
104
119
|
minimumCreditLimit: () => minimumCreditLimit,
|
|
105
120
|
ownCreditsFirstText: () => ownCreditsFirstText,
|
|
121
|
+
parseBillingEntitlement: () => parseBillingEntitlement,
|
|
106
122
|
pausedOwnCreditsText: () => pausedOwnCreditsText,
|
|
107
123
|
poolEndsAtFrom: () => poolEndsAtFrom,
|
|
108
124
|
poolErrorMessage: () => poolErrorMessage,
|
|
@@ -115,7 +131,17 @@ __export(index_exports, {
|
|
|
115
131
|
settingsReturnTarget: () => settingsReturnTarget,
|
|
116
132
|
sharedPoolBannerText: () => sharedPoolBannerText,
|
|
117
133
|
sharedPoolEndsText: () => sharedPoolEndsText,
|
|
134
|
+
sliceUsageLogsPage: () => sliceUsageLogsPage,
|
|
135
|
+
sponsoredCreditsText: () => sponsoredCreditsText,
|
|
136
|
+
toCsv: () => toCsv,
|
|
118
137
|
toLocalDateInput: () => toLocalDateInput,
|
|
138
|
+
usageLogsCsvRows: () => usageLogsCsvRows,
|
|
139
|
+
usageLogsDateRange: () => usageLogsDateRange,
|
|
140
|
+
usageLogsExportFileName: () => usageLogsExportFileName,
|
|
141
|
+
usageLogsPage: () => usageLogsPage,
|
|
142
|
+
usageLogsReducer: () => usageLogsReducer,
|
|
143
|
+
usageLogsSpentByOthers: () => usageLogsSpentByOthers,
|
|
144
|
+
useBillingEntitlement: () => useBillingEntitlement,
|
|
119
145
|
useSettingsAdapters: () => useSettingsAdapters,
|
|
120
146
|
useWorkspaceInvitePermission: () => useWorkspaceInvitePermission,
|
|
121
147
|
useWorkspaceOrganization: () => useWorkspaceOrganization,
|
|
@@ -7888,7 +7914,10 @@ function formatDateRange(date) {
|
|
|
7888
7914
|
function ExportDateModal({
|
|
7889
7915
|
title,
|
|
7890
7916
|
onClose,
|
|
7891
|
-
onExport
|
|
7917
|
+
onExport,
|
|
7918
|
+
busy = false,
|
|
7919
|
+
busyText,
|
|
7920
|
+
notice = null
|
|
7892
7921
|
}) {
|
|
7893
7922
|
useOverlayOpen(true, onClose);
|
|
7894
7923
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -7975,6 +8004,7 @@ function ExportDateModal({
|
|
|
7975
8004
|
[rangeStart, rangeEnd, isFuture]
|
|
7976
8005
|
);
|
|
7977
8006
|
const handleExport = () => {
|
|
8007
|
+
if (busy) return;
|
|
7978
8008
|
if (rangeStart && rangeEnd) onExport(rangeStart, rangeEnd);
|
|
7979
8009
|
};
|
|
7980
8010
|
const getCellVisuals = (cell, idx) => {
|
|
@@ -8196,24 +8226,42 @@ function ExportDateModal({
|
|
|
8196
8226
|
borderTop: "1px solid var(--alias-border-subtle)"
|
|
8197
8227
|
},
|
|
8198
8228
|
children: [
|
|
8199
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8229
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 min-w-0 flex flex-col", children: [
|
|
8230
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
8231
|
+
"p",
|
|
8232
|
+
{
|
|
8233
|
+
className: "min-w-0 truncate",
|
|
8234
|
+
style: {
|
|
8235
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
8236
|
+
fontSize: "var(--font-size-font-size-md)",
|
|
8237
|
+
fontWeight: 400,
|
|
8238
|
+
lineHeight: "var(--line-height-line-height-md)",
|
|
8239
|
+
color: "var(--alias-text---icons-primary)"
|
|
8240
|
+
},
|
|
8241
|
+
"aria-live": "polite",
|
|
8242
|
+
children: busy && busyText ? busyText : rangeStart && rangeEnd ? `${formatDateRange(rangeStart)} - ${formatDateRange(rangeEnd)}` : rangeStart ? `${formatDateRange(rangeStart)} - Select end date` : "Select date range"
|
|
8243
|
+
}
|
|
8244
|
+
),
|
|
8245
|
+
notice && !busy && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
8246
|
+
"p",
|
|
8247
|
+
{
|
|
8248
|
+
role: "alert",
|
|
8249
|
+
style: {
|
|
8250
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
8251
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
8252
|
+
fontWeight: 400,
|
|
8253
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
8254
|
+
color: "var(--alias-text---icons-error)"
|
|
8255
|
+
},
|
|
8256
|
+
children: notice
|
|
8257
|
+
}
|
|
8258
|
+
)
|
|
8259
|
+
] }),
|
|
8213
8260
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
8214
8261
|
"div",
|
|
8215
8262
|
{
|
|
8216
|
-
className: `flex items-center justify-center shrink-0 ${rangeStart && rangeEnd ? "cursor-pointer" : "opacity-50 cursor-not-allowed"}`,
|
|
8263
|
+
className: `flex items-center justify-center shrink-0 ${rangeStart && rangeEnd && !busy ? "cursor-pointer" : "opacity-50 cursor-not-allowed"}`,
|
|
8264
|
+
"aria-disabled": busy || !(rangeStart && rangeEnd),
|
|
8217
8265
|
style: {
|
|
8218
8266
|
backgroundColor: "var(--alias-text---icons-primary)",
|
|
8219
8267
|
borderRadius: "var(--radius-xl)",
|
|
@@ -8231,7 +8279,7 @@ function ExportDateModal({
|
|
|
8231
8279
|
lineHeight: "var(--line-height-line-height-sm)",
|
|
8232
8280
|
color: "var(--alias-text---icons-inverse)"
|
|
8233
8281
|
},
|
|
8234
|
-
children: "Export"
|
|
8282
|
+
children: busy ? "Exporting\u2026" : "Export"
|
|
8235
8283
|
}
|
|
8236
8284
|
)
|
|
8237
8285
|
}
|
|
@@ -10585,6 +10633,261 @@ function SharedCreditsView() {
|
|
|
10585
10633
|
] });
|
|
10586
10634
|
}
|
|
10587
10635
|
|
|
10636
|
+
// src/entitlement.ts
|
|
10637
|
+
function parseBillingEntitlement(value) {
|
|
10638
|
+
if (!value || typeof value !== "object") return null;
|
|
10639
|
+
const raw = value;
|
|
10640
|
+
if (typeof raw.paid !== "boolean") return null;
|
|
10641
|
+
const source = raw.source === "plan" || raw.source === "pool" ? raw.source : null;
|
|
10642
|
+
const entitlement = { paid: raw.paid, source };
|
|
10643
|
+
if (typeof raw.sponsorWorkspaceId === "string") entitlement.sponsorWorkspaceId = raw.sponsorWorkspaceId;
|
|
10644
|
+
if (typeof raw.sponsorName === "string") entitlement.sponsorName = raw.sponsorName;
|
|
10645
|
+
if (typeof raw.endsAt === "string" || raw.endsAt === null) entitlement.endsAt = raw.endsAt;
|
|
10646
|
+
return entitlement;
|
|
10647
|
+
}
|
|
10648
|
+
function entitlementFromBody(body) {
|
|
10649
|
+
if (!body || typeof body !== "object") return null;
|
|
10650
|
+
return parseBillingEntitlement(body.entitlement);
|
|
10651
|
+
}
|
|
10652
|
+
function entitlementFromFailure(failure) {
|
|
10653
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10654
|
+
const visit = (value, depth) => {
|
|
10655
|
+
if (depth > 4 || value == null || typeof value !== "object" || seen.has(value)) return null;
|
|
10656
|
+
seen.add(value);
|
|
10657
|
+
const found = entitlementFromBody(value);
|
|
10658
|
+
if (found) return found;
|
|
10659
|
+
const error = value;
|
|
10660
|
+
const response = error.response;
|
|
10661
|
+
for (const child of [error.body, error.data, response?.data, error.cause]) {
|
|
10662
|
+
const nested = visit(child, depth + 1);
|
|
10663
|
+
if (nested) return nested;
|
|
10664
|
+
}
|
|
10665
|
+
return null;
|
|
10666
|
+
};
|
|
10667
|
+
return visit(failure, 0);
|
|
10668
|
+
}
|
|
10669
|
+
function isPaidPlan(subscription) {
|
|
10670
|
+
const amount = subscription?.plan?.amount;
|
|
10671
|
+
return amount != null && amount > 0;
|
|
10672
|
+
}
|
|
10673
|
+
function hasPaidFeatures({
|
|
10674
|
+
entitlement,
|
|
10675
|
+
subscription
|
|
10676
|
+
}) {
|
|
10677
|
+
return entitlement ? entitlement.paid : isPaidPlan(subscription);
|
|
10678
|
+
}
|
|
10679
|
+
function isSponsoredEntitlement(entitlement) {
|
|
10680
|
+
return entitlement?.source === "pool" && entitlement.paid;
|
|
10681
|
+
}
|
|
10682
|
+
function sponsoredCreditsText(entitlement) {
|
|
10683
|
+
const sponsor = entitlement.sponsorName?.trim() || "another workspace";
|
|
10684
|
+
const ends = entitlement.endsAt ? ` Sharing ends on ${formatPoolEndsAt(entitlement.endsAt)}.` : "";
|
|
10685
|
+
return `Credits shared by ${sponsor}.${ends}`;
|
|
10686
|
+
}
|
|
10687
|
+
async function loadCurrentPlan(fetchCurrentPlan) {
|
|
10688
|
+
let response;
|
|
10689
|
+
try {
|
|
10690
|
+
response = await fetchCurrentPlan();
|
|
10691
|
+
} catch (failure) {
|
|
10692
|
+
const entitlement2 = entitlementFromFailure(failure);
|
|
10693
|
+
if (!entitlement2) throw failure;
|
|
10694
|
+
return { subscription: null, entitlement: entitlement2, paid: entitlement2.paid };
|
|
10695
|
+
}
|
|
10696
|
+
const subscription = response?.data ?? null;
|
|
10697
|
+
const entitlement = entitlementFromBody(response);
|
|
10698
|
+
return { subscription, entitlement, paid: hasPaidFeatures({ entitlement, subscription }) };
|
|
10699
|
+
}
|
|
10700
|
+
|
|
10701
|
+
// src/usage-logs.ts
|
|
10702
|
+
var USAGE_LOGS_PAGE_SIZE = 50;
|
|
10703
|
+
var USAGE_LOGS_EXPORT_BATCH_SIZE = 100;
|
|
10704
|
+
var USAGE_LOGS_EXPORT_MAX_ROWS = 2e4;
|
|
10705
|
+
var USAGE_LOGS_EXPORT_EXTRA_REQUESTS = 10;
|
|
10706
|
+
function rowsOf(res) {
|
|
10707
|
+
return Array.isArray(res?.data) ? res.data : [];
|
|
10708
|
+
}
|
|
10709
|
+
function sliceUsageLogsPage(all, page, pageSize = USAGE_LOGS_PAGE_SIZE) {
|
|
10710
|
+
const total = all.length;
|
|
10711
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
10712
|
+
const safePage = Math.min(Math.max(1, Math.floor(page) || 1), totalPages);
|
|
10713
|
+
return {
|
|
10714
|
+
rows: all.slice((safePage - 1) * pageSize, safePage * pageSize),
|
|
10715
|
+
page: safePage,
|
|
10716
|
+
totalPages,
|
|
10717
|
+
total,
|
|
10718
|
+
unpaged: all
|
|
10719
|
+
};
|
|
10720
|
+
}
|
|
10721
|
+
function usageLogsPage(res, page, pageSize = USAGE_LOGS_PAGE_SIZE) {
|
|
10722
|
+
const data = rowsOf(res);
|
|
10723
|
+
const pagination = res?.pagination;
|
|
10724
|
+
if (!pagination || !Number.isFinite(pagination.total)) {
|
|
10725
|
+
return sliceUsageLogsPage(data, page, pageSize);
|
|
10726
|
+
}
|
|
10727
|
+
const total = Math.max(0, pagination.total);
|
|
10728
|
+
const limit = pagination.limit > 0 ? pagination.limit : pageSize;
|
|
10729
|
+
const totalPages = Math.max(
|
|
10730
|
+
1,
|
|
10731
|
+
pagination.totalPages > 0 ? pagination.totalPages : Math.ceil(total / limit)
|
|
10732
|
+
);
|
|
10733
|
+
return {
|
|
10734
|
+
rows: data.slice(0, pageSize),
|
|
10735
|
+
page: pagination.page > 0 ? pagination.page : page,
|
|
10736
|
+
totalPages,
|
|
10737
|
+
total,
|
|
10738
|
+
unpaged: null
|
|
10739
|
+
};
|
|
10740
|
+
}
|
|
10741
|
+
var EMPTY_USAGE_LOGS_PAGE = {
|
|
10742
|
+
rows: [],
|
|
10743
|
+
page: 1,
|
|
10744
|
+
totalPages: 1,
|
|
10745
|
+
total: 0,
|
|
10746
|
+
unpaged: null
|
|
10747
|
+
};
|
|
10748
|
+
var INITIAL_USAGE_LOGS_STATE = {
|
|
10749
|
+
logs: EMPTY_USAGE_LOGS_PAGE,
|
|
10750
|
+
requestedPage: 1,
|
|
10751
|
+
loading: false,
|
|
10752
|
+
error: null
|
|
10753
|
+
};
|
|
10754
|
+
function usageLogsReducer(state, action) {
|
|
10755
|
+
switch (action.type) {
|
|
10756
|
+
case "request":
|
|
10757
|
+
return { ...state, requestedPage: action.page, loading: true, error: null };
|
|
10758
|
+
case "loaded":
|
|
10759
|
+
return {
|
|
10760
|
+
logs: action.logs,
|
|
10761
|
+
requestedPage: action.logs.page,
|
|
10762
|
+
loading: false,
|
|
10763
|
+
error: null
|
|
10764
|
+
};
|
|
10765
|
+
case "failed":
|
|
10766
|
+
return { ...state, loading: false, error: action.error };
|
|
10767
|
+
case "turn": {
|
|
10768
|
+
if (!state.logs.unpaged || state.error) return state;
|
|
10769
|
+
const logs = sliceUsageLogsPage(state.logs.unpaged, action.page);
|
|
10770
|
+
return { ...state, logs, requestedPage: logs.page };
|
|
10771
|
+
}
|
|
10772
|
+
case "reset":
|
|
10773
|
+
return INITIAL_USAGE_LOGS_STATE;
|
|
10774
|
+
}
|
|
10775
|
+
}
|
|
10776
|
+
function usageLogsSpentByOthers(logs, workspaceId) {
|
|
10777
|
+
return (logs.unpaged ?? logs.rows).some(
|
|
10778
|
+
(log) => usedByWorkspaceKey(log, workspaceId) !== null
|
|
10779
|
+
);
|
|
10780
|
+
}
|
|
10781
|
+
function createUsedByNamesLoader(save) {
|
|
10782
|
+
let savedFor = null;
|
|
10783
|
+
let inFlight = null;
|
|
10784
|
+
return {
|
|
10785
|
+
async load(workspaceId, fetchCreditPool) {
|
|
10786
|
+
if (savedFor === workspaceId || inFlight?.workspaceId === workspaceId) {
|
|
10787
|
+
return;
|
|
10788
|
+
}
|
|
10789
|
+
const lookup = { workspaceId };
|
|
10790
|
+
inFlight = lookup;
|
|
10791
|
+
try {
|
|
10792
|
+
const pool = await fetchCreditPool();
|
|
10793
|
+
if (inFlight !== lookup) return;
|
|
10794
|
+
if (save(workspaceId, usedByNamesFrom(pool))) savedFor = workspaceId;
|
|
10795
|
+
} catch {
|
|
10796
|
+
} finally {
|
|
10797
|
+
if (inFlight === lookup) inFlight = null;
|
|
10798
|
+
}
|
|
10799
|
+
},
|
|
10800
|
+
reset() {
|
|
10801
|
+
savedFor = null;
|
|
10802
|
+
inFlight = null;
|
|
10803
|
+
}
|
|
10804
|
+
};
|
|
10805
|
+
}
|
|
10806
|
+
function usageLogsExportFileName(start, end) {
|
|
10807
|
+
return `usage_logs_${toLocalDateInput(start)}_${toLocalDateInput(end)}.csv`;
|
|
10808
|
+
}
|
|
10809
|
+
function usageLogsDateRange(start, end) {
|
|
10810
|
+
const from = new Date(start);
|
|
10811
|
+
from.setHours(0, 0, 0, 0);
|
|
10812
|
+
const to = new Date(end);
|
|
10813
|
+
to.setHours(23, 59, 59, 999);
|
|
10814
|
+
return { startDate: from.toISOString(), endDate: to.toISOString() };
|
|
10815
|
+
}
|
|
10816
|
+
async function fetchUsageLogsInRange(fetchUsageLogs, range, {
|
|
10817
|
+
batchSize = USAGE_LOGS_EXPORT_BATCH_SIZE,
|
|
10818
|
+
maxRows = USAGE_LOGS_EXPORT_MAX_ROWS,
|
|
10819
|
+
maxRequests,
|
|
10820
|
+
onProgress,
|
|
10821
|
+
signal
|
|
10822
|
+
} = {}) {
|
|
10823
|
+
const first = await fetchUsageLogs({ ...range, page: 1, limit: batchSize });
|
|
10824
|
+
if (signal?.aborted) return { status: "cancelled" };
|
|
10825
|
+
if (!first?.pagination || !Number.isFinite(first.pagination.total)) {
|
|
10826
|
+
const from = Date.parse(range.startDate);
|
|
10827
|
+
const to = Date.parse(range.endDate);
|
|
10828
|
+
const rows2 = rowsOf(first).filter((row) => {
|
|
10829
|
+
const at = Date.parse(row.createdAt);
|
|
10830
|
+
return at >= from && at <= to;
|
|
10831
|
+
});
|
|
10832
|
+
if (rows2.length > maxRows) return { status: "too-large", total: rows2.length };
|
|
10833
|
+
onProgress?.(rows2.length, rows2.length);
|
|
10834
|
+
return { status: "ok", rows: rows2, total: rows2.length };
|
|
10835
|
+
}
|
|
10836
|
+
let total = Math.max(0, first.pagination.total);
|
|
10837
|
+
if (total > maxRows) return { status: "too-large", total };
|
|
10838
|
+
const limit = first.pagination.limit > 0 ? first.pagination.limit : batchSize;
|
|
10839
|
+
const requestCap = maxRequests ?? Math.ceil(maxRows / limit) + USAGE_LOGS_EXPORT_EXTRA_REQUESTS;
|
|
10840
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10841
|
+
const rows = [];
|
|
10842
|
+
const add = (batch) => {
|
|
10843
|
+
for (const row of batch) {
|
|
10844
|
+
if (rows.length >= maxRows) return;
|
|
10845
|
+
if (seen.has(row._id)) continue;
|
|
10846
|
+
seen.add(row._id);
|
|
10847
|
+
rows.push(row);
|
|
10848
|
+
}
|
|
10849
|
+
};
|
|
10850
|
+
const hasNextAfter = (pagination, page) => typeof pagination?.hasNextPage === "boolean" ? pagination.hasNextPage : page * limit < total;
|
|
10851
|
+
add(rowsOf(first));
|
|
10852
|
+
onProgress?.(rows.length, total);
|
|
10853
|
+
let hasNext = hasNextAfter(first.pagination, 1);
|
|
10854
|
+
let requests = 1;
|
|
10855
|
+
for (let page = 2; hasNext && rows.length < total; page++) {
|
|
10856
|
+
if (requests >= requestCap) {
|
|
10857
|
+
return { status: "incomplete", fetched: rows.length, total };
|
|
10858
|
+
}
|
|
10859
|
+
if (signal?.aborted) return { status: "cancelled" };
|
|
10860
|
+
const res = await fetchUsageLogs({ ...range, page, limit });
|
|
10861
|
+
requests++;
|
|
10862
|
+
if (signal?.aborted) return { status: "cancelled" };
|
|
10863
|
+
if (res?.pagination && Number.isFinite(res.pagination.total)) {
|
|
10864
|
+
total = Math.max(0, res.pagination.total);
|
|
10865
|
+
if (total > maxRows) return { status: "too-large", total };
|
|
10866
|
+
}
|
|
10867
|
+
const batch = rowsOf(res);
|
|
10868
|
+
if (batch.length === 0) break;
|
|
10869
|
+
add(batch);
|
|
10870
|
+
onProgress?.(rows.length, total);
|
|
10871
|
+
hasNext = hasNextAfter(res?.pagination, page);
|
|
10872
|
+
}
|
|
10873
|
+
return { status: "ok", rows, total };
|
|
10874
|
+
}
|
|
10875
|
+
function usageLogsCsvRows(logs, userMap, formatDate5) {
|
|
10876
|
+
return [
|
|
10877
|
+
["Date", "Type", "Credits", "Service", "User ID"],
|
|
10878
|
+
...logs.map((log) => [
|
|
10879
|
+
formatDate5(log.createdAt),
|
|
10880
|
+
log.type,
|
|
10881
|
+
log.amount.toLocaleString("en-US"),
|
|
10882
|
+
log.service?.serviceGroupName ?? log.meta?.info ?? "",
|
|
10883
|
+
log.triggeredByUserId != null ? userMap[log.triggeredByUserId] ?? log.triggeredByUserId : ""
|
|
10884
|
+
])
|
|
10885
|
+
];
|
|
10886
|
+
}
|
|
10887
|
+
function toCsv(rows) {
|
|
10888
|
+
return rows.map((r) => r.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(",")).join("\n");
|
|
10889
|
+
}
|
|
10890
|
+
|
|
10588
10891
|
// src/billing-content.tsx
|
|
10589
10892
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
10590
10893
|
var DEFAULT_TOPUP_SERVICE = "catalogix";
|
|
@@ -11470,6 +11773,8 @@ function UsageLogsTable({
|
|
|
11470
11773
|
logs,
|
|
11471
11774
|
userMap,
|
|
11472
11775
|
loading,
|
|
11776
|
+
error = null,
|
|
11777
|
+
onRetry,
|
|
11473
11778
|
currentWorkspaceId = null,
|
|
11474
11779
|
usedByNames = {}
|
|
11475
11780
|
}) {
|
|
@@ -11500,6 +11805,50 @@ function UsageLogsTable({
|
|
|
11500
11805
|
}
|
|
11501
11806
|
);
|
|
11502
11807
|
}
|
|
11808
|
+
if (error) {
|
|
11809
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
11810
|
+
"div",
|
|
11811
|
+
{
|
|
11812
|
+
role: "alert",
|
|
11813
|
+
className: "flex flex-col items-center justify-center w-full",
|
|
11814
|
+
style: {
|
|
11815
|
+
backgroundColor: "var(--alias-surface-base)",
|
|
11816
|
+
borderRadius: "var(--radius-3xl)",
|
|
11817
|
+
border: "1px solid var(--alias-border-subtle)",
|
|
11818
|
+
padding: "var(--spacing-6xl)",
|
|
11819
|
+
gap: "var(--spacing-md)"
|
|
11820
|
+
},
|
|
11821
|
+
children: [
|
|
11822
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
11823
|
+
"span",
|
|
11824
|
+
{
|
|
11825
|
+
style: {
|
|
11826
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
11827
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
11828
|
+
fontWeight: 400,
|
|
11829
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
11830
|
+
color: "var(--alias-text---icons-muted)",
|
|
11831
|
+
textAlign: "center"
|
|
11832
|
+
},
|
|
11833
|
+
children: [
|
|
11834
|
+
"Couldn't load usage logs. ",
|
|
11835
|
+
error
|
|
11836
|
+
]
|
|
11837
|
+
}
|
|
11838
|
+
),
|
|
11839
|
+
onRetry && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
11840
|
+
import_ui17.ScButton,
|
|
11841
|
+
{
|
|
11842
|
+
text: "Try again",
|
|
11843
|
+
variant: "tertiary",
|
|
11844
|
+
size: "sm",
|
|
11845
|
+
onClick: onRetry
|
|
11846
|
+
}
|
|
11847
|
+
)
|
|
11848
|
+
]
|
|
11849
|
+
}
|
|
11850
|
+
);
|
|
11851
|
+
}
|
|
11503
11852
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
11504
11853
|
"div",
|
|
11505
11854
|
{
|
|
@@ -11675,6 +12024,71 @@ function UsageLogsTable({
|
|
|
11675
12024
|
}
|
|
11676
12025
|
);
|
|
11677
12026
|
}
|
|
12027
|
+
function UsageLogsPager({
|
|
12028
|
+
page,
|
|
12029
|
+
totalPages,
|
|
12030
|
+
total,
|
|
12031
|
+
loading = false,
|
|
12032
|
+
onPageChange
|
|
12033
|
+
}) {
|
|
12034
|
+
if (total <= 0) return null;
|
|
12035
|
+
const canPrevious = !loading && page > 1;
|
|
12036
|
+
const canNext = !loading && page < totalPages;
|
|
12037
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
12038
|
+
"div",
|
|
12039
|
+
{
|
|
12040
|
+
className: "flex items-center justify-between shrink-0 w-full",
|
|
12041
|
+
style: { gap: "var(--spacing-md)" },
|
|
12042
|
+
children: [
|
|
12043
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
12044
|
+
"span",
|
|
12045
|
+
{
|
|
12046
|
+
style: {
|
|
12047
|
+
color: "var(--alias-text---icons-muted)",
|
|
12048
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
12049
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
12050
|
+
lineHeight: "var(--line-height-line-height-sm)"
|
|
12051
|
+
},
|
|
12052
|
+
children: [
|
|
12053
|
+
"Page ",
|
|
12054
|
+
page.toLocaleString("en-US"),
|
|
12055
|
+
" of",
|
|
12056
|
+
" ",
|
|
12057
|
+
totalPages.toLocaleString("en-US"),
|
|
12058
|
+
" \xB7",
|
|
12059
|
+
" ",
|
|
12060
|
+
total.toLocaleString("en-US"),
|
|
12061
|
+
" ",
|
|
12062
|
+
total === 1 ? "log" : "logs"
|
|
12063
|
+
]
|
|
12064
|
+
}
|
|
12065
|
+
),
|
|
12066
|
+
totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center", style: { gap: "var(--spacing-md)" }, children: [
|
|
12067
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
12068
|
+
import_ui17.ScButton,
|
|
12069
|
+
{
|
|
12070
|
+
text: "Previous",
|
|
12071
|
+
variant: "tertiary",
|
|
12072
|
+
size: "sm",
|
|
12073
|
+
state: canPrevious ? "default" : "disabled",
|
|
12074
|
+
onClick: canPrevious ? () => onPageChange(page - 1) : void 0
|
|
12075
|
+
}
|
|
12076
|
+
),
|
|
12077
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
12078
|
+
import_ui17.ScButton,
|
|
12079
|
+
{
|
|
12080
|
+
text: "Next",
|
|
12081
|
+
variant: "tertiary",
|
|
12082
|
+
size: "sm",
|
|
12083
|
+
state: canNext ? "default" : "disabled",
|
|
12084
|
+
onClick: canNext ? () => onPageChange(page + 1) : void 0
|
|
12085
|
+
}
|
|
12086
|
+
)
|
|
12087
|
+
] })
|
|
12088
|
+
]
|
|
12089
|
+
}
|
|
12090
|
+
);
|
|
12091
|
+
}
|
|
11678
12092
|
function BackToBilling({ onClick }) {
|
|
11679
12093
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
11680
12094
|
"div",
|
|
@@ -11716,7 +12130,8 @@ function BillingContent({
|
|
|
11716
12130
|
showPerUserLimits: showPerUserLimitsProp,
|
|
11717
12131
|
onShowPerUserLimitsChange,
|
|
11718
12132
|
showSharedCredits: showSharedCreditsProp,
|
|
11719
|
-
onShowSharedCreditsChange
|
|
12133
|
+
onShowSharedCreditsChange,
|
|
12134
|
+
onEntitlementChange
|
|
11720
12135
|
} = {}) {
|
|
11721
12136
|
const { auth, workspace, organization, api, toast, navigation } = useSettingsAdapters();
|
|
11722
12137
|
const user = auth.user;
|
|
@@ -11753,15 +12168,38 @@ function BillingContent({
|
|
|
11753
12168
|
else setShowSharedCreditsInternal(next);
|
|
11754
12169
|
};
|
|
11755
12170
|
const [showExportModal, setShowExportModal] = (0, import_react23.useState)(false);
|
|
12171
|
+
const [exportProgress, setExportProgress] = (0, import_react23.useState)(null);
|
|
12172
|
+
const [exportNotice, setExportNotice] = (0, import_react23.useState)(null);
|
|
12173
|
+
const exportAbortRef = (0, import_react23.useRef)(null);
|
|
11756
12174
|
const [showBuyCreditsModal, setShowBuyCreditsModal] = (0, import_react23.useState)(false);
|
|
11757
12175
|
const [subscription, setSubscription] = (0, import_react23.useState)(null);
|
|
12176
|
+
const [entitlement, setEntitlement] = (0, import_react23.useState)(
|
|
12177
|
+
null
|
|
12178
|
+
);
|
|
11758
12179
|
const [availableCredits, setAvailableCredits] = (0, import_react23.useState)(null);
|
|
11759
12180
|
const [sharedPool, setSharedPool] = (0, import_react23.useState)(null);
|
|
11760
12181
|
const [usedByNames, setUsedByNames] = (0, import_react23.useState)({});
|
|
11761
12182
|
const [products, setProducts] = (0, import_react23.useState)([]);
|
|
11762
12183
|
const [invoices, setInvoices] = (0, import_react23.useState)([]);
|
|
11763
12184
|
const [totalCredits, setTotalCredits] = (0, import_react23.useState)(null);
|
|
11764
|
-
const [
|
|
12185
|
+
const [usageLogsState, dispatchUsageLogs] = (0, import_react23.useReducer)(
|
|
12186
|
+
usageLogsReducer,
|
|
12187
|
+
INITIAL_USAGE_LOGS_STATE
|
|
12188
|
+
);
|
|
12189
|
+
const {
|
|
12190
|
+
logs: usageLogs,
|
|
12191
|
+
loading: loadingUsageLogs,
|
|
12192
|
+
error: usageLogsError
|
|
12193
|
+
} = usageLogsState;
|
|
12194
|
+
const usageLogsRequestRef = (0, import_react23.useRef)(0);
|
|
12195
|
+
const workspaceIdRef = (0, import_react23.useRef)(workspaceId);
|
|
12196
|
+
workspaceIdRef.current = workspaceId;
|
|
12197
|
+
const loadUsedByNamesRef = (0, import_react23.useRef)(null);
|
|
12198
|
+
loadUsedByNamesRef.current ??= createUsedByNamesLoader((forWorkspace, names) => {
|
|
12199
|
+
if (forWorkspace !== workspaceIdRef.current) return false;
|
|
12200
|
+
setUsedByNames(names);
|
|
12201
|
+
return true;
|
|
12202
|
+
});
|
|
11765
12203
|
const [userMap, setUserMap] = (0, import_react23.useState)({});
|
|
11766
12204
|
const [customPlanCredits, setCustomPlanCredits] = (0, import_react23.useState)(
|
|
11767
12205
|
null
|
|
@@ -11800,12 +12238,15 @@ function BillingContent({
|
|
|
11800
12238
|
try {
|
|
11801
12239
|
console.log("calling Billing apis");
|
|
11802
12240
|
const [subRes, creditsRes, productsRes, customRes] = await Promise.all([
|
|
11803
|
-
|
|
12241
|
+
// A sponsored workspace with no subscription of its own gets a 404
|
|
12242
|
+
// that carries its entitlement: loadCurrentPlan answers it, not throws.
|
|
12243
|
+
loadCurrentPlan(api.fetchCurrentPlan).catch(() => null),
|
|
11804
12244
|
api.fetchAvailableCredits().catch(() => null),
|
|
11805
12245
|
api.fetchPlans().catch(() => null),
|
|
11806
12246
|
api.getCustomCreditInfo?.().catch(() => null) ?? Promise.resolve(null)
|
|
11807
12247
|
]);
|
|
11808
|
-
if (subRes?.
|
|
12248
|
+
if (subRes?.subscription) setSubscription(subRes.subscription);
|
|
12249
|
+
if (subRes) setEntitlement(subRes.entitlement);
|
|
11809
12250
|
if (creditsRes?.availableCredits != null)
|
|
11810
12251
|
setAvailableCredits(creditsRes.availableCredits);
|
|
11811
12252
|
setSharedPool(creditsRes?.sharedPool ?? null);
|
|
@@ -11842,8 +12283,68 @@ function BillingContent({
|
|
|
11842
12283
|
} catch {
|
|
11843
12284
|
}
|
|
11844
12285
|
}, [api]);
|
|
12286
|
+
const ensureUsedByNames = (0, import_react23.useCallback)(
|
|
12287
|
+
(logs) => {
|
|
12288
|
+
const fetchCreditPool = api.fetchCreditPool;
|
|
12289
|
+
if (!fetchCreditPool || !workspaceId || !usageLogsSpentByOthers(logs, workspaceId)) {
|
|
12290
|
+
return;
|
|
12291
|
+
}
|
|
12292
|
+
void loadUsedByNamesRef.current?.load(
|
|
12293
|
+
workspaceId,
|
|
12294
|
+
() => fetchCreditPool.call(api)
|
|
12295
|
+
);
|
|
12296
|
+
},
|
|
12297
|
+
[api, workspaceId]
|
|
12298
|
+
);
|
|
12299
|
+
const loadUsageLogsPage = (0, import_react23.useCallback)(
|
|
12300
|
+
async (page) => {
|
|
12301
|
+
const request = ++usageLogsRequestRef.current;
|
|
12302
|
+
if (!api.fetchUsageLogs) {
|
|
12303
|
+
dispatchUsageLogs({ type: "loaded", logs: EMPTY_USAGE_LOGS_PAGE });
|
|
12304
|
+
return;
|
|
12305
|
+
}
|
|
12306
|
+
dispatchUsageLogs({ type: "request", page });
|
|
12307
|
+
try {
|
|
12308
|
+
const res = await api.fetchUsageLogs({
|
|
12309
|
+
page,
|
|
12310
|
+
limit: USAGE_LOGS_PAGE_SIZE
|
|
12311
|
+
});
|
|
12312
|
+
if (request !== usageLogsRequestRef.current) return;
|
|
12313
|
+
const next = usageLogsPage(res, page);
|
|
12314
|
+
dispatchUsageLogs({ type: "loaded", logs: next });
|
|
12315
|
+
ensureUsedByNames(next);
|
|
12316
|
+
} catch (err) {
|
|
12317
|
+
if (request !== usageLogsRequestRef.current) return;
|
|
12318
|
+
dispatchUsageLogs({
|
|
12319
|
+
type: "failed",
|
|
12320
|
+
error: errorMessage(err, { method: "GET" })
|
|
12321
|
+
});
|
|
12322
|
+
}
|
|
12323
|
+
},
|
|
12324
|
+
[api, ensureUsedByNames]
|
|
12325
|
+
);
|
|
12326
|
+
const goToUsageLogsPage = (0, import_react23.useCallback)(
|
|
12327
|
+
(page) => {
|
|
12328
|
+
if (usageLogs.unpaged && !usageLogsError) {
|
|
12329
|
+
dispatchUsageLogs({ type: "turn", page });
|
|
12330
|
+
ensureUsedByNames(usageLogs);
|
|
12331
|
+
return;
|
|
12332
|
+
}
|
|
12333
|
+
void loadUsageLogsPage(page);
|
|
12334
|
+
},
|
|
12335
|
+
[usageLogs, usageLogsError, loadUsageLogsPage, ensureUsedByNames]
|
|
12336
|
+
);
|
|
12337
|
+
(0, import_react23.useEffect)(() => {
|
|
12338
|
+
if (!loadingUsageLogs && !usageLogsError && usageLogs.unpaged === null && usageLogs.rows.length === 0 && usageLogs.total > 0 && usageLogs.page > usageLogs.totalPages) {
|
|
12339
|
+
void loadUsageLogsPage(usageLogs.totalPages);
|
|
12340
|
+
}
|
|
12341
|
+
}, [usageLogs, loadingUsageLogs, usageLogsError, loadUsageLogsPage]);
|
|
11845
12342
|
const fetchTableData = (0, import_react23.useCallback)(
|
|
11846
12343
|
async (tab) => {
|
|
12344
|
+
if (tab === "usage") {
|
|
12345
|
+
await loadUsageLogsPage(1);
|
|
12346
|
+
return;
|
|
12347
|
+
}
|
|
11847
12348
|
setLoadingTable(true);
|
|
11848
12349
|
try {
|
|
11849
12350
|
if (tab === "invoice") {
|
|
@@ -11855,25 +12356,14 @@ function BillingContent({
|
|
|
11855
12356
|
(item) => item.expiresAt && item.expiresAt.split("T")[0] > today
|
|
11856
12357
|
).reduce((total, item) => total + item.amount, 0)
|
|
11857
12358
|
);
|
|
11858
|
-
} else if (api.fetchUsageLogs) {
|
|
11859
|
-
const res = await api.fetchUsageLogs();
|
|
11860
|
-
const logs = res.data?.length ? res.data : [];
|
|
11861
|
-
setUsageLogs(logs);
|
|
11862
|
-
if (api.fetchCreditPool && logs.some((log) => usedByWorkspaceKey(log, workspaceId))) {
|
|
11863
|
-
const pool = await api.fetchCreditPool().catch(() => null);
|
|
11864
|
-
setUsedByNames(usedByNamesFrom(pool));
|
|
11865
|
-
}
|
|
11866
|
-
} else {
|
|
11867
|
-
setUsageLogs([]);
|
|
11868
12359
|
}
|
|
11869
12360
|
} catch {
|
|
11870
|
-
|
|
11871
|
-
else setUsageLogs([]);
|
|
12361
|
+
setInvoices([]);
|
|
11872
12362
|
} finally {
|
|
11873
12363
|
setLoadingTable(false);
|
|
11874
12364
|
}
|
|
11875
12365
|
},
|
|
11876
|
-
[api,
|
|
12366
|
+
[api, loadUsageLogsPage]
|
|
11877
12367
|
);
|
|
11878
12368
|
const handlePlanAction = (0, import_react23.useCallback)(
|
|
11879
12369
|
async ({ productId, priceId, planName: planName2 }) => {
|
|
@@ -11975,10 +12465,15 @@ function BillingContent({
|
|
|
11975
12465
|
setInitialFetchTriggered(false);
|
|
11976
12466
|
setBillingOverviewLoaded(false);
|
|
11977
12467
|
setSubscription(null);
|
|
12468
|
+
setEntitlement(null);
|
|
11978
12469
|
setAvailableCredits(null);
|
|
11979
12470
|
setProducts([]);
|
|
11980
12471
|
setTotalCredits(null);
|
|
11981
12472
|
setCustomPlanCredits(null);
|
|
12473
|
+
usageLogsRequestRef.current++;
|
|
12474
|
+
dispatchUsageLogs({ type: "reset" });
|
|
12475
|
+
loadUsedByNamesRef.current?.reset();
|
|
12476
|
+
setUsedByNames({});
|
|
11982
12477
|
}, [workspaceId]);
|
|
11983
12478
|
(0, import_react23.useEffect)(() => {
|
|
11984
12479
|
setInitialFetchTriggered(true);
|
|
@@ -12030,7 +12525,16 @@ function BillingContent({
|
|
|
12030
12525
|
subscription?.scheduledDowngradeEffectiveDate
|
|
12031
12526
|
);
|
|
12032
12527
|
const scheduledDowngradeText = subscription?.downgradeScheduled && subscription.plan?.name ? `Downgrades to ${subscription.plan.name}${scheduledDowngradeDateLabel ? ` ${scheduledDowngradeDateLabel}` : ""}` : void 0;
|
|
12033
|
-
const
|
|
12528
|
+
const sponsored = isSponsoredEntitlement(entitlement);
|
|
12529
|
+
const sponsoredWithoutPlan = sponsored && billingOverviewLoaded && !loadingBilling && subscription == null;
|
|
12530
|
+
const isPlanDetailsLoading = sponsoredWithoutPlan ? false : !billingOverviewLoaded || loadingBilling || subscription?.plan == null || !planName || planName === "-" || !planAmount || currentPlanCredits == null;
|
|
12531
|
+
const paidFeatures = hasPaidFeatures({ entitlement, subscription });
|
|
12532
|
+
const onEntitlementChangeRef = (0, import_react23.useRef)(onEntitlementChange);
|
|
12533
|
+
onEntitlementChangeRef.current = onEntitlementChange;
|
|
12534
|
+
(0, import_react23.useEffect)(() => {
|
|
12535
|
+
if (!billingOverviewLoaded) return;
|
|
12536
|
+
onEntitlementChangeRef.current?.({ entitlement, paid: paidFeatures });
|
|
12537
|
+
}, [billingOverviewLoaded, entitlement, paidFeatures]);
|
|
12034
12538
|
const {
|
|
12035
12539
|
used: usedCredits,
|
|
12036
12540
|
remaining: remainingCredits,
|
|
@@ -12072,35 +12576,74 @@ function BillingContent({
|
|
|
12072
12576
|
toast.success(`Opened ${successCount} invoice PDF(s).`);
|
|
12073
12577
|
}
|
|
12074
12578
|
} else {
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12579
|
+
await exportUsageLogs(startDate, endDate);
|
|
12580
|
+
}
|
|
12581
|
+
};
|
|
12582
|
+
const closeExportModal = () => {
|
|
12583
|
+
exportAbortRef.current?.abort();
|
|
12584
|
+
exportAbortRef.current = null;
|
|
12585
|
+
setExportProgress(null);
|
|
12586
|
+
setExportNotice(null);
|
|
12587
|
+
setShowExportModal(false);
|
|
12588
|
+
};
|
|
12589
|
+
(0, import_react23.useEffect)(() => () => exportAbortRef.current?.abort(), []);
|
|
12590
|
+
const exportUsageLogs = async (startDate, endDate) => {
|
|
12591
|
+
if (!api.fetchUsageLogs) {
|
|
12592
|
+
toast.info?.("Usage logs are not available in this workspace.");
|
|
12593
|
+
closeExportModal();
|
|
12594
|
+
return;
|
|
12595
|
+
}
|
|
12596
|
+
exportAbortRef.current?.abort();
|
|
12597
|
+
const controller = new AbortController();
|
|
12598
|
+
exportAbortRef.current = controller;
|
|
12599
|
+
setExportNotice(null);
|
|
12600
|
+
setExportProgress({ fetched: 0, total: 0 });
|
|
12601
|
+
const range = usageLogsDateRange(startDate, endDate);
|
|
12602
|
+
try {
|
|
12603
|
+
const result = await fetchUsageLogsInRange(api.fetchUsageLogs, range, {
|
|
12604
|
+
signal: controller.signal,
|
|
12605
|
+
onProgress: (fetched, total) => {
|
|
12606
|
+
if (!controller.signal.aborted) setExportProgress({ fetched, total });
|
|
12607
|
+
}
|
|
12078
12608
|
});
|
|
12079
|
-
if (
|
|
12609
|
+
if (result.status === "cancelled" || controller.signal.aborted) return;
|
|
12610
|
+
if (result.status === "too-large") {
|
|
12611
|
+
setExportNotice(
|
|
12612
|
+
`${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.`
|
|
12613
|
+
);
|
|
12614
|
+
return;
|
|
12615
|
+
}
|
|
12616
|
+
if (result.status === "incomplete") {
|
|
12617
|
+
setExportNotice(
|
|
12618
|
+
"Usage logs kept changing while exporting, so some were missed. Try again."
|
|
12619
|
+
);
|
|
12620
|
+
return;
|
|
12621
|
+
}
|
|
12622
|
+
if (result.rows.length === 0) {
|
|
12080
12623
|
toast.info?.("No usage logs found in the selected date range.");
|
|
12081
|
-
|
|
12624
|
+
closeExportModal();
|
|
12082
12625
|
return;
|
|
12083
12626
|
}
|
|
12084
|
-
const headers = ["Date", "Type", "Credits", "Service", "User ID"];
|
|
12085
|
-
const rows = filtered.map((log) => [
|
|
12086
|
-
formatDate3(log.createdAt),
|
|
12087
|
-
log.type,
|
|
12088
|
-
log.amount.toLocaleString("en-US"),
|
|
12089
|
-
log.service?.serviceGroupName ?? log.meta?.info ?? "",
|
|
12090
|
-
log.triggeredByUserId != null ? userMap[log.triggeredByUserId] ?? log.triggeredByUserId : ""
|
|
12091
|
-
]);
|
|
12092
12627
|
downloadCsv(
|
|
12093
|
-
|
|
12094
|
-
|
|
12628
|
+
usageLogsCsvRows(result.rows, userMap, formatDate3),
|
|
12629
|
+
usageLogsExportFileName(startDate, endDate)
|
|
12095
12630
|
);
|
|
12096
|
-
toast.success(`Exported ${
|
|
12097
|
-
|
|
12631
|
+
toast.success(`Exported ${result.rows.length.toLocaleString("en-US")} log(s).`);
|
|
12632
|
+
closeExportModal();
|
|
12633
|
+
} catch (err) {
|
|
12634
|
+
if (controller.signal.aborted) return;
|
|
12635
|
+
setExportNotice(
|
|
12636
|
+
`Couldn't export usage logs. ${errorMessage(err, { method: "GET" })}`
|
|
12637
|
+
);
|
|
12638
|
+
} finally {
|
|
12639
|
+
if (exportAbortRef.current === controller) {
|
|
12640
|
+
exportAbortRef.current = null;
|
|
12641
|
+
setExportProgress(null);
|
|
12642
|
+
}
|
|
12098
12643
|
}
|
|
12099
12644
|
};
|
|
12100
12645
|
function downloadCsv(rows, filename) {
|
|
12101
|
-
const csv = rows
|
|
12102
|
-
(r) => r.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(",")
|
|
12103
|
-
).join("\n");
|
|
12646
|
+
const csv = toCsv(rows);
|
|
12104
12647
|
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
12105
12648
|
const url = URL.createObjectURL(blob);
|
|
12106
12649
|
const a = document.createElement("a");
|
|
@@ -12111,9 +12654,6 @@ function BillingContent({
|
|
|
12111
12654
|
document.body.removeChild(a);
|
|
12112
12655
|
URL.revokeObjectURL(url);
|
|
12113
12656
|
}
|
|
12114
|
-
function fmtFilename(d) {
|
|
12115
|
-
return d.toISOString().slice(0, 10);
|
|
12116
|
-
}
|
|
12117
12657
|
async function handleBuyCredits(credits2, price) {
|
|
12118
12658
|
if (!workspaceId) {
|
|
12119
12659
|
toast.error("Missing workspace details for credit top-up");
|
|
@@ -12267,9 +12807,9 @@ function BillingContent({
|
|
|
12267
12807
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
12268
12808
|
import_ui17.ScPlanDetailsCard,
|
|
12269
12809
|
{
|
|
12270
|
-
planName,
|
|
12271
|
-
planCredits: planCreditsText,
|
|
12272
|
-
planPrice: planAmount,
|
|
12810
|
+
planName: sponsoredWithoutPlan ? "Free" : planName,
|
|
12811
|
+
planCredits: sponsoredWithoutPlan ? "Shared credits" : planCreditsText,
|
|
12812
|
+
planPrice: sponsoredWithoutPlan ? `${workspace.current?.country?.trim().toUpperCase() === "IN" ? "\u20B9" : "$"}0.00 / month` : planAmount,
|
|
12273
12813
|
badgeText: "Active",
|
|
12274
12814
|
badgeSubText: scheduledDowngradeText,
|
|
12275
12815
|
currentPlanLabel: "Current plan",
|
|
@@ -12358,6 +12898,50 @@ function BillingContent({
|
|
|
12358
12898
|
)
|
|
12359
12899
|
]
|
|
12360
12900
|
}
|
|
12901
|
+
) : sponsored && entitlement ? (
|
|
12902
|
+
// Billing says a pool entitles it, but the balance carried no
|
|
12903
|
+
// allowance (an older credits answer, or it failed): say whose.
|
|
12904
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
12905
|
+
"div",
|
|
12906
|
+
{
|
|
12907
|
+
role: "status",
|
|
12908
|
+
className: "flex flex-col shrink-0 w-full",
|
|
12909
|
+
style: {
|
|
12910
|
+
gap: 4,
|
|
12911
|
+
padding: 16,
|
|
12912
|
+
backgroundColor: "var(--alias-surface-base)",
|
|
12913
|
+
border: "1px solid var(--alias-border-subtle)",
|
|
12914
|
+
borderRadius: "var(--radius-3xl)"
|
|
12915
|
+
},
|
|
12916
|
+
children: [
|
|
12917
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
12918
|
+
"span",
|
|
12919
|
+
{
|
|
12920
|
+
style: {
|
|
12921
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
12922
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
12923
|
+
fontWeight: 500,
|
|
12924
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
12925
|
+
color: "var(--alias-text---icons-primary)"
|
|
12926
|
+
},
|
|
12927
|
+
children: sponsoredCreditsText(entitlement)
|
|
12928
|
+
}
|
|
12929
|
+
),
|
|
12930
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
12931
|
+
"span",
|
|
12932
|
+
{
|
|
12933
|
+
style: {
|
|
12934
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
12935
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
12936
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
12937
|
+
color: "var(--alias-text---icons-muted)"
|
|
12938
|
+
},
|
|
12939
|
+
children: ownCreditsFirstText()
|
|
12940
|
+
}
|
|
12941
|
+
)
|
|
12942
|
+
]
|
|
12943
|
+
}
|
|
12944
|
+
)
|
|
12361
12945
|
) : null,
|
|
12362
12946
|
usage ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
12363
12947
|
"div",
|
|
@@ -12522,22 +13106,39 @@ function BillingContent({
|
|
|
12522
13106
|
loading: loadingTable,
|
|
12523
13107
|
workspaceCountry: workspace.current?.country
|
|
12524
13108
|
}
|
|
12525
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime29.
|
|
12526
|
-
|
|
12527
|
-
|
|
12528
|
-
|
|
12529
|
-
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
13109
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
13110
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
13111
|
+
UsageLogsTable,
|
|
13112
|
+
{
|
|
13113
|
+
logs: usageLogs.rows,
|
|
13114
|
+
userMap,
|
|
13115
|
+
loading: loadingUsageLogs,
|
|
13116
|
+
error: usageLogsError,
|
|
13117
|
+
onRetry: () => void loadUsageLogsPage(usageLogsState.requestedPage),
|
|
13118
|
+
currentWorkspaceId: workspaceId,
|
|
13119
|
+
usedByNames
|
|
13120
|
+
}
|
|
13121
|
+
),
|
|
13122
|
+
!usageLogsError && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
13123
|
+
UsageLogsPager,
|
|
13124
|
+
{
|
|
13125
|
+
page: usageLogs.page,
|
|
13126
|
+
totalPages: usageLogs.totalPages,
|
|
13127
|
+
total: usageLogs.total,
|
|
13128
|
+
loading: loadingUsageLogs,
|
|
13129
|
+
onPageChange: goToUsageLogsPage
|
|
13130
|
+
}
|
|
13131
|
+
)
|
|
13132
|
+
] }),
|
|
12535
13133
|
showExportModal && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
12536
13134
|
ExportDateModal,
|
|
12537
13135
|
{
|
|
12538
13136
|
title: activeTab === "invoice" ? "Export invoices" : "Export log",
|
|
12539
|
-
onClose:
|
|
12540
|
-
onExport: handleExport
|
|
13137
|
+
onClose: closeExportModal,
|
|
13138
|
+
onExport: handleExport,
|
|
13139
|
+
busy: exportProgress !== null,
|
|
13140
|
+
busyText: exportProgress && exportProgress.total > 0 ? `Fetched ${exportProgress.fetched.toLocaleString("en-US")} of ${exportProgress.total.toLocaleString("en-US")} logs\u2026` : "Fetching logs\u2026",
|
|
13141
|
+
notice: exportNotice
|
|
12541
13142
|
}
|
|
12542
13143
|
),
|
|
12543
13144
|
showBuyCreditsModal && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
@@ -16732,6 +17333,30 @@ function OrganizationPicker({
|
|
|
16732
17333
|
);
|
|
16733
17334
|
}
|
|
16734
17335
|
|
|
17336
|
+
// src/use-billing-entitlement.ts
|
|
17337
|
+
var import_react30 = require("react");
|
|
17338
|
+
function useBillingEntitlement(fetchCurrentPlan, key) {
|
|
17339
|
+
const fetchRef = (0, import_react30.useRef)(fetchCurrentPlan);
|
|
17340
|
+
fetchRef.current = fetchCurrentPlan;
|
|
17341
|
+
const [state, setState] = (0, import_react30.useState)(null);
|
|
17342
|
+
const [loading, setLoading] = (0, import_react30.useState)(false);
|
|
17343
|
+
(0, import_react30.useEffect)(() => {
|
|
17344
|
+
const fetch2 = fetchRef.current;
|
|
17345
|
+
setState(null);
|
|
17346
|
+
if (!key || !fetch2) {
|
|
17347
|
+
setLoading(false);
|
|
17348
|
+
return;
|
|
17349
|
+
}
|
|
17350
|
+
let live = true;
|
|
17351
|
+
setLoading(true);
|
|
17352
|
+
loadCurrentPlan(fetch2).then((next) => live && setState(next)).catch(() => live && setState(null)).finally(() => live && setLoading(false));
|
|
17353
|
+
return () => {
|
|
17354
|
+
live = false;
|
|
17355
|
+
};
|
|
17356
|
+
}, [key]);
|
|
17357
|
+
return { state, loading };
|
|
17358
|
+
}
|
|
17359
|
+
|
|
16735
17360
|
// src/administration-navigation.ts
|
|
16736
17361
|
var normalizedBase = (base) => base.replace(/\/$/, "");
|
|
16737
17362
|
var returnKey = (base, entryKey) => `streamoid:settings:return:${base}:${entryKey}`;
|
|
@@ -16778,7 +17403,7 @@ function readSettingsCaller(base, state, entryKey, fallback = `${normalizedBase(
|
|
|
16778
17403
|
}
|
|
16779
17404
|
|
|
16780
17405
|
// src/app-settings-page.tsx
|
|
16781
|
-
var
|
|
17406
|
+
var import_react31 = require("react");
|
|
16782
17407
|
var import_icons20 = require("@streamoid/icons");
|
|
16783
17408
|
|
|
16784
17409
|
// src/app-settings-page.module.css
|
|
@@ -16841,7 +17466,7 @@ function AppSettingsSection({ title, action, children }) {
|
|
|
16841
17466
|
] });
|
|
16842
17467
|
}
|
|
16843
17468
|
function AppSettingsRow({ title, description, summary, href, onClick, disabled, disabledReason, ariaLabel, trailing }) {
|
|
16844
|
-
const reasonId = (0,
|
|
17469
|
+
const reasonId = (0, import_react31.useId)();
|
|
16845
17470
|
const content = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
16846
17471
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: app_settings_page_default.rowCopy, children: [
|
|
16847
17472
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: app_settings_page_default.rowTitle, children: title }),
|