@streamoid/settings 0.10.1 → 0.11.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.mjs CHANGED
@@ -7987,14 +7987,14 @@ function BuyCreditsModal({
7987
7987
  const maxCredits = Math.round(
7988
7988
  topupConfig.maxAmount / topupConfig.pricePerCredit
7989
7989
  );
7990
- const [credits, setCredits] = useState13(minCredits);
7990
+ const [credits2, setCredits] = useState13(minCredits);
7991
7991
  const [creditsInput, setCreditsInput] = useState13(String(minCredits));
7992
7992
  const [priceInput, setPriceInput] = useState13(
7993
7993
  String(minCredits * topupConfig.pricePerCredit)
7994
7994
  );
7995
7995
  const [activeField, setActiveField] = useState13("credits");
7996
- const parsedCredits = creditsInput.length ? Number(creditsInput) : credits;
7997
- const previewCredits = Number.isFinite(parsedCredits) ? parsedCredits : credits;
7996
+ const parsedCredits = creditsInput.length ? Number(creditsInput) : credits2;
7997
+ const previewCredits = Number.isFinite(parsedCredits) ? parsedCredits : credits2;
7998
7998
  const pricedCredits = previewCredits > 0 ? previewCredits : 0;
7999
7999
  const price = Number(
8000
8000
  (pricedCredits * topupConfig.pricePerCredit).toFixed(2)
@@ -8016,8 +8016,8 @@ function BuyCreditsModal({
8016
8016
  setCreditsInput(String(derived));
8017
8017
  setPriceInput(String(amount));
8018
8018
  };
8019
- const handleDecrement = () => syncFromCredits(Math.max(minCredits, credits - STEP));
8020
- const handleIncrement = () => syncFromCredits(Math.min(maxCredits, credits + STEP));
8019
+ const handleDecrement = () => syncFromCredits(Math.max(minCredits, credits2 - STEP));
8020
+ const handleIncrement = () => syncFromCredits(Math.min(maxCredits, credits2 + STEP));
8021
8021
  const handleCreditsChange = (value) => {
8022
8022
  const raw = value.replace(/[^\d]/g, "");
8023
8023
  setCreditsInput(raw);
@@ -8151,7 +8151,7 @@ function BuyCreditsModal({
8151
8151
  style: {
8152
8152
  width: 24,
8153
8153
  height: 24,
8154
- opacity: credits <= minCredits ? 0.3 : 1,
8154
+ opacity: credits2 <= minCredits ? 0.3 : 1,
8155
8155
  transition: "opacity 0.15s ease"
8156
8156
  },
8157
8157
  onClick: handleDecrement,
@@ -8209,7 +8209,7 @@ function BuyCreditsModal({
8209
8209
  style: {
8210
8210
  width: 24,
8211
8211
  height: 24,
8212
- opacity: credits >= maxCredits ? 0.3 : 1,
8212
+ opacity: credits2 >= maxCredits ? 0.3 : 1,
8213
8213
  transition: "opacity 0.15s ease"
8214
8214
  },
8215
8215
  onClick: handleIncrement,
@@ -8341,7 +8341,7 @@ function BuyCreditsModal({
8341
8341
  pointerEvents: isInvalid ? "none" : "auto",
8342
8342
  transition: "opacity 0.2s ease"
8343
8343
  },
8344
- onClick: () => onBuy(credits, price),
8344
+ onClick: () => onBuy(credits2, price),
8345
8345
  children: /* @__PURE__ */ jsx21(
8346
8346
  "span",
8347
8347
  {
@@ -9081,7 +9081,7 @@ function PerUserLimitsView() {
9081
9081
  ] });
9082
9082
  }
9083
9083
 
9084
- // src/credit-allocations-view.tsx
9084
+ // src/shared-credits-view.tsx
9085
9085
  import { useCallback as useCallback7, useEffect as useEffect16, useRef as useRef8, useState as useState19 } from "react";
9086
9086
  import { ScButton as ScButton12 } from "@streamoid/ui";
9087
9087
 
@@ -9330,23 +9330,23 @@ function ConfirmActionModal({
9330
9330
  );
9331
9331
  }
9332
9332
 
9333
- // src/credit-allocation-modal.tsx
9333
+ // src/credit-pool-modal.tsx
9334
9334
  import { useEffect as useEffect15, useState as useState17 } from "react";
9335
- import { ScButton as ScButton10, ScPairtext as ScPairtext2, ScSelect, ScTextArea, ScToggleSwitch } from "@streamoid/ui";
9335
+ import { ScButton as ScButton10, ScPairtext as ScPairtext2, ScSelect, ScTextArea } from "@streamoid/ui";
9336
9336
  import { SiconClose as SiconClose8 } from "@streamoid/icons";
9337
9337
 
9338
- // src/credit-allocations.ts
9339
- var ALLOCATION_NOTE_MAX_LENGTH = 500;
9340
- var CANCEL_REASON_MAX_LENGTH = 500;
9341
- function canShowCreditAllocations(api, workspace, organization) {
9342
- return Boolean(api.fetchCreditAllocations) && Boolean(workspace?.organizationId || organization?.id);
9338
+ // src/credit-pool.ts
9339
+ var POOL_NOTE_MAX_LENGTH = 500;
9340
+ var REMOVE_REASON_MAX_LENGTH = 500;
9341
+ function canShowSharedCredits(api, workspace, organization) {
9342
+ return Boolean(api.fetchCreditPool) && Boolean(workspace?.organizationId || organization?.id);
9343
9343
  }
9344
- function cleanAllocationValueInput(raw) {
9344
+ function cleanPoolValueInput(raw) {
9345
9345
  const kept = raw.replace(/[^\d.]/g, "");
9346
9346
  const dot = kept.indexOf(".");
9347
9347
  return dot === -1 ? kept : kept.slice(0, dot + 1) + kept.slice(dot + 1).replace(/\./g, "");
9348
9348
  }
9349
- function validateAllocationValue(mode, raw, balance) {
9349
+ function validatePoolValue(mode, raw) {
9350
9350
  const trimmed = raw.trim();
9351
9351
  if (!trimmed) return { ok: false, reason: mode === "percent" ? "Enter a percentage" : "Enter a number of credits" };
9352
9352
  if (!/^\d+(\.\d{1,2})?$/.test(trimmed)) {
@@ -9355,64 +9355,61 @@ function validateAllocationValue(mode, raw, balance) {
9355
9355
  const value = Number(trimmed);
9356
9356
  if (!(value > 0)) return { ok: false, reason: "Enter a number greater than 0" };
9357
9357
  if (mode === "percent" && value > 100) return { ok: false, reason: "A percentage can be at most 100" };
9358
- if (mode === "fixed" && typeof balance === "number" && Number.isFinite(balance) && value > balance) {
9359
- return {
9360
- ok: false,
9361
- reason: `This workspace has ${formatCredits(balance)} credits available`
9362
- };
9363
- }
9364
9358
  return { ok: true, value };
9365
9359
  }
9366
- function validateAllocationText(raw, max) {
9360
+ function validatePoolText(raw, max) {
9367
9361
  const value = raw.trim();
9368
9362
  if (!value) return { ok: false, reason: "Required" };
9369
9363
  if (value.length > max) return { ok: false, reason: `At most ${max} characters` };
9370
9364
  return { ok: true, value };
9371
9365
  }
9372
- function formatAllocationSplit(split) {
9366
+ var credits = (value) => `${formatCredits(value)} ${value === 1 ? "credit" : "credits"}`;
9367
+ function formatPoolSplit(split) {
9373
9368
  if (split.mode === "percent") return `${formatCredits(split.value)}%`;
9374
- return `${formatCredits(split.value)} ${split.value === 1 ? "credit" : "credits"}`;
9369
+ return credits(split.value);
9375
9370
  }
9376
- function formatAllocationRepeat(recurring) {
9377
- return recurring ? "Every renewal" : "Once";
9371
+ function describeAllowance(member) {
9372
+ const allowance = `${credits(member.allowance)} this cycle`;
9373
+ return member.mode === "percent" ? `${formatPoolSplit(member)} \xB7 ${allowance}` : allowance;
9378
9374
  }
9379
- function describeAllocation(allocation) {
9380
- const split = formatAllocationSplit(allocation);
9381
- if (!allocation.recurring) return `${split} once`;
9382
- return allocation.mode === "percent" ? `${split} of each renewal` : `${split} every renewal`;
9383
- }
9384
- function allocationWorkspaceName(name) {
9375
+ function poolWorkspaceName(name) {
9385
9376
  return name?.trim() || "Unknown workspace";
9386
9377
  }
9387
- function initialTransferWarning(allocation) {
9388
- const transfer = allocation.initialTransfer;
9389
- if (transfer?.status !== "failed") return null;
9390
- const why = transfer.error?.trim();
9391
- return `The first transfer failed${why ? `: ${why}` : ""}. The allocation is active and renewals will still split, but these credits did not move.`;
9392
- }
9393
- function initialTransferSummary(allocation) {
9394
- const transfer = allocation.initialTransfer;
9395
- if (transfer?.status !== "done") return null;
9396
- return `Sent ${formatCredits(transfer.credits)} ${transfer.credits === 1 ? "credit" : "credits"} at setup`;
9397
- }
9398
- function cancelOutcomeWarning(result, targetName) {
9399
- const stuck = !result.targetMovedToFree && !result.reason?.trim() && result.allocation?.targetPlanChanged === true;
9400
- if (!result.error && !stuck) return null;
9401
- return `Allocation canceled, but ${targetName} is still on the $0 Custom plan and won't get its free monthly credits. Contact Streamoid support with the workspace name ${targetName} and the ID below to move it back to the free plan.`;
9378
+ function sharedPoolBannerText(pool) {
9379
+ const sponsor = pool.sponsorName?.trim() || "another workspace";
9380
+ return `Credits shared by ${sponsor}: ${formatCredits(pool.remaining)} of ${formatCredits(pool.allowance)} left this cycle`;
9402
9381
  }
9403
- function cancelOutcomeSummary(result, targetName) {
9404
- const why = result.reason?.trim();
9405
- if (result.targetMovedToFree) return `Allocation canceled. ${targetName} is back on the free plan.`;
9406
- return why ? `Allocation canceled. ${why.replace(/\.?$/, ".")}` : "Allocation canceled";
9382
+ function creditsMeter({
9383
+ availableCredits,
9384
+ totalCredits,
9385
+ sharedPool
9386
+ }) {
9387
+ if (sharedPool) {
9388
+ const used2 = Math.max(0, sharedPool.used);
9389
+ const remaining = Math.max(0, sharedPool.remaining);
9390
+ const progressPct2 = sharedPool.allowance > 0 ? Math.min(100, Math.round(used2 / sharedPool.allowance * 100)) : 100;
9391
+ return { used: used2, remaining, progressPct: progressPct2, remainingPct: 100 - progressPct2 };
9392
+ }
9393
+ const used = availableCredits != null && totalCredits != null ? Math.max(0, totalCredits - availableCredits) : null;
9394
+ const progressPct = used != null && totalCredits != null && totalCredits > 0 ? Math.min(100, Math.round(used / totalCredits * 100)) : 0;
9395
+ return { used, remaining: availableCredits, progressPct, remainingPct: 100 - progressPct };
9396
+ }
9397
+ function usedByWorkspaceKey(log, currentWorkspaceId) {
9398
+ const usedBy = log.meta?.usedBy;
9399
+ if (typeof usedBy !== "string" || !usedBy) return null;
9400
+ return usedBy.toLowerCase() === String(currentWorkspaceId ?? "").toLowerCase() ? null : usedBy;
9401
+ }
9402
+ function usedByNamesFrom(pool) {
9403
+ const names = {};
9404
+ for (const member of [...pool?.formerMembers ?? [], ...pool?.members ?? []]) {
9405
+ const name = member.memberWorkspaceName?.trim();
9406
+ if (name && member.memberWorkspaceKey) names[member.memberWorkspaceKey.toLowerCase()] = name;
9407
+ }
9408
+ return names;
9407
9409
  }
9408
- var ALLOCATION_ERROR_CODES = [
9409
- "INSUFFICIENT_CREDITS",
9410
- "SOURCE_ON_FREE_PLAN",
9411
- "ALLOCATION_NOT_ALLOWED",
9412
- "TARGET_PLAN_CHANGE_FAILED"
9413
- ];
9414
- function findAllocationError(failure) {
9415
- const error = findBillingError(failure, ALLOCATION_ERROR_CODES);
9410
+ var POOL_ERROR_CODES = ["SPONSOR_ON_FREE_PLAN", "POOL_NOT_ALLOWED"];
9411
+ function findPoolError(failure) {
9412
+ const error = findBillingError(failure, POOL_ERROR_CODES);
9416
9413
  if (!error) return null;
9417
9414
  const status = error.status;
9418
9415
  const message = [status?.message, error.error, error.message].find(
@@ -9421,29 +9418,18 @@ function findAllocationError(failure) {
9421
9418
  const details = error.details && typeof error.details === "object" ? error.details : {};
9422
9419
  return { code: error.code, details, message: message ?? null };
9423
9420
  }
9424
- var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
9425
- function allocationErrorMessage(failure) {
9426
- const found = findAllocationError(failure);
9421
+ function poolErrorMessage(failure) {
9422
+ const found = findPoolError(failure);
9427
9423
  if (!found) return null;
9428
9424
  switch (found.code) {
9429
- case "INSUFFICIENT_CREDITS": {
9430
- const balance = num(found.details.currentBalance);
9431
- const required = num(found.details.requiredCredits);
9432
- if (balance !== null && required !== null) {
9433
- return `Not enough credits: this would send ${formatCredits(required)} now, but the workspace has ${formatCredits(balance)}.`;
9434
- }
9435
- return "This workspace doesn't have enough credits to send that now.";
9436
- }
9437
- case "SOURCE_ON_FREE_PLAN":
9425
+ case "SPONSOR_ON_FREE_PLAN":
9438
9426
  return "A workspace on the free plan can't share credits. Upgrade this workspace's plan first.";
9439
- case "ALLOCATION_NOT_ALLOWED":
9440
- return found.message ? found.message.replace(/\.?$/, ".") : "This allocation isn't allowed.";
9441
- case "TARGET_PLAN_CHANGE_FAILED":
9442
- return found.details.allocationId ? "The target workspace moved to the Custom plan, but its plan details weren't saved. Cancel this allocation to move it back to free." : "Couldn't move the target workspace to the Custom plan, so nothing was transferred. Try again.";
9427
+ case "POOL_NOT_ALLOWED":
9428
+ return found.message ? found.message.replace(/\.?$/, ".") : "Credits can't be shared with that workspace.";
9443
9429
  }
9444
9430
  }
9445
9431
 
9446
- // src/credit-allocation-modal.tsx
9432
+ // src/credit-pool-modal.tsx
9447
9433
  import { Fragment as Fragment9, jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
9448
9434
  var MODAL_T = {
9449
9435
  primary: "var(--alias-text---icons-primary)",
@@ -9476,7 +9462,7 @@ var modalBodyText = {
9476
9462
  fontSize: T16.fsSm,
9477
9463
  lineHeight: T16.lhSm
9478
9464
  };
9479
- function AllocationModalShell({
9465
+ function PoolModalShell({
9480
9466
  title,
9481
9467
  busy,
9482
9468
  onClose,
@@ -9577,7 +9563,7 @@ function AllocationModalShell({
9577
9563
  }
9578
9564
  );
9579
9565
  }
9580
- function AllocationWarning({ children }) {
9566
+ function PoolWarning({ children }) {
9581
9567
  return /* @__PURE__ */ jsx25(
9582
9568
  "div",
9583
9569
  {
@@ -9596,68 +9582,55 @@ function AllocationWarning({ children }) {
9596
9582
  );
9597
9583
  }
9598
9584
  var fieldLabel = { ...modalBodyText, fontWeight: 500, color: T16.primary };
9599
- function CreditAllocationModal(props) {
9585
+ function CreditPoolMemberModal(props) {
9600
9586
  const { toast } = useSettingsAdapters();
9601
- const editing = props.kind === "edit" ? props.allocation : null;
9602
- const [targetId, setTargetId] = useState17("");
9603
- const [mode, setMode] = useState17(editing?.mode ?? "fixed");
9587
+ const editing = props.kind === "edit" ? props.member : null;
9588
+ const [memberId, setMemberId] = useState17("");
9589
+ const [mode, setMode] = useState17(editing?.mode ?? "percent");
9604
9590
  const [raw, setRaw] = useState17(editing ? String(editing.value) : "");
9605
- const [recurring, setRecurring] = useState17(editing?.recurring ?? true);
9606
9591
  const [note, setNote] = useState17("");
9607
9592
  const [touched, setTouched] = useState17(false);
9608
9593
  const [saving, setSaving] = useState17(false);
9609
9594
  const [serverError, setServerError] = useState17(null);
9610
9595
  useEffect15(() => {
9611
9596
  setServerError(null);
9612
- }, [targetId, mode, raw, recurring, note]);
9613
- const balance = props.kind === "create" ? props.balance : null;
9614
- const value = validateAllocationValue(mode, raw, props.kind === "create" ? balance : null);
9615
- const noteCheck = validateAllocationText(note, ALLOCATION_NOTE_MAX_LENGTH);
9616
- const splitChanged = !editing || mode !== editing.mode || !value.ok || value.value !== editing.value;
9617
- const changed = !editing || splitChanged || recurring !== editing.recurring;
9618
- const valid = value.ok && (props.kind === "edit" || Boolean(targetId) && noteCheck.ok);
9597
+ }, [memberId, mode, raw, note]);
9598
+ const value = validatePoolValue(mode, raw);
9599
+ const noteCheck = validatePoolText(note, POOL_NOTE_MAX_LENGTH);
9600
+ const changed = !editing || mode !== editing.mode || !value.ok || value.value !== editing.value;
9601
+ const valid = value.ok && (props.kind === "edit" || Boolean(memberId) && noteCheck.ok);
9619
9602
  const canSave = !saving && changed && valid;
9620
9603
  const valueHint = touched && !value.ok ? value.reason : null;
9621
- const percentNow = props.kind === "create" && mode === "percent" && value.ok && balance !== null ? Math.floor(roundCredits(balance * value.value / 100) * 100) / 100 : null;
9622
9604
  const handleSave = async () => {
9623
9605
  if (!canSave || !value.ok) return;
9624
9606
  setSaving(true);
9625
9607
  try {
9626
- if (props.kind === "create") {
9627
- const created = await props.onSave({
9628
- targetWorkspaceId: targetId,
9608
+ if (props.kind === "add") {
9609
+ const added = await props.onSave({
9610
+ memberWorkspaceId: memberId,
9629
9611
  mode,
9630
9612
  value: value.value,
9631
- recurring,
9632
9613
  note: noteCheck.ok ? noteCheck.value : ""
9633
9614
  });
9634
- const warning = initialTransferWarning(created);
9635
- if (warning) toast.error(warning);
9636
- else {
9637
- const sent = initialTransferSummary(created);
9638
- toast.success(sent ? `Allocation created. ${sent}.` : "Allocation created");
9639
- }
9615
+ toast.success(`Credits shared. ${describeAllowance(added)}.`);
9640
9616
  } else {
9641
- await props.onSave(
9642
- splitChanged ? { mode, value: value.value, recurring } : { recurring }
9643
- );
9644
- toast.success("Allocation updated. Applies from the next renewal.");
9617
+ const updated = await props.onSave({ mode, value: value.value });
9618
+ toast.success(`Share updated. ${describeAllowance(updated)}.`);
9645
9619
  }
9646
9620
  props.onSaved();
9647
9621
  props.onClose();
9648
9622
  } catch (err) {
9649
- const message = allocationErrorMessage(err);
9623
+ const message = poolErrorMessage(err);
9650
9624
  if (message) setServerError(message);
9651
- else toast.error(errorMessage(err, { method: props.kind === "create" ? "POST" : "PATCH" }));
9652
- if (findAllocationError(err)?.details.allocationId) props.onSaved();
9625
+ else toast.error(errorMessage(err, { method: props.kind === "add" ? "POST" : "PATCH" }));
9653
9626
  setSaving(false);
9654
9627
  }
9655
9628
  };
9656
- const targetOptions = props.kind === "create" ? props.targets.map((target) => ({ id: target.id, label: target.name })) : [];
9629
+ const candidateOptions = props.kind === "add" ? props.candidates.map((candidate) => ({ id: candidate.id, label: candidate.name })) : [];
9657
9630
  return /* @__PURE__ */ jsxs22(
9658
- AllocationModalShell,
9631
+ PoolModalShell,
9659
9632
  {
9660
- title: props.kind === "create" ? "New credit allocation" : "Edit credit allocation",
9633
+ title: props.kind === "add" ? "Share credits" : "Edit shared credits",
9661
9634
  busy: saving,
9662
9635
  onClose: props.onClose,
9663
9636
  footer: /* @__PURE__ */ jsxs22(Fragment9, { children: [
@@ -9674,7 +9647,7 @@ function CreditAllocationModal(props) {
9674
9647
  /* @__PURE__ */ jsx25(
9675
9648
  ScButton10,
9676
9649
  {
9677
- text: props.kind === "create" ? "Create allocation" : "Save",
9650
+ text: props.kind === "add" ? "Share credits" : "Save",
9678
9651
  variant: "primary",
9679
9652
  size: "md",
9680
9653
  state: canSave ? "default" : "disabled",
@@ -9684,29 +9657,29 @@ function CreditAllocationModal(props) {
9684
9657
  ] }),
9685
9658
  children: [
9686
9659
  /* @__PURE__ */ jsxs22("div", { className: "flex flex-col w-full", style: { gap: T16.spXs }, children: [
9687
- /* @__PURE__ */ jsx25("span", { style: fieldLabel, children: "Send credits to" }),
9688
- props.kind === "create" ? targetOptions.length ? /* @__PURE__ */ jsx25(
9660
+ /* @__PURE__ */ jsx25("span", { style: fieldLabel, children: "Share with" }),
9661
+ props.kind === "add" ? candidateOptions.length ? /* @__PURE__ */ jsx25(
9689
9662
  ScSelect,
9690
9663
  {
9691
- value: targetId,
9692
- onChange: setTargetId,
9693
- options: targetOptions,
9664
+ value: memberId,
9665
+ onChange: setMemberId,
9666
+ options: candidateOptions,
9694
9667
  placeholder: "Choose a workspace",
9695
9668
  disabled: saving
9696
9669
  }
9697
- ) : /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.muted }, children: "There are no other workspaces in this organization to send credits to." }) : /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.primary }, children: props.targetName })
9670
+ ) : /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.muted }, children: "There are no other workspaces in this organization to share credits with." }) : /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.primary }, children: props.memberName })
9698
9671
  ] }),
9699
9672
  /* @__PURE__ */ jsxs22("div", { className: "flex flex-col w-full", style: { gap: T16.spMd }, children: [
9700
- /* @__PURE__ */ jsx25("span", { style: fieldLabel, children: "How much" }),
9673
+ /* @__PURE__ */ jsx25("span", { style: fieldLabel, children: "How much, each billing cycle" }),
9701
9674
  /* @__PURE__ */ jsxs22("div", { className: "flex items-center", style: { gap: T16.sp3xl }, children: [
9702
9675
  /* @__PURE__ */ jsx25(
9703
9676
  ScPairtext2,
9704
9677
  {
9705
9678
  type: "radio",
9706
- content: "Fixed credits",
9707
- checked: mode === "fixed",
9679
+ content: "Percentage",
9680
+ checked: mode === "percent",
9708
9681
  onChange: () => {
9709
- if (!saving) setMode("fixed");
9682
+ if (!saving) setMode("percent");
9710
9683
  }
9711
9684
  }
9712
9685
  ),
@@ -9714,10 +9687,10 @@ function CreditAllocationModal(props) {
9714
9687
  ScPairtext2,
9715
9688
  {
9716
9689
  type: "radio",
9717
- content: "Percentage",
9718
- checked: mode === "percent",
9690
+ content: "Fixed credits",
9691
+ checked: mode === "fixed",
9719
9692
  onChange: () => {
9720
- if (!saving) setMode("percent");
9693
+ if (!saving) setMode("fixed");
9721
9694
  }
9722
9695
  }
9723
9696
  )
@@ -9745,7 +9718,7 @@ function CreditAllocationModal(props) {
9745
9718
  value: raw,
9746
9719
  disabled: saving,
9747
9720
  onChange: (e) => {
9748
- setRaw(cleanAllocationValueInput(e.target.value));
9721
+ setRaw(cleanPoolValueInput(e.target.value));
9749
9722
  setTouched(true);
9750
9723
  },
9751
9724
  className: "flex-1 min-w-0",
@@ -9763,39 +9736,18 @@ function CreditAllocationModal(props) {
9763
9736
  ]
9764
9737
  }
9765
9738
  ),
9766
- valueHint ? /* @__PURE__ */ jsx25("span", { role: "alert", style: { ...modalBodyText, color: T16.error }, children: valueHint }) : props.kind === "create" ? /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.tertiary }, children: percentNow !== null ? `Sends ${formatCredits(percentNow)} credits now, out of ${formatCredits(balance ?? 0)} available.` : balance !== null ? `Sent now from this workspace's ${formatCredits(balance)} available credits.` : "Sent now from this workspace's available credits." }) : /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.tertiary }, children: "Changes apply from the next renewal; credits already sent stay where they are." })
9767
- ] }),
9768
- /* @__PURE__ */ jsxs22("div", { className: "flex items-start justify-between w-full", style: { gap: T16.sp3xl }, children: [
9769
- /* @__PURE__ */ jsxs22("div", { className: "flex flex-col min-w-0", style: { gap: T16.spXs }, children: [
9770
- /* @__PURE__ */ jsx25("span", { style: fieldLabel, children: "Repeat on every renewal" }),
9771
- /* @__PURE__ */ jsx25("span", { style: { ...modalBodyText, color: T16.muted }, children: mode === "percent" ? "Also send this share of each renewal grant this workspace receives." : "Also send these credits out of each renewal grant this workspace receives." })
9772
- ] }),
9773
- /* @__PURE__ */ jsx25("span", { style: { display: "inline-flex", width: 41, height: 25, alignItems: "center", flexShrink: 0 }, children: /* @__PURE__ */ jsx25(
9774
- ScToggleSwitch,
9775
- {
9776
- checked: recurring,
9777
- disabled: saving,
9778
- onToggle: (next) => setRecurring(next),
9779
- role: "switch",
9780
- "aria-checked": recurring,
9781
- "aria-label": "Repeat on every renewal",
9782
- tabIndex: 0,
9783
- onKeyDown: (event) => {
9784
- if ((event.key === " " || event.key === "Enter") && !saving) {
9785
- event.preventDefault();
9786
- setRecurring(!recurring);
9787
- }
9788
- }
9789
- }
9790
- ) })
9739
+ valueHint ? /* @__PURE__ */ jsx25("span", { role: "alert", style: { ...modalBodyText, color: T16.error }, children: valueHint }) : /* @__PURE__ */ jsxs22("span", { style: { ...modalBodyText, color: T16.tertiary }, children: [
9740
+ 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.",
9741
+ editing ? " Applies immediately." : ""
9742
+ ] })
9791
9743
  ] }),
9792
- props.kind === "create" && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col w-full", style: { gap: T16.spXs }, children: [
9744
+ props.kind === "add" && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col w-full", style: { gap: T16.spXs }, children: [
9793
9745
  /* @__PURE__ */ jsx25("span", { style: fieldLabel, children: "Note" }),
9794
9746
  /* @__PURE__ */ jsx25(
9795
9747
  ScTextArea,
9796
9748
  {
9797
9749
  value: note,
9798
- maxLength: ALLOCATION_NOTE_MAX_LENGTH,
9750
+ maxLength: POOL_NOTE_MAX_LENGTH,
9799
9751
  placeholder: "Why are these credits being shared?",
9800
9752
  disabled: saving,
9801
9753
  onChange: (e) => setNote(e.target.value),
@@ -9807,117 +9759,57 @@ function CreditAllocationModal(props) {
9807
9759
  "Required. ",
9808
9760
  note.trim().length,
9809
9761
  "/",
9810
- ALLOCATION_NOTE_MAX_LENGTH
9762
+ POOL_NOTE_MAX_LENGTH
9811
9763
  ] })
9812
9764
  ] }),
9813
- props.kind === "create" && /* @__PURE__ */ jsx25(AllocationWarning, { children: "A workspace on the free plan moves to a $0 Custom plan funded by this workspace. Cancelling moves it back to free." }),
9765
+ props.kind === "add" && /* @__PURE__ */ jsx25(PoolWarning, { children: "While it spends shared credits, the other workspace uses only this workspace's credits, not its own." }),
9814
9766
  serverError && /* @__PURE__ */ jsx25("span", { role: "alert", style: { ...modalBodyText, color: T16.error }, children: serverError })
9815
9767
  ]
9816
9768
  }
9817
9769
  );
9818
9770
  }
9819
9771
 
9820
- // src/cancel-credit-allocation-modal.tsx
9772
+ // src/remove-pool-member-modal.tsx
9821
9773
  import { useState as useState18 } from "react";
9822
9774
  import { ScButton as ScButton11, ScTextArea as ScTextArea2 } from "@streamoid/ui";
9823
9775
  import { Fragment as Fragment10, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
9824
- function CancelCreditAllocationModal({
9825
- allocation,
9826
- targetName,
9827
- onCancel,
9776
+ function RemovePoolMemberModal({
9777
+ member,
9778
+ memberName,
9779
+ sponsorName,
9780
+ onRemove,
9828
9781
  onClose,
9829
- onCanceled
9782
+ onRemoved
9830
9783
  }) {
9831
9784
  const { toast } = useSettingsAdapters();
9832
9785
  const [reason, setReason] = useState18("");
9833
9786
  const [saving, setSaving] = useState18(false);
9834
- const [outcome, setOutcome] = useState18(null);
9835
- const check = validateAllocationText(reason, CANCEL_REASON_MAX_LENGTH);
9787
+ const check = validatePoolText(reason, REMOVE_REASON_MAX_LENGTH);
9836
9788
  const canConfirm = !saving && check.ok;
9837
9789
  const handleConfirm = async () => {
9838
9790
  if (!canConfirm || !check.ok) return;
9839
9791
  setSaving(true);
9840
9792
  try {
9841
- const result = await onCancel(check.value);
9842
- const warning = cancelOutcomeWarning(
9843
- { ...result, allocation: result.allocation ?? allocation },
9844
- targetName
9845
- );
9846
- onCanceled({ planNotRestored: Boolean(warning) });
9847
- if (warning) {
9848
- setOutcome({ warning, error: result.error });
9849
- setSaving(false);
9850
- return;
9851
- }
9852
- toast.success(cancelOutcomeSummary(result, targetName));
9793
+ await onRemove(check.value);
9794
+ toast.success(`${memberName} can no longer spend ${sponsorName}'s credits`);
9795
+ onRemoved();
9853
9796
  onClose();
9854
9797
  } catch (err) {
9855
9798
  toast.error(errorMessage(err, { method: "DELETE" }));
9856
9799
  setSaving(false);
9857
9800
  }
9858
9801
  };
9859
- const copyId = () => {
9860
- const id = allocation.targetWorkspaceKey;
9861
- const clipboard = typeof navigator !== "undefined" ? navigator.clipboard : void 0;
9862
- if (!clipboard) {
9863
- toast.error("Couldn't copy. Select the ID and copy it instead.");
9864
- return;
9865
- }
9866
- clipboard.writeText(id).then(
9867
- () => toast.success("Workspace ID copied"),
9868
- () => toast.error("Couldn't copy. Select the ID and copy it instead.")
9869
- );
9870
- };
9871
- if (outcome) {
9872
- return /* @__PURE__ */ jsxs23(
9873
- AllocationModalShell,
9874
- {
9875
- title: "Target plan not restored",
9876
- busy: false,
9877
- onClose,
9878
- footer: /* @__PURE__ */ jsx26(ScButton11, { text: "Got it", variant: "primary", size: "md", onClick: onClose }),
9879
- children: [
9880
- /* @__PURE__ */ jsx26(AllocationWarning, { children: outcome.warning }),
9881
- /* @__PURE__ */ jsxs23("div", { className: "flex flex-col w-full", style: { gap: MODAL_T.spXs }, children: [
9882
- /* @__PURE__ */ jsx26("span", { style: { ...modalBodyText, fontWeight: 500, color: MODAL_T.primary }, children: "Workspace ID" }),
9883
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center w-full", style: { gap: MODAL_T.spXl }, children: [
9884
- /* @__PURE__ */ jsx26(
9885
- "code",
9886
- {
9887
- className: "flex-1 min-w-0 truncate",
9888
- style: {
9889
- ...modalBodyText,
9890
- color: MODAL_T.primary,
9891
- background: MODAL_T.fillPlus,
9892
- borderRadius: MODAL_T.r3xl,
9893
- padding: `${MODAL_T.spXl} ${MODAL_T.sp3xl}`,
9894
- userSelect: "all"
9895
- },
9896
- children: allocation.targetWorkspaceKey
9897
- }
9898
- ),
9899
- /* @__PURE__ */ jsx26(ScButton11, { text: "Copy", variant: "secondary", size: "sm", onClick: copyId })
9900
- ] })
9901
- ] }),
9902
- outcome.error && /* @__PURE__ */ jsxs23("span", { style: { ...modalBodyText, color: MODAL_T.muted }, children: [
9903
- "Details: ",
9904
- outcome.error
9905
- ] })
9906
- ]
9907
- }
9908
- );
9909
- }
9910
9802
  return /* @__PURE__ */ jsxs23(
9911
- AllocationModalShell,
9803
+ PoolModalShell,
9912
9804
  {
9913
- title: "Cancel credit allocation",
9805
+ title: "Stop sharing credits",
9914
9806
  busy: saving,
9915
9807
  onClose,
9916
9808
  footer: /* @__PURE__ */ jsxs23(Fragment10, { children: [
9917
9809
  /* @__PURE__ */ jsx26(
9918
9810
  ScButton11,
9919
9811
  {
9920
- text: "Keep allocation",
9812
+ text: "Keep sharing",
9921
9813
  variant: "secondary",
9922
9814
  size: "md",
9923
9815
  state: saving ? "disabled" : "default",
@@ -9927,7 +9819,7 @@ function CancelCreditAllocationModal({
9927
9819
  /* @__PURE__ */ jsx26(
9928
9820
  ScButton11,
9929
9821
  {
9930
- text: "Cancel allocation",
9822
+ text: "Stop sharing",
9931
9823
  variant: "error",
9932
9824
  size: "md",
9933
9825
  state: canConfirm ? "default" : "disabled",
@@ -9937,16 +9829,18 @@ function CancelCreditAllocationModal({
9937
9829
  ] }),
9938
9830
  children: [
9939
9831
  /* @__PURE__ */ jsxs23("span", { style: { ...modalBodyText, color: MODAL_T.primary }, children: [
9940
- "Stop sending ",
9941
- describeAllocation(allocation),
9942
- " to ",
9943
- targetName,
9944
- "?"
9832
+ "This stops ",
9833
+ memberName,
9834
+ " spending ",
9835
+ sponsorName,
9836
+ "'s credits immediately."
9945
9837
  ] }),
9946
9838
  /* @__PURE__ */ jsxs23("span", { style: { ...modalBodyText, color: MODAL_T.tertiary }, children: [
9947
- targetName,
9948
- " keeps the credits it already received until they expire.",
9949
- allocation.targetPlanChanged ? " It moved to a $0 Custom plan for this allocation, so it goes back to the free plan now, unless it has since moved to a plan of its own." : ""
9839
+ "It had ",
9840
+ describeAllowance(member),
9841
+ " and has used ",
9842
+ formatCredits(member.used),
9843
+ ". Credits it already spent are not returned; anything it runs from now uses its own credits."
9950
9844
  ] }),
9951
9845
  /* @__PURE__ */ jsxs23("div", { className: "flex flex-col w-full", style: { gap: MODAL_T.spXs }, children: [
9952
9846
  /* @__PURE__ */ jsx26("span", { style: { ...modalBodyText, fontWeight: 500, color: MODAL_T.primary }, children: "Reason" }),
@@ -9954,8 +9848,8 @@ function CancelCreditAllocationModal({
9954
9848
  ScTextArea2,
9955
9849
  {
9956
9850
  value: reason,
9957
- maxLength: CANCEL_REASON_MAX_LENGTH,
9958
- placeholder: "Why is this allocation ending?",
9851
+ maxLength: REMOVE_REASON_MAX_LENGTH,
9852
+ placeholder: "Why is this workspace no longer sharing these credits?",
9959
9853
  disabled: saving,
9960
9854
  onChange: (e) => setReason(e.target.value),
9961
9855
  rows: 3,
@@ -9969,16 +9863,13 @@ function CancelCreditAllocationModal({
9969
9863
  );
9970
9864
  }
9971
9865
 
9972
- // src/credit-allocations-view.tsx
9866
+ // src/shared-credits-view.tsx
9973
9867
  import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
9974
9868
  var T17 = {
9975
9869
  primary: "var(--alias-text---icons-primary)",
9976
9870
  muted: "var(--alias-text---icons-muted)",
9977
9871
  tertiary: "var(--alias-text---icons-tertiary)",
9978
9872
  warning: "var(--alias-text---icons-warning)",
9979
- success: "var(--alias-text---icons-success)",
9980
- warningFill: "var(--alias-fill-warning-soft)",
9981
- warningBorder: "var(--alias-border-warning)",
9982
9873
  fillPlus: "var(--alias-fill-neutral-neutralplus)",
9983
9874
  surfaceBase: "var(--alias-surface-base)",
9984
9875
  borderSubtle: "var(--alias-border-subtle)",
@@ -9992,9 +9883,7 @@ var T17 = {
9992
9883
  r3xl: "var(--radius-3xl)",
9993
9884
  rFull: "var(--radius-full)"
9994
9885
  };
9995
- var GIVING_COLUMNS = "minmax(220px, 1fr) 120px 120px 150px 110px 150px";
9996
- var TRANSFER_COLUMNS = "120px 170px minmax(200px, 1fr) 120px";
9997
- var TRANSFERS_PAGE = 20;
9886
+ var MEMBER_COLUMNS2 = "minmax(220px, 1fr) 220px 110px 110px 150px";
9998
9887
  var cellText2 = {
9999
9888
  fontFamily: T17.font,
10000
9889
  fontSize: T17.fsSm,
@@ -10016,138 +9905,67 @@ var tableHeader = (columns, labels) => /* @__PURE__ */ jsx27(
10016
9905
  children: labels.map((label) => /* @__PURE__ */ jsx27("span", { children: label }, label))
10017
9906
  }
10018
9907
  );
10019
- function Badge({ text: text2, tone }) {
10020
- return /* @__PURE__ */ jsx27(
10021
- "span",
10022
- {
10023
- className: "inline-flex items-center shrink-0",
10024
- style: {
10025
- ...cellText2,
10026
- fontSize: 12,
10027
- padding: "0 8px",
10028
- height: 22,
10029
- borderRadius: T17.rFull,
10030
- color: tone === "warning" ? T17.primary : T17.muted,
10031
- background: tone === "warning" ? T17.warningFill : T17.fillPlus,
10032
- border: tone === "warning" ? `1px solid ${T17.warningBorder}` : "1px solid transparent"
10033
- },
10034
- children: text2
10035
- }
10036
- );
10037
- }
10038
- function GivingRow({
10039
- allocation,
10040
- planNotRestored,
9908
+ function MemberRow2({
9909
+ member,
10041
9910
  onEdit,
10042
- onCancel
9911
+ onRemove
10043
9912
  }) {
10044
- const failed = initialTransferWarning(allocation);
10045
- const sent = initialTransferSummary(allocation);
10046
- const active = allocation.status === "active";
9913
+ const spent = member.remaining <= 0;
10047
9914
  return /* @__PURE__ */ jsxs24(
10048
9915
  "div",
10049
9916
  {
10050
9917
  style: {
10051
9918
  borderBottom: `1px solid ${T17.borderSubtle}`,
10052
9919
  display: "grid",
10053
- gridTemplateColumns: GIVING_COLUMNS,
9920
+ gridTemplateColumns: MEMBER_COLUMNS2,
10054
9921
  alignItems: "center",
10055
9922
  padding: `10px ${T17.sp3xl}`,
10056
9923
  gap: 24
10057
9924
  },
10058
9925
  children: [
10059
9926
  /* @__PURE__ */ jsxs24("div", { className: "flex flex-col min-w-0", style: { gap: 2 }, children: [
10060
- /* @__PURE__ */ jsx27("span", { className: "truncate", style: { ...cellText2, fontWeight: 500 }, children: allocationWorkspaceName(allocation.targetWorkspaceName) }),
10061
- failed ? /* @__PURE__ */ jsxs24("div", { className: "flex flex-col", style: { gap: 2 }, children: [
10062
- /* @__PURE__ */ jsx27("span", { children: /* @__PURE__ */ jsx27(Badge, { text: "First transfer failed", tone: "warning" }) }),
10063
- /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: T17.muted }, children: failed })
10064
- ] }) : sent ? /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: T17.muted }, children: sent }) : null,
10065
- allocation.note && /* @__PURE__ */ jsx27("span", { className: "truncate", title: allocation.note, style: { ...cellText2, color: T17.tertiary }, children: allocation.note })
10066
- ] }),
10067
- /* @__PURE__ */ jsx27("span", { style: cellText2, children: formatAllocationSplit(allocation) }),
10068
- /* @__PURE__ */ jsx27("span", { style: cellText2, children: formatAllocationRepeat(allocation.recurring) }),
10069
- /* @__PURE__ */ jsxs24("div", { className: "flex flex-wrap items-center", style: { gap: 6 }, children: [
10070
- /* @__PURE__ */ jsx27(Badge, { text: active ? "Active" : "Canceled", tone: "neutral" }),
10071
- planNotRestored && /* @__PURE__ */ jsx27(Badge, { text: "Plan not restored", tone: "warning" })
9927
+ /* @__PURE__ */ jsx27("span", { className: "truncate", style: { ...cellText2, fontWeight: 500 }, children: poolWorkspaceName(member.memberWorkspaceName) }),
9928
+ member.note && /* @__PURE__ */ jsx27("span", { className: "truncate", title: member.note, style: { ...cellText2, color: T17.tertiary }, children: member.note })
10072
9929
  ] }),
10073
- /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: T17.muted }, children: formatDate2(allocation.createdAt) }),
9930
+ /* @__PURE__ */ jsx27("span", { style: cellText2, children: describeAllowance(member) }),
9931
+ /* @__PURE__ */ jsx27("span", { style: cellText2, children: formatCredits(member.used) }),
9932
+ /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: spent ? T17.warning : T17.primary }, children: formatCredits(member.remaining) }),
10074
9933
  /* @__PURE__ */ jsxs24("div", { className: "flex items-center", style: { gap: 8 }, children: [
10075
- active && onEdit && /* @__PURE__ */ jsx27(ScButton12, { text: "Edit", variant: "secondary", size: "sm", onClick: onEdit }),
10076
- active && onCancel && /* @__PURE__ */ jsx27(ScButton12, { text: "Cancel", variant: "secondary", size: "sm", onClick: onCancel })
9934
+ onEdit && /* @__PURE__ */ jsx27(ScButton12, { text: "Edit", variant: "secondary", size: "sm", onClick: onEdit }),
9935
+ onRemove && /* @__PURE__ */ jsx27(ScButton12, { text: "Remove", variant: "secondary", size: "sm", onClick: onRemove })
10077
9936
  ] })
10078
9937
  ]
10079
9938
  }
10080
9939
  );
10081
9940
  }
10082
- function TransferRow({ transfer }) {
10083
- return /* @__PURE__ */ jsxs24(
10084
- "div",
10085
- {
10086
- style: {
10087
- borderBottom: `1px solid ${T17.borderSubtle}`,
10088
- display: "grid",
10089
- gridTemplateColumns: TRANSFER_COLUMNS,
10090
- alignItems: "center",
10091
- padding: `8px ${T17.sp3xl}`,
10092
- gap: 24
10093
- },
10094
- children: [
10095
- /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: T17.muted }, children: formatDate2(transfer.createdAt) }),
10096
- /* @__PURE__ */ jsxs24("span", { style: cellText2, children: [
10097
- transfer.direction === "sent" ? "Sent" : "Received",
10098
- " \xB7",
10099
- " ",
10100
- transfer.kind === "initial" ? "Initial" : "Renewal"
10101
- ] }),
10102
- /* @__PURE__ */ jsx27("span", { className: "truncate", style: cellText2, children: transfer.counterpartWorkspaceName ?? (transfer.counterpartWorkspaceId ? "Unknown workspace" : "\u2014") }),
10103
- /* @__PURE__ */ jsxs24("span", { style: { ...cellText2, color: transfer.direction === "received" ? T17.success : T17.primary }, children: [
10104
- transfer.direction === "received" ? "+" : "\u2212",
10105
- formatCredits(transfer.credits)
10106
- ] })
10107
- ]
10108
- }
10109
- );
10110
- }
10111
- function CreditAllocationsView() {
9941
+ function SharedCreditsView() {
10112
9942
  const { api, workspace, toast } = useSettingsAdapters();
10113
9943
  const workspaceId = workspace.current?.id ?? null;
9944
+ const workspaceName = workspace.current?.name?.trim() || "this workspace";
10114
9945
  const [data, setData] = useState19(null);
10115
9946
  const [loading, setLoading] = useState19(true);
10116
9947
  const [failed, setFailed] = useState19(false);
10117
- const [targets, setTargets] = useState19([]);
10118
- const [transfers, setTransfers] = useState19([]);
10119
- const [transfersTotal, setTransfersTotal] = useState19(0);
10120
- const [transfersPage, setTransfersPage] = useState19(0);
10121
- const [transfersFailed, setTransfersFailed] = useState19(false);
10122
- const [loadingMore, setLoadingMore] = useState19(false);
10123
- const [creating, setCreating] = useState19(false);
9948
+ const [candidates, setCandidates] = useState19([]);
9949
+ const [adding, setAdding] = useState19(false);
10124
9950
  const [editing, setEditing] = useState19(null);
10125
- const [canceling, setCanceling] = useState19(null);
10126
- const [planNotRestored, setPlanNotRestored] = useState19(() => /* @__PURE__ */ new Set());
9951
+ const [removing, setRemoving] = useState19(null);
10127
9952
  const latestLoad = useRef8(0);
10128
9953
  const load = useCallback7(
10129
9954
  async ({ refresh = false } = {}) => {
10130
9955
  const loadId = ++latestLoad.current;
10131
- if (!api.fetchCreditAllocations) {
9956
+ if (!api.fetchCreditPool) {
10132
9957
  setLoading(false);
10133
9958
  setFailed(true);
10134
9959
  return;
10135
9960
  }
10136
- const [next, page] = await Promise.all([
10137
- api.fetchCreditAllocations().catch(() => null),
10138
- api.fetchCreditTransfers ? api.fetchCreditTransfers({ page: 1, limit: TRANSFERS_PAGE }).catch(() => null) : Promise.resolve(null)
10139
- ]);
9961
+ const next = await api.fetchCreditPool().catch(() => null);
10140
9962
  if (loadId !== latestLoad.current) return;
10141
9963
  if (refresh && !next) {
10142
- toast.error("Couldn't refresh credit allocations. Reopen this page to see the latest.");
9964
+ toast.error("Couldn't refresh shared credits. Reopen this page to see the latest.");
10143
9965
  return;
10144
9966
  }
10145
9967
  setFailed(!next);
10146
9968
  setData(next);
10147
- setTransfersFailed(Boolean(api.fetchCreditTransfers) && !page);
10148
- setTransfers(page?.transfers ?? []);
10149
- setTransfersTotal(page?.total ?? 0);
10150
- setTransfersPage(page ? 1 : 0);
10151
9969
  setLoading(false);
10152
9970
  },
10153
9971
  [api, toast]
@@ -10156,11 +9974,9 @@ function CreditAllocationsView() {
10156
9974
  setLoading(true);
10157
9975
  setFailed(false);
10158
9976
  setData(null);
10159
- setTransfers([]);
10160
- setCreating(false);
9977
+ setAdding(false);
10161
9978
  setEditing(null);
10162
- setCanceling(null);
10163
- setPlanNotRestored(/* @__PURE__ */ new Set());
9979
+ setRemoving(null);
10164
9980
  void load();
10165
9981
  }, [workspaceId]);
10166
9982
  useEffect16(
@@ -10170,36 +9986,21 @@ function CreditAllocationsView() {
10170
9986
  []
10171
9987
  );
10172
9988
  const canManage = Boolean(data?.canManage);
10173
- const canCreate = canManage && Boolean(api.createCreditAllocation);
10174
- const openCreate = async () => {
10175
- if (!api.fetchAllocationTargets) {
10176
- setTargets([]);
10177
- setCreating(true);
9989
+ const sponsor = data?.sponsor ?? null;
9990
+ const canAdd = canManage && !sponsor && Boolean(api.addCreditPoolMember);
9991
+ const openAdd = async () => {
9992
+ if (!api.fetchCreditPoolCandidates) {
9993
+ setCandidates([]);
9994
+ setAdding(true);
10178
9995
  return;
10179
9996
  }
10180
9997
  try {
10181
- setTargets(await api.fetchAllocationTargets());
10182
- setCreating(true);
9998
+ setCandidates(await api.fetchCreditPoolCandidates());
9999
+ setAdding(true);
10183
10000
  } catch {
10184
10001
  toast.error("Couldn't load this organization's workspaces. Try again.");
10185
10002
  }
10186
10003
  };
10187
- const loadMoreTransfers = async () => {
10188
- if (!api.fetchCreditTransfers || loadingMore) return;
10189
- const loadId = latestLoad.current;
10190
- setLoadingMore(true);
10191
- try {
10192
- const page = await api.fetchCreditTransfers({ page: transfersPage + 1, limit: TRANSFERS_PAGE });
10193
- if (loadId !== latestLoad.current) return;
10194
- setTransfers((prev) => [...prev, ...page.transfers]);
10195
- setTransfersTotal(page.total);
10196
- setTransfersPage(transfersPage + 1);
10197
- } catch {
10198
- toast.error("Couldn't load more transfers. Try again.");
10199
- } finally {
10200
- setLoadingMore(false);
10201
- }
10202
- };
10203
10004
  if (loading) {
10204
10005
  return /* @__PURE__ */ jsx27(
10205
10006
  "div",
@@ -10222,9 +10023,7 @@ function CreditAllocationsView() {
10222
10023
  }
10223
10024
  );
10224
10025
  }
10225
- const giving = data?.giving ?? [];
10226
- const sponsor = data?.receiving.find((a) => a.status === "active") ?? null;
10227
- const balance = typeof workspace.current?.availableCredits === "number" ? workspace.current.availableCredits : null;
10026
+ const members = data?.members ?? [];
10228
10027
  return /* @__PURE__ */ jsxs24("div", { className: "flex flex-col flex-1 min-h-0 w-full overflow-y-auto", style: { gap: 16 }, children: [
10229
10028
  /* @__PURE__ */ jsxs24("div", { className: "flex items-start justify-between shrink-0 w-full", style: { gap: 12 }, children: [
10230
10029
  /* @__PURE__ */ jsxs24("div", { className: "flex flex-col min-w-0", style: { gap: 4 }, children: [
@@ -10232,12 +10031,12 @@ function CreditAllocationsView() {
10232
10031
  "span",
10233
10032
  {
10234
10033
  style: { fontFamily: T17.font, fontSize: T17.fsXl, fontWeight: 500, lineHeight: T17.lhXl, color: T17.primary },
10235
- children: "Credit allocations"
10034
+ children: "Shared credits"
10236
10035
  }
10237
10036
  ),
10238
- /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: T17.muted }, children: canManage ? "Send this workspace's credits to other workspaces in your organization, once or on every renewal." : "Credits this workspace shares with other workspaces in its organization. Only organization admins can change them." })
10037
+ /* @__PURE__ */ jsx27("span", { style: { ...cellText2, color: T17.muted }, children: canManage ? "Let other workspaces in your organization spend this workspace's credits, up to a share each billing cycle. The credits stay here." : "Workspaces in this organization that spend this workspace's credits. Only organization admins can change them." })
10239
10038
  ] }),
10240
- canCreate && !failed && /* @__PURE__ */ jsx27(ScButton12, { text: "New allocation", variant: "primary", size: "md", onClick: () => void openCreate() })
10039
+ canAdd && !failed && /* @__PURE__ */ jsx27(ScButton12, { text: "Share credits", variant: "primary", size: "md", onClick: () => void openAdd() })
10241
10040
  ] }),
10242
10041
  sponsor && /* @__PURE__ */ jsxs24(
10243
10042
  "div",
@@ -10251,103 +10050,74 @@ function CreditAllocationsView() {
10251
10050
  borderRadius: T17.r3xl
10252
10051
  },
10253
10052
  children: [
10254
- /* @__PURE__ */ jsxs24("span", { style: { ...cellText2, fontWeight: 500 }, children: [
10255
- "Funded by ",
10256
- allocationWorkspaceName(sponsor.sourceWorkspaceName)
10257
- ] }),
10053
+ /* @__PURE__ */ jsx27("span", { style: { ...cellText2, fontWeight: 500 }, children: sharedPoolBannerText({
10054
+ sponsorName: poolWorkspaceName(sponsor.sponsorWorkspaceName),
10055
+ allowance: sponsor.allowance,
10056
+ remaining: sponsor.remaining
10057
+ }) }),
10258
10058
  /* @__PURE__ */ jsxs24("span", { style: { ...cellText2, color: T17.muted }, children: [
10259
- "Receives ",
10260
- describeAllocation(sponsor),
10059
+ "This workspace spends ",
10060
+ poolWorkspaceName(sponsor.sponsorWorkspaceName),
10061
+ "'s credits, not its own",
10062
+ sponsor.cycle?.end ? `, until its allowance starts again on ${formatDate2(sponsor.cycle.end)}` : "",
10261
10063
  "."
10262
10064
  ] })
10263
10065
  ]
10264
10066
  }
10265
10067
  ),
10266
- /* @__PURE__ */ jsxs24("div", { className: "flex flex-col shrink-0 w-full", style: { gap: 8 }, children: [
10267
- /* @__PURE__ */ jsx27("span", { style: { ...cellText2, fontWeight: 500 }, children: "Giving" }),
10068
+ !sponsor && /* @__PURE__ */ jsxs24("div", { className: "flex flex-col shrink-0 w-full", style: { gap: 8 }, children: [
10069
+ /* @__PURE__ */ jsx27("span", { style: { ...cellText2, fontWeight: 500 }, children: "Shared with" }),
10268
10070
  /* @__PURE__ */ jsx27(
10269
10071
  MemberListFrame,
10270
10072
  {
10271
10073
  grow: "cap",
10272
10074
  maxHeight: 420,
10273
- contentMinWidth: 940,
10274
- header: tableHeader(GIVING_COLUMNS, ["Workspace", "Split", "Repeats", "Status", "Created", "Actions"]),
10275
- isEmpty: failed || giving.length === 0,
10276
- emptyText: failed ? "Couldn't load credit allocations. Try again later." : "This workspace doesn't send credits to any other workspace.",
10277
- children: giving.map((allocation) => /* @__PURE__ */ jsx27(
10278
- GivingRow,
10075
+ contentMinWidth: 860,
10076
+ header: tableHeader(MEMBER_COLUMNS2, ["Workspace", "Allowance", "Used", "Remaining", "Actions"]),
10077
+ isEmpty: failed || members.length === 0,
10078
+ emptyText: failed ? "Couldn't load shared credits. Try again later." : "This workspace doesn't share its credits with any other workspace.",
10079
+ children: members.map((member) => /* @__PURE__ */ jsx27(
10080
+ MemberRow2,
10279
10081
  {
10280
- allocation,
10281
- planNotRestored: planNotRestored.has(allocation._id),
10282
- onEdit: canManage && api.updateCreditAllocation ? () => setEditing(allocation) : void 0,
10283
- onCancel: canManage && api.cancelCreditAllocation ? () => setCanceling(allocation) : void 0
10082
+ member,
10083
+ onEdit: canManage && api.updateCreditPoolMember ? () => setEditing(member) : void 0,
10084
+ onRemove: canManage && api.removeCreditPoolMember ? () => setRemoving(member) : void 0
10284
10085
  },
10285
- allocation._id
10086
+ member._id
10286
10087
  ))
10287
10088
  }
10288
10089
  )
10289
10090
  ] }),
10290
- api.fetchCreditTransfers && !failed && /* @__PURE__ */ jsxs24("div", { className: "flex flex-col shrink-0 w-full", style: { gap: 8 }, children: [
10291
- /* @__PURE__ */ jsx27("span", { style: { ...cellText2, fontWeight: 500 }, children: "Transfer history" }),
10292
- /* @__PURE__ */ jsx27(
10293
- MemberListFrame,
10294
- {
10295
- grow: "cap",
10296
- maxHeight: 420,
10297
- contentMinWidth: 680,
10298
- header: tableHeader(TRANSFER_COLUMNS, ["Date", "Type", "Workspace", "Credits"]),
10299
- isEmpty: transfersFailed || transfers.length === 0,
10300
- emptyText: transfersFailed ? "Couldn't load transfers. Try again later." : "No transfers yet",
10301
- children: transfers.map((transfer) => /* @__PURE__ */ jsx27(TransferRow, { transfer }, transfer.id))
10302
- }
10303
- ),
10304
- transfers.length < transfersTotal && /* @__PURE__ */ jsx27("div", { children: /* @__PURE__ */ jsx27(
10305
- ScButton12,
10306
- {
10307
- text: loadingMore ? "Loading\u2026" : "Load more",
10308
- variant: "secondary",
10309
- size: "sm",
10310
- state: loadingMore ? "disabled" : "default",
10311
- onClick: loadingMore ? void 0 : () => void loadMoreTransfers()
10312
- }
10313
- ) })
10314
- ] }),
10315
- creating && api.createCreditAllocation && /* @__PURE__ */ jsx27(
10316
- CreditAllocationModal,
10091
+ adding && api.addCreditPoolMember && /* @__PURE__ */ jsx27(
10092
+ CreditPoolMemberModal,
10317
10093
  {
10318
- kind: "create",
10319
- targets,
10320
- balance,
10321
- onSave: (input) => api.createCreditAllocation(input),
10322
- onClose: () => setCreating(false),
10094
+ kind: "add",
10095
+ candidates,
10096
+ onSave: (input) => api.addCreditPoolMember(input),
10097
+ onClose: () => setAdding(false),
10323
10098
  onSaved: () => void load({ refresh: true })
10324
10099
  }
10325
10100
  ),
10326
- editing && api.updateCreditAllocation && /* @__PURE__ */ jsx27(
10327
- CreditAllocationModal,
10101
+ editing && api.updateCreditPoolMember && /* @__PURE__ */ jsx27(
10102
+ CreditPoolMemberModal,
10328
10103
  {
10329
10104
  kind: "edit",
10330
- allocation: editing,
10331
- targetName: allocationWorkspaceName(editing.targetWorkspaceName),
10332
- onSave: (patch) => api.updateCreditAllocation(editing._id, patch),
10105
+ member: editing,
10106
+ memberName: poolWorkspaceName(editing.memberWorkspaceName),
10107
+ onSave: (patch) => api.updateCreditPoolMember(editing._id, patch),
10333
10108
  onClose: () => setEditing(null),
10334
10109
  onSaved: () => void load({ refresh: true })
10335
10110
  }
10336
10111
  ),
10337
- canceling && api.cancelCreditAllocation && /* @__PURE__ */ jsx27(
10338
- CancelCreditAllocationModal,
10112
+ removing && api.removeCreditPoolMember && /* @__PURE__ */ jsx27(
10113
+ RemovePoolMemberModal,
10339
10114
  {
10340
- allocation: canceling,
10341
- targetName: allocationWorkspaceName(canceling.targetWorkspaceName),
10342
- onCancel: (reason) => api.cancelCreditAllocation(canceling._id, reason),
10343
- onClose: () => setCanceling(null),
10344
- onCanceled: ({ planNotRestored: stuck }) => {
10345
- if (stuck) {
10346
- const id = canceling._id;
10347
- setPlanNotRestored((prev) => new Set(prev).add(id));
10348
- }
10349
- void load({ refresh: true });
10350
- }
10115
+ member: removing,
10116
+ memberName: poolWorkspaceName(removing.memberWorkspaceName),
10117
+ sponsorName: workspaceName,
10118
+ onRemove: (reason) => api.removeCreditPoolMember(removing._id, reason),
10119
+ onClose: () => setRemoving(null),
10120
+ onRemoved: () => void load({ refresh: true })
10351
10121
  }
10352
10122
  )
10353
10123
  ] });
@@ -11237,8 +11007,11 @@ function InvoiceHistoryTable({
11237
11007
  function UsageLogsTable({
11238
11008
  logs,
11239
11009
  userMap,
11240
- loading
11010
+ loading,
11011
+ currentWorkspaceId = null,
11012
+ usedByNames = {}
11241
11013
  }) {
11014
+ const showWorkspace = logs.some((log) => usedByWorkspaceKey(log, currentWorkspaceId));
11242
11015
  if (loading) {
11243
11016
  return /* @__PURE__ */ jsx28(
11244
11017
  "div",
@@ -11315,6 +11088,14 @@ function UsageLogsTable({
11315
11088
  style: { flexShrink: 0, width: "7.5rem" }
11316
11089
  }
11317
11090
  ),
11091
+ showWorkspace && /* @__PURE__ */ jsx28(
11092
+ ScHeader,
11093
+ {
11094
+ text: "Workspace",
11095
+ state: "none",
11096
+ style: { flexShrink: 0, width: "9rem" }
11097
+ }
11098
+ ),
11318
11099
  /* @__PURE__ */ jsx28(
11319
11100
  ScHeader,
11320
11101
  {
@@ -11361,6 +11142,8 @@ function UsageLogsTable({
11361
11142
  logs.map((log) => {
11362
11143
  const userName = log.triggeredByUserId ? userMap[log.triggeredByUserId] || "Unknown" : "System";
11363
11144
  const app = log.service?.serviceGroupName || "\u2014";
11145
+ const usedBy = usedByWorkspaceKey(log, currentWorkspaceId);
11146
+ const workspaceLabel2 = usedBy ? `Used by ${usedByNames[usedBy.toLowerCase()] ?? "another workspace"}` : "This workspace";
11364
11147
  const activity = log.type === "return" ? "Returned Credits" : log.meta?.info || "Credit usage";
11365
11148
  const isPositive = log.type === "return" || log.type === "add";
11366
11149
  const signedAmount = `${isPositive ? "+" : "-"}${log.amount}`;
@@ -11390,6 +11173,18 @@ function UsageLogsTable({
11390
11173
  /* @__PURE__ */ jsx28("div", { style: { ...rowCellRegular, flex: 1 }, children: app }),
11391
11174
  /* @__PURE__ */ jsx28("div", { style: { ...rowCellRegular, flex: 1 }, children: activity }),
11392
11175
  /* @__PURE__ */ jsx28("div", { style: { ...rowCellRegular, width: "7.5rem" }, children: userName }),
11176
+ showWorkspace && /* @__PURE__ */ jsx28(
11177
+ "div",
11178
+ {
11179
+ style: {
11180
+ ...rowCellRegular,
11181
+ width: "9rem",
11182
+ color: usedBy ? "var(--alias-text---icons-primary, #f5f5f5)" : "var(--alias-text---icons-muted)"
11183
+ },
11184
+ title: workspaceLabel2,
11185
+ children: workspaceLabel2
11186
+ }
11187
+ ),
11393
11188
  /* @__PURE__ */ jsx28("div", { style: { ...rowCellRegular, width: "7.5rem" }, children: formatDate3(log.createdAt) }),
11394
11189
  /* @__PURE__ */ jsx28(
11395
11190
  "div",
@@ -11458,8 +11253,8 @@ function BillingContent({
11458
11253
  onShowPlansChange,
11459
11254
  showPerUserLimits: showPerUserLimitsProp,
11460
11255
  onShowPerUserLimitsChange,
11461
- showCreditAllocations: showCreditAllocationsProp,
11462
- onShowCreditAllocationsChange
11256
+ showSharedCredits: showSharedCreditsProp,
11257
+ onShowSharedCreditsChange
11463
11258
  } = {}) {
11464
11259
  const { auth, workspace, organization, api, toast, navigation } = useSettingsAdapters();
11465
11260
  const user = auth.user;
@@ -11484,21 +11279,23 @@ function BillingContent({
11484
11279
  if (onShowPerUserLimitsChange) onShowPerUserLimitsChange(next);
11485
11280
  else setShowPerUserLimitsInternal(next);
11486
11281
  };
11487
- const canAllocateCredits = canShowCreditAllocations(
11282
+ const canShareCredits = canShowSharedCredits(
11488
11283
  api,
11489
11284
  workspace.current,
11490
11285
  organization?.current
11491
11286
  );
11492
- const [showCreditAllocationsInternal, setShowCreditAllocationsInternal] = useState20(false);
11493
- const showCreditAllocations = (showCreditAllocationsProp ?? showCreditAllocationsInternal) && canAllocateCredits;
11494
- const setShowCreditAllocations = (next) => {
11495
- if (onShowCreditAllocationsChange) onShowCreditAllocationsChange(next);
11496
- else setShowCreditAllocationsInternal(next);
11287
+ const [showSharedCreditsInternal, setShowSharedCreditsInternal] = useState20(false);
11288
+ const showSharedCredits = (showSharedCreditsProp ?? showSharedCreditsInternal) && canShareCredits;
11289
+ const setShowSharedCredits = (next) => {
11290
+ if (onShowSharedCreditsChange) onShowSharedCreditsChange(next);
11291
+ else setShowSharedCreditsInternal(next);
11497
11292
  };
11498
11293
  const [showExportModal, setShowExportModal] = useState20(false);
11499
11294
  const [showBuyCreditsModal, setShowBuyCreditsModal] = useState20(false);
11500
11295
  const [subscription, setSubscription] = useState20(null);
11501
11296
  const [availableCredits, setAvailableCredits] = useState20(null);
11297
+ const [sharedPool, setSharedPool] = useState20(null);
11298
+ const [usedByNames, setUsedByNames] = useState20({});
11502
11299
  const [products, setProducts] = useState20([]);
11503
11300
  const [invoices, setInvoices] = useState20([]);
11504
11301
  const [totalCredits, setTotalCredits] = useState20(null);
@@ -11549,6 +11346,7 @@ function BillingContent({
11549
11346
  if (subRes?.data) setSubscription(subRes.data);
11550
11347
  if (creditsRes?.availableCredits != null)
11551
11348
  setAvailableCredits(creditsRes.availableCredits);
11349
+ setSharedPool(creditsRes?.sharedPool ?? null);
11552
11350
  if (productsRes?.data) {
11553
11351
  const normalizedWorkspaceCountry = workspace.current?.country?.trim().toUpperCase();
11554
11352
  const targetCurrency = normalizedWorkspaceCountry === "IN" ? "inr" : "usd";
@@ -11597,7 +11395,12 @@ function BillingContent({
11597
11395
  );
11598
11396
  } else if (api.fetchUsageLogs) {
11599
11397
  const res = await api.fetchUsageLogs();
11600
- setUsageLogs(res.data?.length ? res.data : []);
11398
+ const logs = res.data?.length ? res.data : [];
11399
+ setUsageLogs(logs);
11400
+ if (api.fetchCreditPool && logs.some((log) => usedByWorkspaceKey(log, workspaceId))) {
11401
+ const pool = await api.fetchCreditPool().catch(() => null);
11402
+ setUsedByNames(usedByNamesFrom(pool));
11403
+ }
11601
11404
  } else {
11602
11405
  setUsageLogs([]);
11603
11406
  }
@@ -11608,7 +11411,7 @@ function BillingContent({
11608
11411
  setLoadingTable(false);
11609
11412
  }
11610
11413
  },
11611
- [api]
11414
+ [api, workspaceId]
11612
11415
  );
11613
11416
  const handlePlanAction = useCallback8(
11614
11417
  async ({ productId, priceId, planName: planName2 }) => {
@@ -11766,10 +11569,13 @@ function BillingContent({
11766
11569
  );
11767
11570
  const scheduledDowngradeText = subscription?.downgradeScheduled && subscription.plan?.name ? `Downgrades to ${subscription.plan.name}${scheduledDowngradeDateLabel ? ` ${scheduledDowngradeDateLabel}` : ""}` : void 0;
11768
11571
  const isPlanDetailsLoading = !billingOverviewLoaded || loadingBilling || subscription?.plan == null || !planName || planName === "-" || !planAmount || currentPlanCredits == null;
11769
- const usedCredits = availableCredits != null && totalCredits != null ? Math.max(0, totalCredits - availableCredits) : null;
11770
- const progressPct = usedCredits != null && totalCredits != null && totalCredits > 0 ? Math.min(100, Math.round(usedCredits / totalCredits * 100)) : 0;
11771
- const remainingPct = 100 - progressPct;
11772
- const usageText = usedCredits != null ? formatCredits2(usedCredits) : availableCredits != null ? `${formatCredits2(availableCredits)} available` : customPlanCredits != null ? `${formatCredits2(customPlanCredits)} credits` : "\u2014";
11572
+ const {
11573
+ used: usedCredits,
11574
+ remaining: remainingCredits,
11575
+ progressPct,
11576
+ remainingPct
11577
+ } = creditsMeter({ availableCredits, totalCredits, sharedPool });
11578
+ const usageText = usedCredits != null ? formatCredits2(usedCredits) : remainingCredits != null ? `${formatCredits2(remainingCredits)} available` : customPlanCredits != null ? `${formatCredits2(customPlanCredits)} credits` : "\u2014";
11773
11579
  const handleExport = async (startDate, endDate) => {
11774
11580
  const end = new Date(endDate);
11775
11581
  end.setHours(23, 59, 59, 999);
@@ -11846,7 +11652,7 @@ function BillingContent({
11846
11652
  function fmtFilename(d) {
11847
11653
  return d.toISOString().slice(0, 10);
11848
11654
  }
11849
- async function handleBuyCredits(credits, price) {
11655
+ async function handleBuyCredits(credits2, price) {
11850
11656
  if (!workspaceId) {
11851
11657
  toast.error("Missing workspace details for credit top-up");
11852
11658
  return;
@@ -11858,14 +11664,14 @@ function BillingContent({
11858
11664
  "Buy credits confirmed",
11859
11665
  {
11860
11666
  action: "confirm_buy_credits",
11861
- additionalInfo: `${credits}:${price}`
11667
+ additionalInfo: `${credits2}:${price}`
11862
11668
  }
11863
11669
  );
11864
11670
  try {
11865
11671
  const topupCurrency = workspace.current?.country?.trim().toUpperCase() === "IN" ? "inr" : "usd";
11866
11672
  const res = await api.buyCredits({
11867
11673
  serviceName: DEFAULT_TOPUP_SERVICE,
11868
- credits,
11674
+ credits: credits2,
11869
11675
  unitAmount: price,
11870
11676
  itemCount: 1,
11871
11677
  currency: topupCurrency,
@@ -11883,7 +11689,7 @@ function BillingContent({
11883
11689
  setAvailableCredits(payloadData.availableCredits);
11884
11690
  } else {
11885
11691
  setAvailableCredits(
11886
- (prev) => typeof prev === "number" ? prev + credits : prev
11692
+ (prev) => typeof prev === "number" ? prev + credits2 : prev
11887
11693
  );
11888
11694
  }
11889
11695
  toast.success("Top-up created");
@@ -11953,13 +11759,13 @@ function BillingContent({
11953
11759
  }
11954
11760
  ) });
11955
11761
  }
11956
- if (showCreditAllocations) {
11957
- const allocationsView = /* @__PURE__ */ jsxs25("div", { className: "flex flex-col flex-1 h-full min-h-0 min-w-0 w-full", style: { gap: 16 }, children: [
11958
- !onShowCreditAllocationsChange && /* @__PURE__ */ jsx28(BackToBilling, { onClick: () => setShowCreditAllocations(false) }),
11959
- /* @__PURE__ */ jsx28(CreditAllocationsView, {})
11762
+ if (showSharedCredits) {
11763
+ const sharedCreditsView = /* @__PURE__ */ jsxs25("div", { className: "flex flex-col flex-1 h-full min-h-0 min-w-0 w-full", style: { gap: 16 }, children: [
11764
+ !onShowSharedCreditsChange && /* @__PURE__ */ jsx28(BackToBilling, { onClick: () => setShowSharedCredits(false) }),
11765
+ /* @__PURE__ */ jsx28(SharedCreditsView, {})
11960
11766
  ] });
11961
- if (embedded) return allocationsView;
11962
- return /* @__PURE__ */ jsx28("div", { className: "flex-1 h-full min-h-0 min-w-0 relative", children: allocationsView });
11767
+ if (embedded) return sharedCreditsView;
11768
+ return /* @__PURE__ */ jsx28("div", { className: "flex-1 h-full min-h-0 min-w-0 relative", children: sharedCreditsView });
11963
11769
  }
11964
11770
  if (showPerUserLimits) {
11965
11771
  const limitsView = /* @__PURE__ */ jsxs25("div", { className: "flex flex-col flex-1 h-full min-h-0 min-w-0 w-full", style: { gap: 16 }, children: [
@@ -12026,7 +11832,7 @@ function BillingContent({
12026
11832
  {
12027
11833
  usageLabel: "Credits used",
12028
11834
  usageText,
12029
- remainingText: availableCredits != null ? `${formatCredits2(availableCredits)} (${remainingPct}%) Remaining` : `${remainingPct}% Remaining`,
11835
+ remainingText: remainingCredits != null ? `${formatCredits2(remainingCredits)} (${remainingPct}%) Remaining` : `${remainingPct}% Remaining`,
12030
11836
  buyButtonText: "Buy credits",
12031
11837
  buyButtonState: isFreePlan ? "disabled" : "default",
12032
11838
  buyButtonHoverText: isFreePlan ? "Pro plan required" : void 0,
@@ -12046,6 +11852,50 @@ function BillingContent({
12046
11852
  ]
12047
11853
  }
12048
11854
  ),
11855
+ sharedPool ? /* @__PURE__ */ jsxs25(
11856
+ "div",
11857
+ {
11858
+ role: "status",
11859
+ className: "flex flex-col shrink-0 w-full",
11860
+ style: {
11861
+ gap: 4,
11862
+ padding: 16,
11863
+ backgroundColor: "var(--alias-surface-base)",
11864
+ border: "1px solid var(--alias-border-subtle)",
11865
+ borderRadius: "var(--radius-3xl)"
11866
+ },
11867
+ children: [
11868
+ /* @__PURE__ */ jsx28(
11869
+ "span",
11870
+ {
11871
+ style: {
11872
+ fontFamily: "var(--font-family-font-family-body)",
11873
+ fontSize: "var(--font-size-font-size-sm)",
11874
+ fontWeight: 500,
11875
+ lineHeight: "var(--line-height-line-height-sm)",
11876
+ color: "var(--alias-text---icons-primary)"
11877
+ },
11878
+ children: sharedPoolBannerText(sharedPool)
11879
+ }
11880
+ ),
11881
+ /* @__PURE__ */ jsxs25(
11882
+ "span",
11883
+ {
11884
+ style: {
11885
+ fontFamily: "var(--font-family-font-family-body)",
11886
+ fontSize: "var(--font-size-font-size-sm)",
11887
+ lineHeight: "var(--line-height-line-height-sm)",
11888
+ color: "var(--alias-text---icons-muted)"
11889
+ },
11890
+ children: [
11891
+ "This workspace spends these shared credits, not its own; credits bought here can't be used while it does.",
11892
+ sharedPool.cycleEnd ? ` The allowance starts again on ${formatDate3(sharedPool.cycleEnd)}.` : ""
11893
+ ]
11894
+ }
11895
+ )
11896
+ ]
11897
+ }
11898
+ ) : null,
12049
11899
  usage ? /* @__PURE__ */ jsxs25(
12050
11900
  "div",
12051
11901
  {
@@ -12119,7 +11969,7 @@ function BillingContent({
12119
11969
  ]
12120
11970
  }
12121
11971
  ) : null,
12122
- canAllocateCredits ? /* @__PURE__ */ jsxs25(
11972
+ canShareCredits ? /* @__PURE__ */ jsxs25(
12123
11973
  "div",
12124
11974
  {
12125
11975
  className: "flex items-center justify-between shrink-0 w-full",
@@ -12142,7 +11992,7 @@ function BillingContent({
12142
11992
  lineHeight: "var(--line-height-line-height-sm)",
12143
11993
  color: "var(--alias-text---icons-primary)"
12144
11994
  },
12145
- children: "Credit allocations"
11995
+ children: "Shared credits"
12146
11996
  }
12147
11997
  ),
12148
11998
  /* @__PURE__ */ jsx28(
@@ -12154,7 +12004,7 @@ function BillingContent({
12154
12004
  lineHeight: "var(--line-height-line-height-sm)",
12155
12005
  color: "var(--alias-text---icons-muted)"
12156
12006
  },
12157
- children: "Share this workspace's credits with other workspaces in your organization"
12007
+ children: "Let other workspaces in your organization spend this workspace's credits"
12158
12008
  }
12159
12009
  )
12160
12010
  ] }),
@@ -12164,7 +12014,7 @@ function BillingContent({
12164
12014
  text: "Manage",
12165
12015
  variant: "secondary",
12166
12016
  size: "md",
12167
- onClick: () => setShowCreditAllocations(true)
12017
+ onClick: () => setShowSharedCredits(true)
12168
12018
  }
12169
12019
  )
12170
12020
  ]
@@ -12214,7 +12064,9 @@ function BillingContent({
12214
12064
  {
12215
12065
  logs: usageLogs,
12216
12066
  userMap,
12217
- loading: loadingTable
12067
+ loading: loadingTable,
12068
+ currentWorkspaceId: workspaceId,
12069
+ usedByNames
12218
12070
  }
12219
12071
  ),
12220
12072
  showExportModal && /* @__PURE__ */ jsx28(
@@ -12233,8 +12085,8 @@ function BillingContent({
12233
12085
  workspace.current?.country
12234
12086
  ),
12235
12087
  onClose: () => setShowBuyCreditsModal(false),
12236
- onBuy: (credits, price) => {
12237
- handleBuyCredits(credits, price);
12088
+ onBuy: (credits2, price) => {
12089
+ handleBuyCredits(credits2, price);
12238
12090
  }
12239
12091
  }
12240
12092
  )
@@ -13259,7 +13111,7 @@ function ConnectedAppsContent() {
13259
13111
 
13260
13112
  // src/organization-integrations-content.tsx
13261
13113
  import { useState as useState23 } from "react";
13262
- import { ScButton as ScButton16, ScToggleSwitch as ScToggleSwitch2 } from "@streamoid/ui";
13114
+ import { ScButton as ScButton16, ScToggleSwitch } from "@streamoid/ui";
13263
13115
  import { Fragment as Fragment14, jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
13264
13116
  var COLUMN = { app: "6.5rem", workspace: "9rem", date: "6.5rem", action: "4.75rem" };
13265
13117
  function HeaderRow() {
@@ -13388,7 +13240,7 @@ function AccessCard({
13388
13240
  /* @__PURE__ */ jsxs28("div", { className: "flex items-center shrink-0", style: { alignSelf: "center", gap: T15.spMd, minHeight: 26 }, children: [
13389
13241
  /* @__PURE__ */ jsx31("span", { style: { fontFamily: T15.font, fontSize: T15.fsSm, lineHeight: T15.lhSm, color: T15.muted }, children: enabled === null ? "" : enabled ? "On" : "Off" }),
13390
13242
  /* @__PURE__ */ jsx31("span", { style: { display: "inline-flex", width: 41, height: 25, alignItems: "center" }, children: /* @__PURE__ */ jsx31(
13391
- ScToggleSwitch2,
13243
+ ScToggleSwitch,
13392
13244
  {
13393
13245
  checked: !!enabled,
13394
13246
  disabled: enabled === null || loading,
@@ -14888,20 +14740,20 @@ function SettingsContentInner({
14888
14740
  const isBillingPlans = activeTab === "billing" && isWorkspaceAdmin && billingShowPlans;
14889
14741
  const [billingShowPerUserLimits, setBillingShowPerUserLimits] = useState24(false);
14890
14742
  const isBillingPerUserLimits = activeTab === "billing" && isWorkspaceAdmin && billingShowPerUserLimits && !billingShowPlans;
14891
- const [billingShowCreditAllocations, setBillingShowCreditAllocations] = useState24(false);
14892
- const isBillingCreditAllocations = activeTab === "billing" && isWorkspaceAdmin && billingShowCreditAllocations && !billingShowPlans && !billingShowPerUserLimits;
14893
- const isBillingSubView = isBillingPlans || isBillingPerUserLimits || isBillingCreditAllocations;
14743
+ const [billingShowSharedCredits, setBillingShowSharedCredits] = useState24(false);
14744
+ const isBillingSharedCredits = activeTab === "billing" && isWorkspaceAdmin && billingShowSharedCredits && !billingShowPlans && !billingShowPerUserLimits;
14745
+ const isBillingSubView = isBillingPlans || isBillingPerUserLimits || isBillingSharedCredits;
14894
14746
  useEffect19(() => {
14895
14747
  if (activeTab !== "billing" || !isWorkspaceAdmin) {
14896
14748
  setBillingShowPlans(false);
14897
14749
  setBillingShowPerUserLimits(false);
14898
- setBillingShowCreditAllocations(false);
14750
+ setBillingShowSharedCredits(false);
14899
14751
  }
14900
14752
  }, [activeTab, isWorkspaceAdmin]);
14901
14753
  useEffect19(() => {
14902
14754
  setBillingShowPlans(false);
14903
14755
  setBillingShowPerUserLimits(false);
14904
- setBillingShowCreditAllocations(false);
14756
+ setBillingShowSharedCredits(false);
14905
14757
  }, [workspaceId]);
14906
14758
  useEffect19(() => {
14907
14759
  if (!onClose) return;
@@ -14916,15 +14768,15 @@ function SettingsContentInner({
14916
14768
  setBillingShowPerUserLimits(false);
14917
14769
  return;
14918
14770
  }
14919
- if (billingShowCreditAllocations) {
14920
- setBillingShowCreditAllocations(false);
14771
+ if (billingShowSharedCredits) {
14772
+ setBillingShowSharedCredits(false);
14921
14773
  return;
14922
14774
  }
14923
14775
  onClose();
14924
14776
  };
14925
14777
  document.addEventListener("keydown", handleKeyDown);
14926
14778
  return () => document.removeEventListener("keydown", handleKeyDown);
14927
- }, [onClose, billingShowPlans, billingShowPerUserLimits, billingShowCreditAllocations]);
14779
+ }, [onClose, billingShowPlans, billingShowPerUserLimits, billingShowSharedCredits]);
14928
14780
  const [localWorkspaces, setLocalWorkspaces] = useState24(null);
14929
14781
  const [wsModalOpen, setWsModalOpen] = useState24(false);
14930
14782
  const [wsModalMode, setWsModalMode] = useState24("create");
@@ -15245,7 +15097,7 @@ function SettingsContentInner({
15245
15097
  if (isBillingSubView) {
15246
15098
  setBillingShowPlans(false);
15247
15099
  setBillingShowPerUserLimits(false);
15248
- setBillingShowCreditAllocations(false);
15100
+ setBillingShowSharedCredits(false);
15249
15101
  return;
15250
15102
  }
15251
15103
  if (onBack) onBack();
@@ -15348,7 +15200,7 @@ function SettingsContentInner({
15348
15200
  }
15349
15201
  )
15350
15202
  ] }),
15351
- isBillingCreditAllocations && /* @__PURE__ */ jsxs29(Fragment16, { children: [
15203
+ isBillingSharedCredits && /* @__PURE__ */ jsxs29(Fragment16, { children: [
15352
15204
  /* @__PURE__ */ jsx32(SiconRight3, { className: "w-6 h-6 shrink-0", color: "var(--alias-text---icons-primary)" }),
15353
15205
  /* @__PURE__ */ jsx32(
15354
15206
  "span",
@@ -15360,7 +15212,7 @@ function SettingsContentInner({
15360
15212
  lineHeight: "var(--line-height-line-height-xl)",
15361
15213
  color: "var(--alias-text---icons-primary)"
15362
15214
  },
15363
- children: "Credit allocations"
15215
+ children: "Shared credits"
15364
15216
  }
15365
15217
  )
15366
15218
  ] })
@@ -15573,7 +15425,7 @@ function SettingsContentInner({
15573
15425
  setBillingShowPlans(open);
15574
15426
  if (open) {
15575
15427
  setBillingShowPerUserLimits(false);
15576
- setBillingShowCreditAllocations(false);
15428
+ setBillingShowSharedCredits(false);
15577
15429
  }
15578
15430
  },
15579
15431
  showPerUserLimits: billingShowPerUserLimits,
@@ -15581,12 +15433,12 @@ function SettingsContentInner({
15581
15433
  setBillingShowPerUserLimits(open);
15582
15434
  if (open) {
15583
15435
  setBillingShowPlans(false);
15584
- setBillingShowCreditAllocations(false);
15436
+ setBillingShowSharedCredits(false);
15585
15437
  }
15586
15438
  },
15587
- showCreditAllocations: billingShowCreditAllocations,
15588
- onShowCreditAllocationsChange: (open) => {
15589
- setBillingShowCreditAllocations(open);
15439
+ showSharedCredits: billingShowSharedCredits,
15440
+ onShowSharedCreditsChange: (open) => {
15441
+ setBillingShowSharedCredits(open);
15590
15442
  if (open) {
15591
15443
  setBillingShowPlans(false);
15592
15444
  setBillingShowPerUserLimits(false);
@@ -16342,18 +16194,13 @@ function OrganizationPicker({
16342
16194
  );
16343
16195
  }
16344
16196
  export {
16345
- ALLOCATION_ERROR_CODES,
16346
- ALLOCATION_NOTE_MAX_LENGTH,
16347
16197
  APP_KEYS,
16348
16198
  BillingContent,
16349
16199
  BuyCreditsModal,
16350
- CANCEL_REASON_MAX_LENGTH,
16351
- CancelCreditAllocationModal,
16352
16200
  ConfirmDeleteModal,
16353
16201
  ConfirmLeaveModal,
16354
16202
  ConnectedAppsContent,
16355
- CreditAllocationModal,
16356
- CreditAllocationsView,
16203
+ CreditPoolMemberModal,
16357
16204
  DEFAULT_PERMISSIONS,
16358
16205
  DEFAULT_USER_LANGUAGE,
16359
16206
  ExportDateModal,
@@ -16366,15 +16213,20 @@ export {
16366
16213
  OrganizationMemberModal,
16367
16214
  OrganizationModal,
16368
16215
  OrganizationPicker,
16216
+ POOL_ERROR_CODES,
16217
+ POOL_NOTE_MAX_LENGTH,
16369
16218
  PerUserLimitsView,
16370
16219
  ProfileAvatar,
16371
16220
  ProfileTab,
16221
+ REMOVE_REASON_MAX_LENGTH,
16372
16222
  ReferralTab,
16223
+ RemovePoolMemberModal,
16373
16224
  SCOPE_LABELS,
16374
16225
  ScopeHeader,
16375
16226
  SettingsAdaptersProvider,
16376
16227
  SettingsContent,
16377
16228
  SettingsTooltip,
16229
+ SharedCreditsView,
16378
16230
  SiconKey_default as SiconKey,
16379
16231
  TAB_DESCRIPTIONS,
16380
16232
  TAB_LABELS,
@@ -16390,28 +16242,22 @@ export {
16390
16242
  WorkspaceModal,
16391
16243
  WorkspacePicker,
16392
16244
  WorkspaceTab,
16393
- allocationErrorMessage,
16394
- allocationWorkspaceName,
16395
16245
  belowUsageReason,
16396
- canShowCreditAllocations,
16397
- cancelOutcomeSummary,
16398
- cancelOutcomeWarning,
16399
- cleanAllocationValueInput,
16246
+ canShowSharedCredits,
16400
16247
  cleanCreditLimitInput,
16248
+ cleanPoolValueInput,
16401
16249
  countLabel,
16402
16250
  creditCycleUnit,
16403
- describeAllocation,
16251
+ creditsMeter,
16252
+ describeAllowance,
16404
16253
  describeDefaultLimit,
16405
16254
  describeMemberLimit,
16406
- findAllocationError,
16407
16255
  findBillingError,
16408
- formatAllocationRepeat,
16409
- formatAllocationSplit,
16256
+ findPoolError,
16410
16257
  formatCreditLimit,
16411
16258
  formatCredits,
16259
+ formatPoolSplit,
16412
16260
  groupWorkspaceList,
16413
- initialTransferSummary,
16414
- initialTransferWarning,
16415
16261
  isLimitBelowUsage,
16416
16262
  joinMembersWithLimits,
16417
16263
  lastUsedLabel,
@@ -16420,13 +16266,18 @@ export {
16420
16266
  loadWorkspaceOrganization,
16421
16267
  memberRoleUpdate,
16422
16268
  minimumCreditLimit,
16269
+ poolErrorMessage,
16270
+ poolWorkspaceName,
16423
16271
  resolveSettingsTabFromPath,
16424
16272
  roundCredits,
16273
+ sharedPoolBannerText,
16425
16274
  useSettingsAdapters,
16426
16275
  useWorkspaceOrganization,
16276
+ usedByNamesFrom,
16277
+ usedByWorkspaceKey,
16427
16278
  userLanguageLabel,
16428
- validateAllocationText,
16429
- validateAllocationValue,
16430
16279
  validateCreditLimitInput,
16431
- validateDefaultCreditLimitInput
16280
+ validateDefaultCreditLimitInput,
16281
+ validatePoolText,
16282
+ validatePoolValue
16432
16283
  };