@burdenoff/microfe-organization 2026.601.1 → 2026.601.2

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.
@@ -45,7 +45,7 @@ function w() {
45
45
  }),
46
46
  T && /* @__PURE__ */ a(c, {
47
47
  path: "workspaces",
48
- element: /* @__PURE__ */ a(m, {})
48
+ element: /* @__PURE__ */ a(m, { mode: "admin" })
49
49
  }),
50
50
  T && /* @__PURE__ */ a(c, {
51
51
  path: "workspaces/new",
@@ -1 +1 @@
1
- {"version":3,"file":"AdminOrganizationRoutes.js","names":[],"sources":["../../src/organization-management/AdminOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Outlet, Route, Routes, useParams } from 'react-router-dom';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Eager load - main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\n\n// Lazy load - secondary pages\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\n\n// Workspaces - lazy load all\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\n\n// Members - lazy load all\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id. This layout owns the `:id` or `:organizationId` URL\n * segment directly (see route declarations below) so\n * `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. Mirrors\n * the helper in UserOrganizationRoutes — keep them aligned.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * AdminOrganizationRoutes\n *\n * Full admin routing surface for the organization management MFE,\n * mounted by the admin application. Exposes all pages:\n * organizations CRUD, workspaces CRUD, members management, and\n * user-level invites.\n *\n * Routes start from root (/) since parent route is /organizations/*\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function AdminOrganizationRoutes() {\n // Keep aligned with the platform feature-flag default-allow policy: an\n // unseeded flag must not disable shipped CRUDL. Explicit false still gates a\n // surface off when admins configure the flag.\n const createDetails = useFeatureFlagDetails('organization.create');\n const workspacesDetails = useFeatureFlagDetails('organization.workspaces');\n const membersDetails = useFeatureFlagDetails('organization.members');\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const workspacesEnabled = workspacesDetails ? Boolean(workspacesDetails.value) : true;\n const membersEnabled = membersDetails ? Boolean(membersDetails.value) : true;\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations - index is the list (eager loaded). Admin app\n renders the platform-wide list (organizations query); product-app\n shells mounting this page should leave the default `mode=\"my\"`. */}\n <Route index element={<OrganizationsListPage mode=\"admin\" />} />\n {createEnabled && <Route path=\"new\" element={<OrganizationCreatePage />} />}\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n useParams().id is guaranteed to be set inside\n OrgScopedPermissions. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* Workspaces — literal \"workspaces\" prefix ranks above the bare\n dynamic `:id` / `:organizationId` segments. */}\n {workspacesEnabled && <Route path=\"workspaces\" element={<WorkspacesListPage />} />}\n {workspacesEnabled && <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />}\n {workspacesEnabled && <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />}\n {workspacesEnabled && <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />}\n\n {/* My Invites - user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match. */}\n <Route path=\"invites\" element={<MyInvitesPage activeTab=\"workspace\" />} />\n <Route path=\"invites/organizations\" element={<MyInvitesPage activeTab=\"organization\" />} />\n\n {/* Member routes — layout OWNS the `:organizationId` segment for\n the same reason as the detail block above. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n {inviteEnabled && <Route path=\"members/invite\" element={<InviteMemberPage />} />}\n {membersEnabled && <Route path=\"members\" element={<MembersListPage />} />}\n {membersEnabled && <Route path=\"members/:userId\" element={<MemberDetailPage />} />}\n </Route>\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminOrganizationRoutes.js","names":[],"sources":["../../src/organization-management/AdminOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Outlet, Route, Routes, useParams } from 'react-router-dom';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Eager load - main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\n\n// Lazy load - secondary pages\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\n\n// Workspaces - lazy load all\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\n\n// Members - lazy load all\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id. This layout owns the `:id` or `:organizationId` URL\n * segment directly (see route declarations below) so\n * `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. Mirrors\n * the helper in UserOrganizationRoutes — keep them aligned.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * AdminOrganizationRoutes\n *\n * Full admin routing surface for the organization management MFE,\n * mounted by the admin application. Exposes all pages:\n * organizations CRUD, workspaces CRUD, members management, and\n * user-level invites.\n *\n * Routes start from root (/) since parent route is /organizations/*\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function AdminOrganizationRoutes() {\n // Keep aligned with the platform feature-flag default-allow policy: an\n // unseeded flag must not disable shipped CRUDL. Explicit false still gates a\n // surface off when admins configure the flag.\n const createDetails = useFeatureFlagDetails('organization.create');\n const workspacesDetails = useFeatureFlagDetails('organization.workspaces');\n const membersDetails = useFeatureFlagDetails('organization.members');\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const workspacesEnabled = workspacesDetails ? Boolean(workspacesDetails.value) : true;\n const membersEnabled = membersDetails ? Boolean(membersDetails.value) : true;\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations - index is the list (eager loaded). Admin app\n renders the platform-wide list (organizations query); product-app\n shells mounting this page should leave the default `mode=\"my\"`. */}\n <Route index element={<OrganizationsListPage mode=\"admin\" />} />\n {createEnabled && <Route path=\"new\" element={<OrganizationCreatePage />} />}\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n useParams().id is guaranteed to be set inside\n OrgScopedPermissions. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* Workspaces — literal \"workspaces\" prefix ranks above the bare\n dynamic `:id` / `:organizationId` segments. */}\n {workspacesEnabled && <Route path=\"workspaces\" element={<WorkspacesListPage mode=\"admin\" />} />}\n {workspacesEnabled && <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />}\n {workspacesEnabled && <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />}\n {workspacesEnabled && <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />}\n\n {/* My Invites - user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match. */}\n <Route path=\"invites\" element={<MyInvitesPage activeTab=\"workspace\" />} />\n <Route path=\"invites/organizations\" element={<MyInvitesPage activeTab=\"organization\" />} />\n\n {/* Member routes — layout OWNS the `:organizationId` segment for\n the same reason as the detail block above. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n {inviteEnabled && <Route path=\"members/invite\" element={<InviteMemberPage />} />}\n {membersEnabled && <Route path=\"members\" element={<MembersListPage />} />}\n {membersEnabled && <Route path=\"members/:userId\" element={<MemberDetailPage />} />}\n </Route>\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -55,7 +55,7 @@ function w() {
55
55
  }),
56
56
  /* @__PURE__ */ i(s, {
57
57
  path: "workspaces",
58
- element: /* @__PURE__ */ i(p, {})
58
+ element: /* @__PURE__ */ i(p, { mode: "my" })
59
59
  }),
60
60
  /* @__PURE__ */ i(s, {
61
61
  path: "workspaces/new",
@@ -1 +1 @@
1
- {"version":3,"file":"UserOrganizationRoutes.js","names":[],"sources":["../../src/organization-management/UserOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Outlet, Route, Routes, useParams } from 'react-router-dom';\n\n// Eager load — main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Lazy load — secondary pages. Same set as the admin surface but the list\n// page below renders in `mode=\"my\"` (membership-scoped), and per-action\n// permission gates inside each page (hasPermission(PERMISSIONS.ORG_*))\n// already hide buttons the user can't use.\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\nconst InvitePeoplePage = lazy(() =>\n import('./modules/members/pages/InvitePeoplePage').then((m) => ({\n default: m.InvitePeoplePage,\n }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id (read from the URL). This layout owns the `:id` or\n * `:organizationId` URL segment directly (see route declarations below),\n * so `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. The\n * previous shape mounted this on a pathless parent and relied on child\n * routes to declare the param, which could silently fall through to\n * tenant-scoped permissions if a future refactor moved the layout above\n * a tree that doesn't own the segment.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * UserOrganizationRoutes\n *\n * User-facing routing surface for the organization MFE, mounted by product\n * application shells (vibecontrols, etc.). Exposes the same shape as the\n * admin routing surface but the list page renders membership-scoped via\n * `mode=\"my\"` (calls `myOrganizations` with `@rbac(scopeType: \"self\")`).\n *\n * Per-action authorisation lives inside each page (`hasPermission` checks\n * for create / update / delete), so users with elevated permissions on an\n * org still see those controls; users without them only see view actions.\n *\n * All routes are unconditionally mounted. Feature flags\n * (`organization.create`, `organization.workspaces`, `organization.members`,\n * `organization.invite`) are applied at the call site — buttons, nav\n * items, section headers inside the pages — not on the route graph. This\n * avoids a class of route/nav mismatches where a page unconditionally\n * navigates to a URL that has been gated off (the previous flag-gated\n * routes produced dead URLs from `OrganizationDetailPage`'s workspace\n * cards, \"Create Workspace\" CTA, \"Manage Members\" button, etc.).\n *\n * Access control:\n * - Per-action `hasPermission` checks inside each page hide controls the\n * user can't use.\n * - Backend resolvers (`organization`, `organizationMembers`, etc.)\n * enforce membership server-side via `userHasAccessToOrganization`, so\n * deep-linking to a URL the user has no access to 403s at the resolver.\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function UserOrganizationRoutes() {\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations — index is the membership-scoped list (eager loaded) */}\n <Route index element={<OrganizationsListPage mode=\"my\" />} />\n <Route path=\"new\" element={<OrganizationCreatePage />} />\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n `useParams().id` is guaranteed to be set inside\n OrgScopedPermissions and per-page hasPermission() resolves at\n the org's scope, not the outer tenant scope. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* My Invites — user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match against the dynamic segment. */}\n <Route path=\"invites\" element={<MyInvitesPage activeTab=\"workspace\" />} />\n <Route path=\"invites/organizations\" element={<MyInvitesPage activeTab=\"organization\" />} />\n\n {/* Invite People — globally discoverable chooser. Lists orgs the\n user can invite into, then deep-links to per-org invite page. */}\n <Route path=\"invite\" element={<InvitePeoplePage />} />\n\n {/* Workspaces — `OrganizationDetailPage` navigates here on workspace\n card click and the \"Create Workspace\" CTA. Declared with literal\n \"workspaces\" prefix so it ranks above the bare `:organizationId`\n dynamic-segment routes below. */}\n <Route path=\"workspaces\" element={<WorkspacesListPage />} />\n <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />\n <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />\n <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />\n\n {/* Members — layout OWNS the `:organizationId` segment for the\n same reason as the detail block above. The inner pages already\n read `useParams().organizationId`, so we keep the segment name\n distinct from `:id` even though both resolve to an org id. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n <Route path=\"members/invite\" element={<InviteMemberPage />} />\n <Route path=\"members\" element={<MembersListPage />} />\n <Route path=\"members/:userId\" element={<MemberDetailPage />} />\n </Route>\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"UserOrganizationRoutes.js","names":[],"sources":["../../src/organization-management/UserOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Outlet, Route, Routes, useParams } from 'react-router-dom';\n\n// Eager load — main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Lazy load — secondary pages. Same set as the admin surface but the list\n// page below renders in `mode=\"my\"` (membership-scoped), and per-action\n// permission gates inside each page (hasPermission(PERMISSIONS.ORG_*))\n// already hide buttons the user can't use.\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\nconst InvitePeoplePage = lazy(() =>\n import('./modules/members/pages/InvitePeoplePage').then((m) => ({\n default: m.InvitePeoplePage,\n }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id (read from the URL). This layout owns the `:id` or\n * `:organizationId` URL segment directly (see route declarations below),\n * so `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. The\n * previous shape mounted this on a pathless parent and relied on child\n * routes to declare the param, which could silently fall through to\n * tenant-scoped permissions if a future refactor moved the layout above\n * a tree that doesn't own the segment.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * UserOrganizationRoutes\n *\n * User-facing routing surface for the organization MFE, mounted by product\n * application shells (vibecontrols, etc.). Exposes the same shape as the\n * admin routing surface but the list page renders membership-scoped via\n * `mode=\"my\"` (calls `myOrganizations` with `@rbac(scopeType: \"self\")`).\n *\n * Per-action authorisation lives inside each page (`hasPermission` checks\n * for create / update / delete), so users with elevated permissions on an\n * org still see those controls; users without them only see view actions.\n *\n * All routes are unconditionally mounted. Feature flags\n * (`organization.create`, `organization.workspaces`, `organization.members`,\n * `organization.invite`) are applied at the call site — buttons, nav\n * items, section headers inside the pages — not on the route graph. This\n * avoids a class of route/nav mismatches where a page unconditionally\n * navigates to a URL that has been gated off (the previous flag-gated\n * routes produced dead URLs from `OrganizationDetailPage`'s workspace\n * cards, \"Create Workspace\" CTA, \"Manage Members\" button, etc.).\n *\n * Access control:\n * - Per-action `hasPermission` checks inside each page hide controls the\n * user can't use.\n * - Backend resolvers (`organization`, `organizationMembers`, etc.)\n * enforce membership server-side via `userHasAccessToOrganization`, so\n * deep-linking to a URL the user has no access to 403s at the resolver.\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function UserOrganizationRoutes() {\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations — index is the membership-scoped list (eager loaded) */}\n <Route index element={<OrganizationsListPage mode=\"my\" />} />\n <Route path=\"new\" element={<OrganizationCreatePage />} />\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n `useParams().id` is guaranteed to be set inside\n OrgScopedPermissions and per-page hasPermission() resolves at\n the org's scope, not the outer tenant scope. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* My Invites — user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match against the dynamic segment. */}\n <Route path=\"invites\" element={<MyInvitesPage activeTab=\"workspace\" />} />\n <Route path=\"invites/organizations\" element={<MyInvitesPage activeTab=\"organization\" />} />\n\n {/* Invite People — globally discoverable chooser. Lists orgs the\n user can invite into, then deep-links to per-org invite page. */}\n <Route path=\"invite\" element={<InvitePeoplePage />} />\n\n {/* Workspaces — `OrganizationDetailPage` navigates here on workspace\n card click and the \"Create Workspace\" CTA. Declared with literal\n \"workspaces\" prefix so it ranks above the bare `:organizationId`\n dynamic-segment routes below. */}\n <Route path=\"workspaces\" element={<WorkspacesListPage mode=\"my\" />} />\n <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />\n <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />\n <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />\n\n {/* Members — layout OWNS the `:organizationId` segment for the\n same reason as the detail block above. The inner pages already\n read `useParams().organizationId`, so we keep the segment name\n distinct from `:id` even though both resolve to an org id. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n <Route path=\"members/invite\" element={<InviteMemberPage />} />\n <Route path=\"members\" element={<MembersListPage />} />\n <Route path=\"members/:userId\" element={<MemberDetailPage />} />\n </Route>\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -216,7 +216,7 @@ function D({ mode: D = "my" } = {}) {
216
216
  " ",
217
217
  O("common.selected", "selected")
218
218
  ]
219
- }), j(d.ORG_DELETE) && /* @__PURE__ */ T("button", {
219
+ }), D === "admin" && j(d.ORG_DELETE) && /* @__PURE__ */ T("button", {
220
220
  type: "button",
221
221
  onClick: ve,
222
222
  disabled: q.length === 0 || K,
@@ -335,7 +335,7 @@ function D({ mode: D = "my" } = {}) {
335
335
  "aria-hidden": "true"
336
336
  }), O("common.edit", "Edit")]
337
337
  }),
338
- j(d.ORG_DELETE) && /* @__PURE__ */ T("button", {
338
+ D === "admin" && j(d.ORG_DELETE) && /* @__PURE__ */ T("button", {
339
339
  type: "button",
340
340
  onClick: () => Z(e.id, e.name),
341
341
  disabled: K,
@@ -464,7 +464,7 @@ function D({ mode: D = "my" } = {}) {
464
464
  "aria-hidden": "true"
465
465
  })
466
466
  }),
467
- j(d.ORG_DELETE) && /* @__PURE__ */ w("button", {
467
+ D === "admin" && j(d.ORG_DELETE) && /* @__PURE__ */ w("button", {
468
468
  type: "button",
469
469
  onClick: () => Z(e.id, e.name),
470
470
  disabled: K,
@@ -1 +1 @@
1
- {"version":3,"file":"OrganizationsListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building2, Eye, Mail, Plus, RefreshCw, SquarePen, Trash2 } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n useDeleteOrganization,\n useMyOrganizations,\n useOrganizations,\n} from '../hooks/useOrganizations';\nimport { useMyInvites } from '../../members/hooks/useInvites';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { OrganizationStatusBadge } from '../components/OrganizationStatusBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../../../utils/nativeBridge';\n\nfunction formatDate(date: string) {\n return new Date(date).toLocaleDateString();\n}\n\n/**\n * `mode` controls which scope of organizations the page renders:\n *\n * - `'my'` (default) calls `myOrganizations` (gateway @rbac(scopeType:\"self\")).\n * Use this in product-app shells (e.g. vibecontrols) where the user should\n * see only orgs they are a member of. Membership IS the access list, so no\n * client-side permission gate is needed.\n *\n * - `'admin'` calls `allOrganizations` (gateway @rbac(scopeType: \"global\")).\n * Restricted to actors with global `organization:read` (super admins /\n * service / custom global-admin roles). Use this in the admin app where\n * operators need the tenant-wide list.\n */\nexport interface OrganizationsListPageProps {\n mode?: 'my' | 'admin';\n}\n\nexport function OrganizationsListPage({ mode = 'my' }: OrganizationsListPageProps = {}) {\n useOrganizationPageView('organizations.list');\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped CRUDL, while an explicit false value still gates create.\n const createDetails = useFeatureFlagDetails('organization.create');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);\n const [selectedOrganizationIds, setSelectedOrganizationIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n // Show a count badge of pending invites alongside the Invitations link.\n // Skipped in 'admin' mode since admins land here via tenant-wide management\n // rather than personal invites.\n const { total: pendingInvitesCount } = useMyInvites(InviteStatus.Pending, 1, 0, mode === 'my');\n\n // Page size is large enough to cover realistic memberships / tenant-wide\n // org counts in one round-trip so the client-side search is exhaustive.\n // If a tenant ever exceeds this, wire in fetchMore/pagination instead of\n // silently truncating.\n const ORGANIZATIONS_PAGE_SIZE = 200;\n const myResult = useMyOrganizations(ORGANIZATIONS_PAGE_SIZE, 0, mode === 'my');\n const adminResult = useOrganizations(undefined, ORGANIZATIONS_PAGE_SIZE, 0, mode === 'admin');\n const {\n organizations: allOrganizations,\n loading,\n error,\n refetch,\n } = mode === 'admin' ? adminResult : myResult;\n\n const isSearchFiltered = debouncedSearchQuery.trim().length > 0;\n\n const organizations = useMemo(() => {\n const q = debouncedSearchQuery.trim().toLowerCase();\n if (!q) return allOrganizations;\n return allOrganizations.filter(\n (org) => org.name?.toLowerCase().includes(q) || org.slug?.toLowerCase().includes(q)\n );\n }, [allOrganizations, debouncedSearchQuery]);\n\n const { deleteOrganization, loading: deleteLoading } = useDeleteOrganization();\n\n useEffect(() => {\n setSelectedOrganizationIds((previousSelection) => {\n const visibleIds = new Set(organizations.map((organization) => organization.id));\n const nextSelection = new Set(\n [...previousSelection].filter((organizationId) => visibleIds.has(organizationId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [organizations]);\n\n const selectedOrganizations = useMemo(\n () => organizations.filter((organization) => selectedOrganizationIds.has(organization.id)),\n [organizations, selectedOrganizationIds]\n );\n\n const allVisibleSelected =\n organizations.length > 0 &&\n organizations.every((organization) => selectedOrganizationIds.has(organization.id));\n const partiallySelected =\n organizations.some((organization) => selectedOrganizationIds.has(organization.id)) &&\n !allVisibleSelected;\n\n const executeDelete = async (organizationIds: string[]) => {\n try {\n const results = await Promise.allSettled(\n organizationIds.map(async (organizationId) => {\n const result = await deleteOrganization(organizationId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to delete organization');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedOrganizationIds(new Set());\n await refetch();\n const successMsg =\n organizationIds.length === 1\n ? tr('organizations.delete.successSingle', 'Organization deleted successfully')\n : `${organizationIds.length} ${tr('organizations.delete.successMany', 'organizations deleted successfully')}`;\n toast.success(successMsg);\n void nativeNotify('success');\n if (isNative()) void nativeToast(successMsg);\n } catch (deleteError) {\n const errorMsg =\n deleteError instanceof Error\n ? deleteError.message\n : tr('organizations.delete.failed', 'Failed to delete organization');\n toast.error(errorMsg);\n void nativeNotify('error');\n if (isNative()) void nativeToast(errorMsg);\n }\n };\n\n const handleDelete = (organizationId: string, organizationName: string) => {\n nativeImpact('medium');\n const title = tr('organizations.delete.title', 'Delete organization');\n const description = `${tr('organizations.delete.confirmPrefix', 'Delete')} ${organizationName}? ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`;\n void nativeConfirm({\n title,\n message: description,\n okButtonTitle: tr('common.delete', 'Delete'),\n }).then((nativeResult) => {\n if (nativeResult === null) {\n openConfirmDialog({\n title,\n description,\n confirmText: tr('common.delete', 'Delete'),\n variant: 'danger',\n onConfirm: () => {\n void executeDelete([organizationId]);\n },\n });\n } else if (nativeResult) {\n void executeDelete([organizationId]);\n }\n });\n };\n\n const handleBulkDelete = () => {\n if (selectedOrganizations.length === 0) return;\n\n openConfirmDialog({\n title: tr('organizations.delete.bulkTitle', 'Delete selected organizations'),\n description: `${tr('organizations.delete.bulkPrefix', 'Delete')} ${selectedOrganizations.length} ${tr(\n selectedOrganizations.length === 1\n ? 'organizations.delete.bulkSuffixSingle'\n : 'organizations.delete.bulkSuffixMany',\n selectedOrganizations.length === 1 ? 'selected organization?' : 'selected organizations?'\n )} ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`,\n confirmText: `${tr('common.delete', 'Delete')} ${selectedOrganizations.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeDelete(selectedOrganizations.map((organization) => organization.id));\n },\n });\n };\n\n const toggleOrganizationSelection = (organizationId: string, checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(organizationId);\n } else {\n nextSelection.delete(organizationId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllOrganizations = (checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n organizations.forEach((organization) => {\n if (checked) {\n nextSelection.add(organization.id);\n } else {\n nextSelection.delete(organization.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n // No client-side list gate: this page calls `myOrganizations`\n // (@rbac(scopeType: \"self\")), so the data set IS the access list — if the\n // user is in zero orgs the page renders the empty state, not a deny. The\n // create/delete/edit buttons keep their own per-action `hasPermission`\n // checks below to hide actions the user can't perform on a given org.\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('organizations.list.loading', 'Loading organizations...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\" data-tour=\"organizations-page-header\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('organizations.list.title', 'Organizations')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.list.subtitle',\n 'Manage tenant organizations and the workspaces that belong to them.'\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n {mode === 'my' && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/invites`)}\n className=\"relative inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-[0.98]\"\n aria-label={tr('organizations.invitations.button', 'Invitations')}\n data-tour=\"organizations-invitations-button\"\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.invitations.button', 'Invitations')}\n </span>\n {pendingInvitesCount > 0 && (\n <span\n className=\"ml-1 inline-flex min-w-[1.25rem] items-center justify-center rounded-full bg-[var(--color-action-primary-bg)] px-1.5 py-0.5 text-xs font-semibold text-[var(--color-action-primary-text)]\"\n aria-label={`${pendingInvitesCount} pending`}\n >\n {pendingInvitesCount}\n </span>\n )}\n </button>\n )}\n\n {createEnabled && hasPermission(PERMISSIONS.ORG_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/new`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n data-tour=\"organizations-create-button\"\n >\n <Plus size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.create.button', 'Create Organization')}\n </span>\n </button>\n )}\n </div>\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div\n className=\"flex flex-col gap-3 sm:flex-row sm:items-center\"\n data-tour=\"organizations-search-input\"\n >\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('organizations.list.searchPlaceholder', 'Search organizations...')}\n label={tr('organizations.list.searchLabel', 'Search organizations')}\n inputId=\"organizations-search\"\n className=\"flex-1\"\n />\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('organizations.list.refresh', 'Refresh organizations')}\n title={tr('organizations.list.refresh', 'Refresh organizations')}\n data-tour=\"organizations-refresh-button\"\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {organizations.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedOrganizationIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedOrganizationIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedOrganizationIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedOrganizations.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.deleteSelected', 'Delete Selected')}</span>\n {selectedOrganizations.length > 0 ? (\n <span>({selectedOrganizations.length})</span>\n ) : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {organizations.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n {isSearchFiltered && allOrganizations.length > 0 ? (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.noMatchesTitle', 'No matching organizations')}\n description={tr(\n 'organizations.list.noMatchesDescription',\n 'No organizations match your search. Try a different name or slug, or clear the search.'\n )}\n action={{\n label: tr('common.clearSearch', 'Clear search'),\n onClick: () => setSearchQuery(''),\n }}\n />\n ) : (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.emptyTitle', 'No organizations found')}\n description={tr(\n 'organizations.list.emptyDescription',\n 'Create your first organization to start managing workspaces, members, and invites.'\n )}\n action={\n createEnabled && hasPermission(PERMISSIONS.ORG_CREATE)\n ? {\n label: tr('organizations.create.button', 'Create Organization'),\n onClick: () => navigate(`${basePath}/new`),\n }\n : undefined\n }\n />\n )}\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {organizations.map((organization) => (\n <article\n key={organization.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) => toggleOrganizationSelection(organization.id, checked)}\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-12 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-base font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {organization.name}\n </h2>\n <OrganizationStatusBadge status={organization.status} />\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n <dl className=\"mt-3 grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.workspaces', 'Workspaces')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {formatDate(organization.createdAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <SquarePen size={16} aria-hidden=\"true\" />\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('common.delete', 'Delete')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div\n className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\"\n data-tour=\"organizations-table\"\n >\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.organization', 'Organization')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.workspaces', 'Workspaces')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {organizations.map((organization) => (\n <tr\n key={organization.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) =>\n toggleOrganizationSelection(organization.id, checked)\n }\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n <div className=\"min-w-0\">\n <p className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.name}\n </p>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <OrganizationStatusBadge status={organization.status} />\n </td>\n <td className=\"px-6 py-4 align-top text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(organization.createdAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div\n className=\"flex items-center justify-end gap-2\"\n {...(organization === organizations[0]\n ? { 'data-tour': 'organizations-table-actions' }\n : {})}\n >\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}`)}\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.view', 'View')} ${organization.name}`}\n aria-label={`${tr('common.view', 'View')} ${organization.name}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.edit', 'Edit')} ${organization.name}`}\n aria-label={`${tr('common.edit', 'Edit')} ${organization.name}`}\n >\n <SquarePen size={18} aria-hidden=\"true\" />\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n title={`${tr('common.delete', 'Delete')} ${organization.name}`}\n aria-label={`${tr('common.delete', 'Delete')} ${organization.name}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"OrganizationsListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building2, Eye, Mail, Plus, RefreshCw, SquarePen, Trash2 } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n useDeleteOrganization,\n useMyOrganizations,\n useOrganizations,\n} from '../hooks/useOrganizations';\nimport { useMyInvites } from '../../members/hooks/useInvites';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { OrganizationStatusBadge } from '../components/OrganizationStatusBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../../../utils/nativeBridge';\n\nfunction formatDate(date: string) {\n return new Date(date).toLocaleDateString();\n}\n\n/**\n * `mode` controls which scope of organizations the page renders:\n *\n * - `'my'` (default) calls `myOrganizations` (gateway @rbac(scopeType:\"self\")).\n * Use this in product-app shells (e.g. vibecontrols) where the user should\n * see only orgs they are a member of. Membership IS the access list, so no\n * client-side permission gate is needed.\n *\n * - `'admin'` calls `allOrganizations` (gateway @rbac(scopeType: \"global\")).\n * Restricted to actors with global `organization:read` (super admins /\n * service / custom global-admin roles). Use this in the admin app where\n * operators need the tenant-wide list.\n */\nexport interface OrganizationsListPageProps {\n mode?: 'my' | 'admin';\n}\n\nexport function OrganizationsListPage({ mode = 'my' }: OrganizationsListPageProps = {}) {\n useOrganizationPageView('organizations.list');\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped CRUDL, while an explicit false value still gates create.\n const createDetails = useFeatureFlagDetails('organization.create');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);\n const [selectedOrganizationIds, setSelectedOrganizationIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n // Show a count badge of pending invites alongside the Invitations link.\n // Skipped in 'admin' mode since admins land here via tenant-wide management\n // rather than personal invites.\n const { total: pendingInvitesCount } = useMyInvites(InviteStatus.Pending, 1, 0, mode === 'my');\n\n // Page size is large enough to cover realistic memberships / tenant-wide\n // org counts in one round-trip so the client-side search is exhaustive.\n // If a tenant ever exceeds this, wire in fetchMore/pagination instead of\n // silently truncating.\n const ORGANIZATIONS_PAGE_SIZE = 200;\n const myResult = useMyOrganizations(ORGANIZATIONS_PAGE_SIZE, 0, mode === 'my');\n const adminResult = useOrganizations(undefined, ORGANIZATIONS_PAGE_SIZE, 0, mode === 'admin');\n const {\n organizations: allOrganizations,\n loading,\n error,\n refetch,\n } = mode === 'admin' ? adminResult : myResult;\n\n const isSearchFiltered = debouncedSearchQuery.trim().length > 0;\n\n const organizations = useMemo(() => {\n const q = debouncedSearchQuery.trim().toLowerCase();\n if (!q) return allOrganizations;\n return allOrganizations.filter(\n (org) => org.name?.toLowerCase().includes(q) || org.slug?.toLowerCase().includes(q)\n );\n }, [allOrganizations, debouncedSearchQuery]);\n\n const { deleteOrganization, loading: deleteLoading } = useDeleteOrganization();\n\n useEffect(() => {\n setSelectedOrganizationIds((previousSelection) => {\n const visibleIds = new Set(organizations.map((organization) => organization.id));\n const nextSelection = new Set(\n [...previousSelection].filter((organizationId) => visibleIds.has(organizationId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [organizations]);\n\n const selectedOrganizations = useMemo(\n () => organizations.filter((organization) => selectedOrganizationIds.has(organization.id)),\n [organizations, selectedOrganizationIds]\n );\n\n const allVisibleSelected =\n organizations.length > 0 &&\n organizations.every((organization) => selectedOrganizationIds.has(organization.id));\n const partiallySelected =\n organizations.some((organization) => selectedOrganizationIds.has(organization.id)) &&\n !allVisibleSelected;\n\n const executeDelete = async (organizationIds: string[]) => {\n try {\n const results = await Promise.allSettled(\n organizationIds.map(async (organizationId) => {\n const result = await deleteOrganization(organizationId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to delete organization');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedOrganizationIds(new Set());\n await refetch();\n const successMsg =\n organizationIds.length === 1\n ? tr('organizations.delete.successSingle', 'Organization deleted successfully')\n : `${organizationIds.length} ${tr('organizations.delete.successMany', 'organizations deleted successfully')}`;\n toast.success(successMsg);\n void nativeNotify('success');\n if (isNative()) void nativeToast(successMsg);\n } catch (deleteError) {\n const errorMsg =\n deleteError instanceof Error\n ? deleteError.message\n : tr('organizations.delete.failed', 'Failed to delete organization');\n toast.error(errorMsg);\n void nativeNotify('error');\n if (isNative()) void nativeToast(errorMsg);\n }\n };\n\n const handleDelete = (organizationId: string, organizationName: string) => {\n nativeImpact('medium');\n const title = tr('organizations.delete.title', 'Delete organization');\n const description = `${tr('organizations.delete.confirmPrefix', 'Delete')} ${organizationName}? ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`;\n void nativeConfirm({\n title,\n message: description,\n okButtonTitle: tr('common.delete', 'Delete'),\n }).then((nativeResult) => {\n if (nativeResult === null) {\n openConfirmDialog({\n title,\n description,\n confirmText: tr('common.delete', 'Delete'),\n variant: 'danger',\n onConfirm: () => {\n void executeDelete([organizationId]);\n },\n });\n } else if (nativeResult) {\n void executeDelete([organizationId]);\n }\n });\n };\n\n const handleBulkDelete = () => {\n if (selectedOrganizations.length === 0) return;\n\n openConfirmDialog({\n title: tr('organizations.delete.bulkTitle', 'Delete selected organizations'),\n description: `${tr('organizations.delete.bulkPrefix', 'Delete')} ${selectedOrganizations.length} ${tr(\n selectedOrganizations.length === 1\n ? 'organizations.delete.bulkSuffixSingle'\n : 'organizations.delete.bulkSuffixMany',\n selectedOrganizations.length === 1 ? 'selected organization?' : 'selected organizations?'\n )} ${tr('common.cannotBeUndone', 'This action cannot be undone.')}`,\n confirmText: `${tr('common.delete', 'Delete')} ${selectedOrganizations.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeDelete(selectedOrganizations.map((organization) => organization.id));\n },\n });\n };\n\n const toggleOrganizationSelection = (organizationId: string, checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(organizationId);\n } else {\n nextSelection.delete(organizationId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllOrganizations = (checked: boolean) => {\n setSelectedOrganizationIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n organizations.forEach((organization) => {\n if (checked) {\n nextSelection.add(organization.id);\n } else {\n nextSelection.delete(organization.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n // No client-side list gate: this page calls `myOrganizations`\n // (@rbac(scopeType: \"self\")), so the data set IS the access list — if the\n // user is in zero orgs the page renders the empty state, not a deny. The\n // create/delete/edit buttons keep their own per-action `hasPermission`\n // checks below to hide actions the user can't perform on a given org.\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('organizations.list.loading', 'Loading organizations...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\" data-tour=\"organizations-page-header\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('organizations.list.title', 'Organizations')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.list.subtitle',\n 'Manage tenant organizations and the workspaces that belong to them.'\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n {mode === 'my' && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/invites`)}\n className=\"relative inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-[0.98]\"\n aria-label={tr('organizations.invitations.button', 'Invitations')}\n data-tour=\"organizations-invitations-button\"\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.invitations.button', 'Invitations')}\n </span>\n {pendingInvitesCount > 0 && (\n <span\n className=\"ml-1 inline-flex min-w-[1.25rem] items-center justify-center rounded-full bg-[var(--color-action-primary-bg)] px-1.5 py-0.5 text-xs font-semibold text-[var(--color-action-primary-text)]\"\n aria-label={`${pendingInvitesCount} pending`}\n >\n {pendingInvitesCount}\n </span>\n )}\n </button>\n )}\n\n {createEnabled && hasPermission(PERMISSIONS.ORG_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/new`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n data-tour=\"organizations-create-button\"\n >\n <Plus size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">\n {tr('organizations.create.button', 'Create Organization')}\n </span>\n </button>\n )}\n </div>\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div\n className=\"flex flex-col gap-3 sm:flex-row sm:items-center\"\n data-tour=\"organizations-search-input\"\n >\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('organizations.list.searchPlaceholder', 'Search organizations...')}\n label={tr('organizations.list.searchLabel', 'Search organizations')}\n inputId=\"organizations-search\"\n className=\"flex-1\"\n />\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('organizations.list.refresh', 'Refresh organizations')}\n title={tr('organizations.list.refresh', 'Refresh organizations')}\n data-tour=\"organizations-refresh-button\"\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {organizations.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedOrganizationIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedOrganizationIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedOrganizationIds.size} {tr('common.selected', 'selected')}\n </span>\n {mode === 'admin' && hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={selectedOrganizations.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.deleteSelected', 'Delete Selected')}</span>\n {selectedOrganizations.length > 0 ? (\n <span>({selectedOrganizations.length})</span>\n ) : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {organizations.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n {isSearchFiltered && allOrganizations.length > 0 ? (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.noMatchesTitle', 'No matching organizations')}\n description={tr(\n 'organizations.list.noMatchesDescription',\n 'No organizations match your search. Try a different name or slug, or clear the search.'\n )}\n action={{\n label: tr('common.clearSearch', 'Clear search'),\n onClick: () => setSearchQuery(''),\n }}\n />\n ) : (\n <EmptyState\n icon={<Building2 size={40} aria-hidden=\"true\" />}\n title={tr('organizations.list.emptyTitle', 'No organizations found')}\n description={tr(\n 'organizations.list.emptyDescription',\n 'Create your first organization to start managing workspaces, members, and invites.'\n )}\n action={\n createEnabled && hasPermission(PERMISSIONS.ORG_CREATE)\n ? {\n label: tr('organizations.create.button', 'Create Organization'),\n onClick: () => navigate(`${basePath}/new`),\n }\n : undefined\n }\n />\n )}\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {organizations.map((organization) => (\n <article\n key={organization.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) => toggleOrganizationSelection(organization.id, checked)}\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-12 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-base font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {organization.name}\n </h2>\n <OrganizationStatusBadge status={organization.status} />\n </div>\n <p className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n <dl className=\"mt-3 grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.workspaces', 'Workspaces')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"font-medium text-[var(--color-text-primary)]\">\n {formatDate(organization.createdAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <SquarePen size={16} aria-hidden=\"true\" />\n {tr('common.edit', 'Edit')}\n </button>\n )}\n {mode === 'admin' && hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('common.delete', 'Delete')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div\n className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\"\n data-tour=\"organizations-table\"\n >\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllOrganizations}\n ariaLabel={tr('organizations.list.selectAll', 'Select all organizations')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.organization', 'Organization')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('organizations.list.column.workspaces', 'Workspaces')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {organizations.map((organization) => (\n <tr\n key={organization.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedOrganizationIds.has(organization.id)}\n onChange={(checked) =>\n toggleOrganizationSelection(organization.id, checked)\n }\n ariaLabel={`${tr('organizations.list.selectOne', 'Select organization')} ${organization.name}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n {organization.logo ? (\n <img\n src={organization.logo}\n alt={`${organization.name} ${tr('common.logo', 'logo')}`}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 font-semibold text-[var(--color-action-primary-bg)]\">\n {organization.name[0]}\n </div>\n )}\n <div className=\"min-w-0\">\n <p className=\"font-medium text-[var(--color-text-primary)]\">\n {organization.name}\n </p>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-muted)]\">\n /{organization.slug}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <OrganizationStatusBadge status={organization.status} />\n </td>\n <td className=\"px-6 py-4 align-top text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(organization.createdAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div\n className=\"flex items-center justify-end gap-2\"\n {...(organization === organizations[0]\n ? { 'data-tour': 'organizations-table-actions' }\n : {})}\n >\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}`)}\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.view', 'View')} ${organization.name}`}\n aria-label={`${tr('common.view', 'View')} ${organization.name}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organization.id}/edit`)}\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n title={`${tr('common.edit', 'Edit')} ${organization.name}`}\n aria-label={`${tr('common.edit', 'Edit')} ${organization.name}`}\n >\n <SquarePen size={18} aria-hidden=\"true\" />\n </button>\n )}\n {mode === 'admin' && hasPermission(PERMISSIONS.ORG_DELETE) && (\n <button\n type=\"button\"\n onClick={() => handleDelete(organization.id, organization.name)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n title={`${tr('common.delete', 'Delete')} ${organization.name}`}\n aria-label={`${tr('common.delete', 'Delete')} ${organization.name}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,29 +1,38 @@
1
- import { GetWorkspacesDocument as e, useCreateWorkspaceMutation as t, useGetTenantsQuery as n, useGetWorkspaceQuery as r, useGetWorkspacesByOrganizationQuery as i, useGetWorkspacesQuery as a, useSuspendWorkspaceMutation as o, useUpdateWorkspaceMutation as s } from "../../../../generated/global-operations.js";
1
+ import { GetWorkspacesDocument as e, useCreateWorkspaceMutation as t, useGetMyWorkspacesQuery as n, useGetTenantsQuery as r, useGetWorkspaceQuery as i, useGetWorkspacesByOrganizationQuery as a, useGetWorkspacesQuery as o, useSuspendWorkspaceMutation as s, useUpdateWorkspaceMutation as c } from "../../../../generated/global-operations.js";
2
2
  //#region src/organization-management/modules/workspaces/hooks/useWorkspaces.ts
3
- function c(e, t = 20, n = 0) {
4
- let { data: r, loading: i, error: o, refetch: s, fetchMore: c } = a({
3
+ function l(e, t = 20, r = 0, i = "all") {
4
+ let a = o({
5
5
  variables: {
6
6
  filter: e,
7
7
  limit: t,
8
- offset: n
8
+ offset: r
9
9
  },
10
+ skip: i !== "all",
10
11
  fetchPolicy: "cache-first",
11
12
  nextFetchPolicy: "cache-first"
12
- });
13
+ }), s = n({
14
+ variables: {
15
+ limit: t,
16
+ offset: r
17
+ },
18
+ skip: i !== "my",
19
+ fetchPolicy: "cache-first",
20
+ nextFetchPolicy: "cache-first"
21
+ }), { loading: c, error: l, refetch: u, fetchMore: d } = i === "my" ? s : a, f = i === "my" ? s.data?.myWorkspaces : a.data?.allWorkspaces;
13
22
  return {
14
- workspaces: r?.allWorkspaces?.items || [],
15
- total: r?.allWorkspaces?.total || 0,
16
- limit: r?.allWorkspaces?.limit || t,
17
- offset: r?.allWorkspaces?.offset || n,
18
- hasMore: r?.allWorkspaces?.hasMore || !1,
19
- loading: i,
20
- error: o,
21
- refetch: s,
22
- fetchMore: c
23
+ workspaces: f?.items || [],
24
+ total: f?.total || 0,
25
+ limit: f?.limit || t,
26
+ offset: f?.offset || r,
27
+ hasMore: f?.hasMore || !1,
28
+ loading: c,
29
+ error: l,
30
+ refetch: u,
31
+ fetchMore: d
23
32
  };
24
33
  }
25
- function l(e) {
26
- let { data: t, loading: n, error: i, refetch: a } = r({
34
+ function u(e) {
35
+ let { data: t, loading: n, error: r, refetch: a } = i({
27
36
  variables: { id: e },
28
37
  skip: !e,
29
38
  fetchPolicy: "cache-and-network",
@@ -32,12 +41,12 @@ function l(e) {
32
41
  return {
33
42
  workspace: t?.workspace,
34
43
  loading: n,
35
- error: i,
44
+ error: r,
36
45
  refetch: a
37
46
  };
38
47
  }
39
- function u(e, t = 20, n = 0) {
40
- let { data: r, loading: a, error: o, refetch: s, fetchMore: c } = i({
48
+ function d(e, t = 20, n = 0) {
49
+ let { data: r, loading: i, error: o, refetch: s, fetchMore: c } = a({
41
50
  variables: {
42
51
  organizationId: e,
43
52
  limit: t,
@@ -51,13 +60,13 @@ function u(e, t = 20, n = 0) {
51
60
  workspaces: r?.allWorkspaces?.items || [],
52
61
  total: r?.allWorkspaces?.total || 0,
53
62
  hasMore: r?.allWorkspaces?.hasMore || !1,
54
- loading: a,
63
+ loading: i,
55
64
  error: o,
56
65
  refetch: s,
57
66
  fetchMore: c
58
67
  };
59
68
  }
60
- function d() {
69
+ function f() {
61
70
  let [n, { data: r, loading: i, error: a }] = t({ refetchQueries: [e] });
62
71
  return {
63
72
  createWorkspace: (e) => n({ variables: { input: e } }),
@@ -66,8 +75,8 @@ function d() {
66
75
  error: a
67
76
  };
68
77
  }
69
- function f() {
70
- let [e, { data: t, loading: n, error: r }] = s();
78
+ function p() {
79
+ let [e, { data: t, loading: n, error: r }] = c();
71
80
  return {
72
81
  updateWorkspace: (t, n) => e({ variables: {
73
82
  id: t,
@@ -78,8 +87,8 @@ function f() {
78
87
  error: r
79
88
  };
80
89
  }
81
- function p() {
82
- let [t, { data: n, loading: r, error: i }] = o({ refetchQueries: [e] });
90
+ function m() {
91
+ let [t, { data: n, loading: r, error: i }] = s({ refetchQueries: [e] });
83
92
  return {
84
93
  deleteWorkspace: (e) => t({ variables: { id: e } }),
85
94
  data: n,
@@ -87,13 +96,13 @@ function p() {
87
96
  error: i
88
97
  };
89
98
  }
90
- function m(e = 100, t = 0, r = !0) {
91
- let { data: i, loading: a, error: o, refetch: s } = n({
99
+ function h(e = 100, t = 0, n = !0) {
100
+ let { data: i, loading: a, error: o, refetch: s } = r({
92
101
  variables: {
93
102
  limit: e,
94
103
  offset: t
95
104
  },
96
- skip: !r,
105
+ skip: !n,
97
106
  fetchPolicy: "cache-first",
98
107
  nextFetchPolicy: "cache-first"
99
108
  });
@@ -106,6 +115,6 @@ function m(e = 100, t = 0, r = !0) {
106
115
  };
107
116
  }
108
117
  //#endregion
109
- export { d as useCreateWorkspace, p as useDeleteWorkspace, m as useTenants, f as useUpdateWorkspace, l as useWorkspace, c as useWorkspaces, u as useWorkspacesByOrganization };
118
+ export { f as useCreateWorkspace, m as useDeleteWorkspace, h as useTenants, p as useUpdateWorkspace, u as useWorkspace, l as useWorkspaces, d as useWorkspacesByOrganization };
110
119
 
111
120
  //# sourceMappingURL=useWorkspaces.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWorkspaces.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/hooks/useWorkspaces.ts"],"sourcesContent":["import {\n useGetWorkspacesQuery,\n useGetWorkspaceQuery,\n useGetWorkspacesByOrganizationQuery,\n useGetTenantsQuery,\n useCreateWorkspaceMutation,\n useUpdateWorkspaceMutation,\n useSuspendWorkspaceMutation,\n GetWorkspacesDocument,\n} from '../../../../generated/global-operations';\nimport type {\n Workspace,\n WorkspaceFilterInput,\n CreateWorkspaceInput,\n UpdateWorkspaceInput,\n} from '../types';\n\n/**\n * Hook to fetch workspaces with filtering and pagination\n * Updated to use offset-based pagination (limit/offset) instead of cursor-based\n */\nexport function useWorkspaces(\n filter?: WorkspaceFilterInput,\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch, fetchMore } = useGetWorkspacesQuery({\n variables: { filter, limit, offset },\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspaces: (data?.allWorkspaces?.items as Workspace[]) || [],\n total: data?.allWorkspaces?.total || 0,\n limit: data?.allWorkspaces?.limit || limit,\n offset: data?.allWorkspaces?.offset || offset,\n hasMore: data?.allWorkspaces?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single workspace by ID\n */\nexport function useWorkspace(id: string) {\n const { data, loading, error, refetch } = useGetWorkspaceQuery({\n variables: { id },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspace: data?.workspace as Workspace | undefined,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch workspaces by organization ID\n */\nexport function useWorkspacesByOrganization(\n organizationId: string,\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch, fetchMore } = useGetWorkspacesByOrganizationQuery({\n variables: { organizationId, limit, offset },\n skip: !organizationId,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspaces: (data?.allWorkspaces?.items as Workspace[]) || [],\n total: data?.allWorkspaces?.total || 0,\n hasMore: data?.allWorkspaces?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to create a new workspace\n */\nexport function useCreateWorkspace() {\n const [createWorkspace, { data, loading, error }] = useCreateWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n createWorkspace: (input: CreateWorkspaceInput) => createWorkspace({ variables: { input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to update a workspace\n */\nexport function useUpdateWorkspace() {\n const [updateWorkspace, { data, loading, error }] = useUpdateWorkspaceMutation();\n\n return {\n updateWorkspace: (id: string, input: UpdateWorkspaceInput) =>\n updateWorkspace({ variables: { id, input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to delete (suspend) a workspace\n * Note: Backend doesn't have deleteWorkspace, using suspendWorkspace instead\n */\nexport function useDeleteWorkspace() {\n const [deleteWorkspace, { data, loading, error }] = useSuspendWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n deleteWorkspace: (id: string) => deleteWorkspace({ variables: { id } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Tenant type for the tenants query\n */\ninterface Tenant {\n id: string;\n name: string;\n slug: string;\n status: string;\n}\n\n/**\n * Hook to fetch tenants available to the current actor for workspace creation\n */\nexport function useTenants(limit: number = 100, offset: number = 0, enabled: boolean = true) {\n const { data, loading, error, refetch } = useGetTenantsQuery({\n variables: { limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n tenants: (data?.myTenants?.items as Tenant[]) || [],\n total: data?.myTenants?.total || 0,\n loading,\n error,\n refetch,\n };\n}\n"],"mappings":";;AAqBA,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB;CACA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAc,EAAsB;EACzE,WAAW;GAAE;GAAQ;GAAO;GAAQ;EACpC,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,YAAa,GAAM,eAAe,SAAyB,EAAE;EAC7D,OAAO,GAAM,eAAe,SAAS;EACrC,OAAO,GAAM,eAAe,SAAS;EACrC,QAAQ,GAAM,eAAe,UAAU;EACvC,SAAS,GAAM,eAAe,WAAW;EACzC;EACA;EACA;EACA;EACD;;AAMH,SAAgB,EAAa,GAAY;CACvC,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAqB;EAC7D,WAAW,EAAE,OAAI;EACjB,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,WAAW,GAAM;EACjB;EACA;EACA;EACD;;AAMH,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB;CACA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAc,EAAoC;EACvF,WAAW;GAAE;GAAgB;GAAO;GAAQ;EAC5C,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,YAAa,GAAM,eAAe,SAAyB,EAAE;EAC7D,OAAO,GAAM,eAAe,SAAS;EACrC,SAAS,GAAM,eAAe,WAAW;EACzC;EACA;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA2B,EAC7E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAgC,EAAgB,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;EAC3F;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,GAA4B;AAEhF,QAAO;EACL,kBAAkB,GAAY,MAC5B,EAAgB,EAAE,WAAW;GAAE;GAAI;GAAO,EAAE,CAAC;EAC/C;EACA;EACA;EACD;;AAOH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA4B,EAC9E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAe,EAAgB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC;EACvE;EACA;EACA;EACD;;AAgBH,SAAgB,EAAW,IAAgB,KAAK,IAAiB,GAAG,IAAmB,IAAM;CAC3F,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAmB;EAC3D,WAAW;GAAE;GAAO;GAAQ;EAC5B,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,SAAU,GAAM,WAAW,SAAsB,EAAE;EACnD,OAAO,GAAM,WAAW,SAAS;EACjC;EACA;EACA;EACD"}
1
+ {"version":3,"file":"useWorkspaces.js","names":[],"sources":["../../../../../src/organization-management/modules/workspaces/hooks/useWorkspaces.ts"],"sourcesContent":["import {\n useGetWorkspacesQuery,\n useGetMyWorkspacesQuery,\n useGetWorkspaceQuery,\n useGetWorkspacesByOrganizationQuery,\n useGetTenantsQuery,\n useCreateWorkspaceMutation,\n useUpdateWorkspaceMutation,\n useSuspendWorkspaceMutation,\n GetWorkspacesDocument,\n} from '../../../../generated/global-operations';\nimport type {\n Workspace,\n WorkspaceFilterInput,\n CreateWorkspaceInput,\n UpdateWorkspaceInput,\n} from '../types';\n\n/**\n * Scope of the workspace listing:\n * - `'my'` — membership-scoped via `myWorkspaces` (@rbac scopeType: \"self\").\n * The correct scope for user-facing app shells; a regular user only\n * ever sees the workspaces they belong to. `myWorkspaces` takes no\n * server-side filter arg, so callers filter client-side.\n * - `'all'` — platform-wide via `allWorkspaces` (@rbac scopeType: \"global\",\n * super-admin / platform-admin only). Reserved for the admin app.\n */\nexport type WorkspacesScope = 'my' | 'all';\n\n/**\n * Hook to fetch workspaces with filtering and pagination.\n * Uses offset-based pagination (limit/offset).\n *\n * `scope` selects the backing query so the user shells (`'my'`) never call the\n * global-scoped `allWorkspaces`; the admin app keeps `'all'`. Both queries are\n * declared (rules-of-hooks) and the inactive one is skipped.\n */\nexport function useWorkspaces(\n filter?: WorkspaceFilterInput,\n limit: number = 20,\n offset: number = 0,\n scope: WorkspacesScope = 'all'\n) {\n const allResult = useGetWorkspacesQuery({\n variables: { filter, limit, offset },\n skip: scope !== 'all',\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n const myResult = useGetMyWorkspacesQuery({\n variables: { limit, offset },\n skip: scope !== 'my',\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const { loading, error, refetch, fetchMore } =\n scope === 'my' ? myResult : allResult;\n const list =\n scope === 'my' ? myResult.data?.myWorkspaces : allResult.data?.allWorkspaces;\n\n return {\n workspaces: (list?.items as Workspace[]) || [],\n total: list?.total || 0,\n limit: list?.limit || limit,\n offset: list?.offset || offset,\n hasMore: list?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to fetch a single workspace by ID\n */\nexport function useWorkspace(id: string) {\n const { data, loading, error, refetch } = useGetWorkspaceQuery({\n variables: { id },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspace: data?.workspace as Workspace | undefined,\n loading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook to fetch workspaces by organization ID\n */\nexport function useWorkspacesByOrganization(\n organizationId: string,\n limit: number = 20,\n offset: number = 0\n) {\n const { data, loading, error, refetch, fetchMore } = useGetWorkspacesByOrganizationQuery({\n variables: { organizationId, limit, offset },\n skip: !organizationId,\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n workspaces: (data?.allWorkspaces?.items as Workspace[]) || [],\n total: data?.allWorkspaces?.total || 0,\n hasMore: data?.allWorkspaces?.hasMore || false,\n loading,\n error,\n refetch,\n fetchMore,\n };\n}\n\n/**\n * Hook to create a new workspace\n */\nexport function useCreateWorkspace() {\n const [createWorkspace, { data, loading, error }] = useCreateWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n createWorkspace: (input: CreateWorkspaceInput) => createWorkspace({ variables: { input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to update a workspace\n */\nexport function useUpdateWorkspace() {\n const [updateWorkspace, { data, loading, error }] = useUpdateWorkspaceMutation();\n\n return {\n updateWorkspace: (id: string, input: UpdateWorkspaceInput) =>\n updateWorkspace({ variables: { id, input } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Hook to delete (suspend) a workspace\n * Note: Backend doesn't have deleteWorkspace, using suspendWorkspace instead\n */\nexport function useDeleteWorkspace() {\n const [deleteWorkspace, { data, loading, error }] = useSuspendWorkspaceMutation({\n refetchQueries: [GetWorkspacesDocument],\n });\n\n return {\n deleteWorkspace: (id: string) => deleteWorkspace({ variables: { id } }),\n data,\n loading,\n error,\n };\n}\n\n/**\n * Tenant type for the tenants query\n */\ninterface Tenant {\n id: string;\n name: string;\n slug: string;\n status: string;\n}\n\n/**\n * Hook to fetch tenants available to the current actor for workspace creation\n */\nexport function useTenants(limit: number = 100, offset: number = 0, enabled: boolean = true) {\n const { data, loading, error, refetch } = useGetTenantsQuery({\n variables: { limit, offset },\n skip: !enabled,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n return {\n tenants: (data?.myTenants?.items as Tenant[]) || [],\n total: data?.myTenants?.total || 0,\n loading,\n error,\n refetch,\n };\n}\n"],"mappings":";;AAqCA,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB,IAAyB,OACzB;CACA,IAAM,IAAY,EAAsB;EACtC,WAAW;GAAE;GAAQ;GAAO;GAAQ;EACpC,MAAM,MAAU;EAChB,aAAa;EACb,iBAAiB;EAClB,CAAC,EACI,IAAW,EAAwB;EACvC,WAAW;GAAE;GAAO;GAAQ;EAC5B,MAAM,MAAU;EAChB,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,EAAE,YAAS,UAAO,YAAS,iBAC/B,MAAU,OAAO,IAAW,GACxB,IACJ,MAAU,OAAO,EAAS,MAAM,eAAe,EAAU,MAAM;AAEjE,QAAO;EACL,YAAa,GAAM,SAAyB,EAAE;EAC9C,OAAO,GAAM,SAAS;EACtB,OAAO,GAAM,SAAS;EACtB,QAAQ,GAAM,UAAU;EACxB,SAAS,GAAM,WAAW;EAC1B;EACA;EACA;EACA;EACD;;AAMH,SAAgB,EAAa,GAAY;CACvC,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAqB;EAC7D,WAAW,EAAE,OAAI;EACjB,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,WAAW,GAAM;EACjB;EACA;EACA;EACD;;AAMH,SAAgB,EACd,GACA,IAAgB,IAChB,IAAiB,GACjB;CACA,IAAM,EAAE,SAAM,YAAS,UAAO,YAAS,iBAAc,EAAoC;EACvF,WAAW;GAAE;GAAgB;GAAO;GAAQ;EAC5C,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,YAAa,GAAM,eAAe,SAAyB,EAAE;EAC7D,OAAO,GAAM,eAAe,SAAS;EACrC,SAAS,GAAM,eAAe,WAAW;EACzC;EACA;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA2B,EAC7E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAgC,EAAgB,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;EAC3F;EACA;EACA;EACD;;AAMH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,GAA4B;AAEhF,QAAO;EACL,kBAAkB,GAAY,MAC5B,EAAgB,EAAE,WAAW;GAAE;GAAI;GAAO,EAAE,CAAC;EAC/C;EACA;EACA;EACD;;AAOH,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAiB,EAAE,SAAM,YAAS,cAAW,EAA4B,EAC9E,gBAAgB,CAAC,EAAsB,EACxC,CAAC;AAEF,QAAO;EACL,kBAAkB,MAAe,EAAgB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC;EACvE;EACA;EACA;EACD;;AAgBH,SAAgB,EAAW,IAAgB,KAAK,IAAiB,GAAG,IAAmB,IAAM;CAC3F,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAmB;EAC3D,WAAW;GAAE;GAAO;GAAQ;EAC5B,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC;AAEF,QAAO;EACL,SAAU,GAAM,WAAW,SAAsB,EAAE;EACnD,OAAO,GAAM,WAAW,SAAS;EACjC;EACA;EACA;EACD"}