@streamoid/settings 0.6.5 → 0.7.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.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,
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  WorkspaceGeneralTab: () => WorkspaceGeneralTab,
55
55
  WorkspaceListTab: () => WorkspaceListTab,
56
56
  WorkspaceModal: () => WorkspaceModal,
57
+ WorkspacePicker: () => WorkspacePicker,
57
58
  WorkspaceTab: () => WorkspaceTab,
58
59
  countLabel: () => countLabel,
59
60
  loadWorkspaceOrganization: () => loadWorkspaceOrganization,
@@ -159,9 +160,19 @@ function dataOf(value) {
159
160
  async function loadWorkspaceOrganization(workspaceId, request, signal) {
160
161
  const rows = dataOf(await request("/workspace", signal));
161
162
  if (!Array.isArray(rows)) throw new Error("Invalid workspace list");
162
- const row = rows.find((item) => (item.workspace?._id ?? item._id) === workspaceId);
163
- if (!row) throw new Error("Workspace not found");
164
- const workspace = row.workspace ?? row;
163
+ let row = rows.find((item) => (item.workspace?._id ?? item._id) === workspaceId);
164
+ let workspace = row?.workspace ?? row;
165
+ const ownerId = workspace?.organizationId ?? row?.organizationId;
166
+ const ownerName = workspace?.organizationName ?? row?.organizationName ?? workspace?.organization?.name ?? row?.organization?.name;
167
+ const explicitlyUnowned = workspace?.organizationId === null || workspace?.organization === null;
168
+ if (!workspace || !explicitlyUnowned && ownerId && !ownerName) {
169
+ const detail2 = dataOf(await request(`/workspace/${encodeURIComponent(workspaceId)}`, signal));
170
+ if (!detail2 || (detail2.workspace?._id ?? detail2._id) !== workspaceId) {
171
+ throw new Error("Workspace not found");
172
+ }
173
+ row = detail2;
174
+ workspace = row.workspace ?? row;
175
+ }
165
176
  const owner = workspace.organization !== void 0 ? workspace.organization : row.organization;
166
177
  const ownerRef = workspace.organizationId !== void 0 ? workspace.organizationId : row.organizationId;
167
178
  if (ownerRef === null || owner === null) return null;
@@ -421,8 +432,9 @@ function WorkspaceModal({
421
432
  let cancelled = false;
422
433
  organizationAdapter.list().then((orgs) => {
423
434
  if (cancelled) return;
424
- setAvailableOrgs(orgs);
425
- setSelectedOrgId((prev) => prev || orgs[0]?._id || "");
435
+ const joinable = orgs.filter((org) => org.role !== "guest");
436
+ setAvailableOrgs(joinable);
437
+ setSelectedOrgId((prev) => prev || joinable[0]?._id || "");
426
438
  }).catch(() => {
427
439
  if (!cancelled) setAvailableOrgs([]);
428
440
  });
@@ -537,7 +549,7 @@ function WorkspaceModal({
537
549
  async function handleLeaveOrganization() {
538
550
  if (orgActionSubmitting || !workspaceAdapter.leaveOrganization) return;
539
551
  const confirmed = window.confirm(
540
- "Leave this organization? Members of the organization will lose any implicit access to this workspace."
552
+ "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."
541
553
  );
542
554
  if (!confirmed) return;
543
555
  setOrgActionSubmitting(true);
@@ -882,7 +894,7 @@ function WorkspaceModal({
882
894
  color: active ? "#ffffff" : T.muted,
883
895
  textAlign: "center"
884
896
  },
885
- children: opt === "private" ? "Private" : "Visible to organization"
897
+ children: opt === "private" ? "Private" : "Listed in picker"
886
898
  }
887
899
  ) })
888
900
  },
@@ -1633,7 +1645,7 @@ var TAB_TO_URL = {
1633
1645
  workspaceList: "workspace/all",
1634
1646
  organization: "organization/people",
1635
1647
  organizationWorkspaces: "organization/workspaces",
1636
- organizationDomains: "organization/domains",
1648
+ organizationDomains: "organization/workspaces",
1637
1649
  referral: "referral",
1638
1650
  billing: "billing",
1639
1651
  apikey: "api-keys"
@@ -1645,7 +1657,8 @@ var URL_TO_TAB = {
1645
1657
  "workspace/all": "workspaceList",
1646
1658
  "organization/people": "organization",
1647
1659
  "organization/workspaces": "organizationWorkspaces",
1648
- "organization/domains": "organizationDomains",
1660
+ /* The Domains tab is gone; its join requests live under Workspaces. */
1661
+ "organization/domains": "organizationWorkspaces",
1649
1662
  /* Unscoped destinations. */
1650
1663
  account: "profile",
1651
1664
  referral: "referral",
@@ -1664,7 +1677,7 @@ var TAB_LABELS = {
1664
1677
  workspaceList: "My workspaces",
1665
1678
  organization: "People",
1666
1679
  organizationWorkspaces: "Workspaces",
1667
- organizationDomains: "Domains & requests",
1680
+ organizationDomains: "Workspaces",
1668
1681
  referral: "Referral",
1669
1682
  /* "Billing & usage", not "Billing". The tab has held usage history as well
1670
1683
  as the plan and invoices since the credits work, and hosts had started
@@ -1678,9 +1691,9 @@ var TAB_DESCRIPTIONS = {
1678
1691
  workspace: "Rename or re-brand this workspace, or leave and delete it",
1679
1692
  workspaceMembers: "Invite people to this workspace and control which apps they can use",
1680
1693
  workspaceList: "Switch between your workspaces, or start a new one",
1681
- organization: "Everyone in the organization, and the invites still waiting to be accepted",
1682
- organizationWorkspaces: "Every workspace that sits under this organization",
1683
- organizationDomains: "Auto-join by email domain, and workspaces asking to join the organization",
1694
+ organization: "Everyone in the organization and their role",
1695
+ organizationWorkspaces: "Every workspace in this organization, and workspaces asking to join it",
1696
+ organizationDomains: "Every workspace in this organization, and workspaces asking to join it",
1684
1697
  referral: "Invite friends to Streamoid and track the rewards you have earned",
1685
1698
  billing: "Workspace plan, credits, invoices and usage history",
1686
1699
  apikey: "Create and revoke keys for calling the Streamoid APIs"
@@ -2299,7 +2312,7 @@ function OrganizationAffiliationCard({
2299
2312
  style: { gap: T5.spMd, opacity: submitting ? 0.6 : 1 },
2300
2313
  children: [
2301
2314
  workspaceAdapter.setOrgVisibility && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-start w-full", style: { gap: T5.spXs }, children: [
2302
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldLabel, { children: "Who in the organization can see this workspace" }),
2315
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldLabel, { children: "List this workspace in the organization's workspace picker" }),
2303
2316
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2304
2317
  "div",
2305
2318
  {
@@ -2330,11 +2343,11 @@ function OrganizationAffiliationCard({
2330
2343
  if (active || !workspaceAdapter.setOrgVisibility) return;
2331
2344
  void run(
2332
2345
  () => workspaceAdapter.setOrgVisibility(opt),
2333
- opt === "private" ? "Workspace is now private" : "Workspace is now visible to the organization",
2346
+ opt === "private" ? "Workspace is now private" : "Workspace is now listed in the workspace picker",
2334
2347
  "Failed to change visibility"
2335
2348
  );
2336
2349
  },
2337
- children: opt === "private" ? "Only workspace members" : "Everyone in the organization"
2350
+ children: opt === "private" ? "Private (invite only)" : "Listed in the workspace picker"
2338
2351
  },
2339
2352
  opt
2340
2353
  );
@@ -2360,7 +2373,7 @@ function OrganizationAffiliationCard({
2360
2373
  },
2361
2374
  onClick: () => {
2362
2375
  const confirmed = window.confirm(
2363
- "Leave this organization? Members of the organization will lose any implicit access to this workspace."
2376
+ "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."
2364
2377
  );
2365
2378
  if (!confirmed || !workspaceAdapter.leaveOrganization) return;
2366
2379
  void run(
@@ -2753,12 +2766,13 @@ function InviteUpdateModal({
2753
2766
  organizationName,
2754
2767
  allowGuestStanding = true,
2755
2768
  initialOrganizationStanding = "guest",
2769
+ organizationDomain,
2756
2770
  onInvite
2757
2771
  }) {
2758
2772
  useOverlayOpen(open, onClose);
2759
2773
  const [emailValue, setEmailValue] = (0, import_react8.useState)("");
2760
2774
  const [hasChanges, setHasChanges] = (0, import_react8.useState)(false);
2761
- const [role, setRole] = (0, import_react8.useState)("admin");
2775
+ const [role, setRole] = (0, import_react8.useState)("member");
2762
2776
  const [orgStanding, setOrgStanding] = (0, import_react8.useState)(
2763
2777
  initialOrganizationStanding
2764
2778
  );
@@ -2797,7 +2811,7 @@ function InviteUpdateModal({
2797
2811
  if (!open) {
2798
2812
  setEmailValue("");
2799
2813
  setHasChanges(false);
2800
- setRole("admin");
2814
+ setRole("member");
2801
2815
  setOrgStanding(initialOrganizationStanding);
2802
2816
  setCanUseCredits(true);
2803
2817
  setCanInviteUser(true);
@@ -2809,6 +2823,14 @@ function InviteUpdateModal({
2809
2823
  setSelectedStores(/* @__PURE__ */ new Set());
2810
2824
  }
2811
2825
  }, [open, initialPermissions, hiddenKey, initialOrganizationStanding]);
2826
+ const orgDomain = (organizationDomain || "").trim().toLowerCase();
2827
+ const inviteeDomains = emailValue.split(/[\s,;]+/).map((email) => email.trim().toLowerCase().split("@")[1] || "").filter(Boolean);
2828
+ const inviteeIsGuest = mode === "invite" && inviteeDomains.some(
2829
+ (domain) => !(orgDomain && domain === orgDomain) && (showOrganizationStanding ? orgStanding === "guest" : !!orgDomain)
2830
+ );
2831
+ (0, import_react8.useEffect)(() => {
2832
+ if (inviteeIsGuest && role === "admin") setRole("member");
2833
+ }, [inviteeIsGuest, role]);
2812
2834
  if (!open) return null;
2813
2835
  const isInvite = mode === "invite";
2814
2836
  const hasEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailValue.trim());
@@ -2999,12 +3021,15 @@ function InviteUpdateModal({
2999
3021
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3000
3022
  "div",
3001
3023
  {
3002
- className: "flex-1 min-w-0 cursor-pointer",
3024
+ className: `flex-1 min-w-0 ${inviteeIsGuest ? "cursor-not-allowed" : "cursor-pointer"}`,
3025
+ "aria-disabled": inviteeIsGuest,
3003
3026
  style: {
3004
3027
  background: role === "admin" ? T6.neutral300 : "transparent",
3005
- borderRadius: T6.rMd
3028
+ borderRadius: T6.rMd,
3029
+ opacity: inviteeIsGuest ? 0.4 : 1
3006
3030
  },
3007
3031
  onClick: () => {
3032
+ if (inviteeIsGuest) return;
3008
3033
  setRole("admin");
3009
3034
  setCanUseCredits(true);
3010
3035
  setCanInviteUser(true);
@@ -3078,6 +3103,19 @@ function InviteUpdateModal({
3078
3103
  ) })
3079
3104
  ]
3080
3105
  }
3106
+ ),
3107
+ inviteeIsGuest && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3108
+ "span",
3109
+ {
3110
+ style: {
3111
+ fontFamily: T6.font,
3112
+ fontSize: T6.fsXs,
3113
+ fontWeight: 400,
3114
+ lineHeight: T6.lhXs,
3115
+ color: T6.tertiary
3116
+ },
3117
+ children: "Guests can only be Members."
3118
+ }
3081
3119
  )
3082
3120
  ]
3083
3121
  }
@@ -3267,7 +3305,7 @@ function InviteUpdateModal({
3267
3305
  lineHeight: T6.lhXs,
3268
3306
  color: T6.tertiary
3269
3307
  },
3270
- 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"}.`
3308
+ 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"}.`
3271
3309
  }
3272
3310
  )
3273
3311
  ]
@@ -3962,7 +4000,19 @@ function MemberListToolbar({
3962
4000
  className: "flex flex-wrap items-center justify-between shrink-0 w-full",
3963
4001
  style: { gap: "var(--spacing-md)" },
3964
4002
  children: [
3965
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4003
+ pendingCount === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4004
+ "span",
4005
+ {
4006
+ style: {
4007
+ fontFamily: T7.font,
4008
+ fontSize: T7.fsSm,
4009
+ fontWeight: 500,
4010
+ lineHeight: T7.lhSm,
4011
+ color: T7.primary
4012
+ },
4013
+ children: withCount(activeLabel, activeCount)
4014
+ }
4015
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3966
4016
  import_ui4.ScTabSwitcher,
3967
4017
  {
3968
4018
  tabCount: "2",
@@ -4928,9 +4978,9 @@ function TeamsContent({
4928
4978
  lineHeight: "var(--line-height-line-height-xs)"
4929
4979
  },
4930
4980
  children: [
4931
- "This workspace is visible to everyone in ",
4981
+ "Anyone in ",
4932
4982
  organizationName,
4933
- ", and its admins keep access whether or not they are listed here. Change that under Workspace \u203A General."
4983
+ " 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."
4934
4984
  ]
4935
4985
  }
4936
4986
  ),
@@ -5012,6 +5062,7 @@ function TeamsContent({
5012
5062
  ),
5013
5063
  showOrganizationStanding: mayGrantOrgMembership,
5014
5064
  organizationName: organizationName ?? orgCurrent?.name,
5065
+ organizationDomain: workspaceOrgId && orgCurrent?.id === workspaceOrgId ? orgCurrent?.domain : void 0,
5015
5066
  onInvite: handleInvite
5016
5067
  }
5017
5068
  )
@@ -5082,7 +5133,7 @@ var import_react12 = require("react");
5082
5133
  var import_ui8 = require("@streamoid/ui");
5083
5134
  var import_icons8 = require("@streamoid/icons");
5084
5135
 
5085
- // src/organization-invite-modal.tsx
5136
+ // src/organization-member-modal.tsx
5086
5137
  var import_react10 = require("react");
5087
5138
  var import_ui7 = require("@streamoid/ui");
5088
5139
  var import_icons7 = require("@streamoid/icons");
@@ -5113,33 +5164,6 @@ var T9 = {
5113
5164
  r4xl: "var(--radius-4xl)",
5114
5165
  neutral300: "var(--primitives-neutral-300)"
5115
5166
  };
5116
- function CheckboxSvg2({ checked }) {
5117
- 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: [
5118
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5119
- "rect",
5120
- {
5121
- x: "0.75",
5122
- y: "0.75",
5123
- width: "22.5",
5124
- height: "22.5",
5125
- rx: "5.65",
5126
- fill: T9.primary,
5127
- stroke: T9.primary,
5128
- strokeWidth: "1.5"
5129
- }
5130
- ),
5131
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5132
- "path",
5133
- {
5134
- d: "M7 12.5L10.5 16L17 8",
5135
- stroke: T9.inverse,
5136
- strokeWidth: "1.5",
5137
- strokeLinecap: "round",
5138
- strokeLinejoin: "round"
5139
- }
5140
- )
5141
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: svg_rodctpja6f_default.p4792500, fill: T9.muted }) }) });
5142
- }
5143
5167
  function FieldLabel3({ text }) {
5144
5168
  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)(
5145
5169
  "div",
@@ -5215,69 +5239,29 @@ function SegmentedToggle({
5215
5239
  }
5216
5240
  );
5217
5241
  }
5218
- function OrganizationInviteModal({
5242
+ function OrganizationMemberModal({
5219
5243
  open,
5220
- mode,
5221
5244
  userEmail = "email@domain.in",
5222
5245
  initialRole,
5223
5246
  lockRole,
5224
- canClaimDomain = true,
5225
- claimableDomain,
5226
5247
  lockedRoleLabel,
5227
- initialInviteAccess,
5228
- allowSubmitWithoutChanges = false,
5229
5248
  canRemove = true,
5230
5249
  onClose,
5231
- onInviteEmail,
5232
- onInviteDomain,
5233
5250
  onUpdate,
5234
5251
  onRemove
5235
5252
  }) {
5236
5253
  useOverlayOpen(open, onClose);
5237
- const [inviteMode, setInviteMode] = (0, import_react10.useState)("email");
5238
- const [emailValue, setEmailValue] = (0, import_react10.useState)("");
5239
- const [domainValue, setDomainValue] = (0, import_react10.useState)(claimableDomain ?? "");
5240
5254
  const [hasChanges, setHasChanges] = (0, import_react10.useState)(false);
5241
- const [role, setRole] = (0, import_react10.useState)("admin");
5242
- const [inviteAccess, setInviteAccess] = (0, import_react10.useState)(false);
5255
+ const [role, setRole] = (0, import_react10.useState)("member");
5243
5256
  (0, import_react10.useEffect)(() => {
5244
- if (open && mode === "update") {
5257
+ if (open) {
5245
5258
  setRole(initialRole ?? "member");
5246
- setInviteAccess(initialInviteAccess ?? false);
5247
- }
5248
- if (!open) {
5249
- setInviteMode("email");
5250
- setEmailValue("");
5251
- setDomainValue(claimableDomain ?? "");
5259
+ } else {
5252
5260
  setHasChanges(false);
5253
- setRole("admin");
5254
- setInviteAccess(true);
5255
5261
  }
5256
- }, [open, mode, initialRole, initialInviteAccess, claimableDomain]);
5262
+ }, [open, initialRole]);
5257
5263
  if (!open) return null;
5258
- const isInvite = mode === "invite";
5259
- const isEmailInvite = isInvite && inviteMode === "email";
5260
- const isDomainInvite = isInvite && inviteMode === "domain";
5261
- const hasEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailValue.trim());
5262
- const hasDomain = /^[^\s@]+\.[^\s@]+$/.test(domainValue.trim());
5263
- const isSubmitDisabled = isEmailInvite ? !hasEmail : isDomainInvite ? !hasDomain : (
5264
- // A locked role has nothing this modal can legitimately submit.
5265
- lockRole || !(allowSubmitWithoutChanges || hasChanges)
5266
- );
5267
- function markChanged() {
5268
- setHasChanges(true);
5269
- }
5270
- function handleSubmit() {
5271
- if (isEmailInvite) {
5272
- onInviteEmail?.({ email: emailValue.trim(), role, inviteAccess });
5273
- return;
5274
- }
5275
- if (isDomainInvite) {
5276
- onInviteDomain?.({ domain: domainValue.trim(), autoJoinRole: role });
5277
- return;
5278
- }
5279
- onUpdate?.({ role, inviteAccess });
5280
- }
5264
+ const isSubmitDisabled = lockRole || !hasChanges;
5281
5265
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5282
5266
  "div",
5283
5267
  {
@@ -5295,6 +5279,7 @@ function OrganizationInviteModal({
5295
5279
  borderRadius: T9.r4xl,
5296
5280
  border: `1px solid ${T9.borderSubtle}`,
5297
5281
  width: 480,
5282
+ maxWidth: "calc(100vw - 32px)",
5298
5283
  maxHeight: "85vh"
5299
5284
  },
5300
5285
  children: [
@@ -5324,7 +5309,7 @@ function OrganizationInviteModal({
5324
5309
  fontWeight: 600,
5325
5310
  lineHeight: T9.lhLg
5326
5311
  },
5327
- children: isInvite ? "Invite to organization" : "Update member"
5312
+ children: "Update member"
5328
5313
  }
5329
5314
  ),
5330
5315
  /* @__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 }) })
@@ -5338,116 +5323,41 @@ function OrganizationInviteModal({
5338
5323
  className: "flex flex-col items-start w-full",
5339
5324
  style: { padding: `${T9.sp3xl} ${T9.sp6xl}`, gap: T9.sp3xl },
5340
5325
  children: [
5341
- isInvite && canClaimDomain && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5342
- "div",
5343
- {
5344
- className: "flex flex-col items-start justify-center shrink-0 w-full",
5345
- style: { gap: T9.spXs },
5346
- children: [
5347
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: "Invite by" }),
5348
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5349
- SegmentedToggle,
5350
- {
5351
- options: [
5352
- { value: "email", label: "Email" },
5353
- { value: "domain", label: "Domain" }
5354
- ],
5355
- value: inviteMode,
5356
- onChange: setInviteMode
5357
- }
5358
- )
5359
- ]
5360
- }
5361
- ),
5362
5326
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5363
5327
  "div",
5364
5328
  {
5365
5329
  className: "flex flex-col items-start justify-center shrink-0 w-full",
5366
5330
  style: { gap: T9.spXs },
5367
5331
  children: [
5368
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5369
- FieldLabel3,
5370
- {
5371
- text: isDomainInvite ? "Domain" : isEmailInvite ? "Email ID" : "Email ID"
5372
- }
5373
- ),
5332
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: "Email ID" }),
5374
5333
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5375
5334
  "div",
5376
5335
  {
5377
5336
  className: "shrink-0 w-full",
5378
5337
  style: { background: T9.fillPlus, height: 48, borderRadius: T9.r3xl },
5379
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5338
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5380
5339
  "div",
5381
5340
  {
5382
5341
  className: "flex items-center relative size-full",
5383
5342
  style: { padding: `${T9.spXl} ${T9.sp3xl}` },
5384
- children: [
5385
- isEmailInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5386
- "input",
5387
- {
5388
- className: "flex-1 min-w-0 bg-transparent border-none outline-none",
5389
- style: {
5390
- fontFamily: T9.font,
5391
- fontSize: T9.fsSm,
5392
- fontWeight: 400,
5393
- lineHeight: T9.lhSm,
5394
- color: T9.primary
5395
- },
5396
- placeholder: "email@domain.in",
5397
- value: emailValue,
5398
- onChange: (e) => setEmailValue(e.target.value)
5399
- }
5400
- ),
5401
- isDomainInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5402
- "span",
5403
- {
5404
- className: "flex-1 min-w-0 truncate",
5405
- style: {
5406
- fontFamily: T9.font,
5407
- fontSize: T9.fsSm,
5408
- fontWeight: 400,
5409
- lineHeight: T9.lhSm,
5410
- color: T9.primary
5411
- },
5412
- children: domainValue
5413
- }
5414
- ),
5415
- !isInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5416
- "div",
5417
- {
5418
- className: "flex-1 flex flex-col justify-center",
5419
- style: {
5420
- fontFamily: T9.font,
5421
- fontSize: T9.fsSm,
5422
- fontWeight: 400,
5423
- lineHeight: T9.lhSm,
5424
- color: T9.muted,
5425
- opacity: 0.6
5426
- },
5427
- children: userEmail
5428
- }
5429
- )
5430
- ]
5343
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5344
+ "div",
5345
+ {
5346
+ className: "flex-1 flex flex-col justify-center truncate",
5347
+ style: {
5348
+ fontFamily: T9.font,
5349
+ fontSize: T9.fsSm,
5350
+ fontWeight: 400,
5351
+ lineHeight: T9.lhSm,
5352
+ color: T9.muted,
5353
+ opacity: 0.6
5354
+ },
5355
+ children: userEmail
5356
+ }
5357
+ )
5431
5358
  }
5432
5359
  ) })
5433
5360
  }
5434
- ),
5435
- isDomainInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5436
- "span",
5437
- {
5438
- style: {
5439
- fontFamily: T9.font,
5440
- fontSize: T9.fsSm,
5441
- fontWeight: 400,
5442
- color: T9.muted
5443
- },
5444
- children: [
5445
- "Anyone who signs up or logs in with an @",
5446
- domainValue.trim(),
5447
- " ",
5448
- "address will automatically join this organization from now on."
5449
- ]
5450
- }
5451
5361
  )
5452
5362
  ]
5453
5363
  }
@@ -5458,7 +5368,7 @@ function OrganizationInviteModal({
5458
5368
  className: "flex flex-col items-start justify-center shrink-0 w-full",
5459
5369
  style: { gap: T9.spXs },
5460
5370
  children: [
5461
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: isDomainInvite ? "Auto-join role" : "Role" }),
5371
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldLabel3, { text: "Role" }),
5462
5372
  lockRole ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5463
5373
  "span",
5464
5374
  {
@@ -5470,49 +5380,33 @@ function OrganizationInviteModal({
5470
5380
  },
5471
5381
  children: [
5472
5382
  lockedRoleLabel || "Guest",
5473
- " \u2014 invited to specific workspaces only, with no access to the rest of the organization. Remove them to end that access."
5383
+ " \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."
5474
5384
  ]
5475
5385
  }
5476
5386
  ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5477
5387
  SegmentedToggle,
5478
5388
  {
5479
5389
  options: [
5480
- { value: "admin", label: "Admin" },
5390
+ { value: "admin", label: "Org admin" },
5481
5391
  { value: "member", label: "Member" }
5482
5392
  ],
5483
5393
  value: role,
5484
5394
  onChange: (v) => {
5485
5395
  setRole(v);
5486
- if (v === "admin") setInviteAccess(true);
5487
- markChanged();
5396
+ setHasChanges(v !== (initialRole ?? "member"));
5488
5397
  }
5489
5398
  }
5490
- )
5491
- ]
5492
- }
5493
- ),
5494
- !isDomainInvite && !lockRole && role === "member" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5495
- "div",
5496
- {
5497
- className: "flex items-center shrink-0 w-full cursor-pointer",
5498
- style: { gap: T9.spMd },
5499
- onClick: () => {
5500
- setInviteAccess(!inviteAccess);
5501
- markChanged();
5502
- },
5503
- children: [
5504
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CheckboxSvg2, { checked: inviteAccess }),
5505
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5399
+ ),
5400
+ !lockRole && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5506
5401
  "span",
5507
5402
  {
5508
5403
  style: {
5509
5404
  fontFamily: T9.font,
5510
5405
  fontSize: T9.fsSm,
5511
- fontWeight: 400,
5512
5406
  lineHeight: T9.lhSm,
5513
- color: T9.primary
5407
+ color: T9.muted
5514
5408
  },
5515
- children: "Can invite members"
5409
+ children: "Org admins can see and manage every workspace in the organization."
5516
5410
  }
5517
5411
  )
5518
5412
  ]
@@ -5533,10 +5427,10 @@ function OrganizationInviteModal({
5533
5427
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5534
5428
  "div",
5535
5429
  {
5536
- className: `flex items-center w-full ${isInvite ? "justify-end" : "justify-between"}`,
5430
+ className: "flex items-center w-full justify-between",
5537
5431
  style: { padding: `${T9.sp3xl} ${T9.sp6xl}` },
5538
5432
  children: [
5539
- !isInvite && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5433
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5540
5434
  import_ui7.ScButton,
5541
5435
  {
5542
5436
  text: "Remove member",
@@ -5549,11 +5443,11 @@ function OrganizationInviteModal({
5549
5443
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5550
5444
  import_ui7.ScButton,
5551
5445
  {
5552
- text: isEmailInvite ? "Send invite" : isDomainInvite ? "Add domain" : allowSubmitWithoutChanges ? "Resend invite" : "Save changes",
5446
+ text: "Save changes",
5553
5447
  variant: "primary",
5554
5448
  size: "md",
5555
5449
  state: isSubmitDisabled ? "disabled" : "default",
5556
- onClick: !isSubmitDisabled ? handleSubmit : void 0
5450
+ onClick: !isSubmitDisabled ? () => onUpdate?.({ role }) : void 0
5557
5451
  }
5558
5452
  )
5559
5453
  ]
@@ -6047,20 +5941,6 @@ function formatDate(iso) {
6047
5941
  return iso;
6048
5942
  }
6049
5943
  }
6050
- function toDisplayText2(value, fallback = "\u2013") {
6051
- if (typeof value === "string") {
6052
- const trimmed = value.trim();
6053
- return trimmed.length > 0 ? trimmed : fallback;
6054
- }
6055
- if (value && typeof value === "object") {
6056
- const record = value;
6057
- const name = typeof record.name === "string" ? record.name.trim() : "";
6058
- if (name) return name;
6059
- const email = typeof record.email === "string" ? record.email.trim() : "";
6060
- if (email) return email;
6061
- }
6062
- return fallback;
6063
- }
6064
5944
  function VisibilityBadge({ visibility }) {
6065
5945
  const isShared = visibility === "shared";
6066
5946
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
@@ -6081,10 +5961,46 @@ function VisibilityBadge({ visibility }) {
6081
5961
  }
6082
5962
  );
6083
5963
  }
5964
+ function RowIconButton({
5965
+ label,
5966
+ onClick,
5967
+ children,
5968
+ visible = true
5969
+ }) {
5970
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5971
+ "button",
5972
+ {
5973
+ type: "button",
5974
+ "aria-label": label,
5975
+ title: label,
5976
+ className: "flex items-center justify-center shrink-0 cursor-pointer border-none",
5977
+ style: {
5978
+ width: 32,
5979
+ height: 32,
5980
+ borderRadius: T11.rLg,
5981
+ background: "transparent",
5982
+ opacity: visible ? 1 : 0,
5983
+ transition: "opacity 120ms ease"
5984
+ },
5985
+ onMouseEnter: (e) => {
5986
+ e.currentTarget.style.background = T11.fillPlus;
5987
+ },
5988
+ onMouseLeave: (e) => {
5989
+ e.currentTarget.style.background = "transparent";
5990
+ },
5991
+ onClick,
5992
+ children
5993
+ }
5994
+ );
5995
+ }
6084
5996
  function MemberRow({
6085
5997
  member,
6086
- onClick
5998
+ onEdit,
5999
+ onCopyEmail
6087
6000
  }) {
6001
+ const [copied, setCopied] = (0, import_react12.useState)(false);
6002
+ const [active, setActive] = (0, import_react12.useState)(false);
6003
+ const noHover = typeof window !== "undefined" && window.matchMedia?.("(hover: none)").matches === true;
6088
6004
  const name = member.user.name?.trim();
6089
6005
  const email = member.user.email || "";
6090
6006
  const displayIdent = name || email.split("@")[0] || "user";
@@ -6096,18 +6012,20 @@ function MemberRow({
6096
6012
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6097
6013
  "div",
6098
6014
  {
6099
- className: `flex items-center self-stretch shrink-0 ${onClick ? "cursor-pointer" : "cursor-default"}`,
6015
+ className: "flex items-center self-stretch shrink-0 cursor-default",
6100
6016
  style: {
6101
6017
  borderBottom: `1px solid ${T11.borderSubtle}`,
6102
6018
  padding: `${T11.sp5xl} ${T11.sp3xl}`,
6103
- gap: T11.sp6xl
6104
- },
6105
- onClick,
6106
- onMouseEnter: (e) => {
6107
- if (onClick) e.currentTarget.style.background = T11.fillPlus;
6019
+ gap: T11.sp6xl,
6020
+ background: active && onEdit ? T11.fillPlus : "transparent"
6108
6021
  },
6109
- onMouseLeave: (e) => {
6110
- e.currentTarget.style.background = "transparent";
6022
+ onMouseEnter: () => setActive(true),
6023
+ onMouseLeave: () => setActive(false),
6024
+ onFocus: () => setActive(true),
6025
+ onBlur: (e) => {
6026
+ if (!e.currentTarget.contains(e.relatedTarget)) {
6027
+ setActive(false);
6028
+ }
6111
6029
  },
6112
6030
  children: [
6113
6031
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
@@ -6186,112 +6104,29 @@ function MemberRow({
6186
6104
  },
6187
6105
  children: formatDate(member.createdAt || member.user.createdAt)
6188
6106
  }
6189
- )
6190
- ]
6191
- }
6192
- );
6193
- }
6194
- function InviteRow({
6195
- invite,
6196
- onCancel,
6197
- onResend
6198
- }) {
6199
- const isDomain = invite.kind === "domain";
6200
- const invitedByText = (() => {
6201
- if (!invite.invitedBy) return "\u2013";
6202
- if (typeof invite.invitedBy === "string") return invite.invitedBy;
6203
- return invite.invitedBy.name || invite.invitedBy.email || "\u2013";
6204
- })();
6205
- const role = isDomain ? invite.autoJoinRole : invite.role;
6206
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6207
- "div",
6208
- {
6209
- className: "flex items-center self-stretch shrink-0",
6210
- style: {
6211
- borderBottom: `1px solid ${T11.borderSubtle}`,
6212
- padding: `${T11.sp5xl} ${T11.sp3xl}`,
6213
- gap: T11.sp6xl
6214
- },
6215
- children: [
6216
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center min-w-0", style: { flex: 1, gap: T11.spMd }, children: [
6217
- isDomain ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6218
- "div",
6219
- {
6220
- className: "shrink-0 flex items-center justify-center",
6221
- style: {
6222
- width: 32,
6223
- height: 32,
6224
- borderRadius: T11.rXl,
6225
- background: T11.fillNeutral
6226
- },
6227
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconGlobe, { className: "w-4 h-4", color: T11.primary })
6228
- }
6229
- ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ProfileAvatar, { name: invite.email || "invite", size: 32 }),
6230
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col min-w-0", children: [
6231
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6232
- "span",
6233
- {
6234
- className: "truncate",
6235
- style: {
6236
- fontFamily: T11.font,
6237
- fontSize: T11.fsSm,
6238
- fontWeight: 500,
6239
- lineHeight: T11.lhSm,
6240
- color: T11.primary
6241
- },
6242
- children: isDomain ? `Domain: ${invite.domain}` : toDisplayText2(invite.email, "")
6243
- }
6244
- ),
6245
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6246
- "span",
6247
- {
6248
- className: "truncate",
6249
- style: {
6250
- fontFamily: T11.font,
6251
- fontSize: T11.fsXs,
6252
- fontWeight: 400,
6253
- lineHeight: T11.lhXs,
6254
- color: T11.muted
6255
- },
6256
- children: isDomain ? "Auto-join by verified domain" : "Email invite"
6257
- }
6258
- )
6259
- ] })
6260
- ] }),
6261
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6262
- RoleLabel,
6107
+ ),
6108
+ /* @__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)(
6109
+ RowIconButton,
6263
6110
  {
6264
- role: role === "admin" ? "admin" : "member",
6265
- label: orgRoleLabel(role)
6111
+ label: `Manage ${email || "member"}`,
6112
+ onClick: onEdit,
6113
+ visible: active || noHover,
6114
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconSettings, { className: "w-4 h-4", color: T11.primary })
6266
6115
  }
6267
- ),
6268
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6269
- "div",
6116
+ ) : email && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6117
+ RowIconButton,
6270
6118
  {
6271
- className: "truncate shrink-0",
6272
- style: {
6273
- width: "7.5rem",
6274
- fontFamily: T11.font,
6275
- fontSize: T11.fsSm,
6276
- fontWeight: 400,
6277
- lineHeight: T11.lhSm,
6278
- color: T11.primary
6119
+ label: copied ? "Copied" : `Copy ${email}`,
6120
+ onClick: () => {
6121
+ void onCopyEmail().then((ok) => {
6122
+ if (!ok) return;
6123
+ setCopied(true);
6124
+ setTimeout(() => setCopied(false), 2e3);
6125
+ });
6279
6126
  },
6280
- children: invitedByText
6127
+ 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 })
6281
6128
  }
6282
- ),
6283
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6284
- !isDomain && onResend && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui8.ScButton, { text: "Resend", variant: "tertiary", size: "sm", onClick: onResend }),
6285
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6286
- import_ui8.ScButton,
6287
- {
6288
- text: isDomain ? "Revoke" : "Cancel",
6289
- variant: "error",
6290
- size: "sm",
6291
- onClick: onCancel
6292
- }
6293
- )
6294
- ] })
6129
+ ) })
6295
6130
  ]
6296
6131
  }
6297
6132
  );
@@ -6398,7 +6233,7 @@ function JoinRequestRow({
6398
6233
  )
6399
6234
  ] })
6400
6235
  ] }),
6401
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6236
+ onApprove && onReject && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6402
6237
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui8.ScButton, { text: "Reject", variant: "error", size: "sm", onClick: onReject }),
6403
6238
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui8.ScButton, { text: "Approve", variant: "primary", size: "sm", onClick: onApprove })
6404
6239
  ] })
@@ -6454,42 +6289,29 @@ function Section({
6454
6289
  }
6455
6290
  function OrganizationContent({
6456
6291
  embedded = false,
6457
- view = "people",
6458
- onInviteOpen,
6459
- onInviteOpenChange
6292
+ view = "people"
6460
6293
  } = {}) {
6461
6294
  const { organization, toast } = useSettingsAdapters();
6462
6295
  const current = organization.current;
6463
- const [activeTab, setActiveTab] = (0, import_react12.useState)("active");
6464
6296
  const [members, setMembers] = (0, import_react12.useState)([]);
6465
- const [invites, setInvites] = (0, import_react12.useState)([]);
6466
6297
  const [workspaces, setWorkspaces] = (0, import_react12.useState)([]);
6467
6298
  const [joinRequests, setJoinRequests] = (0, import_react12.useState)([]);
6468
6299
  const [highestRole, setHighestRole] = (0, import_react12.useState)("guest");
6469
- const [emailDomain, setEmailDomain] = (0, import_react12.useState)(null);
6470
- const [canClaimEmailDomain, setCanClaimEmailDomain] = (0, import_react12.useState)(true);
6471
6300
  const [searchQuery, setSearchQuery] = (0, import_react12.useState)("");
6472
6301
  const [loadingData, setLoadingData] = (0, import_react12.useState)(false);
6473
- const [modal, setModal] = (0, import_react12.useState)({ open: false, mode: "invite" });
6302
+ const [modal, setModal] = (0, import_react12.useState)({ open: false });
6474
6303
  const fetchData = (0, import_react12.useCallback)(async () => {
6304
+ if (!organization.current) return;
6475
6305
  setLoadingData(true);
6476
6306
  try {
6477
- const [membersRes, invitesRes, workspacesRes, joinRequestsRes, highestRoleRes] = await Promise.all([
6307
+ const [membersRes, workspacesRes, joinRequestsRes, highestRoleRes] = await Promise.all([
6478
6308
  organization.fetchMembers().catch(() => null),
6479
- organization.fetchInvites().catch(() => null),
6480
6309
  organization.fetchWorkspaces().catch(() => null),
6481
6310
  organization.fetchJoinRequests().catch(() => null),
6482
6311
  organization.getHighestRole?.().catch(() => null) ?? Promise.resolve(null)
6483
6312
  ]);
6484
6313
  if (highestRoleRes?.role) setHighestRole(highestRoleRes.role);
6485
- if (highestRoleRes?.emailDomain !== void 0) {
6486
- setEmailDomain(highestRoleRes.emailDomain ?? null);
6487
- }
6488
- if (highestRoleRes?.canClaimEmailDomain !== void 0) {
6489
- setCanClaimEmailDomain(highestRoleRes.canClaimEmailDomain);
6490
- }
6491
6314
  if (membersRes) setMembers(membersRes.filter((m) => m?.user));
6492
- if (invitesRes) setInvites(invitesRes);
6493
6315
  if (workspacesRes) setWorkspaces(workspacesRes);
6494
6316
  if (joinRequestsRes) setJoinRequests(joinRequestsRes);
6495
6317
  } catch {
@@ -6500,51 +6322,6 @@ function OrganizationContent({
6500
6322
  (0, import_react12.useEffect)(() => {
6501
6323
  fetchData();
6502
6324
  }, [fetchData]);
6503
- const [domainMatch, setDomainMatch] = (0, import_react12.useState)(null);
6504
- const [checkingDomainMatch, setCheckingDomainMatch] = (0, import_react12.useState)(false);
6505
- const [joiningByDomain, setJoiningByDomain] = (0, import_react12.useState)(false);
6506
- const [createModalOpen, setCreateModalOpen] = (0, import_react12.useState)(false);
6507
- (0, import_react12.useEffect)(() => {
6508
- if (current) return;
6509
- let cancelled = false;
6510
- setCheckingDomainMatch(true);
6511
- organization.checkDomainMatch().then((match) => {
6512
- if (!cancelled) setDomainMatch(match);
6513
- }).catch(() => {
6514
- if (!cancelled) setDomainMatch(null);
6515
- }).finally(() => {
6516
- if (!cancelled) setCheckingDomainMatch(false);
6517
- });
6518
- return () => {
6519
- cancelled = true;
6520
- };
6521
- }, [current, organization]);
6522
- const handleJoinByDomain = async () => {
6523
- if (!domainMatch) return;
6524
- setJoiningByDomain(true);
6525
- try {
6526
- await organization.joinByDomain();
6527
- await organization.switch(domainMatch.organizationId);
6528
- toast.success(`Joined ${domainMatch.organizationName}`);
6529
- } catch (err) {
6530
- toast.error(err instanceof Error ? err.message : "Failed to join organization");
6531
- } finally {
6532
- setJoiningByDomain(false);
6533
- }
6534
- };
6535
- const handleCreateOrganization = async (name, imageFile) => {
6536
- try {
6537
- const result = await organization.create({ name, imageFile });
6538
- if (result && "id" in result && result.id) {
6539
- await organization.switch(result.id);
6540
- }
6541
- toast.success("Organization created");
6542
- return true;
6543
- } catch (err) {
6544
- toast.error(err instanceof Error ? err.message : "Failed to create organization");
6545
- return false;
6546
- }
6547
- };
6548
6325
  const [manageModalOpen, setManageModalOpen] = (0, import_react12.useState)(false);
6549
6326
  const handleUpdateOrganization = async (name, imageFile) => {
6550
6327
  if (!current) return false;
@@ -6568,45 +6345,9 @@ function OrganizationContent({
6568
6345
  return false;
6569
6346
  }
6570
6347
  };
6571
- const openInviteModal = () => setModal({ open: true, mode: "invite" });
6572
- (0, import_react12.useEffect)(() => {
6573
- if (onInviteOpen) {
6574
- openInviteModal();
6575
- onInviteOpenChange?.(false);
6576
- }
6577
- }, [onInviteOpen]);
6578
- const openUpdateModal = (member) => setModal({ open: true, mode: "update", target: member });
6579
- const closeModal = () => setModal({ open: false, mode: "invite" });
6580
- const handleInviteEmail = async (input) => {
6581
- if (highestRole !== "admin") {
6582
- toast.error("You do not have permission to invite members");
6583
- closeModal();
6584
- return;
6585
- }
6586
- try {
6587
- await organization.inviteMemberByEmail(input);
6588
- toast.success(`Invitation sent to ${input.email}`);
6589
- await fetchData();
6590
- closeModal();
6591
- } catch (err) {
6592
- toast.error(err instanceof Error ? err.message : "Failed to send invite");
6593
- }
6594
- };
6595
- const handleInviteDomain = async (input) => {
6596
- if (highestRole !== "admin") {
6597
- toast.error("You do not have permission to invite members");
6598
- closeModal();
6599
- return;
6600
- }
6601
- try {
6602
- await organization.inviteDomain(input);
6603
- toast.success(`Domain ${input.domain} added`);
6604
- await fetchData();
6605
- closeModal();
6606
- } catch (err) {
6607
- toast.error(err instanceof Error ? err.message : "Failed to add domain");
6608
- }
6609
- };
6348
+ const openUpdateModal = (member) => setModal({ open: true, target: member });
6349
+ const closeModal = () => setModal({ open: false });
6350
+ const adminCount = members.filter((m) => m.role === "admin").length;
6610
6351
  const handleUpdateMember = async (input) => {
6611
6352
  const target = modal.target;
6612
6353
  if (!target) {
@@ -6618,20 +6359,16 @@ function OrganizationContent({
6618
6359
  closeModal();
6619
6360
  return;
6620
6361
  }
6621
- if (target.role === "admin" && input.role === "member") {
6622
- const adminCount = members.filter((m) => m.role === "admin").length;
6623
- if (adminCount <= 1) {
6624
- toast.error(
6625
- "You're the only admin. Promote another member to admin before changing your role."
6626
- );
6627
- return;
6628
- }
6362
+ if (target.role === "admin" && input.role === "member" && adminCount <= 1) {
6363
+ toast.error(
6364
+ "This is the only org admin. Make someone else an org admin first."
6365
+ );
6366
+ return;
6629
6367
  }
6630
6368
  try {
6631
6369
  await organization.updateMemberRole({
6632
6370
  userId: target.user._id,
6633
- role: input.role,
6634
- inviteAccess: input.inviteAccess
6371
+ role: input.role
6635
6372
  });
6636
6373
  toast.success("Member updated");
6637
6374
  await fetchData();
@@ -6651,8 +6388,14 @@ function OrganizationContent({
6651
6388
  closeModal();
6652
6389
  return;
6653
6390
  }
6391
+ if (target.role === "admin" && adminCount <= 1) {
6392
+ toast.error(
6393
+ "This is the only org admin. Make someone else an org admin first."
6394
+ );
6395
+ return;
6396
+ }
6654
6397
  const confirmed = window.confirm(
6655
- `Remove ${target.user.email} from this organization?`
6398
+ `Remove ${target.user.email} from this organization? They'll also be removed from every workspace in it.`
6656
6399
  );
6657
6400
  if (!confirmed) return;
6658
6401
  try {
@@ -6664,44 +6407,6 @@ function OrganizationContent({
6664
6407
  }
6665
6408
  closeModal();
6666
6409
  };
6667
- const handleCancelInvite = async (invite) => {
6668
- if (highestRole !== "admin") {
6669
- toast.error("You do not have permission to manage invites");
6670
- return;
6671
- }
6672
- const label = invite.kind === "domain" ? invite.domain : invite.email;
6673
- const confirmed = window.confirm(
6674
- invite.kind === "domain" ? `Revoke domain invite for ${label}? Members who already joined will keep their access.` : `Cancel invitation for ${label}?`
6675
- );
6676
- if (!confirmed) return;
6677
- try {
6678
- if (invite.kind === "domain") {
6679
- await organization.revokeDomainInvite(invite._id);
6680
- } else if (invite.token) {
6681
- await organization.cancelInvite(invite.token);
6682
- } else {
6683
- await organization.removeMember({ invitationIds: [invite._id] });
6684
- }
6685
- toast.success(invite.kind === "domain" ? "Domain invite revoked" : "Invitation removed");
6686
- await fetchData();
6687
- } catch (err) {
6688
- toast.error(err instanceof Error ? err.message : "Failed to update invite");
6689
- }
6690
- };
6691
- const handleResendInvite = async (invite) => {
6692
- if (invite.kind !== "email") return;
6693
- if (highestRole !== "admin") {
6694
- toast.error("You do not have permission to invite members");
6695
- return;
6696
- }
6697
- try {
6698
- await organization.resendInvite(invite._id);
6699
- toast.success("Invitation resent");
6700
- await fetchData();
6701
- } catch (err) {
6702
- toast.error(err instanceof Error ? err.message : "Failed to resend invitation");
6703
- }
6704
- };
6705
6410
  const handleApproveJoinRequest = async (request) => {
6706
6411
  try {
6707
6412
  await organization.approveJoinRequest(request._id);
@@ -6723,101 +6428,29 @@ function OrganizationContent({
6723
6428
  }
6724
6429
  };
6725
6430
  if (!current) {
6726
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6727
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6728
- "div",
6729
- {
6730
- className: "flex flex-col flex-1 min-w-0 min-h-0 w-full items-center justify-center",
6731
- style: { gap: T11.spXl, padding: T11.sp6xl },
6732
- children: [
6733
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconGlobe, { className: "w-10 h-10", color: T11.muted }),
6734
- domainMatch ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6735
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6736
- "p",
6737
- {
6738
- className: "text-center",
6739
- style: { color: T11.primary, fontFamily: T11.font, fontSize: T11.fsMd, fontWeight: 600 },
6740
- children: [
6741
- "An organization already exists for ",
6742
- domainMatch.domain
6743
- ]
6744
- }
6745
- ),
6746
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6747
- "p",
6748
- {
6749
- className: "text-center",
6750
- style: { color: T11.muted, fontFamily: T11.font, fontSize: T11.fsSm },
6751
- children: [
6752
- domainMatch.organizationName,
6753
- " \u2014 join it instead of creating a new one and fragmenting your company across multiple orgs."
6754
- ]
6755
- }
6756
- ),
6757
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6758
- import_ui8.ScButton,
6759
- {
6760
- text: joiningByDomain ? "Joining\u2026" : `Join ${domainMatch.organizationName}`,
6761
- variant: "primary",
6762
- size: "md",
6763
- state: joiningByDomain ? "disabled" : "default",
6764
- onClick: handleJoinByDomain
6765
- }
6766
- )
6767
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6768
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6769
- "p",
6770
- {
6771
- className: "text-center",
6772
- style: { color: T11.primary, fontFamily: T11.font, fontSize: T11.fsMd, fontWeight: 600 },
6773
- children: "You're not part of an organization yet"
6774
- }
6775
- ),
6776
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6777
- "p",
6778
- {
6779
- className: "text-center",
6780
- style: { color: T11.muted, fontFamily: T11.font, fontSize: T11.fsSm },
6781
- children: "Create one to invite your team and manage workspaces together."
6782
- }
6783
- ),
6784
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6785
- import_ui8.ScButton,
6786
- {
6787
- text: "Create organization",
6788
- variant: "primary",
6789
- size: "md",
6790
- styleVariant: "icon-left",
6791
- icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconUserAdd, { className: "w-5 h-5", color: T11.inverse }),
6792
- state: checkingDomainMatch ? "disabled" : "default",
6793
- onClick: () => setCreateModalOpen(true)
6794
- }
6795
- )
6796
- ] })
6797
- ]
6798
- }
6799
- ),
6800
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6801
- OrganizationModal,
6802
- {
6803
- open: createModalOpen,
6804
- mode: "create",
6805
- onClose: () => setCreateModalOpen(false),
6806
- onCreate: handleCreateOrganization
6807
- }
6808
- )
6809
- ] });
6431
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6432
+ "div",
6433
+ {
6434
+ className: "flex flex-col flex-1 min-w-0 min-h-0 w-full items-center justify-center",
6435
+ style: { gap: T11.spXl, padding: T11.sp6xl },
6436
+ children: [
6437
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconGlobe, { className: "w-10 h-10", color: T11.muted }),
6438
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6439
+ "p",
6440
+ {
6441
+ className: "text-center",
6442
+ style: { color: T11.primary, fontFamily: T11.font, fontSize: T11.fsMd, fontWeight: 600 },
6443
+ children: "Your workspace isn't part of an organization"
6444
+ }
6445
+ )
6446
+ ]
6447
+ }
6448
+ );
6810
6449
  }
6811
6450
  const q = searchQuery.toLowerCase();
6812
6451
  const filteredMembers = members.filter(
6813
6452
  (m) => (m.user.email || "").toLowerCase().includes(q) || (m.user.name || "").toLowerCase().includes(q) || m.role.toLowerCase().includes(q)
6814
6453
  );
6815
- const filteredInvites = invites.filter((inv) => {
6816
- const label = inv.kind === "domain" ? inv.domain ?? "" : inv.email ?? "";
6817
- return label.toLowerCase().includes(q);
6818
- });
6819
- const emailInvites = filteredInvites.filter((inv) => inv.kind !== "domain");
6820
- const domainInvites = invites.filter((inv) => inv.kind === "domain");
6821
6454
  const scopeHeader = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6822
6455
  ScopeHeader,
6823
6456
  {
@@ -6866,15 +6499,26 @@ function OrganizationContent({
6866
6499
  )
6867
6500
  }
6868
6501
  );
6502
+ const isOrgAdmin = highestRole === "admin";
6503
+ const copyEmail = async (email) => {
6504
+ try {
6505
+ await navigator.clipboard.writeText(email);
6506
+ toast.success("Email copied");
6507
+ return true;
6508
+ } catch {
6509
+ toast.error("Couldn't copy the email");
6510
+ return false;
6511
+ }
6512
+ };
6869
6513
  const peopleView = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6870
6514
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6871
6515
  MemberListToolbar,
6872
6516
  {
6873
- activeTab,
6874
- onTabChange: setActiveTab,
6517
+ activeTab: "active",
6518
+ onTabChange: () => {
6519
+ },
6875
6520
  activeLabel: "Members",
6876
6521
  activeCount: filteredMembers.length,
6877
- pendingCount: emailInvites.length,
6878
6522
  searchQuery,
6879
6523
  onSearchChange: setSearchQuery
6880
6524
  }
@@ -6882,47 +6526,31 @@ function OrganizationContent({
6882
6526
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6883
6527
  MemberListFrame,
6884
6528
  {
6885
- isEmpty: activeTab === "active" ? filteredMembers.length === 0 : emailInvites.length === 0,
6886
- emptyText: activeTab === "active" ? "No members yet" : "No pending invites",
6529
+ isEmpty: filteredMembers.length === 0,
6530
+ emptyText: "No members yet",
6887
6531
  grow: "fill",
6888
- 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)(
6889
- InviteRow,
6532
+ children: filteredMembers.map((m) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6533
+ MemberRow,
6890
6534
  {
6891
- invite: inv,
6892
- onCancel: () => void handleCancelInvite(inv),
6893
- onResend: () => void handleResendInvite(inv)
6535
+ member: m,
6536
+ onEdit: isOrgAdmin ? () => openUpdateModal(m) : void 0,
6537
+ onCopyEmail: () => copyEmail(m.user.email)
6894
6538
  },
6895
- inv._id
6539
+ m._id
6896
6540
  ))
6897
6541
  }
6898
6542
  )
6899
6543
  ] });
6900
- const workspacesView = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6901
- Section,
6902
- {
6903
- title: "Workspaces in this organization",
6904
- empty: workspaces.length === 0,
6905
- children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(WorkspaceRow, { workspace: ws }, ws._id))
6906
- }
6907
- );
6908
- const domainsView = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6544
+ const workspacesView = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6909
6545
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6910
6546
  Section,
6911
6547
  {
6912
- title: "Auto-join by email domain",
6913
- empty: domainInvites.length === 0,
6914
- 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.`,
6915
- children: domainInvites.map((inv) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6916
- InviteRow,
6917
- {
6918
- invite: inv,
6919
- onCancel: () => void handleCancelInvite(inv)
6920
- },
6921
- inv._id
6922
- ))
6548
+ title: "Workspaces in this organization",
6549
+ empty: workspaces.length === 0,
6550
+ children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(WorkspaceRow, { workspace: ws }, ws._id))
6923
6551
  }
6924
6552
  ),
6925
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6553
+ (isOrgAdmin || joinRequests.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6926
6554
  Section,
6927
6555
  {
6928
6556
  title: "Workspaces asking to join",
@@ -6931,8 +6559,8 @@ function OrganizationContent({
6931
6559
  JoinRequestRow,
6932
6560
  {
6933
6561
  request: req,
6934
- onApprove: () => void handleApproveJoinRequest(req),
6935
- onReject: () => void handleRejectJoinRequest(req)
6562
+ onApprove: isOrgAdmin ? () => void handleApproveJoinRequest(req) : void 0,
6563
+ onReject: isOrgAdmin ? () => void handleRejectJoinRequest(req) : void 0
6936
6564
  },
6937
6565
  req._id
6938
6566
  ))
@@ -6943,25 +6571,17 @@ function OrganizationContent({
6943
6571
  scopeHeader,
6944
6572
  loadingData && loadingBlock,
6945
6573
  !loadingData && view === "people" && peopleView,
6946
- !loadingData && view === "workspaces" && workspacesView,
6947
- !loadingData && view === "domains" && domainsView,
6574
+ !loadingData && view !== "people" && workspacesView,
6948
6575
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6949
- OrganizationInviteModal,
6576
+ OrganizationMemberModal,
6950
6577
  {
6951
6578
  open: modal.open,
6952
- mode: modal.mode,
6953
6579
  userEmail: modal.target?.user.email,
6954
6580
  initialRole: modal.target?.role === "admin" || modal.target?.role === "member" ? modal.target.role : void 0,
6955
- initialInviteAccess: modal.target?.inviteAccess,
6956
- allowSubmitWithoutChanges: false,
6957
6581
  lockRole: modal.target?.role === "guest",
6958
6582
  lockedRoleLabel: orgRoleLabel(modal.target?.role),
6959
6583
  canRemove: highestRole === "admin",
6960
- canClaimDomain: canClaimEmailDomain,
6961
- claimableDomain: emailDomain,
6962
6584
  onClose: closeModal,
6963
- onInviteEmail: handleInviteEmail,
6964
- onInviteDomain: handleInviteDomain,
6965
6585
  onUpdate: handleUpdateMember,
6966
6586
  onRemove: handleRemoveMember
6967
6587
  }
@@ -7006,34 +6626,20 @@ function OrganizationContent({
7006
6626
  backgroundSize: "24px 24px"
7007
6627
  },
7008
6628
  children: [
7009
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between shrink-0 w-full", children: [
7010
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7011
- "p",
7012
- {
7013
- className: "flex-1 min-w-0 truncate",
7014
- style: {
7015
- color: T11.primary,
7016
- fontFamily: T11.font,
7017
- fontSize: "var(--font-size-font-size-xxl)",
7018
- fontWeight: 600,
7019
- lineHeight: "var(--line-height-line-height-xxl)"
7020
- },
7021
- children: current?.name ? `${current.name} \u2014 Organization` : "Organization"
7022
- }
7023
- ),
7024
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7025
- import_ui8.ScButton,
7026
- {
7027
- text: "Invite",
7028
- variant: "primary",
7029
- size: "md",
7030
- styleVariant: "icon-left",
7031
- state: highestRole !== "admin" ? "disabled" : "default",
7032
- icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons8.SiconUserAdd, { className: "w-5 h-5", color: "var(--alias-text---icons-inverse)" }),
7033
- onClick: openInviteModal
7034
- }
7035
- )
7036
- ] }),
6629
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center justify-between shrink-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6630
+ "p",
6631
+ {
6632
+ className: "flex-1 min-w-0 truncate",
6633
+ style: {
6634
+ color: T11.primary,
6635
+ fontFamily: T11.font,
6636
+ fontSize: "var(--font-size-font-size-xxl)",
6637
+ fontWeight: 600,
6638
+ lineHeight: "var(--line-height-line-height-xxl)"
6639
+ },
6640
+ children: current?.name ? `${current.name} \u2014 Organization` : "Organization"
6641
+ }
6642
+ ) }),
7037
6643
  innerContent
7038
6644
  ]
7039
6645
  }
@@ -10605,8 +10211,7 @@ function SettingsNav({
10605
10211
  /* @__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: [
10606
10212
  /* @__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" }),
10607
10213
  item("organization", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconTeam, { className: "w-5 h-5", color })),
10608
- item("organizationWorkspaces", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconWorkspace, { className: "w-5 h-5", color })),
10609
- item("organizationDomains", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconLock, { className: "w-5 h-5", color }))
10214
+ item("organizationWorkspaces", (color) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_icons13.SiconWorkspace, { className: "w-5 h-5", color }))
10610
10215
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
10611
10216
  SettingsNavItem,
10612
10217
  {
@@ -11578,7 +11183,6 @@ function SettingsContentInner({
11578
11183
  const [creatingWorkspace, setCreatingWorkspace] = (0, import_react17.useState)(false);
11579
11184
  const [updatingWorkspace, setUpdatingWorkspace] = (0, import_react17.useState)(false);
11580
11185
  const [teamInviteOpen, setTeamInviteOpen] = (0, import_react17.useState)(false);
11581
- const [organizationInviteOpen, setOrganizationInviteOpen] = (0, import_react17.useState)(false);
11582
11186
  const [tokenShowRevoked, setTokenShowRevoked] = (0, import_react17.useState)(false);
11583
11187
  const [tokenCreateOpen, setTokenCreateOpen] = (0, import_react17.useState)(false);
11584
11188
  const [billingShowPlans, setBillingShowPlans] = (0, import_react17.useState)(false);
@@ -12085,31 +11689,6 @@ function SettingsContentInner({
12085
11689
  )
12086
11690
  }
12087
11691
  ),
12088
- activeTab === "organization" && hasOrganization && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12089
- SettingsTooltip,
12090
- {
12091
- content: "Invite someone into your organization",
12092
- placement: "bottom",
12093
- inline: true,
12094
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12095
- import_ui13.ScButton,
12096
- {
12097
- text: "Invite",
12098
- variant: "secondary",
12099
- size: "md",
12100
- styleVariant: "icon-left",
12101
- icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12102
- import_icons13.SiconUserAdd,
12103
- {
12104
- className: "w-5 h-5",
12105
- color: "var(--alias-text---icons-inverse)"
12106
- }
12107
- ),
12108
- onClick: () => setOrganizationInviteOpen(true)
12109
- }
12110
- )
12111
- }
12112
- ),
12113
11692
  activeTab === "apikey" && isWorkspaceAdmin && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: "var(--spacing-md)" }, children: [
12114
11693
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
12115
11694
  SettingsTooltip,
@@ -12268,9 +11847,7 @@ function SettingsContentInner({
12268
11847
  OrganizationContent,
12269
11848
  {
12270
11849
  embedded: true,
12271
- view: activeTab === "organizationWorkspaces" ? "workspaces" : activeTab === "organizationDomains" ? "domains" : "people",
12272
- onInviteOpen: organizationInviteOpen,
12273
- onInviteOpenChange: setOrganizationInviteOpen
11850
+ view: activeTab === "organizationWorkspaces" || activeTab === "organizationDomains" ? "workspaces" : "people"
12274
11851
  }
12275
11852
  ) }),
12276
11853
  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)(
@@ -12408,3 +11985,246 @@ function SettingsContentInner({
12408
11985
  }
12409
11986
  );
12410
11987
  }
11988
+
11989
+ // src/workspace-picker.tsx
11990
+ var import_react18 = require("react");
11991
+ var import_ui15 = require("@streamoid/ui");
11992
+ var import_jsx_runtime23 = require("react/jsx-runtime");
11993
+ var T13 = {
11994
+ primary: "var(--alias-text---icons-primary)",
11995
+ muted: "var(--alias-text---icons-muted)",
11996
+ tertiary: "var(--alias-text---icons-tertiary)",
11997
+ surface: "var(--alias-surface-base)",
11998
+ fillPlus: "var(--alias-fill-neutral-neutralplus)",
11999
+ borderSubtle: "var(--alias-border-subtle)",
12000
+ font: "var(--font-family-font-family-body)",
12001
+ fsXs: "var(--font-size-font-size-xs)",
12002
+ fsSm: "var(--font-size-font-size-sm)",
12003
+ fsXl: "var(--font-size-font-size-xl)",
12004
+ lhXs: "var(--line-height-line-height-xs)",
12005
+ lhSm: "var(--line-height-line-height-sm)",
12006
+ lhXl: "var(--line-height-line-height-xl)",
12007
+ spXs: "var(--spacing-xs)",
12008
+ spMd: "var(--spacing-md)",
12009
+ spXl: "var(--spacing-xl)",
12010
+ sp3xl: "var(--spacing-3xl)",
12011
+ sp5xl: "var(--spacing-5xl)",
12012
+ sp6xl: "var(--spacing-6xl)",
12013
+ rLg: "var(--radius-lg)",
12014
+ r3xl: "var(--radius-3xl)"
12015
+ };
12016
+ function WorkspacePicker({
12017
+ organizationName,
12018
+ workspaces,
12019
+ loading = false,
12020
+ onJoin,
12021
+ onCreate
12022
+ }) {
12023
+ const [selected, setSelected] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
12024
+ const [creating, setCreating] = (0, import_react18.useState)(false);
12025
+ const [name, setName] = (0, import_react18.useState)("");
12026
+ const [busy, setBusy] = (0, import_react18.useState)(false);
12027
+ const hasWorkspaces = workspaces.length > 0;
12028
+ const showCreate = creating || !loading && !hasWorkspaces;
12029
+ const orgLabel = organizationName || "your organization";
12030
+ const toggle = (id) => setSelected((prev) => {
12031
+ const next = new Set(prev);
12032
+ if (next.has(id)) next.delete(id);
12033
+ else next.add(id);
12034
+ return next;
12035
+ });
12036
+ const run = async (action) => {
12037
+ setBusy(true);
12038
+ try {
12039
+ await action();
12040
+ } catch {
12041
+ } finally {
12042
+ setBusy(false);
12043
+ }
12044
+ };
12045
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12046
+ "div",
12047
+ {
12048
+ className: "flex flex-col w-full",
12049
+ style: { maxWidth: 520, gap: T13.sp5xl, fontFamily: T13.font },
12050
+ children: [
12051
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col", style: { gap: T13.spMd }, children: [
12052
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12053
+ "p",
12054
+ {
12055
+ style: {
12056
+ color: T13.primary,
12057
+ fontSize: T13.fsXl,
12058
+ fontWeight: 600,
12059
+ lineHeight: T13.lhXl,
12060
+ margin: 0
12061
+ },
12062
+ children: "Choose your workspaces"
12063
+ }
12064
+ ),
12065
+ /* @__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.` })
12066
+ ] }),
12067
+ (loading || hasWorkspaces) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12068
+ "div",
12069
+ {
12070
+ className: "flex flex-col w-full overflow-y-auto",
12071
+ style: {
12072
+ maxHeight: 360,
12073
+ background: T13.surface,
12074
+ border: `1px solid ${T13.borderSubtle}`,
12075
+ borderRadius: T13.r3xl
12076
+ },
12077
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12078
+ "div",
12079
+ {
12080
+ className: "flex items-center justify-center",
12081
+ style: { padding: T13.sp6xl, color: T13.muted, fontSize: T13.fsSm },
12082
+ children: "Loading workspaces\u2026"
12083
+ }
12084
+ ) : workspaces.map((workspace, index) => {
12085
+ const checked = selected.has(workspace._id);
12086
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12087
+ "div",
12088
+ {
12089
+ role: "checkbox",
12090
+ "aria-checked": checked,
12091
+ tabIndex: 0,
12092
+ className: "flex items-center cursor-pointer",
12093
+ style: {
12094
+ gap: T13.spXl,
12095
+ padding: `${T13.spXl} ${T13.sp3xl}`,
12096
+ borderTop: index ? `1px solid ${T13.borderSubtle}` : "none"
12097
+ },
12098
+ onClick: () => toggle(workspace._id),
12099
+ onKeyDown: (e) => {
12100
+ if (e.key === " " || e.key === "Enter") {
12101
+ e.preventDefault();
12102
+ toggle(workspace._id);
12103
+ }
12104
+ },
12105
+ children: [
12106
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui15.ScCheckbox, { checked }),
12107
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12108
+ ProfileAvatar,
12109
+ {
12110
+ name: workspace.name,
12111
+ profileImage: workspace.imageUrl,
12112
+ size: 32,
12113
+ borderRadius: T13.rLg
12114
+ }
12115
+ ),
12116
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col min-w-0", children: [
12117
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12118
+ "span",
12119
+ {
12120
+ className: "truncate",
12121
+ style: {
12122
+ color: T13.primary,
12123
+ fontSize: T13.fsSm,
12124
+ fontWeight: 500,
12125
+ lineHeight: T13.lhSm
12126
+ },
12127
+ children: workspace.name
12128
+ }
12129
+ ),
12130
+ workspace.ownerName && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12131
+ "span",
12132
+ {
12133
+ className: "truncate",
12134
+ style: { color: T13.muted, fontSize: T13.fsXs, lineHeight: T13.lhXs },
12135
+ children: [
12136
+ "Created by ",
12137
+ workspace.ownerName
12138
+ ]
12139
+ }
12140
+ )
12141
+ ] })
12142
+ ]
12143
+ },
12144
+ workspace._id
12145
+ );
12146
+ })
12147
+ }
12148
+ ),
12149
+ showCreate ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col w-full", style: { gap: T13.spMd }, children: [
12150
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12151
+ "label",
12152
+ {
12153
+ htmlFor: "workspace-picker-name",
12154
+ style: { color: T13.tertiary, fontSize: T13.fsSm, lineHeight: T13.lhSm },
12155
+ children: "New workspace name"
12156
+ }
12157
+ ),
12158
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12159
+ "input",
12160
+ {
12161
+ id: "workspace-picker-name",
12162
+ autoFocus: true,
12163
+ value: name,
12164
+ onChange: (e) => setName(e.target.value),
12165
+ onKeyDown: (e) => {
12166
+ if (e.key === "Enter" && name.trim() && !busy) {
12167
+ void run(() => onCreate(name.trim()));
12168
+ }
12169
+ },
12170
+ placeholder: "e.g. Marketing",
12171
+ style: {
12172
+ height: 48,
12173
+ padding: `0 ${T13.sp3xl}`,
12174
+ background: T13.fillPlus,
12175
+ border: "none",
12176
+ outline: "none",
12177
+ borderRadius: T13.r3xl,
12178
+ color: T13.primary,
12179
+ fontFamily: T13.font,
12180
+ fontSize: T13.fsSm
12181
+ }
12182
+ }
12183
+ ),
12184
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-end w-full", style: { gap: T13.spMd }, children: [
12185
+ hasWorkspaces && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12186
+ import_ui15.ScButton,
12187
+ {
12188
+ text: "Back",
12189
+ variant: "tertiary",
12190
+ size: "md",
12191
+ onClick: () => setCreating(false)
12192
+ }
12193
+ ),
12194
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12195
+ import_ui15.ScButton,
12196
+ {
12197
+ text: busy ? "Creating\u2026" : "Create workspace",
12198
+ variant: "primary",
12199
+ size: "md",
12200
+ state: !name.trim() || busy ? "disabled" : "default",
12201
+ onClick: name.trim() && !busy ? () => void run(() => onCreate(name.trim())) : void 0
12202
+ }
12203
+ )
12204
+ ] })
12205
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap items-center justify-between w-full", style: { gap: T13.spMd }, children: [
12206
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12207
+ import_ui15.ScButton,
12208
+ {
12209
+ text: "Create a workspace",
12210
+ variant: "tertiary",
12211
+ size: "md",
12212
+ state: loading || busy ? "disabled" : "default",
12213
+ onClick: loading || busy ? void 0 : () => setCreating(true)
12214
+ }
12215
+ ),
12216
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12217
+ import_ui15.ScButton,
12218
+ {
12219
+ text: busy ? "Joining\u2026" : selected.size > 1 ? `Join ${selected.size} workspaces` : "Join workspace",
12220
+ variant: "primary",
12221
+ size: "md",
12222
+ state: !selected.size || busy ? "disabled" : "default",
12223
+ onClick: selected.size && !busy ? () => void run(() => onJoin([...selected])) : void 0
12224
+ }
12225
+ )
12226
+ ] })
12227
+ ]
12228
+ }
12229
+ );
12230
+ }