@streamoid/settings 0.6.6 → 0.7.1

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.js CHANGED
@@ -32,7 +32,7 @@ __export(index_exports, {
32
32
  MemberListFrame: () => MemberListFrame,
33
33
  MemberListToolbar: () => MemberListToolbar,
34
34
  OrganizationContent: () => OrganizationContent,
35
- OrganizationInviteModal: () => OrganizationInviteModal,
35
+ OrganizationMemberModal: () => OrganizationMemberModal,
36
36
  OrganizationModal: () => OrganizationModal,
37
37
  ProfileAvatar: () => ProfileAvatar,
38
38
  ProfileTab: () => ProfileTab,
@@ -51,9 +51,11 @@ __export(index_exports, {
51
51
  TokensContent: () => TokensContent,
52
52
  URL_TO_TAB: () => URL_TO_TAB,
53
53
  USER_LANGUAGES: () => USER_LANGUAGES,
54
+ WORKSPACE_LIST_PAGE_SIZE: () => WORKSPACE_LIST_PAGE_SIZE,
54
55
  WorkspaceGeneralTab: () => WorkspaceGeneralTab,
55
56
  WorkspaceListTab: () => WorkspaceListTab,
56
57
  WorkspaceModal: () => WorkspaceModal,
58
+ WorkspacePicker: () => WorkspacePicker,
57
59
  WorkspaceTab: () => WorkspaceTab,
58
60
  countLabel: () => countLabel,
59
61
  loadWorkspaceOrganization: () => loadWorkspaceOrganization,
@@ -431,8 +433,9 @@ function WorkspaceModal({
431
433
  let cancelled = false;
432
434
  organizationAdapter.list().then((orgs) => {
433
435
  if (cancelled) return;
434
- setAvailableOrgs(orgs);
435
- setSelectedOrgId((prev) => prev || orgs[0]?._id || "");
436
+ const joinable = orgs.filter((org) => org.role !== "guest");
437
+ setAvailableOrgs(joinable);
438
+ setSelectedOrgId((prev) => prev || joinable[0]?._id || "");
436
439
  }).catch(() => {
437
440
  if (!cancelled) setAvailableOrgs([]);
438
441
  });
@@ -547,7 +550,7 @@ function WorkspaceModal({
547
550
  async function handleLeaveOrganization() {
548
551
  if (orgActionSubmitting || !workspaceAdapter.leaveOrganization) return;
549
552
  const confirmed = window.confirm(
550
- "Leave this organization? Members of the organization will lose any implicit access to this workspace."
553
+ "Take this workspace out of the organization? It will no longer be listed in the workspace picker, and org admins will lose access unless they are members."
551
554
  );
552
555
  if (!confirmed) return;
553
556
  setOrgActionSubmitting(true);
@@ -892,7 +895,7 @@ function WorkspaceModal({
892
895
  color: active ? "#ffffff" : T.muted,
893
896
  textAlign: "center"
894
897
  },
895
- children: opt === "private" ? "Private" : "Visible to organization"
898
+ children: opt === "private" ? "Private" : "Listed in picker"
896
899
  }
897
900
  ) })
898
901
  },
@@ -1643,7 +1646,7 @@ var TAB_TO_URL = {
1643
1646
  workspaceList: "workspace/all",
1644
1647
  organization: "organization/people",
1645
1648
  organizationWorkspaces: "organization/workspaces",
1646
- organizationDomains: "organization/domains",
1649
+ organizationDomains: "organization/workspaces",
1647
1650
  referral: "referral",
1648
1651
  billing: "billing",
1649
1652
  apikey: "api-keys"
@@ -1655,7 +1658,8 @@ var URL_TO_TAB = {
1655
1658
  "workspace/all": "workspaceList",
1656
1659
  "organization/people": "organization",
1657
1660
  "organization/workspaces": "organizationWorkspaces",
1658
- "organization/domains": "organizationDomains",
1661
+ /* The Domains tab is gone; its join requests live under Workspaces. */
1662
+ "organization/domains": "organizationWorkspaces",
1659
1663
  /* Unscoped destinations. */
1660
1664
  account: "profile",
1661
1665
  referral: "referral",
@@ -1674,7 +1678,7 @@ var TAB_LABELS = {
1674
1678
  workspaceList: "My workspaces",
1675
1679
  organization: "People",
1676
1680
  organizationWorkspaces: "Workspaces",
1677
- organizationDomains: "Domains & requests",
1681
+ organizationDomains: "Workspaces",
1678
1682
  referral: "Referral",
1679
1683
  /* "Billing & usage", not "Billing". The tab has held usage history as well
1680
1684
  as the plan and invoices since the credits work, and hosts had started
@@ -1688,9 +1692,9 @@ var TAB_DESCRIPTIONS = {
1688
1692
  workspace: "Rename or re-brand this workspace, or leave and delete it",
1689
1693
  workspaceMembers: "Invite people to this workspace and control which apps they can use",
1690
1694
  workspaceList: "Switch between your workspaces, or start a new one",
1691
- organization: "Everyone in the organization, and the invites still waiting to be accepted",
1692
- organizationWorkspaces: "Every workspace that sits under this organization",
1693
- organizationDomains: "Auto-join by email domain, and workspaces asking to join the organization",
1695
+ organization: "Everyone in the organization and their role",
1696
+ organizationWorkspaces: "Every workspace in this organization, and workspaces asking to join it",
1697
+ organizationDomains: "Every workspace in this organization, and workspaces asking to join it",
1694
1698
  referral: "Invite friends to Streamoid and track the rewards you have earned",
1695
1699
  billing: "Workspace plan, credits, invoices and usage history",
1696
1700
  apikey: "Create and revoke keys for calling the Streamoid APIs"
@@ -2309,7 +2313,7 @@ function OrganizationAffiliationCard({
2309
2313
  style: { gap: T5.spMd, opacity: submitting ? 0.6 : 1 },
2310
2314
  children: [
2311
2315
  workspaceAdapter.setOrgVisibility && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-start w-full", style: { gap: T5.spXs }, children: [
2312
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldLabel, { children: "Who in the organization can see this workspace" }),
2316
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldLabel, { children: "List this workspace in the organization's workspace picker" }),
2313
2317
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2314
2318
  "div",
2315
2319
  {
@@ -2340,11 +2344,11 @@ function OrganizationAffiliationCard({
2340
2344
  if (active || !workspaceAdapter.setOrgVisibility) return;
2341
2345
  void run(
2342
2346
  () => workspaceAdapter.setOrgVisibility(opt),
2343
- opt === "private" ? "Workspace is now private" : "Workspace is now visible to the organization",
2347
+ opt === "private" ? "Workspace is now private" : "Workspace is now listed in the workspace picker",
2344
2348
  "Failed to change visibility"
2345
2349
  );
2346
2350
  },
2347
- children: opt === "private" ? "Only workspace members" : "Everyone in the organization"
2351
+ children: opt === "private" ? "Private (invite only)" : "Listed in the workspace picker"
2348
2352
  },
2349
2353
  opt
2350
2354
  );
@@ -2370,7 +2374,7 @@ function OrganizationAffiliationCard({
2370
2374
  },
2371
2375
  onClick: () => {
2372
2376
  const confirmed = window.confirm(
2373
- "Leave this organization? Members of the organization will lose any implicit access to this workspace."
2377
+ "Take this workspace out of the organization? It will no longer be listed in the workspace picker, and org admins will lose access unless they are members."
2374
2378
  );
2375
2379
  if (!confirmed || !workspaceAdapter.leaveOrganization) return;
2376
2380
  void run(
@@ -2763,12 +2767,13 @@ function InviteUpdateModal({
2763
2767
  organizationName,
2764
2768
  allowGuestStanding = true,
2765
2769
  initialOrganizationStanding = "guest",
2770
+ organizationDomain,
2766
2771
  onInvite
2767
2772
  }) {
2768
2773
  useOverlayOpen(open, onClose);
2769
2774
  const [emailValue, setEmailValue] = (0, import_react8.useState)("");
2770
2775
  const [hasChanges, setHasChanges] = (0, import_react8.useState)(false);
2771
- const [role, setRole] = (0, import_react8.useState)("admin");
2776
+ const [role, setRole] = (0, import_react8.useState)("member");
2772
2777
  const [orgStanding, setOrgStanding] = (0, import_react8.useState)(
2773
2778
  initialOrganizationStanding
2774
2779
  );
@@ -2807,7 +2812,7 @@ function InviteUpdateModal({
2807
2812
  if (!open) {
2808
2813
  setEmailValue("");
2809
2814
  setHasChanges(false);
2810
- setRole("admin");
2815
+ setRole("member");
2811
2816
  setOrgStanding(initialOrganizationStanding);
2812
2817
  setCanUseCredits(true);
2813
2818
  setCanInviteUser(true);
@@ -2819,6 +2824,14 @@ function InviteUpdateModal({
2819
2824
  setSelectedStores(/* @__PURE__ */ new Set());
2820
2825
  }
2821
2826
  }, [open, initialPermissions, hiddenKey, initialOrganizationStanding]);
2827
+ const orgDomain = (organizationDomain || "").trim().toLowerCase();
2828
+ const inviteeDomains = emailValue.split(/[\s,;]+/).map((email) => email.trim().toLowerCase().split("@")[1] || "").filter(Boolean);
2829
+ const inviteeIsGuest = mode === "invite" && inviteeDomains.some(
2830
+ (domain) => !(orgDomain && domain === orgDomain) && (showOrganizationStanding ? orgStanding === "guest" : !!orgDomain)
2831
+ );
2832
+ (0, import_react8.useEffect)(() => {
2833
+ if (inviteeIsGuest && role === "admin") setRole("member");
2834
+ }, [inviteeIsGuest, role]);
2822
2835
  if (!open) return null;
2823
2836
  const isInvite = mode === "invite";
2824
2837
  const hasEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailValue.trim());
@@ -3009,12 +3022,15 @@ function InviteUpdateModal({
3009
3022
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3010
3023
  "div",
3011
3024
  {
3012
- className: "flex-1 min-w-0 cursor-pointer",
3025
+ className: `flex-1 min-w-0 ${inviteeIsGuest ? "cursor-not-allowed" : "cursor-pointer"}`,
3026
+ "aria-disabled": inviteeIsGuest,
3013
3027
  style: {
3014
3028
  background: role === "admin" ? T6.neutral300 : "transparent",
3015
- borderRadius: T6.rMd
3029
+ borderRadius: T6.rMd,
3030
+ opacity: inviteeIsGuest ? 0.4 : 1
3016
3031
  },
3017
3032
  onClick: () => {
3033
+ if (inviteeIsGuest) return;
3018
3034
  setRole("admin");
3019
3035
  setCanUseCredits(true);
3020
3036
  setCanInviteUser(true);
@@ -3088,6 +3104,19 @@ function InviteUpdateModal({
3088
3104
  ) })
3089
3105
  ]
3090
3106
  }
3107
+ ),
3108
+ inviteeIsGuest && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3109
+ "span",
3110
+ {
3111
+ style: {
3112
+ fontFamily: T6.font,
3113
+ fontSize: T6.fsXs,
3114
+ fontWeight: 400,
3115
+ lineHeight: T6.lhXs,
3116
+ color: T6.tertiary
3117
+ },
3118
+ children: "Guests can only be Members."
3119
+ }
3091
3120
  )
3092
3121
  ]
3093
3122
  }
@@ -3277,7 +3306,7 @@ function InviteUpdateModal({
3277
3306
  lineHeight: T6.lhXs,
3278
3307
  color: T6.tertiary
3279
3308
  },
3280
- children: orgStanding === "colleague" ? `Joins ${organizationName || "the organization"} as a member \u2014 they'll also see its shared workspaces.` : `This workspace only. They see nothing else in ${organizationName || "the organization"}.`
3309
+ children: orgStanding === "colleague" ? `Joins ${organizationName || "the organization"} as a member \u2014 they can also join its shared workspaces.` : `This workspace only, as a member. They see nothing else in ${organizationName || "the organization"}.`
3281
3310
  }
3282
3311
  )
3283
3312
  ]
@@ -3972,7 +4001,19 @@ function MemberListToolbar({
3972
4001
  className: "flex flex-wrap items-center justify-between shrink-0 w-full",
3973
4002
  style: { gap: "var(--spacing-md)" },
3974
4003
  children: [
3975
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4004
+ pendingCount === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4005
+ "span",
4006
+ {
4007
+ style: {
4008
+ fontFamily: T7.font,
4009
+ fontSize: T7.fsSm,
4010
+ fontWeight: 500,
4011
+ lineHeight: T7.lhSm,
4012
+ color: T7.primary
4013
+ },
4014
+ children: withCount(activeLabel, activeCount)
4015
+ }
4016
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3976
4017
  import_ui4.ScTabSwitcher,
3977
4018
  {
3978
4019
  tabCount: "2",
@@ -4938,9 +4979,9 @@ function TeamsContent({
4938
4979
  lineHeight: "var(--line-height-line-height-xs)"
4939
4980
  },
4940
4981
  children: [
4941
- "This workspace is visible to everyone in ",
4982
+ "Anyone in ",
4942
4983
  organizationName,
4943
- ", and its admins keep access whether or not they are listed here. Change that under Workspace \u203A General."
4984
+ " can join this workspace as a member from the workspace picker, and its org admins keep access whether or not they are listed here. Change that under Workspace \u203A General."
4944
4985
  ]
4945
4986
  }
4946
4987
  ),
@@ -5022,6 +5063,7 @@ function TeamsContent({
5022
5063
  ),
5023
5064
  showOrganizationStanding: mayGrantOrgMembership,
5024
5065
  organizationName: organizationName ?? orgCurrent?.name,
5066
+ organizationDomain: workspaceOrgId && orgCurrent?.id === workspaceOrgId ? orgCurrent?.domain : void 0,
5025
5067
  onInvite: handleInvite
5026
5068
  }
5027
5069
  )
@@ -5092,7 +5134,7 @@ var import_react12 = require("react");
5092
5134
  var import_ui8 = require("@streamoid/ui");
5093
5135
  var import_icons8 = require("@streamoid/icons");
5094
5136
 
5095
- // src/organization-invite-modal.tsx
5137
+ // src/organization-member-modal.tsx
5096
5138
  var import_react10 = require("react");
5097
5139
  var import_ui7 = require("@streamoid/ui");
5098
5140
  var import_icons7 = require("@streamoid/icons");
@@ -5123,33 +5165,6 @@ var T9 = {
5123
5165
  r4xl: "var(--radius-4xl)",
5124
5166
  neutral300: "var(--primitives-neutral-300)"
5125
5167
  };
5126
- function CheckboxSvg2({ checked }) {
5127
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "relative shrink-0", style: { width: 24, height: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: checked ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
5128
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5129
- "rect",
5130
- {
5131
- x: "0.75",
5132
- y: "0.75",
5133
- width: "22.5",
5134
- height: "22.5",
5135
- rx: "5.65",
5136
- fill: T9.primary,
5137
- stroke: T9.primary,
5138
- strokeWidth: "1.5"
5139
- }
5140
- ),
5141
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5142
- "path",
5143
- {
5144
- d: "M7 12.5L10.5 16L17 8",
5145
- stroke: T9.inverse,
5146
- strokeWidth: "1.5",
5147
- strokeLinecap: "round",
5148
- strokeLinejoin: "round"
5149
- }
5150
- )
5151
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: svg_rodctpja6f_default.p4792500, fill: T9.muted }) }) });
5152
- }
5153
5168
  function FieldLabel3({ text }) {
5154
5169
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center shrink-0 w-full", style: { height: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5155
5170
  "div",
@@ -5225,69 +5240,29 @@ function SegmentedToggle({
5225
5240
  }
5226
5241
  );
5227
5242
  }
5228
- function OrganizationInviteModal({
5243
+ function OrganizationMemberModal({
5229
5244
  open,
5230
- mode,
5231
5245
  userEmail = "email@domain.in",
5232
5246
  initialRole,
5233
5247
  lockRole,
5234
- canClaimDomain = true,
5235
- claimableDomain,
5236
5248
  lockedRoleLabel,
5237
- initialInviteAccess,
5238
- allowSubmitWithoutChanges = false,
5239
5249
  canRemove = true,
5240
5250
  onClose,
5241
- onInviteEmail,
5242
- onInviteDomain,
5243
5251
  onUpdate,
5244
5252
  onRemove
5245
5253
  }) {
5246
5254
  useOverlayOpen(open, onClose);
5247
- const [inviteMode, setInviteMode] = (0, import_react10.useState)("email");
5248
- const [emailValue, setEmailValue] = (0, import_react10.useState)("");
5249
- const [domainValue, setDomainValue] = (0, import_react10.useState)(claimableDomain ?? "");
5250
5255
  const [hasChanges, setHasChanges] = (0, import_react10.useState)(false);
5251
- const [role, setRole] = (0, import_react10.useState)("admin");
5252
- const [inviteAccess, setInviteAccess] = (0, import_react10.useState)(false);
5256
+ const [role, setRole] = (0, import_react10.useState)("member");
5253
5257
  (0, import_react10.useEffect)(() => {
5254
- if (open && mode === "update") {
5258
+ if (open) {
5255
5259
  setRole(initialRole ?? "member");
5256
- setInviteAccess(initialInviteAccess ?? false);
5257
- }
5258
- if (!open) {
5259
- setInviteMode("email");
5260
- setEmailValue("");
5261
- setDomainValue(claimableDomain ?? "");
5260
+ } else {
5262
5261
  setHasChanges(false);
5263
- setRole("admin");
5264
- setInviteAccess(true);
5265
5262
  }
5266
- }, [open, mode, initialRole, initialInviteAccess, claimableDomain]);
5263
+ }, [open, initialRole]);
5267
5264
  if (!open) return null;
5268
- const isInvite = mode === "invite";
5269
- const isEmailInvite = isInvite && inviteMode === "email";
5270
- const isDomainInvite = isInvite && inviteMode === "domain";
5271
- const hasEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailValue.trim());
5272
- const hasDomain = /^[^\s@]+\.[^\s@]+$/.test(domainValue.trim());
5273
- const isSubmitDisabled = isEmailInvite ? !hasEmail : isDomainInvite ? !hasDomain : (
5274
- // A locked role has nothing this modal can legitimately submit.
5275
- lockRole || !(allowSubmitWithoutChanges || hasChanges)
5276
- );
5277
- function markChanged() {
5278
- setHasChanges(true);
5279
- }
5280
- function handleSubmit() {
5281
- if (isEmailInvite) {
5282
- onInviteEmail?.({ email: emailValue.trim(), role, inviteAccess });
5283
- return;
5284
- }
5285
- if (isDomainInvite) {
5286
- onInviteDomain?.({ domain: domainValue.trim(), autoJoinRole: role });
5287
- return;
5288
- }
5289
- onUpdate?.({ role, inviteAccess });
5290
- }
5265
+ const isSubmitDisabled = lockRole || !hasChanges;
5291
5266
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5292
5267
  "div",
5293
5268
  {
@@ -5305,6 +5280,7 @@ function OrganizationInviteModal({
5305
5280
  borderRadius: T9.r4xl,
5306
5281
  border: `1px solid ${T9.borderSubtle}`,
5307
5282
  width: 480,
5283
+ maxWidth: "calc(100vw - 32px)",
5308
5284
  maxHeight: "85vh"
5309
5285
  },
5310
5286
  children: [
@@ -5334,7 +5310,7 @@ function OrganizationInviteModal({
5334
5310
  fontWeight: 600,
5335
5311
  lineHeight: T9.lhLg
5336
5312
  },
5337
- children: isInvite ? "Invite to organization" : "Update member"
5313
+ children: "Update member"
5338
5314
  }
5339
5315
  ),
5340
5316
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "shrink-0 cursor-pointer", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons7.SiconClose, { className: "w-6 h-6", color: T9.primary }) })
@@ -5348,116 +5324,41 @@ function OrganizationInviteModal({
5348
5324
  className: "flex flex-col items-start w-full",
5349
5325
  style: { padding: `${T9.sp3xl} ${T9.sp6xl}`, gap: T9.sp3xl },
5350
5326
  children: [
5351
- isInvite && canClaimDomain && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5352
- "div",
5353
- {
5354
- className: "flex flex-col items-start justify-center shrink-0 w-full",
5355
- style: { gap: T9.spXs },
5356
- children: [
5357
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: "Invite by" }),
5358
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5359
- SegmentedToggle,
5360
- {
5361
- options: [
5362
- { value: "email", label: "Email" },
5363
- { value: "domain", label: "Domain" }
5364
- ],
5365
- value: inviteMode,
5366
- onChange: setInviteMode
5367
- }
5368
- )
5369
- ]
5370
- }
5371
- ),
5372
5327
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5373
5328
  "div",
5374
5329
  {
5375
5330
  className: "flex flex-col items-start justify-center shrink-0 w-full",
5376
5331
  style: { gap: T9.spXs },
5377
5332
  children: [
5378
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5379
- FieldLabel3,
5380
- {
5381
- text: isDomainInvite ? "Domain" : isEmailInvite ? "Email ID" : "Email ID"
5382
- }
5383
- ),
5333
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: "Email ID" }),
5384
5334
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5385
5335
  "div",
5386
5336
  {
5387
5337
  className: "shrink-0 w-full",
5388
5338
  style: { background: T9.fillPlus, height: 48, borderRadius: T9.r3xl },
5389
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5339
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5390
5340
  "div",
5391
5341
  {
5392
5342
  className: "flex items-center relative size-full",
5393
5343
  style: { padding: `${T9.spXl} ${T9.sp3xl}` },
5394
- children: [
5395
- isEmailInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5396
- "input",
5397
- {
5398
- className: "flex-1 min-w-0 bg-transparent border-none outline-none",
5399
- style: {
5400
- fontFamily: T9.font,
5401
- fontSize: T9.fsSm,
5402
- fontWeight: 400,
5403
- lineHeight: T9.lhSm,
5404
- color: T9.primary
5405
- },
5406
- placeholder: "email@domain.in",
5407
- value: emailValue,
5408
- onChange: (e) => setEmailValue(e.target.value)
5409
- }
5410
- ),
5411
- isDomainInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5412
- "span",
5413
- {
5414
- className: "flex-1 min-w-0 truncate",
5415
- style: {
5416
- fontFamily: T9.font,
5417
- fontSize: T9.fsSm,
5418
- fontWeight: 400,
5419
- lineHeight: T9.lhSm,
5420
- color: T9.primary
5421
- },
5422
- children: domainValue
5423
- }
5424
- ),
5425
- !isInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5426
- "div",
5427
- {
5428
- className: "flex-1 flex flex-col justify-center",
5429
- style: {
5430
- fontFamily: T9.font,
5431
- fontSize: T9.fsSm,
5432
- fontWeight: 400,
5433
- lineHeight: T9.lhSm,
5434
- color: T9.muted,
5435
- opacity: 0.6
5436
- },
5437
- children: userEmail
5438
- }
5439
- )
5440
- ]
5344
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5345
+ "div",
5346
+ {
5347
+ className: "flex-1 flex flex-col justify-center truncate",
5348
+ style: {
5349
+ fontFamily: T9.font,
5350
+ fontSize: T9.fsSm,
5351
+ fontWeight: 400,
5352
+ lineHeight: T9.lhSm,
5353
+ color: T9.muted,
5354
+ opacity: 0.6
5355
+ },
5356
+ children: userEmail
5357
+ }
5358
+ )
5441
5359
  }
5442
5360
  ) })
5443
5361
  }
5444
- ),
5445
- isDomainInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5446
- "span",
5447
- {
5448
- style: {
5449
- fontFamily: T9.font,
5450
- fontSize: T9.fsSm,
5451
- fontWeight: 400,
5452
- color: T9.muted
5453
- },
5454
- children: [
5455
- "Anyone who signs up or logs in with an @",
5456
- domainValue.trim(),
5457
- " ",
5458
- "address will automatically join this organization from now on."
5459
- ]
5460
- }
5461
5362
  )
5462
5363
  ]
5463
5364
  }
@@ -5468,7 +5369,7 @@ function OrganizationInviteModal({
5468
5369
  className: "flex flex-col items-start justify-center shrink-0 w-full",
5469
5370
  style: { gap: T9.spXs },
5470
5371
  children: [
5471
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: isDomainInvite ? "Auto-join role" : "Role" }),
5372
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: "Role" }),
5472
5373
  lockRole ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5473
5374
  "span",
5474
5375
  {
@@ -5480,49 +5381,33 @@ function OrganizationInviteModal({
5480
5381
  },
5481
5382
  children: [
5482
5383
  lockedRoleLabel || "Guest",
5483
- " \u2014 invited to specific workspaces only, with no access to the rest of the organization. Remove them to end that access."
5384
+ " \u2014 invited to specific workspaces only, with no access to the rest of the organization. Guests can't be made members or admins. Remove them to end that access."
5484
5385
  ]
5485
5386
  }
5486
5387
  ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5487
5388
  SegmentedToggle,
5488
5389
  {
5489
5390
  options: [
5490
- { value: "admin", label: "Admin" },
5391
+ { value: "admin", label: "Org admin" },
5491
5392
  { value: "member", label: "Member" }
5492
5393
  ],
5493
5394
  value: role,
5494
5395
  onChange: (v) => {
5495
5396
  setRole(v);
5496
- if (v === "admin") setInviteAccess(true);
5497
- markChanged();
5397
+ setHasChanges(v !== (initialRole ?? "member"));
5498
5398
  }
5499
5399
  }
5500
- )
5501
- ]
5502
- }
5503
- ),
5504
- !isDomainInvite && !lockRole && role === "member" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5505
- "div",
5506
- {
5507
- className: "flex items-center shrink-0 w-full cursor-pointer",
5508
- style: { gap: T9.spMd },
5509
- onClick: () => {
5510
- setInviteAccess(!inviteAccess);
5511
- markChanged();
5512
- },
5513
- children: [
5514
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CheckboxSvg2, { checked: inviteAccess }),
5515
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5400
+ ),
5401
+ !lockRole && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5516
5402
  "span",
5517
5403
  {
5518
5404
  style: {
5519
5405
  fontFamily: T9.font,
5520
5406
  fontSize: T9.fsSm,
5521
- fontWeight: 400,
5522
5407
  lineHeight: T9.lhSm,
5523
- color: T9.primary
5408
+ color: T9.muted
5524
5409
  },
5525
- children: "Can invite members"
5410
+ children: "Org admins can see and manage every workspace in the organization."
5526
5411
  }
5527
5412
  )
5528
5413
  ]
@@ -5543,10 +5428,10 @@ function OrganizationInviteModal({
5543
5428
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5544
5429
  "div",
5545
5430
  {
5546
- className: `flex items-center w-full ${isInvite ? "justify-end" : "justify-between"}`,
5431
+ className: "flex items-center w-full justify-between",
5547
5432
  style: { padding: `${T9.sp3xl} ${T9.sp6xl}` },
5548
5433
  children: [
5549
- !isInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5434
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5550
5435
  import_ui7.ScButton,
5551
5436
  {
5552
5437
  text: "Remove member",
@@ -5559,11 +5444,11 @@ function OrganizationInviteModal({
5559
5444
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5560
5445
  import_ui7.ScButton,
5561
5446
  {
5562
- text: isEmailInvite ? "Send invite" : isDomainInvite ? "Add domain" : allowSubmitWithoutChanges ? "Resend invite" : "Save changes",
5447
+ text: "Save changes",
5563
5448
  variant: "primary",
5564
5449
  size: "md",
5565
5450
  state: isSubmitDisabled ? "disabled" : "default",
5566
- onClick: !isSubmitDisabled ? handleSubmit : void 0
5451
+ onClick: !isSubmitDisabled ? () => onUpdate?.({ role }) : void 0
5567
5452
  }
5568
5453
  )
5569
5454
  ]
@@ -6057,20 +5942,6 @@ function formatDate(iso) {
6057
5942
  return iso;
6058
5943
  }
6059
5944
  }
6060
- function toDisplayText2(value, fallback = "\u2013") {
6061
- if (typeof value === "string") {
6062
- const trimmed = value.trim();
6063
- return trimmed.length > 0 ? trimmed : fallback;
6064
- }
6065
- if (value && typeof value === "object") {
6066
- const record = value;
6067
- const name = typeof record.name === "string" ? record.name.trim() : "";
6068
- if (name) return name;
6069
- const email = typeof record.email === "string" ? record.email.trim() : "";
6070
- if (email) return email;
6071
- }
6072
- return fallback;
6073
- }
6074
5945
  function VisibilityBadge({ visibility }) {
6075
5946
  const isShared = visibility === "shared";
6076
5947
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
@@ -6091,10 +5962,46 @@ function VisibilityBadge({ visibility }) {
6091
5962
  }
6092
5963
  );
6093
5964
  }
5965
+ function RowIconButton({
5966
+ label,
5967
+ onClick,
5968
+ children,
5969
+ visible = true
5970
+ }) {
5971
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5972
+ "button",
5973
+ {
5974
+ type: "button",
5975
+ "aria-label": label,
5976
+ title: label,
5977
+ className: "flex items-center justify-center shrink-0 cursor-pointer border-none",
5978
+ style: {
5979
+ width: 32,
5980
+ height: 32,
5981
+ borderRadius: T11.rLg,
5982
+ background: "transparent",
5983
+ opacity: visible ? 1 : 0,
5984
+ transition: "opacity 120ms ease"
5985
+ },
5986
+ onMouseEnter: (e) => {
5987
+ e.currentTarget.style.background = T11.fillPlus;
5988
+ },
5989
+ onMouseLeave: (e) => {
5990
+ e.currentTarget.style.background = "transparent";
5991
+ },
5992
+ onClick,
5993
+ children
5994
+ }
5995
+ );
5996
+ }
6094
5997
  function MemberRow({
6095
5998
  member,
6096
- onClick
5999
+ onEdit,
6000
+ onCopyEmail
6097
6001
  }) {
6002
+ const [copied, setCopied] = (0, import_react12.useState)(false);
6003
+ const [active, setActive] = (0, import_react12.useState)(false);
6004
+ const noHover = typeof window !== "undefined" && window.matchMedia?.("(hover: none)").matches === true;
6098
6005
  const name = member.user.name?.trim();
6099
6006
  const email = member.user.email || "";
6100
6007
  const displayIdent = name || email.split("@")[0] || "user";
@@ -6106,18 +6013,20 @@ function MemberRow({
6106
6013
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6107
6014
  "div",
6108
6015
  {
6109
- className: `flex items-center self-stretch shrink-0 ${onClick ? "cursor-pointer" : "cursor-default"}`,
6016
+ className: "flex items-center self-stretch shrink-0 cursor-default",
6110
6017
  style: {
6111
6018
  borderBottom: `1px solid ${T11.borderSubtle}`,
6112
6019
  padding: `${T11.sp5xl} ${T11.sp3xl}`,
6113
- gap: T11.sp6xl
6114
- },
6115
- onClick,
6116
- onMouseEnter: (e) => {
6117
- if (onClick) e.currentTarget.style.background = T11.fillPlus;
6020
+ gap: T11.sp6xl,
6021
+ background: active && onEdit ? T11.fillPlus : "transparent"
6118
6022
  },
6119
- onMouseLeave: (e) => {
6120
- e.currentTarget.style.background = "transparent";
6023
+ onMouseEnter: () => setActive(true),
6024
+ onMouseLeave: () => setActive(false),
6025
+ onFocus: () => setActive(true),
6026
+ onBlur: (e) => {
6027
+ if (!e.currentTarget.contains(e.relatedTarget)) {
6028
+ setActive(false);
6029
+ }
6121
6030
  },
6122
6031
  children: [
6123
6032
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
@@ -6196,112 +6105,29 @@ function MemberRow({
6196
6105
  },
6197
6106
  children: formatDate(member.createdAt || member.user.createdAt)
6198
6107
  }
6199
- )
6200
- ]
6201
- }
6202
- );
6203
- }
6204
- function InviteRow({
6205
- invite,
6206
- onCancel,
6207
- onResend
6208
- }) {
6209
- const isDomain = invite.kind === "domain";
6210
- const invitedByText = (() => {
6211
- if (!invite.invitedBy) return "\u2013";
6212
- if (typeof invite.invitedBy === "string") return invite.invitedBy;
6213
- return invite.invitedBy.name || invite.invitedBy.email || "\u2013";
6214
- })();
6215
- const role = isDomain ? invite.autoJoinRole : invite.role;
6216
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6217
- "div",
6218
- {
6219
- className: "flex items-center self-stretch shrink-0",
6220
- style: {
6221
- borderBottom: `1px solid ${T11.borderSubtle}`,
6222
- padding: `${T11.sp5xl} ${T11.sp3xl}`,
6223
- gap: T11.sp6xl
6224
- },
6225
- children: [
6226
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center min-w-0", style: { flex: 1, gap: T11.spMd }, children: [
6227
- isDomain ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6228
- "div",
6229
- {
6230
- className: "shrink-0 flex items-center justify-center",
6231
- style: {
6232
- width: 32,
6233
- height: 32,
6234
- borderRadius: T11.rXl,
6235
- background: T11.fillNeutral
6236
- },
6237
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconGlobe, { className: "w-4 h-4", color: T11.primary })
6238
- }
6239
- ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ProfileAvatar, { name: invite.email || "invite", size: 32 }),
6240
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col min-w-0", children: [
6241
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6242
- "span",
6243
- {
6244
- className: "truncate",
6245
- style: {
6246
- fontFamily: T11.font,
6247
- fontSize: T11.fsSm,
6248
- fontWeight: 500,
6249
- lineHeight: T11.lhSm,
6250
- color: T11.primary
6251
- },
6252
- children: isDomain ? `Domain: ${invite.domain}` : toDisplayText2(invite.email, "")
6253
- }
6254
- ),
6255
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6256
- "span",
6257
- {
6258
- className: "truncate",
6259
- style: {
6260
- fontFamily: T11.font,
6261
- fontSize: T11.fsXs,
6262
- fontWeight: 400,
6263
- lineHeight: T11.lhXs,
6264
- color: T11.muted
6265
- },
6266
- children: isDomain ? "Auto-join by verified domain" : "Email invite"
6267
- }
6268
- )
6269
- ] })
6270
- ] }),
6271
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6272
- RoleLabel,
6108
+ ),
6109
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center justify-end shrink-0", style: { width: 32 }, children: onEdit ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6110
+ RowIconButton,
6273
6111
  {
6274
- role: role === "admin" ? "admin" : "member",
6275
- label: orgRoleLabel(role)
6112
+ label: `Manage ${email || "member"}`,
6113
+ onClick: onEdit,
6114
+ visible: active || noHover,
6115
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconSettings, { className: "w-4 h-4", color: T11.primary })
6276
6116
  }
6277
- ),
6278
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6279
- "div",
6117
+ ) : email && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6118
+ RowIconButton,
6280
6119
  {
6281
- className: "truncate shrink-0",
6282
- style: {
6283
- width: "7.5rem",
6284
- fontFamily: T11.font,
6285
- fontSize: T11.fsSm,
6286
- fontWeight: 400,
6287
- lineHeight: T11.lhSm,
6288
- color: T11.primary
6120
+ label: copied ? "Copied" : `Copy ${email}`,
6121
+ onClick: () => {
6122
+ void onCopyEmail().then((ok) => {
6123
+ if (!ok) return;
6124
+ setCopied(true);
6125
+ setTimeout(() => setCopied(false), 2e3);
6126
+ });
6289
6127
  },
6290
- children: invitedByText
6128
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconCheck, { className: "w-4 h-4", color: T11.primary }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconCopy, { className: "w-4 h-4", color: T11.primary })
6291
6129
  }
6292
- ),
6293
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6294
- !isDomain && onResend && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui8.ScButton, { text: "Resend", variant: "tertiary", size: "sm", onClick: onResend }),
6295
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6296
- import_ui8.ScButton,
6297
- {
6298
- text: isDomain ? "Revoke" : "Cancel",
6299
- variant: "error",
6300
- size: "sm",
6301
- onClick: onCancel
6302
- }
6303
- )
6304
- ] })
6130
+ ) })
6305
6131
  ]
6306
6132
  }
6307
6133
  );
@@ -6408,7 +6234,7 @@ function JoinRequestRow({
6408
6234
  )
6409
6235
  ] })
6410
6236
  ] }),
6411
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6237
+ onApprove && onReject && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6412
6238
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui8.ScButton, { text: "Reject", variant: "error", size: "sm", onClick: onReject }),
6413
6239
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui8.ScButton, { text: "Approve", variant: "primary", size: "sm", onClick: onApprove })
6414
6240
  ] })
@@ -6464,42 +6290,29 @@ function Section({
6464
6290
  }
6465
6291
  function OrganizationContent({
6466
6292
  embedded = false,
6467
- view = "people",
6468
- onInviteOpen,
6469
- onInviteOpenChange
6293
+ view = "people"
6470
6294
  } = {}) {
6471
6295
  const { organization, toast } = useSettingsAdapters();
6472
6296
  const current = organization.current;
6473
- const [activeTab, setActiveTab] = (0, import_react12.useState)("active");
6474
6297
  const [members, setMembers] = (0, import_react12.useState)([]);
6475
- const [invites, setInvites] = (0, import_react12.useState)([]);
6476
6298
  const [workspaces, setWorkspaces] = (0, import_react12.useState)([]);
6477
6299
  const [joinRequests, setJoinRequests] = (0, import_react12.useState)([]);
6478
6300
  const [highestRole, setHighestRole] = (0, import_react12.useState)("guest");
6479
- const [emailDomain, setEmailDomain] = (0, import_react12.useState)(null);
6480
- const [canClaimEmailDomain, setCanClaimEmailDomain] = (0, import_react12.useState)(true);
6481
6301
  const [searchQuery, setSearchQuery] = (0, import_react12.useState)("");
6482
6302
  const [loadingData, setLoadingData] = (0, import_react12.useState)(false);
6483
- const [modal, setModal] = (0, import_react12.useState)({ open: false, mode: "invite" });
6303
+ const [modal, setModal] = (0, import_react12.useState)({ open: false });
6484
6304
  const fetchData = (0, import_react12.useCallback)(async () => {
6305
+ if (!organization.current) return;
6485
6306
  setLoadingData(true);
6486
6307
  try {
6487
- const [membersRes, invitesRes, workspacesRes, joinRequestsRes, highestRoleRes] = await Promise.all([
6308
+ const [membersRes, workspacesRes, joinRequestsRes, highestRoleRes] = await Promise.all([
6488
6309
  organization.fetchMembers().catch(() => null),
6489
- organization.fetchInvites().catch(() => null),
6490
6310
  organization.fetchWorkspaces().catch(() => null),
6491
6311
  organization.fetchJoinRequests().catch(() => null),
6492
6312
  organization.getHighestRole?.().catch(() => null) ?? Promise.resolve(null)
6493
6313
  ]);
6494
6314
  if (highestRoleRes?.role) setHighestRole(highestRoleRes.role);
6495
- if (highestRoleRes?.emailDomain !== void 0) {
6496
- setEmailDomain(highestRoleRes.emailDomain ?? null);
6497
- }
6498
- if (highestRoleRes?.canClaimEmailDomain !== void 0) {
6499
- setCanClaimEmailDomain(highestRoleRes.canClaimEmailDomain);
6500
- }
6501
6315
  if (membersRes) setMembers(membersRes.filter((m) => m?.user));
6502
- if (invitesRes) setInvites(invitesRes);
6503
6316
  if (workspacesRes) setWorkspaces(workspacesRes);
6504
6317
  if (joinRequestsRes) setJoinRequests(joinRequestsRes);
6505
6318
  } catch {
@@ -6510,51 +6323,6 @@ function OrganizationContent({
6510
6323
  (0, import_react12.useEffect)(() => {
6511
6324
  fetchData();
6512
6325
  }, [fetchData]);
6513
- const [domainMatch, setDomainMatch] = (0, import_react12.useState)(null);
6514
- const [checkingDomainMatch, setCheckingDomainMatch] = (0, import_react12.useState)(false);
6515
- const [joiningByDomain, setJoiningByDomain] = (0, import_react12.useState)(false);
6516
- const [createModalOpen, setCreateModalOpen] = (0, import_react12.useState)(false);
6517
- (0, import_react12.useEffect)(() => {
6518
- if (current) return;
6519
- let cancelled = false;
6520
- setCheckingDomainMatch(true);
6521
- organization.checkDomainMatch().then((match) => {
6522
- if (!cancelled) setDomainMatch(match);
6523
- }).catch(() => {
6524
- if (!cancelled) setDomainMatch(null);
6525
- }).finally(() => {
6526
- if (!cancelled) setCheckingDomainMatch(false);
6527
- });
6528
- return () => {
6529
- cancelled = true;
6530
- };
6531
- }, [current, organization]);
6532
- const handleJoinByDomain = async () => {
6533
- if (!domainMatch) return;
6534
- setJoiningByDomain(true);
6535
- try {
6536
- await organization.joinByDomain();
6537
- await organization.switch(domainMatch.organizationId);
6538
- toast.success(`Joined ${domainMatch.organizationName}`);
6539
- } catch (err) {
6540
- toast.error(err instanceof Error ? err.message : "Failed to join organization");
6541
- } finally {
6542
- setJoiningByDomain(false);
6543
- }
6544
- };
6545
- const handleCreateOrganization = async (name, imageFile) => {
6546
- try {
6547
- const result = await organization.create({ name, imageFile });
6548
- if (result && "id" in result && result.id) {
6549
- await organization.switch(result.id);
6550
- }
6551
- toast.success("Organization created");
6552
- return true;
6553
- } catch (err) {
6554
- toast.error(err instanceof Error ? err.message : "Failed to create organization");
6555
- return false;
6556
- }
6557
- };
6558
6326
  const [manageModalOpen, setManageModalOpen] = (0, import_react12.useState)(false);
6559
6327
  const handleUpdateOrganization = async (name, imageFile) => {
6560
6328
  if (!current) return false;
@@ -6578,45 +6346,9 @@ function OrganizationContent({
6578
6346
  return false;
6579
6347
  }
6580
6348
  };
6581
- const openInviteModal = () => setModal({ open: true, mode: "invite" });
6582
- (0, import_react12.useEffect)(() => {
6583
- if (onInviteOpen) {
6584
- openInviteModal();
6585
- onInviteOpenChange?.(false);
6586
- }
6587
- }, [onInviteOpen]);
6588
- const openUpdateModal = (member) => setModal({ open: true, mode: "update", target: member });
6589
- const closeModal = () => setModal({ open: false, mode: "invite" });
6590
- const handleInviteEmail = async (input) => {
6591
- if (highestRole !== "admin") {
6592
- toast.error("You do not have permission to invite members");
6593
- closeModal();
6594
- return;
6595
- }
6596
- try {
6597
- await organization.inviteMemberByEmail(input);
6598
- toast.success(`Invitation sent to ${input.email}`);
6599
- await fetchData();
6600
- closeModal();
6601
- } catch (err) {
6602
- toast.error(err instanceof Error ? err.message : "Failed to send invite");
6603
- }
6604
- };
6605
- const handleInviteDomain = async (input) => {
6606
- if (highestRole !== "admin") {
6607
- toast.error("You do not have permission to invite members");
6608
- closeModal();
6609
- return;
6610
- }
6611
- try {
6612
- await organization.inviteDomain(input);
6613
- toast.success(`Domain ${input.domain} added`);
6614
- await fetchData();
6615
- closeModal();
6616
- } catch (err) {
6617
- toast.error(err instanceof Error ? err.message : "Failed to add domain");
6618
- }
6619
- };
6349
+ const openUpdateModal = (member) => setModal({ open: true, target: member });
6350
+ const closeModal = () => setModal({ open: false });
6351
+ const adminCount = members.filter((m) => m.role === "admin").length;
6620
6352
  const handleUpdateMember = async (input) => {
6621
6353
  const target = modal.target;
6622
6354
  if (!target) {
@@ -6628,20 +6360,16 @@ function OrganizationContent({
6628
6360
  closeModal();
6629
6361
  return;
6630
6362
  }
6631
- if (target.role === "admin" && input.role === "member") {
6632
- const adminCount = members.filter((m) => m.role === "admin").length;
6633
- if (adminCount <= 1) {
6634
- toast.error(
6635
- "You're the only admin. Promote another member to admin before changing your role."
6636
- );
6637
- return;
6638
- }
6363
+ if (target.role === "admin" && input.role === "member" && adminCount <= 1) {
6364
+ toast.error(
6365
+ "This is the only org admin. Make someone else an org admin first."
6366
+ );
6367
+ return;
6639
6368
  }
6640
6369
  try {
6641
6370
  await organization.updateMemberRole({
6642
6371
  userId: target.user._id,
6643
- role: input.role,
6644
- inviteAccess: input.inviteAccess
6372
+ role: input.role
6645
6373
  });
6646
6374
  toast.success("Member updated");
6647
6375
  await fetchData();
@@ -6661,8 +6389,14 @@ function OrganizationContent({
6661
6389
  closeModal();
6662
6390
  return;
6663
6391
  }
6392
+ if (target.role === "admin" && adminCount <= 1) {
6393
+ toast.error(
6394
+ "This is the only org admin. Make someone else an org admin first."
6395
+ );
6396
+ return;
6397
+ }
6664
6398
  const confirmed = window.confirm(
6665
- `Remove ${target.user.email} from this organization?`
6399
+ `Remove ${target.user.email} from this organization? They'll also be removed from every workspace in it.`
6666
6400
  );
6667
6401
  if (!confirmed) return;
6668
6402
  try {
@@ -6674,44 +6408,6 @@ function OrganizationContent({
6674
6408
  }
6675
6409
  closeModal();
6676
6410
  };
6677
- const handleCancelInvite = async (invite) => {
6678
- if (highestRole !== "admin") {
6679
- toast.error("You do not have permission to manage invites");
6680
- return;
6681
- }
6682
- const label = invite.kind === "domain" ? invite.domain : invite.email;
6683
- const confirmed = window.confirm(
6684
- invite.kind === "domain" ? `Revoke domain invite for ${label}? Members who already joined will keep their access.` : `Cancel invitation for ${label}?`
6685
- );
6686
- if (!confirmed) return;
6687
- try {
6688
- if (invite.kind === "domain") {
6689
- await organization.revokeDomainInvite(invite._id);
6690
- } else if (invite.token) {
6691
- await organization.cancelInvite(invite.token);
6692
- } else {
6693
- await organization.removeMember({ invitationIds: [invite._id] });
6694
- }
6695
- toast.success(invite.kind === "domain" ? "Domain invite revoked" : "Invitation removed");
6696
- await fetchData();
6697
- } catch (err) {
6698
- toast.error(err instanceof Error ? err.message : "Failed to update invite");
6699
- }
6700
- };
6701
- const handleResendInvite = async (invite) => {
6702
- if (invite.kind !== "email") return;
6703
- if (highestRole !== "admin") {
6704
- toast.error("You do not have permission to invite members");
6705
- return;
6706
- }
6707
- try {
6708
- await organization.resendInvite(invite._id);
6709
- toast.success("Invitation resent");
6710
- await fetchData();
6711
- } catch (err) {
6712
- toast.error(err instanceof Error ? err.message : "Failed to resend invitation");
6713
- }
6714
- };
6715
6411
  const handleApproveJoinRequest = async (request) => {
6716
6412
  try {
6717
6413
  await organization.approveJoinRequest(request._id);
@@ -6733,101 +6429,29 @@ function OrganizationContent({
6733
6429
  }
6734
6430
  };
6735
6431
  if (!current) {
6736
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6737
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6738
- "div",
6739
- {
6740
- className: "flex flex-col flex-1 min-w-0 min-h-0 w-full items-center justify-center",
6741
- style: { gap: T11.spXl, padding: T11.sp6xl },
6742
- children: [
6743
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconGlobe, { className: "w-10 h-10", color: T11.muted }),
6744
- domainMatch ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6745
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6746
- "p",
6747
- {
6748
- className: "text-center",
6749
- style: { color: T11.primary, fontFamily: T11.font, fontSize: T11.fsMd, fontWeight: 600 },
6750
- children: [
6751
- "An organization already exists for ",
6752
- domainMatch.domain
6753
- ]
6754
- }
6755
- ),
6756
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6757
- "p",
6758
- {
6759
- className: "text-center",
6760
- style: { color: T11.muted, fontFamily: T11.font, fontSize: T11.fsSm },
6761
- children: [
6762
- domainMatch.organizationName,
6763
- " \u2014 join it instead of creating a new one and fragmenting your company across multiple orgs."
6764
- ]
6765
- }
6766
- ),
6767
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6768
- import_ui8.ScButton,
6769
- {
6770
- text: joiningByDomain ? "Joining\u2026" : `Join ${domainMatch.organizationName}`,
6771
- variant: "primary",
6772
- size: "md",
6773
- state: joiningByDomain ? "disabled" : "default",
6774
- onClick: handleJoinByDomain
6775
- }
6776
- )
6777
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6778
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6779
- "p",
6780
- {
6781
- className: "text-center",
6782
- style: { color: T11.primary, fontFamily: T11.font, fontSize: T11.fsMd, fontWeight: 600 },
6783
- children: "You're not part of an organization yet"
6784
- }
6785
- ),
6786
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6787
- "p",
6788
- {
6789
- className: "text-center",
6790
- style: { color: T11.muted, fontFamily: T11.font, fontSize: T11.fsSm },
6791
- children: "Create one to invite your team and manage workspaces together."
6792
- }
6793
- ),
6794
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6795
- import_ui8.ScButton,
6796
- {
6797
- text: "Create organization",
6798
- variant: "primary",
6799
- size: "md",
6800
- styleVariant: "icon-left",
6801
- icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconUserAdd, { className: "w-5 h-5", color: T11.inverse }),
6802
- state: checkingDomainMatch ? "disabled" : "default",
6803
- onClick: () => setCreateModalOpen(true)
6804
- }
6805
- )
6806
- ] })
6807
- ]
6808
- }
6809
- ),
6810
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6811
- OrganizationModal,
6812
- {
6813
- open: createModalOpen,
6814
- mode: "create",
6815
- onClose: () => setCreateModalOpen(false),
6816
- onCreate: handleCreateOrganization
6817
- }
6818
- )
6819
- ] });
6432
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6433
+ "div",
6434
+ {
6435
+ className: "flex flex-col flex-1 min-w-0 min-h-0 w-full items-center justify-center",
6436
+ style: { gap: T11.spXl, padding: T11.sp6xl },
6437
+ children: [
6438
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconGlobe, { className: "w-10 h-10", color: T11.muted }),
6439
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6440
+ "p",
6441
+ {
6442
+ className: "text-center",
6443
+ style: { color: T11.primary, fontFamily: T11.font, fontSize: T11.fsMd, fontWeight: 600 },
6444
+ children: "Your workspace isn't part of an organization"
6445
+ }
6446
+ )
6447
+ ]
6448
+ }
6449
+ );
6820
6450
  }
6821
6451
  const q = searchQuery.toLowerCase();
6822
6452
  const filteredMembers = members.filter(
6823
6453
  (m) => (m.user.email || "").toLowerCase().includes(q) || (m.user.name || "").toLowerCase().includes(q) || m.role.toLowerCase().includes(q)
6824
6454
  );
6825
- const filteredInvites = invites.filter((inv) => {
6826
- const label = inv.kind === "domain" ? inv.domain ?? "" : inv.email ?? "";
6827
- return label.toLowerCase().includes(q);
6828
- });
6829
- const emailInvites = filteredInvites.filter((inv) => inv.kind !== "domain");
6830
- const domainInvites = invites.filter((inv) => inv.kind === "domain");
6831
6455
  const scopeHeader = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6832
6456
  ScopeHeader,
6833
6457
  {
@@ -6876,15 +6500,26 @@ function OrganizationContent({
6876
6500
  )
6877
6501
  }
6878
6502
  );
6503
+ const isOrgAdmin = highestRole === "admin";
6504
+ const copyEmail = async (email) => {
6505
+ try {
6506
+ await navigator.clipboard.writeText(email);
6507
+ toast.success("Email copied");
6508
+ return true;
6509
+ } catch {
6510
+ toast.error("Couldn't copy the email");
6511
+ return false;
6512
+ }
6513
+ };
6879
6514
  const peopleView = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6880
6515
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6881
6516
  MemberListToolbar,
6882
6517
  {
6883
- activeTab,
6884
- onTabChange: setActiveTab,
6518
+ activeTab: "active",
6519
+ onTabChange: () => {
6520
+ },
6885
6521
  activeLabel: "Members",
6886
6522
  activeCount: filteredMembers.length,
6887
- pendingCount: emailInvites.length,
6888
6523
  searchQuery,
6889
6524
  onSearchChange: setSearchQuery
6890
6525
  }
@@ -6892,47 +6527,31 @@ function OrganizationContent({
6892
6527
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6893
6528
  MemberListFrame,
6894
6529
  {
6895
- isEmpty: activeTab === "active" ? filteredMembers.length === 0 : emailInvites.length === 0,
6896
- emptyText: activeTab === "active" ? "No members yet" : "No pending invites",
6530
+ isEmpty: filteredMembers.length === 0,
6531
+ emptyText: "No members yet",
6897
6532
  grow: "fill",
6898
- children: activeTab === "active" ? filteredMembers.map((m) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MemberRow, { member: m, onClick: () => openUpdateModal(m) }, m._id)) : emailInvites.map((inv) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6899
- InviteRow,
6533
+ children: filteredMembers.map((m) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6534
+ MemberRow,
6900
6535
  {
6901
- invite: inv,
6902
- onCancel: () => void handleCancelInvite(inv),
6903
- onResend: () => void handleResendInvite(inv)
6536
+ member: m,
6537
+ onEdit: isOrgAdmin ? () => openUpdateModal(m) : void 0,
6538
+ onCopyEmail: () => copyEmail(m.user.email)
6904
6539
  },
6905
- inv._id
6540
+ m._id
6906
6541
  ))
6907
6542
  }
6908
6543
  )
6909
6544
  ] });
6910
- const workspacesView = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6911
- Section,
6912
- {
6913
- title: "Workspaces in this organization",
6914
- empty: workspaces.length === 0,
6915
- children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(WorkspaceRow, { workspace: ws }, ws._id))
6916
- }
6917
- );
6918
- const domainsView = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6545
+ const workspacesView = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6919
6546
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6920
6547
  Section,
6921
6548
  {
6922
- title: "Auto-join by email domain",
6923
- empty: domainInvites.length === 0,
6924
- emptyText: canClaimEmailDomain ? void 0 : `Auto-join needs a company email domain.${emailDomain ? ` ${emailDomain} is a public email provider, so it` : " A public email provider"} can't be claimed \u2014 invite people by email instead.`,
6925
- children: domainInvites.map((inv) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6926
- InviteRow,
6927
- {
6928
- invite: inv,
6929
- onCancel: () => void handleCancelInvite(inv)
6930
- },
6931
- inv._id
6932
- ))
6549
+ title: "Workspaces in this organization",
6550
+ empty: workspaces.length === 0,
6551
+ children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(WorkspaceRow, { workspace: ws }, ws._id))
6933
6552
  }
6934
6553
  ),
6935
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6554
+ (isOrgAdmin || joinRequests.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6936
6555
  Section,
6937
6556
  {
6938
6557
  title: "Workspaces asking to join",
@@ -6941,8 +6560,8 @@ function OrganizationContent({
6941
6560
  JoinRequestRow,
6942
6561
  {
6943
6562
  request: req,
6944
- onApprove: () => void handleApproveJoinRequest(req),
6945
- onReject: () => void handleRejectJoinRequest(req)
6563
+ onApprove: isOrgAdmin ? () => void handleApproveJoinRequest(req) : void 0,
6564
+ onReject: isOrgAdmin ? () => void handleRejectJoinRequest(req) : void 0
6946
6565
  },
6947
6566
  req._id
6948
6567
  ))
@@ -6953,25 +6572,17 @@ function OrganizationContent({
6953
6572
  scopeHeader,
6954
6573
  loadingData && loadingBlock,
6955
6574
  !loadingData && view === "people" && peopleView,
6956
- !loadingData && view === "workspaces" && workspacesView,
6957
- !loadingData && view === "domains" && domainsView,
6575
+ !loadingData && view !== "people" && workspacesView,
6958
6576
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6959
- OrganizationInviteModal,
6577
+ OrganizationMemberModal,
6960
6578
  {
6961
6579
  open: modal.open,
6962
- mode: modal.mode,
6963
6580
  userEmail: modal.target?.user.email,
6964
6581
  initialRole: modal.target?.role === "admin" || modal.target?.role === "member" ? modal.target.role : void 0,
6965
- initialInviteAccess: modal.target?.inviteAccess,
6966
- allowSubmitWithoutChanges: false,
6967
6582
  lockRole: modal.target?.role === "guest",
6968
6583
  lockedRoleLabel: orgRoleLabel(modal.target?.role),
6969
6584
  canRemove: highestRole === "admin",
6970
- canClaimDomain: canClaimEmailDomain,
6971
- claimableDomain: emailDomain,
6972
6585
  onClose: closeModal,
6973
- onInviteEmail: handleInviteEmail,
6974
- onInviteDomain: handleInviteDomain,
6975
6586
  onUpdate: handleUpdateMember,
6976
6587
  onRemove: handleRemoveMember
6977
6588
  }
@@ -7016,34 +6627,20 @@ function OrganizationContent({
7016
6627
  backgroundSize: "24px 24px"
7017
6628
  },
7018
6629
  children: [
7019
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between shrink-0 w-full", children: [
7020
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7021
- "p",
7022
- {
7023
- className: "flex-1 min-w-0 truncate",
7024
- style: {
7025
- color: T11.primary,
7026
- fontFamily: T11.font,
7027
- fontSize: "var(--font-size-font-size-xxl)",
7028
- fontWeight: 600,
7029
- lineHeight: "var(--line-height-line-height-xxl)"
7030
- },
7031
- children: current?.name ? `${current.name} \u2014 Organization` : "Organization"
7032
- }
7033
- ),
7034
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7035
- import_ui8.ScButton,
7036
- {
7037
- text: "Invite",
7038
- variant: "primary",
7039
- size: "md",
7040
- styleVariant: "icon-left",
7041
- state: highestRole !== "admin" ? "disabled" : "default",
7042
- icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconUserAdd, { className: "w-5 h-5", color: "var(--alias-text---icons-inverse)" }),
7043
- onClick: openInviteModal
7044
- }
7045
- )
7046
- ] }),
6630
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center justify-between shrink-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6631
+ "p",
6632
+ {
6633
+ className: "flex-1 min-w-0 truncate",
6634
+ style: {
6635
+ color: T11.primary,
6636
+ fontFamily: T11.font,
6637
+ fontSize: "var(--font-size-font-size-xxl)",
6638
+ fontWeight: 600,
6639
+ lineHeight: "var(--line-height-line-height-xxl)"
6640
+ },
6641
+ children: current?.name ? `${current.name} \u2014 Organization` : "Organization"
6642
+ }
6643
+ ) }),
7047
6644
  innerContent
7048
6645
  ]
7049
6646
  }
@@ -10615,8 +10212,7 @@ function SettingsNav({
10615
10212
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex flex-col w-full", style: { gap: "var(--spacing-md)" }, children: hasOrganization ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
10616
10213
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: "4px 12px", fontSize: 11, fontWeight: 500, color: MUTED_COLOR2, textTransform: "uppercase", letterSpacing: "0.08em" }, children: "Organization" }),
10617
10214
  item("organization", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconTeam, { className: "w-5 h-5", color })),
10618
- item("organizationWorkspaces", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconWorkspace, { className: "w-5 h-5", color })),
10619
- item("organizationDomains", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconLock, { className: "w-5 h-5", color }))
10215
+ item("organizationWorkspaces", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconWorkspace, { className: "w-5 h-5", color }))
10620
10216
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
10621
10217
  SettingsNavItem,
10622
10218
  {
@@ -11301,16 +10897,49 @@ function ProfileTab() {
11301
10897
  }
11302
10898
  );
11303
10899
  }
10900
+ var WORKSPACE_LIST_PAGE_SIZE = 10;
11304
10901
  function WorkspaceListTab({
11305
10902
  workspaces,
11306
10903
  loading,
11307
10904
  onCreateClick: _onCreateClick,
11308
10905
  onEditClick,
11309
10906
  onSwitchClick,
11310
- onLeaveClick
10907
+ onLeaveClick,
10908
+ fetchPlans,
10909
+ pageSize = WORKSPACE_LIST_PAGE_SIZE
11311
10910
  }) {
11312
10911
  const currentWorkspace = workspaces.find((ws) => ws.isCurrent);
11313
10912
  const otherWorkspaces = workspaces.filter((ws) => !ws.isCurrent);
10913
+ const pageCount = Math.max(1, Math.ceil(otherWorkspaces.length / pageSize));
10914
+ const [page, setPage] = (0, import_react17.useState)(0);
10915
+ const safePage = Math.min(page, pageCount - 1);
10916
+ const pageStart = safePage * pageSize;
10917
+ const pageWorkspaces = otherWorkspaces.slice(pageStart, pageStart + pageSize);
10918
+ const [plans, setPlans] = (0, import_react17.useState)({});
10919
+ const requestedPlans = (0, import_react17.useRef)(/* @__PURE__ */ new Set());
10920
+ const visibleIds = [
10921
+ ...currentWorkspace ? [currentWorkspace._id] : [],
10922
+ ...pageWorkspaces.map((ws) => ws._id)
10923
+ ];
10924
+ const visibleKey = visibleIds.join(",");
10925
+ const mounted = (0, import_react17.useRef)(true);
10926
+ (0, import_react17.useEffect)(() => {
10927
+ mounted.current = true;
10928
+ return () => {
10929
+ mounted.current = false;
10930
+ };
10931
+ }, []);
10932
+ (0, import_react17.useEffect)(() => {
10933
+ if (!fetchPlans || loading) return;
10934
+ const missing = visibleKey.split(",").filter((id) => id && !requestedPlans.current.has(id));
10935
+ if (!missing.length) return;
10936
+ missing.forEach((id) => requestedPlans.current.add(id));
10937
+ fetchPlans(missing).then((found) => {
10938
+ if (mounted.current) setPlans((prev) => ({ ...prev, ...found }));
10939
+ }).catch(() => {
10940
+ });
10941
+ }, [fetchPlans, loading, visibleKey]);
10942
+ const planOf = (ws) => fetchPlans ? plans[ws._id] ?? "" : ws.plan ?? "";
11314
10943
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
11315
10944
  "div",
11316
10945
  {
@@ -11368,7 +10997,7 @@ function WorkspaceListTab({
11368
10997
  initials: currentWorkspace.initials,
11369
10998
  imageUrl: currentWorkspace.imageUrl,
11370
10999
  role: currentWorkspace.role,
11371
- plan: currentWorkspace.plan,
11000
+ plan: planOf(currentWorkspace),
11372
11001
  ownerId: currentWorkspace.ownerEmail,
11373
11002
  type: "currentWS",
11374
11003
  onSettings: () => onEditClick(currentWorkspace),
@@ -11413,14 +11042,14 @@ function WorkspaceListTab({
11413
11042
  {
11414
11043
  className: "flex flex-col items-start w-full",
11415
11044
  style: { gap: 16 },
11416
- children: otherWorkspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
11045
+ children: pageWorkspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
11417
11046
  import_ui14.ScWorkspaceSwitchCard,
11418
11047
  {
11419
11048
  wsName: ws.name,
11420
11049
  initials: ws.initials,
11421
11050
  imageUrl: ws.imageUrl,
11422
11051
  role: ws.role,
11423
- plan: ws.plan,
11052
+ plan: planOf(ws),
11424
11053
  ownerId: ws.ownerEmail,
11425
11054
  type: "otherWS",
11426
11055
  onSwitch: () => onSwitchClick(ws),
@@ -11430,6 +11059,56 @@ function WorkspaceListTab({
11430
11059
  ws._id
11431
11060
  ))
11432
11061
  }
11062
+ ),
11063
+ pageCount > 1 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
11064
+ "div",
11065
+ {
11066
+ className: "flex items-center justify-between shrink-0 w-full",
11067
+ style: { gap: "var(--spacing-md)" },
11068
+ children: [
11069
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
11070
+ "span",
11071
+ {
11072
+ style: {
11073
+ color: "var(--alias-text---icons-muted)",
11074
+ fontFamily: "var(--font-family-font-family-body)",
11075
+ fontSize: "var(--font-size-font-size-sm)",
11076
+ lineHeight: "var(--line-height-line-height-sm)"
11077
+ },
11078
+ children: [
11079
+ pageStart + 1,
11080
+ "\u2013",
11081
+ pageStart + pageWorkspaces.length,
11082
+ " of",
11083
+ " ",
11084
+ otherWorkspaces.length
11085
+ ]
11086
+ }
11087
+ ),
11088
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center", style: { gap: "var(--spacing-md)" }, children: [
11089
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
11090
+ import_ui13.ScButton,
11091
+ {
11092
+ text: "Previous",
11093
+ variant: "tertiary",
11094
+ size: "sm",
11095
+ state: safePage === 0 ? "disabled" : "default",
11096
+ onClick: safePage === 0 ? void 0 : () => setPage(safePage - 1)
11097
+ }
11098
+ ),
11099
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
11100
+ import_ui13.ScButton,
11101
+ {
11102
+ text: "Next",
11103
+ variant: "tertiary",
11104
+ size: "sm",
11105
+ state: safePage >= pageCount - 1 ? "disabled" : "default",
11106
+ onClick: safePage >= pageCount - 1 ? void 0 : () => setPage(safePage + 1)
11107
+ }
11108
+ )
11109
+ ] })
11110
+ ]
11111
+ }
11433
11112
  )
11434
11113
  ]
11435
11114
  }
@@ -11588,7 +11267,6 @@ function SettingsContentInner({
11588
11267
  const [creatingWorkspace, setCreatingWorkspace] = (0, import_react17.useState)(false);
11589
11268
  const [updatingWorkspace, setUpdatingWorkspace] = (0, import_react17.useState)(false);
11590
11269
  const [teamInviteOpen, setTeamInviteOpen] = (0, import_react17.useState)(false);
11591
- const [organizationInviteOpen, setOrganizationInviteOpen] = (0, import_react17.useState)(false);
11592
11270
  const [tokenShowRevoked, setTokenShowRevoked] = (0, import_react17.useState)(false);
11593
11271
  const [tokenCreateOpen, setTokenCreateOpen] = (0, import_react17.useState)(false);
11594
11272
  const [billingShowPlans, setBillingShowPlans] = (0, import_react17.useState)(false);
@@ -12095,31 +11773,6 @@ function SettingsContentInner({
12095
11773
  )
12096
11774
  }
12097
11775
  ),
12098
- activeTab === "organization" && hasOrganization && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12099
- SettingsTooltip,
12100
- {
12101
- content: "Invite someone into your organization",
12102
- placement: "bottom",
12103
- inline: true,
12104
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12105
- import_ui13.ScButton,
12106
- {
12107
- text: "Invite",
12108
- variant: "secondary",
12109
- size: "md",
12110
- styleVariant: "icon-left",
12111
- icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12112
- import_icons13.SiconUserAdd,
12113
- {
12114
- className: "w-5 h-5",
12115
- color: "var(--alias-text---icons-inverse)"
12116
- }
12117
- ),
12118
- onClick: () => setOrganizationInviteOpen(true)
12119
- }
12120
- )
12121
- }
12122
- ),
12123
11776
  activeTab === "apikey" && isWorkspaceAdmin && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: "var(--spacing-md)" }, children: [
12124
11777
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12125
11778
  SettingsTooltip,
@@ -12249,6 +11902,7 @@ function SettingsContentInner({
12249
11902
  {
12250
11903
  workspaces: displayWorkspaces,
12251
11904
  loading: workspaceListLoading && workspaceList.length === 0,
11905
+ fetchPlans: workspace.fetchPlans,
12252
11906
  onCreateClick: handleCreateClick,
12253
11907
  onEditClick: handleEditClick,
12254
11908
  onSwitchClick: async (ws) => {
@@ -12278,9 +11932,7 @@ function SettingsContentInner({
12278
11932
  OrganizationContent,
12279
11933
  {
12280
11934
  embedded: true,
12281
- view: activeTab === "organizationWorkspaces" ? "workspaces" : activeTab === "organizationDomains" ? "domains" : "people",
12282
- onInviteOpen: organizationInviteOpen,
12283
- onInviteOpenChange: setOrganizationInviteOpen
11935
+ view: activeTab === "organizationWorkspaces" || activeTab === "organizationDomains" ? "workspaces" : "people"
12284
11936
  }
12285
11937
  ) }),
12286
11938
  activeTab === "billing" && isWorkspaceAdmin && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex flex-col flex-1 min-w-0 min-h-0 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
@@ -12418,3 +12070,246 @@ function SettingsContentInner({
12418
12070
  }
12419
12071
  );
12420
12072
  }
12073
+
12074
+ // src/workspace-picker.tsx
12075
+ var import_react18 = require("react");
12076
+ var import_ui15 = require("@streamoid/ui");
12077
+ var import_jsx_runtime23 = require("react/jsx-runtime");
12078
+ var T13 = {
12079
+ primary: "var(--alias-text---icons-primary)",
12080
+ muted: "var(--alias-text---icons-muted)",
12081
+ tertiary: "var(--alias-text---icons-tertiary)",
12082
+ surface: "var(--alias-surface-base)",
12083
+ fillPlus: "var(--alias-fill-neutral-neutralplus)",
12084
+ borderSubtle: "var(--alias-border-subtle)",
12085
+ font: "var(--font-family-font-family-body)",
12086
+ fsXs: "var(--font-size-font-size-xs)",
12087
+ fsSm: "var(--font-size-font-size-sm)",
12088
+ fsXl: "var(--font-size-font-size-xl)",
12089
+ lhXs: "var(--line-height-line-height-xs)",
12090
+ lhSm: "var(--line-height-line-height-sm)",
12091
+ lhXl: "var(--line-height-line-height-xl)",
12092
+ spXs: "var(--spacing-xs)",
12093
+ spMd: "var(--spacing-md)",
12094
+ spXl: "var(--spacing-xl)",
12095
+ sp3xl: "var(--spacing-3xl)",
12096
+ sp5xl: "var(--spacing-5xl)",
12097
+ sp6xl: "var(--spacing-6xl)",
12098
+ rLg: "var(--radius-lg)",
12099
+ r3xl: "var(--radius-3xl)"
12100
+ };
12101
+ function WorkspacePicker({
12102
+ organizationName,
12103
+ workspaces,
12104
+ loading = false,
12105
+ onJoin,
12106
+ onCreate
12107
+ }) {
12108
+ const [selected, setSelected] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
12109
+ const [creating, setCreating] = (0, import_react18.useState)(false);
12110
+ const [name, setName] = (0, import_react18.useState)("");
12111
+ const [busy, setBusy] = (0, import_react18.useState)(false);
12112
+ const hasWorkspaces = workspaces.length > 0;
12113
+ const showCreate = creating || !loading && !hasWorkspaces;
12114
+ const orgLabel = organizationName || "your organization";
12115
+ const toggle = (id) => setSelected((prev) => {
12116
+ const next = new Set(prev);
12117
+ if (next.has(id)) next.delete(id);
12118
+ else next.add(id);
12119
+ return next;
12120
+ });
12121
+ const run = async (action) => {
12122
+ setBusy(true);
12123
+ try {
12124
+ await action();
12125
+ } catch {
12126
+ } finally {
12127
+ setBusy(false);
12128
+ }
12129
+ };
12130
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12131
+ "div",
12132
+ {
12133
+ className: "flex flex-col w-full",
12134
+ style: { maxWidth: 520, gap: T13.sp5xl, fontFamily: T13.font },
12135
+ children: [
12136
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col", style: { gap: T13.spMd }, children: [
12137
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12138
+ "p",
12139
+ {
12140
+ style: {
12141
+ color: T13.primary,
12142
+ fontSize: T13.fsXl,
12143
+ fontWeight: 600,
12144
+ lineHeight: T13.lhXl,
12145
+ margin: 0
12146
+ },
12147
+ children: "Choose your workspaces"
12148
+ }
12149
+ ),
12150
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { style: { color: T13.muted, fontSize: T13.fsSm, lineHeight: T13.lhSm, margin: 0 }, children: hasWorkspaces || loading ? `Join one or more workspaces in ${orgLabel} as a member, or create your own.` : `There are no workspaces to join in ${orgLabel} yet. Create one to get started.` })
12151
+ ] }),
12152
+ (loading || hasWorkspaces) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12153
+ "div",
12154
+ {
12155
+ className: "flex flex-col w-full overflow-y-auto",
12156
+ style: {
12157
+ maxHeight: 360,
12158
+ background: T13.surface,
12159
+ border: `1px solid ${T13.borderSubtle}`,
12160
+ borderRadius: T13.r3xl
12161
+ },
12162
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12163
+ "div",
12164
+ {
12165
+ className: "flex items-center justify-center",
12166
+ style: { padding: T13.sp6xl, color: T13.muted, fontSize: T13.fsSm },
12167
+ children: "Loading workspaces\u2026"
12168
+ }
12169
+ ) : workspaces.map((workspace, index) => {
12170
+ const checked = selected.has(workspace._id);
12171
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12172
+ "div",
12173
+ {
12174
+ role: "checkbox",
12175
+ "aria-checked": checked,
12176
+ tabIndex: 0,
12177
+ className: "flex items-center cursor-pointer",
12178
+ style: {
12179
+ gap: T13.spXl,
12180
+ padding: `${T13.spXl} ${T13.sp3xl}`,
12181
+ borderTop: index ? `1px solid ${T13.borderSubtle}` : "none"
12182
+ },
12183
+ onClick: () => toggle(workspace._id),
12184
+ onKeyDown: (e) => {
12185
+ if (e.key === " " || e.key === "Enter") {
12186
+ e.preventDefault();
12187
+ toggle(workspace._id);
12188
+ }
12189
+ },
12190
+ children: [
12191
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui15.ScCheckbox, { checked }),
12192
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12193
+ ProfileAvatar,
12194
+ {
12195
+ name: workspace.name,
12196
+ profileImage: workspace.imageUrl,
12197
+ size: 32,
12198
+ borderRadius: T13.rLg
12199
+ }
12200
+ ),
12201
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col min-w-0", children: [
12202
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12203
+ "span",
12204
+ {
12205
+ className: "truncate",
12206
+ style: {
12207
+ color: T13.primary,
12208
+ fontSize: T13.fsSm,
12209
+ fontWeight: 500,
12210
+ lineHeight: T13.lhSm
12211
+ },
12212
+ children: workspace.name
12213
+ }
12214
+ ),
12215
+ workspace.ownerName && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12216
+ "span",
12217
+ {
12218
+ className: "truncate",
12219
+ style: { color: T13.muted, fontSize: T13.fsXs, lineHeight: T13.lhXs },
12220
+ children: [
12221
+ "Created by ",
12222
+ workspace.ownerName
12223
+ ]
12224
+ }
12225
+ )
12226
+ ] })
12227
+ ]
12228
+ },
12229
+ workspace._id
12230
+ );
12231
+ })
12232
+ }
12233
+ ),
12234
+ showCreate ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col w-full", style: { gap: T13.spMd }, children: [
12235
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12236
+ "label",
12237
+ {
12238
+ htmlFor: "workspace-picker-name",
12239
+ style: { color: T13.tertiary, fontSize: T13.fsSm, lineHeight: T13.lhSm },
12240
+ children: "New workspace name"
12241
+ }
12242
+ ),
12243
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12244
+ "input",
12245
+ {
12246
+ id: "workspace-picker-name",
12247
+ autoFocus: true,
12248
+ value: name,
12249
+ onChange: (e) => setName(e.target.value),
12250
+ onKeyDown: (e) => {
12251
+ if (e.key === "Enter" && name.trim() && !busy) {
12252
+ void run(() => onCreate(name.trim()));
12253
+ }
12254
+ },
12255
+ placeholder: "e.g. Marketing",
12256
+ style: {
12257
+ height: 48,
12258
+ padding: `0 ${T13.sp3xl}`,
12259
+ background: T13.fillPlus,
12260
+ border: "none",
12261
+ outline: "none",
12262
+ borderRadius: T13.r3xl,
12263
+ color: T13.primary,
12264
+ fontFamily: T13.font,
12265
+ fontSize: T13.fsSm
12266
+ }
12267
+ }
12268
+ ),
12269
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-end w-full", style: { gap: T13.spMd }, children: [
12270
+ hasWorkspaces && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12271
+ import_ui15.ScButton,
12272
+ {
12273
+ text: "Back",
12274
+ variant: "tertiary",
12275
+ size: "md",
12276
+ onClick: () => setCreating(false)
12277
+ }
12278
+ ),
12279
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12280
+ import_ui15.ScButton,
12281
+ {
12282
+ text: busy ? "Creating\u2026" : "Create workspace",
12283
+ variant: "primary",
12284
+ size: "md",
12285
+ state: !name.trim() || busy ? "disabled" : "default",
12286
+ onClick: name.trim() && !busy ? () => void run(() => onCreate(name.trim())) : void 0
12287
+ }
12288
+ )
12289
+ ] })
12290
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap items-center justify-between w-full", style: { gap: T13.spMd }, children: [
12291
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12292
+ import_ui15.ScButton,
12293
+ {
12294
+ text: "Create a workspace",
12295
+ variant: "tertiary",
12296
+ size: "md",
12297
+ state: loading || busy ? "disabled" : "default",
12298
+ onClick: loading || busy ? void 0 : () => setCreating(true)
12299
+ }
12300
+ ),
12301
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12302
+ import_ui15.ScButton,
12303
+ {
12304
+ text: busy ? "Joining\u2026" : selected.size > 1 ? `Join ${selected.size} workspaces` : "Join workspace",
12305
+ variant: "primary",
12306
+ size: "md",
12307
+ state: !selected.size || busy ? "disabled" : "default",
12308
+ onClick: selected.size && !busy ? () => void run(() => onJoin([...selected])) : void 0
12309
+ }
12310
+ )
12311
+ ] })
12312
+ ]
12313
+ }
12314
+ );
12315
+ }