@burdenoff/microfe-organization 2026.530.1 → 2026.601.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,7 +61,11 @@ function w() {
61
61
  }),
62
62
  /* @__PURE__ */ a(c, {
63
63
  path: "invites",
64
- element: /* @__PURE__ */ a(x, {})
64
+ element: /* @__PURE__ */ a(x, { activeTab: "workspace" })
65
+ }),
66
+ /* @__PURE__ */ a(c, {
67
+ path: "invites/organizations",
68
+ element: /* @__PURE__ */ a(x, { activeTab: "organization" })
65
69
  }),
66
70
  /* @__PURE__ */ o(c, {
67
71
  path: ":organizationId",
@@ -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 />} />\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 />} />}\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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"}
@@ -43,7 +43,11 @@ function w() {
43
43
  }),
44
44
  /* @__PURE__ */ i(s, {
45
45
  path: "invites",
46
- element: /* @__PURE__ */ i(b, {})
46
+ element: /* @__PURE__ */ i(b, { activeTab: "workspace" })
47
+ }),
48
+ /* @__PURE__ */ i(s, {
49
+ path: "invites/organizations",
50
+ element: /* @__PURE__ */ i(b, { activeTab: "organization" })
47
51
  }),
48
52
  /* @__PURE__ */ i(s, {
49
53
  path: "invite",
@@ -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 />} />\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 />} />\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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"}
@@ -0,0 +1,113 @@
1
+ import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
2
+ import { useCallback as t, useEffect as n, useMemo as r, useRef as i, useState as a } from "react";
3
+ import { graphqlFetch as o } from "@burdenoff/fe-libs/shared/graphql";
4
+ //#region src/organization-management/modules/members/hooks/useWorkspaceInvites.ts
5
+ var s = "\n query GetMyPendingWorkspaceInvites($email: String!) {\n myPendingInvitations(email: $email) {\n id\n email\n status\n roleIds\n workspaceId\n invitedById\n expiresAt\n createdAt\n projectId\n invitedUserId\n updatedAt\n deletedAt\n context {\n type\n metadata\n }\n }\n }\n", c = "\n mutation AcceptWorkspaceInvite($id: ID!) {\n acceptInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n", l = "\n mutation RejectWorkspaceInvite($id: ID!) {\n rejectInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n";
6
+ function u(e, t) {
7
+ return e instanceof Error ? e : Error(t);
8
+ }
9
+ function d(c, l = !0) {
10
+ let { authToken: d } = e(), f = l && !!c, [p, m] = a([]), [h, g] = a(f), [_, v] = a(null), y = i(0), b = i(null), x = t(async (e) => {
11
+ if (!l || !c) return m([]), v(null), g(!1), [];
12
+ let t = ++y.current;
13
+ g(!0), v(null);
14
+ try {
15
+ let n = await o({
16
+ gateway: "workspace",
17
+ authToken: d || void 0,
18
+ query: s,
19
+ variables: { email: c },
20
+ signal: e,
21
+ workspaceToken: !1
22
+ });
23
+ if (n.errors?.length) throw Error(n.errors[0]?.message || "Failed to fetch workspace invites");
24
+ let r = n.data?.myPendingInvitations ?? [];
25
+ return y.current === t && m(r), r;
26
+ } catch (n) {
27
+ if (e?.aborted) return [];
28
+ let r = u(n, "Failed to fetch workspace invites");
29
+ throw y.current === t && (v(r), m([])), r;
30
+ } finally {
31
+ y.current === t && g(!1);
32
+ }
33
+ }, [
34
+ d,
35
+ c,
36
+ l
37
+ ]);
38
+ n(() => {
39
+ let e = new AbortController();
40
+ return b.current?.abort(), b.current = e, x(e.signal).catch(() => void 0), () => {
41
+ b.current === e && (b.current = null), e.abort();
42
+ };
43
+ }, [x]);
44
+ let S = h || f && p.length === 0 && _ === null && y.current === 0;
45
+ return r(() => ({
46
+ invites: p,
47
+ total: p.length,
48
+ loading: S,
49
+ error: _,
50
+ refetch: x
51
+ }), [
52
+ _,
53
+ p,
54
+ S,
55
+ x
56
+ ]);
57
+ }
58
+ function f() {
59
+ let { authToken: n } = e(), [r, i] = a(!1), [s, l] = a(null);
60
+ return {
61
+ acceptInvite: t(async (e) => {
62
+ i(!0), l(null);
63
+ try {
64
+ let t = await o({
65
+ gateway: "workspace",
66
+ authToken: n || void 0,
67
+ query: c,
68
+ variables: { id: e },
69
+ workspaceToken: !1
70
+ });
71
+ if (t.errors?.length) throw Error(t.errors[0]?.message || "Failed to accept workspace invite");
72
+ return t.data?.acceptInvitation ?? null;
73
+ } catch (e) {
74
+ let t = u(e, "Failed to accept workspace invite");
75
+ throw l(t), t;
76
+ } finally {
77
+ i(!1);
78
+ }
79
+ }, [n]),
80
+ loading: r,
81
+ error: s
82
+ };
83
+ }
84
+ function p() {
85
+ let { authToken: n } = e(), [r, i] = a(!1), [s, c] = a(null);
86
+ return {
87
+ rejectInvite: t(async (e) => {
88
+ i(!0), c(null);
89
+ try {
90
+ let t = await o({
91
+ gateway: "workspace",
92
+ authToken: n || void 0,
93
+ query: l,
94
+ variables: { id: e },
95
+ workspaceToken: !1
96
+ });
97
+ if (t.errors?.length) throw Error(t.errors[0]?.message || "Failed to reject workspace invite");
98
+ return t.data?.rejectInvitation ?? null;
99
+ } catch (e) {
100
+ let t = u(e, "Failed to reject workspace invite");
101
+ throw c(t), t;
102
+ } finally {
103
+ i(!1);
104
+ }
105
+ }, [n]),
106
+ loading: r,
107
+ error: s
108
+ };
109
+ }
110
+ //#endregion
111
+ export { f as useAcceptWorkspaceInvite, d as useMyWorkspaceInvites, p as useRejectWorkspaceInvite };
112
+
113
+ //# sourceMappingURL=useWorkspaceInvites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWorkspaceInvites.js","names":[],"sources":["../../../../../src/organization-management/modules/members/hooks/useWorkspaceInvites.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { Invitation } from '../../../../generated/wspace-types';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\n\nconst GET_MY_PENDING_WORKSPACE_INVITES = `\n query GetMyPendingWorkspaceInvites($email: String!) {\n myPendingInvitations(email: $email) {\n id\n email\n status\n roleIds\n workspaceId\n invitedById\n expiresAt\n createdAt\n projectId\n invitedUserId\n updatedAt\n deletedAt\n context {\n type\n metadata\n }\n }\n }\n`;\n\nconst ACCEPT_WORKSPACE_INVITE = `\n mutation AcceptWorkspaceInvite($id: ID!) {\n acceptInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n`;\n\nconst REJECT_WORKSPACE_INVITE = `\n mutation RejectWorkspaceInvite($id: ID!) {\n rejectInvitation(id: $id) {\n id\n status\n workspaceId\n updatedAt\n }\n }\n`;\n\ninterface WorkspaceInvitesResponse {\n myPendingInvitations: Invitation[];\n}\n\ninterface WorkspaceInviteMutationResponse {\n acceptInvitation?: Pick<Invitation, 'id' | 'status' | 'workspaceId' | 'updatedAt'>;\n rejectInvitation?: Pick<Invitation, 'id' | 'status' | 'workspaceId' | 'updatedAt'>;\n}\n\nfunction toError(error: unknown, fallback: string) {\n return error instanceof Error ? error : new Error(fallback);\n}\n\nexport function useMyWorkspaceInvites(email?: string, enabled: boolean = true) {\n const { authToken } = useOrgConsole();\n const shouldFetch = enabled && Boolean(email);\n const [invites, setInvites] = useState<Invitation[]>([]);\n const [loading, setLoading] = useState(shouldFetch);\n const [error, setError] = useState<Error | null>(null);\n const requestIdRef = useRef(0);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const loadInvites = useCallback(\n async (signal?: AbortSignal) => {\n if (!enabled || !email) {\n setInvites([]);\n setError(null);\n setLoading(false);\n return [] as Invitation[];\n }\n\n const requestId = ++requestIdRef.current;\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInvitesResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: GET_MY_PENDING_WORKSPACE_INVITES,\n variables: { email },\n signal,\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to fetch workspace invites');\n }\n\n const nextInvites = result.data?.myPendingInvitations ?? [];\n if (requestIdRef.current === requestId) {\n setInvites(nextInvites);\n }\n return nextInvites;\n } catch (fetchError) {\n if (signal?.aborted) {\n return [] as Invitation[];\n }\n\n const normalizedError = toError(fetchError, 'Failed to fetch workspace invites');\n if (requestIdRef.current === requestId) {\n setError(normalizedError);\n setInvites([]);\n }\n throw normalizedError;\n } finally {\n if (requestIdRef.current === requestId) {\n setLoading(false);\n }\n }\n },\n [authToken, email, enabled]\n );\n\n useEffect(() => {\n const abortController = new AbortController();\n abortControllerRef.current?.abort();\n abortControllerRef.current = abortController;\n\n void loadInvites(abortController.signal).catch(() => undefined);\n\n return () => {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n abortController.abort();\n };\n }, [loadInvites]);\n\n const isLoading =\n loading ||\n (shouldFetch && invites.length === 0 && error === null && requestIdRef.current === 0);\n\n return useMemo(\n () => ({\n invites,\n total: invites.length,\n loading: isLoading,\n error,\n refetch: loadInvites,\n }),\n [error, invites, isLoading, loadInvites]\n );\n}\n\nexport function useAcceptWorkspaceInvite() {\n const { authToken } = useOrgConsole();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const acceptInvite = useCallback(\n async (inviteId: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInviteMutationResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: ACCEPT_WORKSPACE_INVITE,\n variables: { id: inviteId },\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to accept workspace invite');\n }\n\n return result.data?.acceptInvitation ?? null;\n } catch (mutationError) {\n const normalizedError = toError(mutationError, 'Failed to accept workspace invite');\n setError(normalizedError);\n throw normalizedError;\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n return { acceptInvite, loading, error };\n}\n\nexport function useRejectWorkspaceInvite() {\n const { authToken } = useOrgConsole();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const rejectInvite = useCallback(\n async (inviteId: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<WorkspaceInviteMutationResponse>({\n gateway: 'workspace',\n authToken: authToken || undefined,\n query: REJECT_WORKSPACE_INVITE,\n variables: { id: inviteId },\n workspaceToken: false,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message || 'Failed to reject workspace invite');\n }\n\n return result.data?.rejectInvitation ?? null;\n } catch (mutationError) {\n const normalizedError = toError(mutationError, 'Failed to reject workspace invite');\n setError(normalizedError);\n throw normalizedError;\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n return { rejectInvite, loading, error };\n}\n"],"mappings":"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"}
@@ -1,107 +1,413 @@
1
- import { useAcceptInvite as e, useMyInvites as t, useRejectInvite as n } from "../hooks/useInvites.js";
2
- import { InviteStatus as r } from "../../../../generated/global-types.js";
3
- import { useTr as i } from "../../../shared/hooks/useTr.js";
4
- import { LoadingState as a } from "../../../shared/components/feedback/LoadingState.js";
5
- import { useOrganizationPageView as o } from "../../../hooks/useOrganizationPageView.js";
6
- import { MyInviteCard as s } from "../components/MyInviteCard.js";
7
- import { useState as c } from "react";
8
- import { AlertCircle as l, Loader2 as u, Mail as d } from "lucide-react";
9
- import { usePermissions as f } from "@burdenoff/fe-libs/shared/providers/shell";
10
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
1
+ import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
2
+ import { useAcceptInvite as t, useMyInvites as n, useRejectInvite as r } from "../hooks/useInvites.js";
3
+ import { InviteStatus as i } from "../../../../generated/global-types.js";
4
+ import { useTr as a } from "../../../shared/hooks/useTr.js";
5
+ import { LoadingState as o } from "../../../shared/components/feedback/LoadingState.js";
6
+ import { useOrganizationPageView as s } from "../../../hooks/useOrganizationPageView.js";
7
+ import { useAcceptWorkspaceInvite as ee, useMyWorkspaceInvites as te, useRejectWorkspaceInvite as c } from "../hooks/useWorkspaceInvites.js";
8
+ import { MyInviteCard as l } from "../components/MyInviteCard.js";
9
+ import { getProjectDisplayNames as u, getRoleDisplayNames as ne, getWorkspaceDisplayName as d } from "../utils/workspaceInvitationDisplay.js";
10
+ import { useMemo as f, useState as p } from "react";
11
+ import { AlertCircle as re, BriefcaseBusiness as m, Building2 as h, Loader2 as g, Mail as _ } from "lucide-react";
12
+ import { Badge as v } from "@burdenoff/fe-libs/ui";
13
+ import { usePermissions as ie } from "@burdenoff/fe-libs/shared/providers/shell";
14
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
+ import { NavLink as x } from "react-router-dom";
16
+ import { ActorIdentity as S, useActorProfiles as C } from "@burdenoff/fe-libs/shared";
11
17
  //#region src/organization-management/modules/members/pages/MyInvitesPage.tsx
12
- function h() {
13
- o("organizations.my-invites");
14
- let h = i(), { isLoading: g } = f(), { invites: _, loading: v, error: y, total: b } = t(r.Pending), { acceptInvite: x, loading: S } = e(), { rejectInvite: C, loading: w } = n(), [T, E] = c(null), D = async (e) => {
18
+ function w(e) {
19
+ if (!e) return null;
20
+ let t = new Date(e);
21
+ return Number.isNaN(t.getTime()) ? null : t.toLocaleDateString(void 0, {
22
+ year: "numeric",
23
+ month: "short",
24
+ day: "numeric",
25
+ hour: "2-digit",
26
+ minute: "2-digit"
27
+ });
28
+ }
29
+ function T(e, t) {
30
+ return (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
31
+ }
32
+ function E({ activeTab: e, basePath: t, tr: n }) {
33
+ let r = (e) => ["border-b-2 px-4 py-2 text-sm font-medium transition-colors", e ? "border-[var(--color-border-focus)] text-[var(--color-text-primary)]" : "border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]"].join(" ");
34
+ return /* @__PURE__ */ b("div", {
35
+ className: "mb-6 flex gap-2 border-b border-[var(--color-border-default)]",
36
+ children: [/* @__PURE__ */ y(x, {
37
+ to: T(t, "/invites"),
38
+ end: !0,
39
+ className: r(e === "workspace"),
40
+ children: n("members.myInvites.workspaceTab", "Workspace Invites")
41
+ }), /* @__PURE__ */ y(x, {
42
+ to: T(t, "/invites/organizations"),
43
+ className: r(e === "organization"),
44
+ children: n("members.myInvites.organizationTab", "Organization Invites")
45
+ })]
46
+ });
47
+ }
48
+ function D({ activeTab: x = "workspace" }) {
49
+ s(x === "workspace" ? "organizations.my-workspace-invites" : "organizations.my-organization-invites");
50
+ let T = a(), { isLoading: D } = ie(), { basePath: O = "/organizations", currentUser: k, authToken: ae, apiGatewayUrl: oe } = e(), A = x === "organization", j = x === "workspace", { invites: M, loading: N, error: P, total: F } = n(i.Pending, 20, 0, A), { invites: I, loading: L, error: R, total: z, refetch: B } = te(k?.email, j), { acceptInvite: V, loading: H } = t(), { rejectInvite: U, loading: W } = r(), { acceptInvite: G, loading: K } = ee(), { rejectInvite: se, loading: ce } = c(), [q, J] = p(null), Y = q?.tab === x ? q.message : null, [X, Z] = p(null), le = C(f(() => I.flatMap((e) => e.invitedById ? [{
51
+ actorId: e.invitedById,
52
+ actorType: "user"
53
+ }] : []), [I]), {
54
+ apiGatewayUrl: oe,
55
+ authToken: ae
56
+ }), Q = f(() => [...I].sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime()), [I]), $ = x === "workspace" ? z : F, ue = async (e) => {
15
57
  try {
16
- E(null), await x(e);
58
+ J(null), await V(e);
17
59
  } catch (e) {
18
- E(e instanceof Error ? e.message : h("members.myInvites.acceptFailed", "Failed to accept invite"));
60
+ J({
61
+ tab: "organization",
62
+ message: e instanceof Error ? e.message : T("members.myInvites.acceptFailed", "Failed to accept invite")
63
+ });
19
64
  }
20
- }, O = async (e) => {
65
+ }, de = async (e) => {
21
66
  try {
22
- E(null), await C(e);
67
+ J(null), await U(e);
23
68
  } catch (e) {
24
- E(e instanceof Error ? e.message : h("members.myInvites.rejectFailed", "Failed to reject invite"));
69
+ J({
70
+ tab: "organization",
71
+ message: e instanceof Error ? e.message : T("members.myInvites.rejectFailed", "Failed to reject invite")
72
+ });
73
+ }
74
+ }, fe = async (e) => {
75
+ try {
76
+ Z({
77
+ inviteId: e,
78
+ action: "accept"
79
+ }), J(null), await G(e), await B().catch(() => void 0);
80
+ } catch (e) {
81
+ J({
82
+ tab: "workspace",
83
+ message: e instanceof Error ? e.message : T("members.myInvites.workspaceAcceptFailed", "Failed to accept workspace invite")
84
+ });
85
+ } finally {
86
+ Z((t) => t?.inviteId === e && t.action === "accept" ? null : t);
87
+ }
88
+ }, pe = async (e) => {
89
+ try {
90
+ Z({
91
+ inviteId: e,
92
+ action: "reject"
93
+ }), J(null), await se(e), await B().catch(() => void 0);
94
+ } catch (e) {
95
+ J({
96
+ tab: "workspace",
97
+ message: e instanceof Error ? e.message : T("members.myInvites.workspaceRejectFailed", "Failed to reject workspace invite")
98
+ });
99
+ } finally {
100
+ Z((t) => t?.inviteId === e && t.action === "reject" ? null : t);
25
101
  }
26
102
  };
27
- return g ? /* @__PURE__ */ p("div", {
103
+ return D ? /* @__PURE__ */ y("div", {
28
104
  className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
29
- children: /* @__PURE__ */ p(a, { message: h("common.loadingPermissions", "Loading permissions...") })
30
- }) : v && _.length === 0 ? /* @__PURE__ */ p("div", {
31
- className: "flex items-center justify-center min-h-[300px]",
32
- children: /* @__PURE__ */ p(u, {
33
- size: 24,
34
- className: "animate-spin text-[var(--color-text-muted)]"
105
+ children: /* @__PURE__ */ y(o, { message: T("common.loadingPermissions", "Loading permissions...") })
106
+ }) : j && !k?.email ? /* @__PURE__ */ y("div", {
107
+ className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
108
+ children: /* @__PURE__ */ b("div", {
109
+ className: "max-w-4xl mx-auto",
110
+ children: [
111
+ /* @__PURE__ */ b("div", {
112
+ className: "mb-6 flex items-center gap-3",
113
+ children: [/* @__PURE__ */ y(_, {
114
+ size: 24,
115
+ className: "text-[var(--color-text-primary)]"
116
+ }), /* @__PURE__ */ y("h1", {
117
+ className: "text-2xl font-bold text-[var(--color-text-primary)]",
118
+ children: T("members.myInvites.title", "My Invites")
119
+ })]
120
+ }),
121
+ /* @__PURE__ */ y(E, {
122
+ activeTab: x,
123
+ basePath: O,
124
+ tr: T
125
+ }),
126
+ /* @__PURE__ */ y("div", {
127
+ className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 text-center",
128
+ children: /* @__PURE__ */ y("p", {
129
+ className: "text-[var(--color-text-secondary)]",
130
+ children: T("members.myInvites.signInWorkspace", "Sign in to view your workspace invites.")
131
+ })
132
+ })
133
+ ]
134
+ })
135
+ }) : A && N && M.length === 0 || j && L && I.length === 0 ? /* @__PURE__ */ y("div", {
136
+ className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
137
+ children: /* @__PURE__ */ b("div", {
138
+ className: "max-w-4xl mx-auto",
139
+ children: [
140
+ /* @__PURE__ */ b("div", {
141
+ className: "mb-6 flex items-center gap-3",
142
+ children: [/* @__PURE__ */ y(_, {
143
+ size: 24,
144
+ className: "text-[var(--color-text-primary)]"
145
+ }), /* @__PURE__ */ y("h1", {
146
+ className: "text-2xl font-bold text-[var(--color-text-primary)]",
147
+ children: T("members.myInvites.title", "My Invites")
148
+ })]
149
+ }),
150
+ /* @__PURE__ */ y(E, {
151
+ activeTab: x,
152
+ basePath: O,
153
+ tr: T
154
+ }),
155
+ /* @__PURE__ */ y("div", {
156
+ className: "flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]",
157
+ children: /* @__PURE__ */ y(g, {
158
+ size: 24,
159
+ className: "animate-spin text-[var(--color-text-muted)]"
160
+ })
161
+ })
162
+ ]
35
163
  })
36
- }) : y ? /* @__PURE__ */ p("div", {
164
+ }) : A && P ? /* @__PURE__ */ y("div", {
37
165
  className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
38
- children: /* @__PURE__ */ p("div", {
39
- className: "max-w-3xl mx-auto text-center py-12",
40
- children: /* @__PURE__ */ m("p", {
41
- className: "text-[var(--color-status-error-text)]",
42
- children: [
43
- h("members.myInvites.loadFailed", "Failed to load invites"),
44
- ": ",
45
- y.message
46
- ]
47
- })
166
+ children: /* @__PURE__ */ b("div", {
167
+ className: "max-w-4xl mx-auto",
168
+ children: [
169
+ /* @__PURE__ */ b("div", {
170
+ className: "mb-6 flex items-center gap-3",
171
+ children: [/* @__PURE__ */ y(_, {
172
+ size: 24,
173
+ className: "text-[var(--color-text-primary)]"
174
+ }), /* @__PURE__ */ y("h1", {
175
+ className: "text-2xl font-bold text-[var(--color-text-primary)]",
176
+ children: T("members.myInvites.title", "My Invites")
177
+ })]
178
+ }),
179
+ /* @__PURE__ */ y(E, {
180
+ activeTab: x,
181
+ basePath: O,
182
+ tr: T
183
+ }),
184
+ /* @__PURE__ */ y("div", {
185
+ className: "rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center",
186
+ children: /* @__PURE__ */ b("p", {
187
+ className: "text-[var(--color-status-error-text)]",
188
+ children: [
189
+ T("members.myInvites.loadFailed", "Failed to load invites"),
190
+ ": ",
191
+ P.message
192
+ ]
193
+ })
194
+ })
195
+ ]
196
+ })
197
+ }) : j && R ? /* @__PURE__ */ y("div", {
198
+ className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
199
+ children: /* @__PURE__ */ b("div", {
200
+ className: "max-w-4xl mx-auto",
201
+ children: [
202
+ /* @__PURE__ */ b("div", {
203
+ className: "mb-6 flex items-center gap-3",
204
+ children: [/* @__PURE__ */ y(_, {
205
+ size: 24,
206
+ className: "text-[var(--color-text-primary)]"
207
+ }), /* @__PURE__ */ y("h1", {
208
+ className: "text-2xl font-bold text-[var(--color-text-primary)]",
209
+ children: T("members.myInvites.title", "My Invites")
210
+ })]
211
+ }),
212
+ /* @__PURE__ */ y(E, {
213
+ activeTab: x,
214
+ basePath: O,
215
+ tr: T
216
+ }),
217
+ /* @__PURE__ */ y("div", {
218
+ className: "rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center",
219
+ children: /* @__PURE__ */ b("p", {
220
+ className: "text-[var(--color-status-error-text)]",
221
+ children: [
222
+ T("members.myInvites.workspaceLoadFailed", "Failed to load workspace invites"),
223
+ ":",
224
+ " ",
225
+ R.message
226
+ ]
227
+ })
228
+ })
229
+ ]
48
230
  })
49
- }) : /* @__PURE__ */ p("div", {
231
+ }) : /* @__PURE__ */ y("div", {
50
232
  className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
51
- children: /* @__PURE__ */ m("div", {
52
- className: "max-w-3xl mx-auto",
233
+ children: /* @__PURE__ */ b("div", {
234
+ className: "max-w-4xl mx-auto",
53
235
  children: [
54
- /* @__PURE__ */ m("div", {
236
+ /* @__PURE__ */ b("div", {
55
237
  className: "flex items-center gap-3 mb-6",
56
238
  children: [
57
- /* @__PURE__ */ p(d, {
239
+ /* @__PURE__ */ y(_, {
58
240
  size: 24,
59
241
  className: "text-[var(--color-text-primary)]"
60
242
  }),
61
- /* @__PURE__ */ p("h1", {
243
+ /* @__PURE__ */ y("h1", {
62
244
  className: "text-2xl font-bold text-[var(--color-text-primary)]",
63
- children: h("members.myInvites.title", "My Invites")
245
+ children: T("members.myInvites.title", "My Invites")
64
246
  }),
65
- b > 0 && /* @__PURE__ */ p("span", {
247
+ $ > 0 && /* @__PURE__ */ y("span", {
66
248
  className: "px-2.5 py-0.5 rounded-full text-xs font-medium bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]",
67
- children: b
249
+ children: $
68
250
  })
69
251
  ]
70
252
  }),
71
- T && /* @__PURE__ */ m("div", {
253
+ /* @__PURE__ */ y(E, {
254
+ activeTab: x,
255
+ basePath: O,
256
+ tr: T
257
+ }),
258
+ Y && /* @__PURE__ */ b("div", {
72
259
  className: "mb-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3",
73
- children: [/* @__PURE__ */ p(l, {
260
+ children: [/* @__PURE__ */ y(re, {
74
261
  size: 16,
75
262
  className: "shrink-0 text-[var(--color-status-error-text)]"
76
- }), /* @__PURE__ */ p("p", {
263
+ }), /* @__PURE__ */ y("p", {
77
264
  className: "text-sm text-[var(--color-status-error-text)]",
78
- children: T
265
+ children: Y
79
266
  })]
80
267
  }),
81
- _.length === 0 ? /* @__PURE__ */ m("div", {
268
+ j ? Q.length === 0 ? /* @__PURE__ */ b("div", {
269
+ className: "text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]",
270
+ children: [
271
+ /* @__PURE__ */ y(m, {
272
+ size: 48,
273
+ className: "mx-auto mb-4 text-[var(--color-text-muted)] opacity-50"
274
+ }),
275
+ /* @__PURE__ */ y("h3", {
276
+ className: "text-lg font-medium text-[var(--color-text-primary)] mb-1",
277
+ children: T("members.myInvites.workspaceEmptyTitle", "No pending workspace invites")
278
+ }),
279
+ /* @__PURE__ */ y("p", {
280
+ className: "text-sm text-[var(--color-text-muted)]",
281
+ children: T("members.myInvites.workspaceEmptyDescription", "When someone invites you to a workspace, it will appear here.")
282
+ })
283
+ ]
284
+ }) : /* @__PURE__ */ y("div", {
285
+ className: "space-y-3",
286
+ children: Q.map((e) => {
287
+ let t = d(e), n = ne(e), r = u(e), i = e.invitedById ? le[e.invitedById] : void 0, a = i?.displayName || i?.email || T("members.myInvites.invitedByFallback", "Someone"), o = X?.inviteId === e.id && X?.action === "accept" && K, s = X?.inviteId === e.id && X?.action === "reject" && ce;
288
+ return /* @__PURE__ */ y("div", {
289
+ className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5",
290
+ children: /* @__PURE__ */ b("div", {
291
+ className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between",
292
+ children: [/* @__PURE__ */ b("div", {
293
+ className: "min-w-0 space-y-3",
294
+ children: [
295
+ /* @__PURE__ */ b("div", {
296
+ className: "flex items-start gap-3",
297
+ children: [/* @__PURE__ */ y("div", {
298
+ className: "rounded-lg bg-[var(--color-bg-subtle)] p-2",
299
+ children: /* @__PURE__ */ y(m, {
300
+ size: 20,
301
+ className: "text-[var(--color-text-secondary)]"
302
+ })
303
+ }), /* @__PURE__ */ b("div", {
304
+ className: "min-w-0",
305
+ children: [/* @__PURE__ */ y("h3", {
306
+ className: "font-semibold text-[var(--color-text-primary)] break-words",
307
+ children: t
308
+ }), /* @__PURE__ */ y("p", {
309
+ className: "mt-1 text-sm text-[var(--color-text-secondary)] break-all",
310
+ children: e.email
311
+ })]
312
+ })]
313
+ }),
314
+ /* @__PURE__ */ b("div", {
315
+ className: "flex flex-wrap items-center gap-2",
316
+ children: [/* @__PURE__ */ y(v, {
317
+ variant: "secondary",
318
+ children: e.status
319
+ }), n.map((t) => /* @__PURE__ */ y(v, {
320
+ variant: "outline",
321
+ children: t
322
+ }, `${e.id}-${t}`))]
323
+ }),
324
+ /* @__PURE__ */ b("dl", {
325
+ className: "grid gap-3 text-sm text-[var(--color-text-secondary)] sm:grid-cols-2",
326
+ children: [
327
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("dt", {
328
+ className: "font-medium text-[var(--color-text-primary)]",
329
+ children: T("members.myInvites.invitedByLabel", "Invited by")
330
+ }), /* @__PURE__ */ y("dd", {
331
+ className: "break-all",
332
+ children: i ? /* @__PURE__ */ y(S, {
333
+ profile: i,
334
+ variant: "inline",
335
+ hideIdDrilldown: !0
336
+ }) : a
337
+ })] }),
338
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("dt", {
339
+ className: "font-medium text-[var(--color-text-primary)]",
340
+ children: T("members.myInvites.receivedLabel", "Received")
341
+ }), /* @__PURE__ */ y("dd", { children: w(e.createdAt) ?? "—" })] }),
342
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("dt", {
343
+ className: "font-medium text-[var(--color-text-primary)]",
344
+ children: T("members.myInvites.expiresLabel", "Expires")
345
+ }), /* @__PURE__ */ y("dd", { children: w(e.expiresAt) ?? "—" })] }),
346
+ r.length > 0 ? /* @__PURE__ */ b("div", {
347
+ className: "sm:col-span-2",
348
+ children: [/* @__PURE__ */ y("dt", {
349
+ className: "font-medium text-[var(--color-text-primary)]",
350
+ children: T("members.myInvites.projectsLabel", "Projects")
351
+ }), /* @__PURE__ */ y("dd", {
352
+ className: "mt-1 flex flex-wrap gap-2",
353
+ children: r.map((t) => /* @__PURE__ */ y(v, {
354
+ variant: "outline",
355
+ children: t
356
+ }, `${e.id}-${t}`))
357
+ })]
358
+ }) : null
359
+ ]
360
+ })
361
+ ]
362
+ }), /* @__PURE__ */ b("div", {
363
+ className: "flex flex-wrap items-center gap-2 shrink-0",
364
+ children: [/* @__PURE__ */ b("button", {
365
+ type: "button",
366
+ onClick: () => void fe(e.id),
367
+ disabled: o || s,
368
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-[var(--color-status-success-bg)] px-4 py-2 text-sm font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50",
369
+ children: [o ? /* @__PURE__ */ y(g, {
370
+ size: 14,
371
+ className: "animate-spin"
372
+ }) : null, T("members.myInvites.accept", "Accept")]
373
+ }), /* @__PURE__ */ b("button", {
374
+ type: "button",
375
+ onClick: () => void pe(e.id),
376
+ disabled: o || s,
377
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-[var(--color-border-default)] bg-transparent px-4 py-2 text-sm font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:border-[var(--color-status-error-border)] hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)] active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50",
378
+ children: [s ? /* @__PURE__ */ y(g, {
379
+ size: 14,
380
+ className: "animate-spin"
381
+ }) : null, T("members.myInvites.decline", "Decline")]
382
+ })]
383
+ })]
384
+ })
385
+ }, e.id);
386
+ })
387
+ }) : M.length === 0 ? /* @__PURE__ */ b("div", {
82
388
  className: "text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]",
83
389
  children: [
84
- /* @__PURE__ */ p(d, {
390
+ /* @__PURE__ */ y(h, {
85
391
  size: 48,
86
392
  className: "mx-auto mb-4 text-[var(--color-text-muted)] opacity-50"
87
393
  }),
88
- /* @__PURE__ */ p("h3", {
394
+ /* @__PURE__ */ y("h3", {
89
395
  className: "text-lg font-medium text-[var(--color-text-primary)] mb-1",
90
- children: h("members.myInvites.emptyTitle", "No pending invites")
396
+ children: T("members.myInvites.emptyTitle", "No pending invites")
91
397
  }),
92
- /* @__PURE__ */ p("p", {
398
+ /* @__PURE__ */ y("p", {
93
399
  className: "text-sm text-[var(--color-text-muted)]",
94
- children: h("members.myInvites.emptyDescription", "When someone invites you to an organization, it will appear here.")
400
+ children: T("members.myInvites.emptyDescription", "When someone invites you to an organization, it will appear here.")
95
401
  })
96
402
  ]
97
- }) : /* @__PURE__ */ p("div", {
403
+ }) : /* @__PURE__ */ y("div", {
98
404
  className: "space-y-3",
99
- children: _.map((e) => /* @__PURE__ */ p(s, {
405
+ children: M.map((e) => /* @__PURE__ */ y(l, {
100
406
  invite: e,
101
- onAccept: D,
102
- onReject: O,
103
- accepting: S,
104
- rejecting: w
407
+ onAccept: ue,
408
+ onReject: de,
409
+ accepting: H,
410
+ rejecting: W
105
411
  }, e.id))
106
412
  })
107
413
  ]
@@ -109,6 +415,6 @@ function h() {
109
415
  });
110
416
  }
111
417
  //#endregion
112
- export { h as MyInvitesPage };
418
+ export { D as MyInvitesPage };
113
419
 
114
420
  //# sourceMappingURL=MyInvitesPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MyInvitesPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MyInvitesPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Mail, Loader2, AlertCircle } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useMyInvites, useAcceptInvite, useRejectInvite } from '../hooks/useInvites';\nimport { MyInviteCard } from '../components/MyInviteCard';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\n\n/**\n * My Invites Page\n * Shows pending invites for the current user with accept/reject actions\n */\nexport function MyInvitesPage() {\n useOrganizationPageView('organizations.my-invites');\n const tr = useTr();\n const { isLoading: permissionsLoading } = usePermissions();\n const { invites, loading, error, total } = useMyInvites(InviteStatus.Pending);\n const { acceptInvite, loading: accepting } = useAcceptInvite();\n const { rejectInvite, loading: rejecting } = useRejectInvite();\n const [actionError, setActionError] = useState<string | null>(null);\n\n const handleAccept = async (inviteId: string) => {\n try {\n setActionError(null);\n await acceptInvite(inviteId);\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('members.myInvites.acceptFailed', 'Failed to accept invite')\n );\n }\n };\n\n const handleReject = async (inviteId: string) => {\n try {\n setActionError(null);\n await rejectInvite(inviteId);\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('members.myInvites.rejectFailed', 'Failed to reject invite')\n );\n }\n };\n\n // Own invites are always accessible — no permission denial needed.\n // We still wait for permissions to load to ensure consistent UX.\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 if (loading && invites.length === 0) {\n return (\n <div className=\"flex items-center justify-center min-h-[300px]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\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 <div className=\"max-w-3xl mx-auto text-center py-12\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n </div>\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\">\n <div className=\"max-w-3xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-6\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n {total > 0 && (\n <span className=\"px-2.5 py-0.5 rounded-full text-xs font-medium bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]\">\n {total}\n </span>\n )}\n </div>\n\n {actionError && (\n <div className=\"mb-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <AlertCircle size={16} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{actionError}</p>\n </div>\n )}\n\n {/* Invites list */}\n {invites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <Mail size={48} className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\" />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.emptyDescription',\n 'When someone invites you to an organization, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {invites.map((invite) => (\n <MyInviteCard\n key={invite.id}\n invite={invite}\n onAccept={handleAccept}\n onReject={handleReject}\n accepting={accepting}\n rejecting={rejecting}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,IAAgB;AAC9B,GAAwB,2BAA2B;CACnD,IAAM,IAAK,GAAO,EACZ,EAAE,WAAW,MAAuB,GAAgB,EACpD,EAAE,YAAS,YAAS,UAAO,aAAU,EAAa,EAAa,QAAQ,EACvE,EAAE,iBAAc,SAAS,MAAc,GAAiB,EACxD,EAAE,iBAAc,SAAS,MAAc,GAAiB,EACxD,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAE7D,IAAe,OAAO,MAAqB;AAC/C,MAAI;AAEF,GADA,EAAe,KAAK,EACpB,MAAM,EAAa,EAAS;WACrB,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EAAG,kCAAkC,0BAA0B,CACpE;;IAIC,IAAe,OAAO,MAAqB;AAC/C,MAAI;AAEF,GADA,EAAe,KAAK,EACpB,MAAM,EAAa,EAAS;WACrB,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EAAG,kCAAkC,0BAA0B,CACpE;;;AAkCL,QA5BI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,6BAA6B,yBAAyB,EAAI,CAAA;EAChF,CAAA,GAIN,KAAW,EAAQ,WAAW,IAE9B,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAgD,CAAA;EACzE,CAAA,GAIN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,KAAD;IAAG,WAAU;cAAb;KACG,EAAG,gCAAgC,yBAAyB;KAAC;KAAG,EAAM;KACrE;;GACA,CAAA;EACF,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAM,MAAM;OAAI,WAAU;OAAqC,CAAA;MAC/D,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,2BAA2B,aAAa;OACzC,CAAA;MACJ,IAAQ,KACP,kBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA;MAEL;;IAEL,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAa,MAAM;MAAI,WAAU;MAAmD,CAAA,EACpF,kBAAC,KAAD;MAAG,WAAU;gBAAiD;MAAgB,CAAA,CAC1E;;IAIP,EAAQ,WAAW,IAClB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAM,MAAM;OAAI,WAAU;OAA2D,CAAA;MACrF,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,gCAAgC,qBAAqB;OACtD,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,sCACA,oEACD;OACC,CAAA;MACA;SAEN,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAQ,KAAK,MACZ,kBAAC,GAAD;MAEU;MACR,UAAU;MACV,UAAU;MACC;MACA;MACX,EANK,EAAO,GAMZ,CACF;KACE,CAAA;IAEJ;;EACF,CAAA"}
1
+ {"version":3,"file":"MyInvitesPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MyInvitesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { NavLink } from 'react-router-dom';\nimport { Mail, Loader2, AlertCircle, BriefcaseBusiness, Building2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { useMyInvites, useAcceptInvite, useRejectInvite } from '../hooks/useInvites';\nimport {\n useAcceptWorkspaceInvite,\n useMyWorkspaceInvites,\n useRejectWorkspaceInvite,\n} from '../hooks/useWorkspaceInvites';\nimport { MyInviteCard } from '../components/MyInviteCard';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { InviteStatus as OrganizationInviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/workspaceInvitationDisplay';\n\n/**\n * My Invites Page\n * Shows pending invites for the current user with accept/reject actions\n */\ntype MyInvitesTab = 'workspace' | 'organization';\n\ninterface MyInvitesPageProps {\n activeTab?: MyInvitesTab;\n}\n\ninterface TabActionError {\n tab: MyInvitesTab;\n message: string;\n}\n\ninterface InvitesTabNavProps {\n activeTab: MyInvitesTab;\n basePath: string;\n tr: ReturnType<typeof useTr>;\n}\n\nfunction formatDate(value?: string | null) {\n if (!value) return null;\n\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction joinPath(base: string, path: string): string {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n}\n\nfunction InvitesTabNav({ activeTab, basePath, tr }: InvitesTabNavProps) {\n const getTabClassName = (isActive: boolean) =>\n [\n 'border-b-2 px-4 py-2 text-sm font-medium transition-colors',\n isActive\n ? 'border-[var(--color-border-focus)] text-[var(--color-text-primary)]'\n : 'border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)]',\n ].join(' ');\n\n return (\n <div className=\"mb-6 flex gap-2 border-b border-[var(--color-border-default)]\">\n <NavLink\n to={joinPath(basePath, '/invites')}\n end\n className={getTabClassName(activeTab === 'workspace')}\n >\n {tr('members.myInvites.workspaceTab', 'Workspace Invites')}\n </NavLink>\n <NavLink\n to={joinPath(basePath, '/invites/organizations')}\n className={getTabClassName(activeTab === 'organization')}\n >\n {tr('members.myInvites.organizationTab', 'Organization Invites')}\n </NavLink>\n </div>\n );\n}\n\nexport function MyInvitesPage({ activeTab = 'workspace' }: MyInvitesPageProps) {\n useOrganizationPageView(\n activeTab === 'workspace'\n ? 'organizations.my-workspace-invites'\n : 'organizations.my-organization-invites'\n );\n const tr = useTr();\n const { isLoading: permissionsLoading } = usePermissions();\n const { basePath = '/organizations', currentUser, authToken, apiGatewayUrl } = useOrgConsole();\n const orgInvitesEnabled = activeTab === 'organization';\n const workspaceInvitesEnabled = activeTab === 'workspace';\n const { invites, loading, error, total } = useMyInvites(\n OrganizationInviteStatus.Pending,\n 20,\n 0,\n orgInvitesEnabled\n );\n const {\n invites: workspaceInvites,\n loading: workspaceLoading,\n error: workspaceError,\n total: workspaceTotal,\n refetch: refetchWorkspaceInvites,\n } = useMyWorkspaceInvites(currentUser?.email, workspaceInvitesEnabled);\n const { acceptInvite, loading: accepting } = useAcceptInvite();\n const { rejectInvite, loading: rejecting } = useRejectInvite();\n const { acceptInvite: acceptWorkspaceInvite, loading: acceptingWorkspace } =\n useAcceptWorkspaceInvite();\n const { rejectInvite: rejectWorkspaceInvite, loading: rejectingWorkspace } =\n useRejectWorkspaceInvite();\n const [actionError, setActionError] = useState<TabActionError | null>(null);\n\n const visibleActionError = actionError?.tab === activeTab ? actionError.message : null;\n const [processingWorkspaceAction, setProcessingWorkspaceAction] = useState<{\n inviteId: string;\n action: 'accept' | 'reject';\n } | null>(null);\n\n const workspaceInviteActors = useMemo<ActorInput[]>(\n () =>\n workspaceInvites.flatMap((invite) =>\n invite.invitedById ? [{ actorId: invite.invitedById, actorType: 'user' as const }] : []\n ),\n [workspaceInvites]\n );\n\n const inviterProfiles: ActorProfileMap = useActorProfiles(workspaceInviteActors, {\n apiGatewayUrl,\n authToken,\n });\n\n const sortedWorkspaceInvites = useMemo(\n () =>\n [...workspaceInvites].sort(\n (left: (typeof workspaceInvites)[number], right: (typeof workspaceInvites)[number]) =>\n new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n ),\n [workspaceInvites]\n );\n\n const totalCount = activeTab === 'workspace' ? workspaceTotal : total;\n\n const handleAccept = async (inviteId: string) => {\n try {\n setActionError(null);\n await acceptInvite(inviteId);\n } catch (err) {\n setActionError({\n tab: 'organization',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.acceptFailed', 'Failed to accept invite'),\n });\n }\n };\n\n const handleReject = async (inviteId: string) => {\n try {\n setActionError(null);\n await rejectInvite(inviteId);\n } catch (err) {\n setActionError({\n tab: 'organization',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.rejectFailed', 'Failed to reject invite'),\n });\n }\n };\n\n const handleAcceptWorkspace = async (inviteId: string) => {\n try {\n setProcessingWorkspaceAction({ inviteId, action: 'accept' });\n setActionError(null);\n await acceptWorkspaceInvite(inviteId);\n await refetchWorkspaceInvites().catch(() => undefined);\n } catch (err) {\n setActionError({\n tab: 'workspace',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.workspaceAcceptFailed', 'Failed to accept workspace invite'),\n });\n } finally {\n setProcessingWorkspaceAction((current) =>\n current?.inviteId === inviteId && current.action === 'accept' ? null : current\n );\n }\n };\n\n const handleRejectWorkspace = async (inviteId: string) => {\n try {\n setProcessingWorkspaceAction({ inviteId, action: 'reject' });\n setActionError(null);\n await rejectWorkspaceInvite(inviteId);\n await refetchWorkspaceInvites().catch(() => undefined);\n } catch (err) {\n setActionError({\n tab: 'workspace',\n message:\n err instanceof Error\n ? err.message\n : tr('members.myInvites.workspaceRejectFailed', 'Failed to reject workspace invite'),\n });\n } finally {\n setProcessingWorkspaceAction((current) =>\n current?.inviteId === inviteId && current.action === 'reject' ? null : current\n );\n }\n };\n\n // Own invites are always accessible — no permission denial needed.\n // We still wait for permissions to load to ensure consistent UX.\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 if (workspaceInvitesEnabled && !currentUser?.email) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 text-center\">\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr('members.myInvites.signInWorkspace', 'Sign in to view your workspace invites.')}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (orgInvitesEnabled && loading && invites.length === 0) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (workspaceInvitesEnabled && workspaceLoading && workspaceInvites.length === 0) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"flex items-center justify-center min-h-[300px] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)]\">\n <Loader2 size={24} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (orgInvitesEnabled && error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (workspaceInvitesEnabled && workspaceError) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"mb-6 flex items-center gap-3\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n <div className=\"rounded-xl border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-6 text-center\">\n <p className=\"text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.workspaceLoadFailed', 'Failed to load workspace invites')}:{' '}\n {workspaceError.message}\n </p>\n </div>\n </div>\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\">\n <div className=\"max-w-4xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-6\">\n <Mail size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.myInvites.title', 'My Invites')}\n </h1>\n {totalCount > 0 && (\n <span className=\"px-2.5 py-0.5 rounded-full text-xs font-medium bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]\">\n {totalCount}\n </span>\n )}\n </div>\n\n <InvitesTabNav activeTab={activeTab} basePath={basePath} tr={tr} />\n\n {visibleActionError && (\n <div className=\"mb-4 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <AlertCircle size={16} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{visibleActionError}</p>\n </div>\n )}\n\n {workspaceInvitesEnabled ? (\n sortedWorkspaceInvites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <BriefcaseBusiness\n size={48}\n className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\"\n />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.workspaceEmptyTitle', 'No pending workspace invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.workspaceEmptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {sortedWorkspaceInvites.map((invite) => {\n const workspaceName = getWorkspaceDisplayName(invite);\n const roleNames = getRoleDisplayNames(invite);\n const projectNames = getProjectDisplayNames(invite);\n const inviterProfile = invite.invitedById\n ? inviterProfiles[invite.invitedById]\n : undefined;\n const inviterLabel =\n inviterProfile?.displayName ||\n inviterProfile?.email ||\n tr('members.myInvites.invitedByFallback', 'Someone');\n const isProcessingWorkspaceAccept =\n processingWorkspaceAction?.inviteId === invite.id &&\n processingWorkspaceAction?.action === 'accept' &&\n acceptingWorkspace;\n const isProcessingWorkspaceReject =\n processingWorkspaceAction?.inviteId === invite.id &&\n processingWorkspaceAction?.action === 'reject' &&\n rejectingWorkspace;\n\n return (\n <div\n key={invite.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5\"\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-[var(--color-bg-subtle)] p-2\">\n <BriefcaseBusiness\n size={20}\n className=\"text-[var(--color-text-secondary)]\"\n />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] break-words\">\n {workspaceName}\n </h3>\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)] break-all\">\n {invite.email}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\">{invite.status}</Badge>\n {roleNames.map((roleName) => (\n <Badge key={`${invite.id}-${roleName}`} variant=\"outline\">\n {roleName}\n </Badge>\n ))}\n </div>\n\n <dl className=\"grid gap-3 text-sm text-[var(--color-text-secondary)] sm:grid-cols-2\">\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.invitedByLabel', 'Invited by')}\n </dt>\n <dd className=\"break-all\">\n {inviterProfile ? (\n <ActorIdentity\n profile={inviterProfile}\n variant=\"inline\"\n hideIdDrilldown\n />\n ) : (\n inviterLabel\n )}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.receivedLabel', 'Received')}\n </dt>\n <dd>{formatDate(invite.createdAt) ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.expiresLabel', 'Expires')}\n </dt>\n <dd>{formatDate(invite.expiresAt) ?? '—'}</dd>\n </div>\n {projectNames.length > 0 ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"font-medium text-[var(--color-text-primary)]\">\n {tr('members.myInvites.projectsLabel', 'Projects')}\n </dt>\n <dd className=\"mt-1 flex flex-wrap gap-2\">\n {projectNames.map((projectName) => (\n <Badge key={`${invite.id}-${projectName}`} variant=\"outline\">\n {projectName}\n </Badge>\n ))}\n </dd>\n </div>\n ) : null}\n </dl>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button\n type=\"button\"\n onClick={() => void handleAcceptWorkspace(invite.id)}\n disabled={isProcessingWorkspaceAccept || isProcessingWorkspaceReject}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-[var(--color-status-success-bg)] px-4 py-2 text-sm font-medium text-[var(--color-text-inverse)] transition-all duration-200 hover:opacity-90 active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isProcessingWorkspaceAccept ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : null}\n {tr('members.myInvites.accept', 'Accept')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleRejectWorkspace(invite.id)}\n disabled={isProcessingWorkspaceAccept || isProcessingWorkspaceReject}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-[var(--color-border-default)] bg-transparent px-4 py-2 text-sm font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:border-[var(--color-status-error-border)] hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)] active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isProcessingWorkspaceReject ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : null}\n {tr('members.myInvites.decline', 'Decline')}\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )\n ) : invites.length === 0 ? (\n <div className=\"text-center py-12 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <Building2\n size={48}\n className=\"mx-auto mb-4 text-[var(--color-text-muted)] opacity-50\"\n />\n <h3 className=\"text-lg font-medium text-[var(--color-text-primary)] mb-1\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </h3>\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.myInvites.emptyDescription',\n 'When someone invites you to an organization, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {invites.map((invite) => (\n <MyInviteCard\n key={invite.id}\n invite={invite}\n onAccept={handleAccept}\n onReject={handleReject}\n accepting={accepting}\n rejecting={rejecting}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ //#region src/organization-management/modules/members/utils/workspaceInvitationDisplay.ts
2
+ function e(e) {
3
+ let t = e.context?.metadata;
4
+ return t && typeof t == "object" && !Array.isArray(t) ? t : null;
5
+ }
6
+ function t(t) {
7
+ if (t.workspaceName) return t.workspaceName;
8
+ let n = e(t)?.workspaceName;
9
+ return typeof n == "string" && n.trim().length > 0 ? n : t.workspaceId || "—";
10
+ }
11
+ function n(t) {
12
+ if (Array.isArray(t.roleNames) && t.roleNames.length > 0) return t.roleNames;
13
+ let n = e(t)?.roleNames;
14
+ return Array.isArray(n) && n.every((e) => typeof e == "string") ? n : [];
15
+ }
16
+ function r(t) {
17
+ let n = e(t), r = n?.projectNames;
18
+ if (Array.isArray(r) && r.every((e) => typeof e == "string")) return r;
19
+ let i = n?.projectIds, a = [
20
+ ...Array.isArray(t.projectIds) ? t.projectIds : [],
21
+ ...t.projectId ? [t.projectId] : [],
22
+ ...Array.isArray(i) ? i.filter((e) => typeof e == "string") : []
23
+ ], o = new Set(a).size;
24
+ return o === 0 ? [] : [`${o} project${o === 1 ? "" : "s"} selected`];
25
+ }
26
+ //#endregion
27
+ export { r as getProjectDisplayNames, n as getRoleDisplayNames, t as getWorkspaceDisplayName };
28
+
29
+ //# sourceMappingURL=workspaceInvitationDisplay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaceInvitationDisplay.js","names":[],"sources":["../../../../../src/organization-management/modules/members/utils/workspaceInvitationDisplay.ts"],"sourcesContent":["import type { Invitation } from '../../../../generated/wspace-types';\n\ntype InvitationLike = Pick<Invitation, 'workspaceId' | 'roleIds' | 'projectId' | 'context'> & {\n workspaceName?: string;\n projectIds?: string[];\n roleNames?: string[];\n};\n\nfunction readMetadata(invitation: InvitationLike): Record<string, unknown> | null {\n const metadata = invitation.context?.metadata;\n if (metadata && typeof metadata === 'object' && !Array.isArray(metadata)) {\n return metadata as Record<string, unknown>;\n }\n\n return null;\n}\n\nexport function getWorkspaceDisplayName(invitation: InvitationLike): string {\n if (invitation.workspaceName) {\n return invitation.workspaceName;\n }\n\n const metadata = readMetadata(invitation);\n const workspaceName = metadata?.workspaceName;\n if (typeof workspaceName === 'string' && workspaceName.trim().length > 0) {\n return workspaceName;\n }\n\n return invitation.workspaceId || '—';\n}\n\nexport function getRoleDisplayNames(invitation: InvitationLike): string[] {\n if (Array.isArray(invitation.roleNames) && invitation.roleNames.length > 0) {\n return invitation.roleNames;\n }\n\n const metadata = readMetadata(invitation);\n const roleNames = metadata?.roleNames;\n if (Array.isArray(roleNames) && roleNames.every((value) => typeof value === 'string')) {\n return roleNames;\n }\n\n return [];\n}\n\nexport function getProjectDisplayNames(invitation: InvitationLike): string[] {\n const metadata = readMetadata(invitation);\n const projectNames = metadata?.projectNames;\n if (Array.isArray(projectNames) && projectNames.every((value) => typeof value === 'string')) {\n return projectNames;\n }\n\n const metadataProjectIds = metadata?.projectIds;\n const projectIds = [\n ...(Array.isArray(invitation.projectIds) ? invitation.projectIds : []),\n ...(invitation.projectId ? [invitation.projectId] : []),\n ...(Array.isArray(metadataProjectIds)\n ? metadataProjectIds.filter((value): value is string => typeof value === 'string')\n : []),\n ];\n const projectCount = new Set(projectIds).size;\n\n if (projectCount === 0) {\n return [];\n }\n\n return [`${projectCount} project${projectCount === 1 ? '' : 's'} selected`];\n}\n"],"mappings":";AAQA,SAAS,EAAa,GAA4D;CAChF,IAAM,IAAW,EAAW,SAAS;AAKrC,QAJI,KAAY,OAAO,KAAa,YAAY,CAAC,MAAM,QAAQ,EAAS,GAC/D,IAGF;;AAGT,SAAgB,EAAwB,GAAoC;AAC1E,KAAI,EAAW,cACb,QAAO,EAAW;CAIpB,IAAM,IADW,EAAa,EAAW,EACT;AAKhC,QAJI,OAAO,KAAkB,YAAY,EAAc,MAAM,CAAC,SAAS,IAC9D,IAGF,EAAW,eAAe;;AAGnC,SAAgB,EAAoB,GAAsC;AACxE,KAAI,MAAM,QAAQ,EAAW,UAAU,IAAI,EAAW,UAAU,SAAS,EACvE,QAAO,EAAW;CAIpB,IAAM,IADW,EAAa,EAAW,EACb;AAK5B,QAJI,MAAM,QAAQ,EAAU,IAAI,EAAU,OAAO,MAAU,OAAO,KAAU,SAAS,GAC5E,IAGF,EAAE;;AAGX,SAAgB,EAAuB,GAAsC;CAC3E,IAAM,IAAW,EAAa,EAAW,EACnC,IAAe,GAAU;AAC/B,KAAI,MAAM,QAAQ,EAAa,IAAI,EAAa,OAAO,MAAU,OAAO,KAAU,SAAS,CACzF,QAAO;CAGT,IAAM,IAAqB,GAAU,YAC/B,IAAa;EACjB,GAAI,MAAM,QAAQ,EAAW,WAAW,GAAG,EAAW,aAAa,EAAE;EACrE,GAAI,EAAW,YAAY,CAAC,EAAW,UAAU,GAAG,EAAE;EACtD,GAAI,MAAM,QAAQ,EAAmB,GACjC,EAAmB,QAAQ,MAA2B,OAAO,KAAU,SAAS,GAChF,EAAE;EACP,EACK,IAAe,IAAI,IAAI,EAAW,CAAC;AAMzC,QAJI,MAAiB,IACZ,EAAE,GAGJ,CAAC,GAAG,EAAa,UAAU,MAAiB,IAAI,KAAK,IAAI,WAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-organization",
3
- "version": "2026.530.1",
3
+ "version": "2026.601.1",
4
4
  "description": "Organization management microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [