@streamoid/settings 0.12.4 → 0.14.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 +164 -8
- package/dist/index.d.ts +164 -8
- package/dist/index.js +392 -69
- package/dist/index.mjs +390 -67
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9601,6 +9601,76 @@ function describeAllowance(member) {
|
|
|
9601
9601
|
const allowance = `${credits(member.allowance)} this cycle`;
|
|
9602
9602
|
return member.mode === "percent" ? `${formatPoolSplit(member)} \xB7 ${allowance}` : allowance;
|
|
9603
9603
|
}
|
|
9604
|
+
var POOL_EXPIRY_MAX_MONTHS = 120;
|
|
9605
|
+
function addCalendarMonths(date, months) {
|
|
9606
|
+
const target = new Date(date.getFullYear(), date.getMonth() + months, 1);
|
|
9607
|
+
const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
|
|
9608
|
+
return new Date(
|
|
9609
|
+
target.getFullYear(),
|
|
9610
|
+
target.getMonth(),
|
|
9611
|
+
Math.min(date.getDate(), lastDay),
|
|
9612
|
+
date.getHours(),
|
|
9613
|
+
date.getMinutes(),
|
|
9614
|
+
date.getSeconds(),
|
|
9615
|
+
date.getMilliseconds()
|
|
9616
|
+
);
|
|
9617
|
+
}
|
|
9618
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
9619
|
+
function toLocalDateInput(date) {
|
|
9620
|
+
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
|
|
9621
|
+
}
|
|
9622
|
+
function minPoolEndDate(now = /* @__PURE__ */ new Date()) {
|
|
9623
|
+
return toLocalDateInput(new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1));
|
|
9624
|
+
}
|
|
9625
|
+
function parseLocalDate(value) {
|
|
9626
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
|
|
9627
|
+
if (!match) return null;
|
|
9628
|
+
const [year, month, day] = [Number(match[1]), Number(match[2]) - 1, Number(match[3])];
|
|
9629
|
+
const date = new Date(year, month, day);
|
|
9630
|
+
return date.getFullYear() === year && date.getMonth() === month && date.getDate() === day ? date : null;
|
|
9631
|
+
}
|
|
9632
|
+
function poolEndsAtFrom(choice, now = /* @__PURE__ */ new Date()) {
|
|
9633
|
+
switch (choice.kind) {
|
|
9634
|
+
case "never":
|
|
9635
|
+
return { ok: true, value: null };
|
|
9636
|
+
case "month":
|
|
9637
|
+
return { ok: true, value: addCalendarMonths(now, 1).toISOString() };
|
|
9638
|
+
case "year":
|
|
9639
|
+
return { ok: true, value: addCalendarMonths(now, 12).toISOString() };
|
|
9640
|
+
case "months": {
|
|
9641
|
+
const { months } = choice;
|
|
9642
|
+
if (!Number.isInteger(months) || months < 1 || months > POOL_EXPIRY_MAX_MONTHS) {
|
|
9643
|
+
return { ok: false, reason: `Enter a whole number of months from 1 to ${POOL_EXPIRY_MAX_MONTHS}` };
|
|
9644
|
+
}
|
|
9645
|
+
return { ok: true, value: addCalendarMonths(now, months).toISOString() };
|
|
9646
|
+
}
|
|
9647
|
+
case "date": {
|
|
9648
|
+
if (!choice.date) return { ok: false, reason: "Choose a date" };
|
|
9649
|
+
const date = parseLocalDate(choice.date);
|
|
9650
|
+
if (!date) return { ok: false, reason: "Choose a valid date" };
|
|
9651
|
+
if (choice.date < minPoolEndDate(now)) return { ok: false, reason: "Choose a date after today" };
|
|
9652
|
+
return { ok: true, value: date.toISOString() };
|
|
9653
|
+
}
|
|
9654
|
+
}
|
|
9655
|
+
}
|
|
9656
|
+
function expiryChoiceFor(member) {
|
|
9657
|
+
const endsAt = member?.endsAt ? new Date(member.endsAt) : null;
|
|
9658
|
+
if (!endsAt || !Number.isFinite(endsAt.getTime())) return { kind: "never" };
|
|
9659
|
+
return { kind: "date", date: toLocalDateInput(endsAt) };
|
|
9660
|
+
}
|
|
9661
|
+
function formatPoolEndsAt(endsAt) {
|
|
9662
|
+
const date = endsAt ? new Date(endsAt) : null;
|
|
9663
|
+
if (!date || !Number.isFinite(date.getTime())) return "Never";
|
|
9664
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
9665
|
+
}
|
|
9666
|
+
function sharedPoolEndsText(pool) {
|
|
9667
|
+
return pool.endsAt ? `Sharing ends on ${formatPoolEndsAt(pool.endsAt)}.` : "Sharing ends: Never.";
|
|
9668
|
+
}
|
|
9669
|
+
function poolRenewsAtCycleEnd(pool) {
|
|
9670
|
+
if (!pool.cycleEnd) return false;
|
|
9671
|
+
if (!pool.endsAt) return true;
|
|
9672
|
+
return new Date(pool.endsAt).getTime() > new Date(pool.cycleEnd).getTime();
|
|
9673
|
+
}
|
|
9604
9674
|
function poolWorkspaceName(name) {
|
|
9605
9675
|
return name?.trim() || "Unknown workspace";
|
|
9606
9676
|
}
|
|
@@ -9608,12 +9678,8 @@ function sharedPoolBannerText(pool) {
|
|
|
9608
9678
|
const sponsor = pool.sponsorName?.trim() || "another workspace";
|
|
9609
9679
|
return `Credits shared by ${sponsor}: ${formatCredits(pool.remaining)} of ${formatCredits(pool.allowance)} left this cycle`;
|
|
9610
9680
|
}
|
|
9611
|
-
function pausedOwnCreditsText(
|
|
9612
|
-
|
|
9613
|
-
if (typeof own !== "number" || !Number.isFinite(own)) {
|
|
9614
|
-
return "This workspace spends these shared credits, not its own; credits bought here can't be used while it does.";
|
|
9615
|
-
}
|
|
9616
|
-
return `This workspace's own ${credits(own)} ${own === 1 ? "is" : "are"} paused while it uses shared credits. They stay with it and can be used again once sharing stops.`;
|
|
9681
|
+
function pausedOwnCreditsText(_pool) {
|
|
9682
|
+
return ownCreditsFirstText();
|
|
9617
9683
|
}
|
|
9618
9684
|
function ownCreditsFirstText() {
|
|
9619
9685
|
return "This workspace uses its own credits first, then these shared credits.";
|
|
@@ -9808,25 +9874,36 @@ function PoolModalShell({
|
|
|
9808
9874
|
}
|
|
9809
9875
|
);
|
|
9810
9876
|
}
|
|
9811
|
-
function PoolWarning({ children }) {
|
|
9812
|
-
return /* @__PURE__ */ jsx26(
|
|
9813
|
-
"div",
|
|
9814
|
-
{
|
|
9815
|
-
role: "alert",
|
|
9816
|
-
className: "w-full",
|
|
9817
|
-
style: {
|
|
9818
|
-
...modalBodyText,
|
|
9819
|
-
color: T16.primary,
|
|
9820
|
-
background: T16.warningFill,
|
|
9821
|
-
border: `1px solid ${T16.warningBorder}`,
|
|
9822
|
-
borderRadius: T16.r3xl,
|
|
9823
|
-
padding: `${T16.spXl} ${T16.sp3xl}`
|
|
9824
|
-
},
|
|
9825
|
-
children
|
|
9826
|
-
}
|
|
9827
|
-
);
|
|
9828
|
-
}
|
|
9829
9877
|
var fieldLabel = { ...modalBodyText, fontWeight: 500, color: T16.primary };
|
|
9878
|
+
var fieldBox = {
|
|
9879
|
+
background: T16.fillPlus,
|
|
9880
|
+
height: 48,
|
|
9881
|
+
borderRadius: T16.r3xl,
|
|
9882
|
+
padding: `${T16.spXl} ${T16.sp3xl}`,
|
|
9883
|
+
gap: T16.spXl
|
|
9884
|
+
};
|
|
9885
|
+
var bareInput = {
|
|
9886
|
+
...modalBodyText,
|
|
9887
|
+
background: "transparent",
|
|
9888
|
+
border: "none",
|
|
9889
|
+
outline: "none",
|
|
9890
|
+
color: T16.primary,
|
|
9891
|
+
padding: 0
|
|
9892
|
+
};
|
|
9893
|
+
var EXPIRY_OPTIONS = [
|
|
9894
|
+
{ id: "never", label: "Never" },
|
|
9895
|
+
{ id: "month", label: "In 1 month" },
|
|
9896
|
+
{ id: "months", label: "In a number of months" },
|
|
9897
|
+
{ id: "year", label: "In 1 year" },
|
|
9898
|
+
{ id: "date", label: "On a date" }
|
|
9899
|
+
];
|
|
9900
|
+
var isExpiryKind = (id) => EXPIRY_OPTIONS.some((o) => o.id === id);
|
|
9901
|
+
var endsText = (member) => member.endsAt ? `, until ${formatPoolEndsAt(member.endsAt)}` : "";
|
|
9902
|
+
function endsAtHelp(kind, endsAt) {
|
|
9903
|
+
if (kind === "never") return "Shares every billing cycle until it is removed.";
|
|
9904
|
+
const stop = "After that, the other workspace spends only its own credits.";
|
|
9905
|
+
return endsAt ? `Sharing stops on ${formatPoolEndsAt(endsAt)}. ${stop}` : stop;
|
|
9906
|
+
}
|
|
9830
9907
|
function CreditPoolMemberModal(props) {
|
|
9831
9908
|
const { toast } = useSettingsAdapters();
|
|
9832
9909
|
const editing = props.kind === "edit" ? props.member : null;
|
|
@@ -9837,17 +9914,28 @@ function CreditPoolMemberModal(props) {
|
|
|
9837
9914
|
const [touched, setTouched] = useState18(false);
|
|
9838
9915
|
const [saving, setSaving] = useState18(false);
|
|
9839
9916
|
const [serverError, setServerError] = useState18(null);
|
|
9917
|
+
const initialExpiry = expiryChoiceFor(editing);
|
|
9918
|
+
const [expiryKind, setExpiryKind] = useState18(initialExpiry.kind);
|
|
9919
|
+
const [monthsRaw, setMonthsRaw] = useState18("3");
|
|
9920
|
+
const [endDate, setEndDate] = useState18(initialExpiry.kind === "date" ? initialExpiry.date : "");
|
|
9840
9921
|
useEffect16(() => {
|
|
9841
9922
|
setServerError(null);
|
|
9842
|
-
}, [memberId, mode, raw, note]);
|
|
9923
|
+
}, [memberId, mode, raw, note, expiryKind, monthsRaw, endDate]);
|
|
9843
9924
|
const value = validatePoolValue(mode, raw);
|
|
9844
9925
|
const noteCheck = validatePoolText(note, POOL_NOTE_MAX_LENGTH);
|
|
9845
|
-
const
|
|
9846
|
-
const
|
|
9926
|
+
const expiry = expiryKind === "months" ? { kind: "months", months: monthsRaw.trim() ? Number(monthsRaw) : NaN } : expiryKind === "date" ? { kind: "date", date: endDate } : { kind: expiryKind };
|
|
9927
|
+
const endsAtUnchanged = Boolean(editing) && expiry.kind === initialExpiry.kind && (expiry.kind !== "date" || initialExpiry.kind === "date" && expiry.date === initialExpiry.date);
|
|
9928
|
+
const endsAt = endsAtUnchanged ? null : poolEndsAtFrom(expiry);
|
|
9929
|
+
const splitChanged = !editing || mode !== editing.mode || !value.ok || value.value !== editing.value;
|
|
9930
|
+
const changed = splitChanged || !endsAtUnchanged;
|
|
9931
|
+
const valid = value.ok && (endsAt === null || endsAt.ok) && (props.kind === "edit" || Boolean(memberId) && noteCheck.ok);
|
|
9847
9932
|
const canSave = !saving && changed && valid;
|
|
9848
9933
|
const valueHint = touched && !value.ok ? value.reason : null;
|
|
9934
|
+
const expiryHint = endsAt && !endsAt.ok && !(expiry.kind === "date" && !expiry.date) ? endsAt.reason : null;
|
|
9849
9935
|
const handleSave = async () => {
|
|
9850
9936
|
if (!canSave || !value.ok) return;
|
|
9937
|
+
const sendEndsAt = endsAtUnchanged ? null : poolEndsAtFrom(expiry);
|
|
9938
|
+
if (sendEndsAt && !sendEndsAt.ok) return;
|
|
9851
9939
|
setSaving(true);
|
|
9852
9940
|
try {
|
|
9853
9941
|
if (props.kind === "add") {
|
|
@@ -9855,12 +9943,17 @@ function CreditPoolMemberModal(props) {
|
|
|
9855
9943
|
memberWorkspaceId: memberId,
|
|
9856
9944
|
mode,
|
|
9857
9945
|
value: value.value,
|
|
9858
|
-
note: noteCheck.ok ? noteCheck.value : ""
|
|
9946
|
+
note: noteCheck.ok ? noteCheck.value : "",
|
|
9947
|
+
...sendEndsAt?.value ? { endsAt: sendEndsAt.value } : {}
|
|
9859
9948
|
});
|
|
9860
|
-
toast.success(`Credits shared. ${describeAllowance(added)}.`);
|
|
9949
|
+
toast.success(`Credits shared. ${describeAllowance(added)}${endsText(added)}.`);
|
|
9861
9950
|
} else {
|
|
9862
|
-
const updated = await props.onSave({
|
|
9863
|
-
|
|
9951
|
+
const updated = await props.onSave({
|
|
9952
|
+
mode,
|
|
9953
|
+
value: value.value,
|
|
9954
|
+
...sendEndsAt ? { endsAt: sendEndsAt.value } : {}
|
|
9955
|
+
});
|
|
9956
|
+
toast.success(`Share updated. ${describeAllowance(updated)}${endsText(updated)}.`);
|
|
9864
9957
|
}
|
|
9865
9958
|
props.onSaved();
|
|
9866
9959
|
props.onClose();
|
|
@@ -9945,11 +10038,7 @@ function CreditPoolMemberModal(props) {
|
|
|
9945
10038
|
{
|
|
9946
10039
|
className: "flex items-center w-full",
|
|
9947
10040
|
style: {
|
|
9948
|
-
|
|
9949
|
-
height: 48,
|
|
9950
|
-
borderRadius: T16.r3xl,
|
|
9951
|
-
padding: `${T16.spXl} ${T16.sp3xl}`,
|
|
9952
|
-
gap: T16.spXl,
|
|
10041
|
+
...fieldBox,
|
|
9953
10042
|
border: valueHint ? `1px solid ${T16.error}` : "1px solid transparent"
|
|
9954
10043
|
},
|
|
9955
10044
|
children: [
|
|
@@ -9967,14 +10056,7 @@ function CreditPoolMemberModal(props) {
|
|
|
9967
10056
|
setTouched(true);
|
|
9968
10057
|
},
|
|
9969
10058
|
className: "flex-1 min-w-0",
|
|
9970
|
-
style:
|
|
9971
|
-
...modalBodyText,
|
|
9972
|
-
background: "transparent",
|
|
9973
|
-
border: "none",
|
|
9974
|
-
outline: "none",
|
|
9975
|
-
color: T16.primary,
|
|
9976
|
-
padding: 0
|
|
9977
|
-
}
|
|
10059
|
+
style: bareInput
|
|
9978
10060
|
}
|
|
9979
10061
|
),
|
|
9980
10062
|
/* @__PURE__ */ jsx26("span", { className: "shrink-0", style: { ...modalBodyText, color: T16.muted }, children: mode === "percent" ? "%" : "credits" })
|
|
@@ -9982,10 +10064,76 @@ function CreditPoolMemberModal(props) {
|
|
|
9982
10064
|
}
|
|
9983
10065
|
),
|
|
9984
10066
|
valueHint ? /* @__PURE__ */ jsx26("span", { role: "alert", style: { ...modalBodyText, color: T16.error }, children: valueHint }) : /* @__PURE__ */ jsxs23("span", { style: { ...modalBodyText, color: T16.tertiary }, children: [
|
|
9985
|
-
mode === "percent" ? "A share of this workspace's plan credits for each billing cycle. Credits stay with this workspace; the other workspace spends them up to this share." : "Credits the other workspace may spend each billing cycle. Credits stay with this workspace until spent.",
|
|
10067
|
+
mode === "percent" ? "A share of this workspace's plan credits for each billing cycle. Credits stay with this workspace; the other workspace spends them up to this share, after its own credits." : "Credits the other workspace may spend each billing cycle, after its own credits. Credits stay with this workspace until spent.",
|
|
9986
10068
|
editing ? " Applies immediately." : ""
|
|
9987
10069
|
] })
|
|
9988
10070
|
] }),
|
|
10071
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex flex-col w-full", style: { gap: T16.spMd }, children: [
|
|
10072
|
+
/* @__PURE__ */ jsx26("span", { style: fieldLabel, children: "Ends" }),
|
|
10073
|
+
/* @__PURE__ */ jsx26(
|
|
10074
|
+
ScSelect,
|
|
10075
|
+
{
|
|
10076
|
+
value: expiryKind,
|
|
10077
|
+
onChange: (id) => {
|
|
10078
|
+
if (isExpiryKind(id)) setExpiryKind(id);
|
|
10079
|
+
},
|
|
10080
|
+
options: EXPIRY_OPTIONS,
|
|
10081
|
+
disabled: saving
|
|
10082
|
+
}
|
|
10083
|
+
),
|
|
10084
|
+
expiryKind === "months" && /* @__PURE__ */ jsxs23(
|
|
10085
|
+
"div",
|
|
10086
|
+
{
|
|
10087
|
+
className: "flex items-center w-full",
|
|
10088
|
+
style: {
|
|
10089
|
+
...fieldBox,
|
|
10090
|
+
border: expiryHint ? `1px solid ${T16.error}` : "1px solid transparent"
|
|
10091
|
+
},
|
|
10092
|
+
children: [
|
|
10093
|
+
/* @__PURE__ */ jsx26(
|
|
10094
|
+
"input",
|
|
10095
|
+
{
|
|
10096
|
+
inputMode: "numeric",
|
|
10097
|
+
"aria-label": "Number of months",
|
|
10098
|
+
"aria-invalid": expiryHint ? true : void 0,
|
|
10099
|
+
placeholder: "Enter a number of months",
|
|
10100
|
+
value: monthsRaw,
|
|
10101
|
+
disabled: saving,
|
|
10102
|
+
onChange: (e) => setMonthsRaw(e.target.value.replace(/\D/g, "").slice(0, 3)),
|
|
10103
|
+
className: "flex-1 min-w-0",
|
|
10104
|
+
style: bareInput
|
|
10105
|
+
}
|
|
10106
|
+
),
|
|
10107
|
+
/* @__PURE__ */ jsx26("span", { className: "shrink-0", style: { ...modalBodyText, color: T16.muted }, children: "months" })
|
|
10108
|
+
]
|
|
10109
|
+
}
|
|
10110
|
+
),
|
|
10111
|
+
expiryKind === "date" && /* @__PURE__ */ jsx26(
|
|
10112
|
+
"div",
|
|
10113
|
+
{
|
|
10114
|
+
className: "flex items-center w-full",
|
|
10115
|
+
style: {
|
|
10116
|
+
...fieldBox,
|
|
10117
|
+
border: expiryHint ? `1px solid ${T16.error}` : "1px solid transparent"
|
|
10118
|
+
},
|
|
10119
|
+
children: /* @__PURE__ */ jsx26(
|
|
10120
|
+
"input",
|
|
10121
|
+
{
|
|
10122
|
+
type: "date",
|
|
10123
|
+
"aria-label": "End date",
|
|
10124
|
+
"aria-invalid": expiryHint ? true : void 0,
|
|
10125
|
+
min: minPoolEndDate(),
|
|
10126
|
+
value: endDate,
|
|
10127
|
+
disabled: saving,
|
|
10128
|
+
onChange: (e) => setEndDate(e.target.value),
|
|
10129
|
+
className: "flex-1 min-w-0",
|
|
10130
|
+
style: { ...bareInput, colorScheme: "light dark" }
|
|
10131
|
+
}
|
|
10132
|
+
)
|
|
10133
|
+
}
|
|
10134
|
+
),
|
|
10135
|
+
expiryHint ? /* @__PURE__ */ jsx26("span", { role: "alert", style: { ...modalBodyText, color: T16.error }, children: expiryHint }) : /* @__PURE__ */ jsx26("span", { style: { ...modalBodyText, color: T16.tertiary }, children: endsAtHelp(expiryKind, endsAtUnchanged ? editing?.endsAt ?? null : endsAt?.ok ? endsAt.value : null) })
|
|
10136
|
+
] }),
|
|
9989
10137
|
props.kind === "add" && /* @__PURE__ */ jsxs23("div", { className: "flex flex-col w-full", style: { gap: T16.spXs }, children: [
|
|
9990
10138
|
/* @__PURE__ */ jsx26("span", { style: fieldLabel, children: "Note" }),
|
|
9991
10139
|
/* @__PURE__ */ jsx26(
|
|
@@ -10007,7 +10155,6 @@ function CreditPoolMemberModal(props) {
|
|
|
10007
10155
|
POOL_NOTE_MAX_LENGTH
|
|
10008
10156
|
] })
|
|
10009
10157
|
] }),
|
|
10010
|
-
props.kind === "add" && /* @__PURE__ */ jsx26(PoolWarning, { children: "While it spends shared credits, the other workspace uses only this workspace's credits, not its own." }),
|
|
10011
10158
|
serverError && /* @__PURE__ */ jsx26("span", { role: "alert", style: { ...modalBodyText, color: T16.error }, children: serverError })
|
|
10012
10159
|
]
|
|
10013
10160
|
}
|
|
@@ -10128,7 +10275,9 @@ var T17 = {
|
|
|
10128
10275
|
r3xl: "var(--radius-3xl)",
|
|
10129
10276
|
rFull: "var(--radius-full)"
|
|
10130
10277
|
};
|
|
10131
|
-
var MEMBER_COLUMNS2 = "minmax(
|
|
10278
|
+
var MEMBER_COLUMNS2 = "minmax(180px, 1fr) 150px 80px 90px 110px 150px";
|
|
10279
|
+
var MEMBER_GAP = 16;
|
|
10280
|
+
var MEMBER_MIN_WIDTH = 880;
|
|
10132
10281
|
var cellText2 = {
|
|
10133
10282
|
fontFamily: T17.font,
|
|
10134
10283
|
fontSize: T17.fsSm,
|
|
@@ -10141,7 +10290,7 @@ var tableHeader = (columns, labels) => /* @__PURE__ */ jsx28(
|
|
|
10141
10290
|
style: {
|
|
10142
10291
|
display: "grid",
|
|
10143
10292
|
gridTemplateColumns: columns,
|
|
10144
|
-
gap:
|
|
10293
|
+
gap: MEMBER_GAP,
|
|
10145
10294
|
padding: "8px 16px",
|
|
10146
10295
|
fontSize: 11,
|
|
10147
10296
|
color: T17.muted,
|
|
@@ -10165,17 +10314,21 @@ function MemberRow2({
|
|
|
10165
10314
|
gridTemplateColumns: MEMBER_COLUMNS2,
|
|
10166
10315
|
alignItems: "center",
|
|
10167
10316
|
padding: `10px ${T17.sp3xl}`,
|
|
10168
|
-
gap:
|
|
10317
|
+
gap: MEMBER_GAP
|
|
10169
10318
|
},
|
|
10170
10319
|
children: [
|
|
10171
10320
|
/* @__PURE__ */ jsxs25("div", { className: "flex flex-col min-w-0", style: { gap: 2 }, children: [
|
|
10172
10321
|
/* @__PURE__ */ jsx28("span", { className: "truncate", style: { ...cellText2, fontWeight: 500 }, children: poolWorkspaceName(member.memberWorkspaceName) }),
|
|
10173
10322
|
member.note && /* @__PURE__ */ jsx28("span", { className: "truncate", title: member.note, style: { ...cellText2, color: T17.tertiary }, children: member.note })
|
|
10174
10323
|
] }),
|
|
10175
|
-
/* @__PURE__ */
|
|
10324
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex flex-col min-w-0", style: { gap: 2 }, children: [
|
|
10325
|
+
/* @__PURE__ */ jsx28("span", { style: cellText2, children: formatPoolSplit({ mode: "fixed", value: member.allowance }) }),
|
|
10326
|
+
/* @__PURE__ */ jsx28("span", { style: { ...cellText2, color: T17.tertiary }, children: member.mode === "percent" ? `${formatPoolSplit(member)} \xB7 this cycle` : "this cycle" })
|
|
10327
|
+
] }),
|
|
10176
10328
|
/* @__PURE__ */ jsx28("span", { style: cellText2, children: formatCredits(member.used) }),
|
|
10177
10329
|
/* @__PURE__ */ jsx28("span", { style: { ...cellText2, color: spent ? T17.warning : T17.primary }, children: formatCredits(member.remaining) }),
|
|
10178
|
-
/* @__PURE__ */
|
|
10330
|
+
/* @__PURE__ */ jsx28("span", { style: { ...cellText2, color: member.endsAt ? T17.primary : T17.muted }, children: formatPoolEndsAt(member.endsAt) }),
|
|
10331
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center whitespace-nowrap", style: { gap: 8 }, children: [
|
|
10179
10332
|
onEdit && /* @__PURE__ */ jsx28(ScButton13, { text: "Edit", variant: "secondary", size: "sm", onClick: onEdit }),
|
|
10180
10333
|
onRemove && /* @__PURE__ */ jsx28(ScButton13, { text: "Remove", variant: "secondary", size: "sm", onClick: onRemove })
|
|
10181
10334
|
] })
|
|
@@ -10301,11 +10454,13 @@ function SharedCreditsView() {
|
|
|
10301
10454
|
remaining: sponsor.remaining
|
|
10302
10455
|
}) }),
|
|
10303
10456
|
/* @__PURE__ */ jsxs25("span", { style: { ...cellText2, color: T17.muted }, children: [
|
|
10304
|
-
"This workspace
|
|
10457
|
+
"This workspace uses its own credits first, then",
|
|
10458
|
+
" ",
|
|
10305
10459
|
poolWorkspaceName(sponsor.sponsorWorkspaceName),
|
|
10306
|
-
"'s
|
|
10307
|
-
sponsor.cycle?.end ? `,
|
|
10308
|
-
"."
|
|
10460
|
+
"'s, up to its allowance",
|
|
10461
|
+
sponsor.cycle?.end && poolRenewsAtCycleEnd({ cycleEnd: sponsor.cycle.end, endsAt: sponsor.endsAt }) ? `, which starts again on ${formatDate2(sponsor.cycle.end)}` : "",
|
|
10462
|
+
". ",
|
|
10463
|
+
sharedPoolEndsText(sponsor)
|
|
10309
10464
|
] })
|
|
10310
10465
|
]
|
|
10311
10466
|
}
|
|
@@ -10317,8 +10472,8 @@ function SharedCreditsView() {
|
|
|
10317
10472
|
{
|
|
10318
10473
|
grow: "cap",
|
|
10319
10474
|
maxHeight: 420,
|
|
10320
|
-
contentMinWidth:
|
|
10321
|
-
header: tableHeader(MEMBER_COLUMNS2, ["Workspace", "Allowance", "Used", "Remaining", "Actions"]),
|
|
10475
|
+
contentMinWidth: MEMBER_MIN_WIDTH,
|
|
10476
|
+
header: tableHeader(MEMBER_COLUMNS2, ["Workspace", "Allowance", "Used", "Remaining", "Ends", "Actions"]),
|
|
10322
10477
|
isEmpty: failed || members.length === 0,
|
|
10323
10478
|
emptyText: failed ? "Couldn't load shared credits. Try again later." : "This workspace doesn't share its credits with any other workspace.",
|
|
10324
10479
|
children: members.map((member) => /* @__PURE__ */ jsx28(
|
|
@@ -10368,6 +10523,71 @@ function SharedCreditsView() {
|
|
|
10368
10523
|
] });
|
|
10369
10524
|
}
|
|
10370
10525
|
|
|
10526
|
+
// src/entitlement.ts
|
|
10527
|
+
function parseBillingEntitlement(value) {
|
|
10528
|
+
if (!value || typeof value !== "object") return null;
|
|
10529
|
+
const raw = value;
|
|
10530
|
+
if (typeof raw.paid !== "boolean") return null;
|
|
10531
|
+
const source = raw.source === "plan" || raw.source === "pool" ? raw.source : null;
|
|
10532
|
+
const entitlement = { paid: raw.paid, source };
|
|
10533
|
+
if (typeof raw.sponsorWorkspaceId === "string") entitlement.sponsorWorkspaceId = raw.sponsorWorkspaceId;
|
|
10534
|
+
if (typeof raw.sponsorName === "string") entitlement.sponsorName = raw.sponsorName;
|
|
10535
|
+
if (typeof raw.endsAt === "string" || raw.endsAt === null) entitlement.endsAt = raw.endsAt;
|
|
10536
|
+
return entitlement;
|
|
10537
|
+
}
|
|
10538
|
+
function entitlementFromBody(body) {
|
|
10539
|
+
if (!body || typeof body !== "object") return null;
|
|
10540
|
+
return parseBillingEntitlement(body.entitlement);
|
|
10541
|
+
}
|
|
10542
|
+
function entitlementFromFailure(failure) {
|
|
10543
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10544
|
+
const visit = (value, depth) => {
|
|
10545
|
+
if (depth > 4 || value == null || typeof value !== "object" || seen.has(value)) return null;
|
|
10546
|
+
seen.add(value);
|
|
10547
|
+
const found = entitlementFromBody(value);
|
|
10548
|
+
if (found) return found;
|
|
10549
|
+
const error = value;
|
|
10550
|
+
const response = error.response;
|
|
10551
|
+
for (const child of [error.body, error.data, response?.data, error.cause]) {
|
|
10552
|
+
const nested = visit(child, depth + 1);
|
|
10553
|
+
if (nested) return nested;
|
|
10554
|
+
}
|
|
10555
|
+
return null;
|
|
10556
|
+
};
|
|
10557
|
+
return visit(failure, 0);
|
|
10558
|
+
}
|
|
10559
|
+
function isPaidPlan(subscription) {
|
|
10560
|
+
const amount = subscription?.plan?.amount;
|
|
10561
|
+
return amount != null && amount > 0;
|
|
10562
|
+
}
|
|
10563
|
+
function hasPaidFeatures({
|
|
10564
|
+
entitlement,
|
|
10565
|
+
subscription
|
|
10566
|
+
}) {
|
|
10567
|
+
return entitlement ? entitlement.paid : isPaidPlan(subscription);
|
|
10568
|
+
}
|
|
10569
|
+
function isSponsoredEntitlement(entitlement) {
|
|
10570
|
+
return entitlement?.source === "pool" && entitlement.paid;
|
|
10571
|
+
}
|
|
10572
|
+
function sponsoredCreditsText(entitlement) {
|
|
10573
|
+
const sponsor = entitlement.sponsorName?.trim() || "another workspace";
|
|
10574
|
+
const ends = entitlement.endsAt ? ` Sharing ends on ${formatPoolEndsAt(entitlement.endsAt)}.` : "";
|
|
10575
|
+
return `Credits shared by ${sponsor}.${ends}`;
|
|
10576
|
+
}
|
|
10577
|
+
async function loadCurrentPlan(fetchCurrentPlan) {
|
|
10578
|
+
let response;
|
|
10579
|
+
try {
|
|
10580
|
+
response = await fetchCurrentPlan();
|
|
10581
|
+
} catch (failure) {
|
|
10582
|
+
const entitlement2 = entitlementFromFailure(failure);
|
|
10583
|
+
if (!entitlement2) throw failure;
|
|
10584
|
+
return { subscription: null, entitlement: entitlement2, paid: entitlement2.paid };
|
|
10585
|
+
}
|
|
10586
|
+
const subscription = response?.data ?? null;
|
|
10587
|
+
const entitlement = entitlementFromBody(response);
|
|
10588
|
+
return { subscription, entitlement, paid: hasPaidFeatures({ entitlement, subscription }) };
|
|
10589
|
+
}
|
|
10590
|
+
|
|
10371
10591
|
// src/billing-content.tsx
|
|
10372
10592
|
import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
10373
10593
|
var DEFAULT_TOPUP_SERVICE = "catalogix";
|
|
@@ -11499,7 +11719,8 @@ function BillingContent({
|
|
|
11499
11719
|
showPerUserLimits: showPerUserLimitsProp,
|
|
11500
11720
|
onShowPerUserLimitsChange,
|
|
11501
11721
|
showSharedCredits: showSharedCreditsProp,
|
|
11502
|
-
onShowSharedCreditsChange
|
|
11722
|
+
onShowSharedCreditsChange,
|
|
11723
|
+
onEntitlementChange
|
|
11503
11724
|
} = {}) {
|
|
11504
11725
|
const { auth, workspace, organization, api, toast, navigation } = useSettingsAdapters();
|
|
11505
11726
|
const user = auth.user;
|
|
@@ -11538,6 +11759,9 @@ function BillingContent({
|
|
|
11538
11759
|
const [showExportModal, setShowExportModal] = useState21(false);
|
|
11539
11760
|
const [showBuyCreditsModal, setShowBuyCreditsModal] = useState21(false);
|
|
11540
11761
|
const [subscription, setSubscription] = useState21(null);
|
|
11762
|
+
const [entitlement, setEntitlement] = useState21(
|
|
11763
|
+
null
|
|
11764
|
+
);
|
|
11541
11765
|
const [availableCredits, setAvailableCredits] = useState21(null);
|
|
11542
11766
|
const [sharedPool, setSharedPool] = useState21(null);
|
|
11543
11767
|
const [usedByNames, setUsedByNames] = useState21({});
|
|
@@ -11583,12 +11807,15 @@ function BillingContent({
|
|
|
11583
11807
|
try {
|
|
11584
11808
|
console.log("calling Billing apis");
|
|
11585
11809
|
const [subRes, creditsRes, productsRes, customRes] = await Promise.all([
|
|
11586
|
-
|
|
11810
|
+
// A sponsored workspace with no subscription of its own gets a 404
|
|
11811
|
+
// that carries its entitlement: loadCurrentPlan answers it, not throws.
|
|
11812
|
+
loadCurrentPlan(api.fetchCurrentPlan).catch(() => null),
|
|
11587
11813
|
api.fetchAvailableCredits().catch(() => null),
|
|
11588
11814
|
api.fetchPlans().catch(() => null),
|
|
11589
11815
|
api.getCustomCreditInfo?.().catch(() => null) ?? Promise.resolve(null)
|
|
11590
11816
|
]);
|
|
11591
|
-
if (subRes?.
|
|
11817
|
+
if (subRes?.subscription) setSubscription(subRes.subscription);
|
|
11818
|
+
if (subRes) setEntitlement(subRes.entitlement);
|
|
11592
11819
|
if (creditsRes?.availableCredits != null)
|
|
11593
11820
|
setAvailableCredits(creditsRes.availableCredits);
|
|
11594
11821
|
setSharedPool(creditsRes?.sharedPool ?? null);
|
|
@@ -11758,6 +11985,7 @@ function BillingContent({
|
|
|
11758
11985
|
setInitialFetchTriggered(false);
|
|
11759
11986
|
setBillingOverviewLoaded(false);
|
|
11760
11987
|
setSubscription(null);
|
|
11988
|
+
setEntitlement(null);
|
|
11761
11989
|
setAvailableCredits(null);
|
|
11762
11990
|
setProducts([]);
|
|
11763
11991
|
setTotalCredits(null);
|
|
@@ -11813,7 +12041,16 @@ function BillingContent({
|
|
|
11813
12041
|
subscription?.scheduledDowngradeEffectiveDate
|
|
11814
12042
|
);
|
|
11815
12043
|
const scheduledDowngradeText = subscription?.downgradeScheduled && subscription.plan?.name ? `Downgrades to ${subscription.plan.name}${scheduledDowngradeDateLabel ? ` ${scheduledDowngradeDateLabel}` : ""}` : void 0;
|
|
11816
|
-
const
|
|
12044
|
+
const sponsored = isSponsoredEntitlement(entitlement);
|
|
12045
|
+
const sponsoredWithoutPlan = sponsored && billingOverviewLoaded && !loadingBilling && subscription == null;
|
|
12046
|
+
const isPlanDetailsLoading = sponsoredWithoutPlan ? false : !billingOverviewLoaded || loadingBilling || subscription?.plan == null || !planName || planName === "-" || !planAmount || currentPlanCredits == null;
|
|
12047
|
+
const paidFeatures = hasPaidFeatures({ entitlement, subscription });
|
|
12048
|
+
const onEntitlementChangeRef = useRef10(onEntitlementChange);
|
|
12049
|
+
onEntitlementChangeRef.current = onEntitlementChange;
|
|
12050
|
+
useEffect18(() => {
|
|
12051
|
+
if (!billingOverviewLoaded) return;
|
|
12052
|
+
onEntitlementChangeRef.current?.({ entitlement, paid: paidFeatures });
|
|
12053
|
+
}, [billingOverviewLoaded, entitlement, paidFeatures]);
|
|
11817
12054
|
const {
|
|
11818
12055
|
used: usedCredits,
|
|
11819
12056
|
remaining: remainingCredits,
|
|
@@ -12050,9 +12287,9 @@ function BillingContent({
|
|
|
12050
12287
|
/* @__PURE__ */ jsx29(
|
|
12051
12288
|
ScPlanDetailsCard,
|
|
12052
12289
|
{
|
|
12053
|
-
planName,
|
|
12054
|
-
planCredits: planCreditsText,
|
|
12055
|
-
planPrice: planAmount,
|
|
12290
|
+
planName: sponsoredWithoutPlan ? "Free" : planName,
|
|
12291
|
+
planCredits: sponsoredWithoutPlan ? "Shared credits" : planCreditsText,
|
|
12292
|
+
planPrice: sponsoredWithoutPlan ? `${workspace.current?.country?.trim().toUpperCase() === "IN" ? "\u20B9" : "$"}0.00 / month` : planAmount,
|
|
12056
12293
|
badgeText: "Active",
|
|
12057
12294
|
badgeSubText: scheduledDowngradeText,
|
|
12058
12295
|
currentPlanLabel: "Current plan",
|
|
@@ -12134,12 +12371,57 @@ function BillingContent({
|
|
|
12134
12371
|
},
|
|
12135
12372
|
children: [
|
|
12136
12373
|
ownCreditsFirstText(),
|
|
12137
|
-
sharedPool
|
|
12374
|
+
poolRenewsAtCycleEnd(sharedPool) ? ` The allowance starts again on ${formatDate3(sharedPool.cycleEnd)}.` : "",
|
|
12375
|
+
` ${sharedPoolEndsText(sharedPool)}`
|
|
12138
12376
|
]
|
|
12139
12377
|
}
|
|
12140
12378
|
)
|
|
12141
12379
|
]
|
|
12142
12380
|
}
|
|
12381
|
+
) : sponsored && entitlement ? (
|
|
12382
|
+
// Billing says a pool entitles it, but the balance carried no
|
|
12383
|
+
// allowance (an older credits answer, or it failed): say whose.
|
|
12384
|
+
/* @__PURE__ */ jsxs26(
|
|
12385
|
+
"div",
|
|
12386
|
+
{
|
|
12387
|
+
role: "status",
|
|
12388
|
+
className: "flex flex-col shrink-0 w-full",
|
|
12389
|
+
style: {
|
|
12390
|
+
gap: 4,
|
|
12391
|
+
padding: 16,
|
|
12392
|
+
backgroundColor: "var(--alias-surface-base)",
|
|
12393
|
+
border: "1px solid var(--alias-border-subtle)",
|
|
12394
|
+
borderRadius: "var(--radius-3xl)"
|
|
12395
|
+
},
|
|
12396
|
+
children: [
|
|
12397
|
+
/* @__PURE__ */ jsx29(
|
|
12398
|
+
"span",
|
|
12399
|
+
{
|
|
12400
|
+
style: {
|
|
12401
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
12402
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
12403
|
+
fontWeight: 500,
|
|
12404
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
12405
|
+
color: "var(--alias-text---icons-primary)"
|
|
12406
|
+
},
|
|
12407
|
+
children: sponsoredCreditsText(entitlement)
|
|
12408
|
+
}
|
|
12409
|
+
),
|
|
12410
|
+
/* @__PURE__ */ jsx29(
|
|
12411
|
+
"span",
|
|
12412
|
+
{
|
|
12413
|
+
style: {
|
|
12414
|
+
fontFamily: "var(--font-family-font-family-body)",
|
|
12415
|
+
fontSize: "var(--font-size-font-size-sm)",
|
|
12416
|
+
lineHeight: "var(--line-height-line-height-sm)",
|
|
12417
|
+
color: "var(--alias-text---icons-muted)"
|
|
12418
|
+
},
|
|
12419
|
+
children: ownCreditsFirstText()
|
|
12420
|
+
}
|
|
12421
|
+
)
|
|
12422
|
+
]
|
|
12423
|
+
}
|
|
12424
|
+
)
|
|
12143
12425
|
) : null,
|
|
12144
12426
|
usage ? /* @__PURE__ */ jsxs26(
|
|
12145
12427
|
"div",
|
|
@@ -12388,7 +12670,7 @@ import {
|
|
|
12388
12670
|
SiconClose as SiconClose10
|
|
12389
12671
|
} from "@streamoid/icons";
|
|
12390
12672
|
import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
12391
|
-
var
|
|
12673
|
+
var EXPIRY_OPTIONS2 = [
|
|
12392
12674
|
{ label: "30 days", value: "30d" },
|
|
12393
12675
|
{ label: "60 days", value: "60d" },
|
|
12394
12676
|
{ label: "90 days", value: "90d" },
|
|
@@ -12579,7 +12861,7 @@ function CreateTokenModal({ onClose, onCreated }) {
|
|
|
12579
12861
|
children: "Expiry"
|
|
12580
12862
|
}
|
|
12581
12863
|
) }),
|
|
12582
|
-
/* @__PURE__ */ jsx30("div", { style: { display: "flex", gap: "var(--spacing-md)", flexWrap: "wrap" }, children:
|
|
12864
|
+
/* @__PURE__ */ jsx30("div", { style: { display: "flex", gap: "var(--spacing-md)", flexWrap: "wrap" }, children: EXPIRY_OPTIONS2.map((opt) => /* @__PURE__ */ jsx30(
|
|
12583
12865
|
ScButton15,
|
|
12584
12866
|
{
|
|
12585
12867
|
text: opt.label,
|
|
@@ -16520,6 +16802,30 @@ function OrganizationPicker({
|
|
|
16520
16802
|
);
|
|
16521
16803
|
}
|
|
16522
16804
|
|
|
16805
|
+
// src/use-billing-entitlement.ts
|
|
16806
|
+
import { useEffect as useEffect22, useRef as useRef12, useState as useState28 } from "react";
|
|
16807
|
+
function useBillingEntitlement(fetchCurrentPlan, key) {
|
|
16808
|
+
const fetchRef = useRef12(fetchCurrentPlan);
|
|
16809
|
+
fetchRef.current = fetchCurrentPlan;
|
|
16810
|
+
const [state, setState] = useState28(null);
|
|
16811
|
+
const [loading, setLoading] = useState28(false);
|
|
16812
|
+
useEffect22(() => {
|
|
16813
|
+
const fetch2 = fetchRef.current;
|
|
16814
|
+
setState(null);
|
|
16815
|
+
if (!key || !fetch2) {
|
|
16816
|
+
setLoading(false);
|
|
16817
|
+
return;
|
|
16818
|
+
}
|
|
16819
|
+
let live = true;
|
|
16820
|
+
setLoading(true);
|
|
16821
|
+
loadCurrentPlan(fetch2).then((next) => live && setState(next)).catch(() => live && setState(null)).finally(() => live && setLoading(false));
|
|
16822
|
+
return () => {
|
|
16823
|
+
live = false;
|
|
16824
|
+
};
|
|
16825
|
+
}, [key]);
|
|
16826
|
+
return { state, loading };
|
|
16827
|
+
}
|
|
16828
|
+
|
|
16523
16829
|
// src/administration-navigation.ts
|
|
16524
16830
|
var normalizedBase = (base) => base.replace(/\/$/, "");
|
|
16525
16831
|
var returnKey = (base, entryKey) => `streamoid:settings:return:${base}:${entryKey}`;
|
|
@@ -16677,6 +16983,7 @@ export {
|
|
|
16677
16983
|
OrganizationModal,
|
|
16678
16984
|
OrganizationPicker,
|
|
16679
16985
|
POOL_ERROR_CODES,
|
|
16986
|
+
POOL_EXPIRY_MAX_MONTHS,
|
|
16680
16987
|
POOL_NOTE_MAX_LENGTH,
|
|
16681
16988
|
PerUserLimitsView,
|
|
16682
16989
|
ProfileAvatar,
|
|
@@ -16716,23 +17023,35 @@ export {
|
|
|
16716
17023
|
describeAllowance,
|
|
16717
17024
|
describeDefaultLimit,
|
|
16718
17025
|
describeMemberLimit,
|
|
17026
|
+
entitlementFromBody,
|
|
17027
|
+
entitlementFromFailure,
|
|
17028
|
+
expiryChoiceFor,
|
|
16719
17029
|
findBillingError,
|
|
16720
17030
|
findPoolError,
|
|
16721
17031
|
formatCreditLimit,
|
|
16722
17032
|
formatCredits,
|
|
17033
|
+
formatPoolEndsAt,
|
|
16723
17034
|
formatPoolSplit,
|
|
16724
17035
|
groupWorkspaceList,
|
|
17036
|
+
hasPaidFeatures,
|
|
16725
17037
|
isLimitBelowUsage,
|
|
17038
|
+
isPaidPlan,
|
|
17039
|
+
isSponsoredEntitlement,
|
|
16726
17040
|
joinMembersWithLimits,
|
|
16727
17041
|
lastUsedLabel,
|
|
16728
17042
|
limitBelowUsageUsed,
|
|
16729
17043
|
limitSourceLabel,
|
|
17044
|
+
loadCurrentPlan,
|
|
16730
17045
|
loadWorkspaceOrganization,
|
|
16731
17046
|
memberRoleUpdate,
|
|
17047
|
+
minPoolEndDate,
|
|
16732
17048
|
minimumCreditLimit,
|
|
16733
17049
|
ownCreditsFirstText,
|
|
17050
|
+
parseBillingEntitlement,
|
|
16734
17051
|
pausedOwnCreditsText,
|
|
17052
|
+
poolEndsAtFrom,
|
|
16735
17053
|
poolErrorMessage,
|
|
17054
|
+
poolRenewsAtCycleEnd,
|
|
16736
17055
|
poolWorkspaceName,
|
|
16737
17056
|
readSettingsCaller,
|
|
16738
17057
|
rememberSettingsCaller,
|
|
@@ -16740,6 +17059,10 @@ export {
|
|
|
16740
17059
|
roundCredits,
|
|
16741
17060
|
settingsReturnTarget,
|
|
16742
17061
|
sharedPoolBannerText,
|
|
17062
|
+
sharedPoolEndsText,
|
|
17063
|
+
sponsoredCreditsText,
|
|
17064
|
+
toLocalDateInput,
|
|
17065
|
+
useBillingEntitlement,
|
|
16743
17066
|
useSettingsAdapters,
|
|
16744
17067
|
useWorkspaceInvitePermission,
|
|
16745
17068
|
useWorkspaceOrganization,
|