@burdenoff/microfe-organization 2026.1013.2 → 2026.1101.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.
Files changed (172) hide show
  1. package/dist/generated/global-operations.js +57 -58
  2. package/dist/generated/global-operations.js.map +1 -1
  3. package/dist/generated/global-types.js +1 -2
  4. package/dist/generated/global-types.js.map +1 -1
  5. package/dist/node_modules/sonner/dist/index.js +2 -3
  6. package/dist/node_modules/sonner/dist/index.js.map +1 -1
  7. package/dist/node_modules/zod/v4/classic/errors.js +44 -0
  8. package/dist/node_modules/zod/v4/classic/errors.js.map +1 -0
  9. package/dist/node_modules/zod/v4/classic/parse.js +7 -0
  10. package/dist/node_modules/zod/v4/classic/parse.js.map +1 -0
  11. package/dist/node_modules/zod/v4/classic/schemas.js +652 -0
  12. package/dist/node_modules/zod/v4/classic/schemas.js.map +1 -0
  13. package/dist/node_modules/zod/v4/core/api.js +429 -0
  14. package/dist/node_modules/zod/v4/core/api.js.map +1 -0
  15. package/dist/node_modules/zod/v4/core/checks.js +143 -0
  16. package/dist/node_modules/zod/v4/core/checks.js.map +1 -0
  17. package/dist/node_modules/zod/v4/core/core.js +84 -0
  18. package/dist/node_modules/zod/v4/core/core.js.map +1 -0
  19. package/dist/node_modules/zod/v4/core/doc.js +29 -0
  20. package/dist/node_modules/zod/v4/core/doc.js.map +1 -0
  21. package/dist/node_modules/zod/v4/core/errors.js +90 -0
  22. package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
  23. package/dist/node_modules/zod/v4/core/json-schema-processors.js +285 -0
  24. package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +1 -0
  25. package/dist/node_modules/zod/v4/core/memoizer.js +188 -0
  26. package/dist/node_modules/zod/v4/core/memoizer.js.map +1 -0
  27. package/dist/node_modules/zod/v4/core/parse.js +159 -0
  28. package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
  29. package/dist/node_modules/zod/v4/core/regexes.js +34 -0
  30. package/dist/node_modules/zod/v4/core/regexes.js.map +1 -0
  31. package/dist/node_modules/zod/v4/core/registries.js +41 -0
  32. package/dist/node_modules/zod/v4/core/registries.js.map +1 -0
  33. package/dist/node_modules/zod/v4/core/schemas.js +979 -0
  34. package/dist/node_modules/zod/v4/core/schemas.js.map +1 -0
  35. package/dist/node_modules/zod/v4/core/to-json-schema.js +330 -0
  36. package/dist/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
  37. package/dist/node_modules/zod/v4/core/util.js +417 -0
  38. package/dist/node_modules/zod/v4/core/util.js.map +1 -0
  39. package/dist/node_modules/zod/v4/core/versions.js +9 -0
  40. package/dist/node_modules/zod/v4/core/versions.js.map +1 -0
  41. package/dist/node_modules/zod/v4/locales/en.js +96 -0
  42. package/dist/node_modules/zod/v4/locales/en.js.map +1 -0
  43. package/dist/organization-management/AdminOrganizationRoot.js +13 -10
  44. package/dist/organization-management/AdminOrganizationRoot.js.map +1 -1
  45. package/dist/organization-management/AdminOrganizationRoutes.js +53 -47
  46. package/dist/organization-management/AdminOrganizationRoutes.js.map +1 -1
  47. package/dist/organization-management/UserOrganizationRoot.js +13 -10
  48. package/dist/organization-management/UserOrganizationRoot.js.map +1 -1
  49. package/dist/organization-management/UserOrganizationRoutes.js +52 -46
  50. package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
  51. package/dist/organization-management/constants/permissions.js +1 -2
  52. package/dist/organization-management/constants/permissions.js.map +1 -1
  53. package/dist/organization-management/hooks/useOrganizationEventEmitter.js +1 -2
  54. package/dist/organization-management/hooks/useOrganizationEventEmitter.js.map +1 -1
  55. package/dist/organization-management/hooks/useOrganizationPageView.js +1 -2
  56. package/dist/organization-management/hooks/useOrganizationPageView.js.map +1 -1
  57. package/dist/organization-management/modules/members/components/BulkInviteForm.js +3 -3
  58. package/dist/organization-management/modules/members/components/BulkInviteForm.js.map +1 -1
  59. package/dist/organization-management/modules/members/components/InviteMemberForm.js +1 -2
  60. package/dist/organization-management/modules/members/components/InviteMemberForm.js.map +1 -1
  61. package/dist/organization-management/modules/members/components/InviteStatusBadge.js +1 -2
  62. package/dist/organization-management/modules/members/components/InviteStatusBadge.js.map +1 -1
  63. package/dist/organization-management/modules/members/components/MemberRoleBadge.js +1 -2
  64. package/dist/organization-management/modules/members/components/MemberRoleBadge.js.map +1 -1
  65. package/dist/organization-management/modules/members/components/MyInviteCard.js +5 -6
  66. package/dist/organization-management/modules/members/components/MyInviteCard.js.map +1 -1
  67. package/dist/organization-management/modules/members/components/PendingInvitesList.js +8 -9
  68. package/dist/organization-management/modules/members/components/PendingInvitesList.js.map +1 -1
  69. package/dist/organization-management/modules/members/hooks/useInvites.js +2 -3
  70. package/dist/organization-management/modules/members/hooks/useInvites.js.map +1 -1
  71. package/dist/organization-management/modules/members/hooks/useMembers.js +1 -2
  72. package/dist/organization-management/modules/members/hooks/useMembers.js.map +1 -1
  73. package/dist/organization-management/modules/members/hooks/useWorkspaceInvites.js +1 -2
  74. package/dist/organization-management/modules/members/hooks/useWorkspaceInvites.js.map +1 -1
  75. package/dist/organization-management/modules/members/pages/InviteMemberPage.js +16 -17
  76. package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
  77. package/dist/organization-management/modules/members/pages/InvitePeoplePage.js +14 -15
  78. package/dist/organization-management/modules/members/pages/InvitePeoplePage.js.map +1 -1
  79. package/dist/organization-management/modules/members/pages/MemberDetailPage.js +18 -19
  80. package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
  81. package/dist/organization-management/modules/members/pages/MembersListPage.js +142 -143
  82. package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
  83. package/dist/organization-management/modules/members/pages/MyInvitesPage.js +168 -169
  84. package/dist/organization-management/modules/members/pages/MyInvitesPage.js.map +1 -1
  85. package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +13 -14
  86. package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -1
  87. package/dist/organization-management/modules/members/utils/bulkInviteCsv.js +4 -10
  88. package/dist/organization-management/modules/members/utils/bulkInviteCsv.js.map +1 -1
  89. package/dist/organization-management/modules/members/utils/workspaceInvitationDisplay.js +1 -2
  90. package/dist/organization-management/modules/members/utils/workspaceInvitationDisplay.js.map +1 -1
  91. package/dist/organization-management/modules/organizations/components/OrganizationForm.js +14 -15
  92. package/dist/organization-management/modules/organizations/components/OrganizationForm.js.map +1 -1
  93. package/dist/organization-management/modules/organizations/components/OrganizationStatusBadge.js +1 -2
  94. package/dist/organization-management/modules/organizations/components/OrganizationStatusBadge.js.map +1 -1
  95. package/dist/organization-management/modules/organizations/hooks/useOrganizations.js +2 -3
  96. package/dist/organization-management/modules/organizations/hooks/useOrganizations.js.map +1 -1
  97. package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js +22 -21
  98. package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js.map +1 -1
  99. package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js +18 -19
  100. package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
  101. package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +29 -30
  102. package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
  103. package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js +14 -15
  104. package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js.map +1 -1
  105. package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js +33 -34
  106. package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
  107. package/dist/organization-management/modules/organizations/schemas/index.js +11 -12
  108. package/dist/organization-management/modules/organizations/schemas/index.js.map +1 -1
  109. package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js +16 -24
  110. package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map +1 -1
  111. package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +5 -6
  112. package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
  113. package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +31 -32
  114. package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
  115. package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js +1 -2
  116. package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js.map +1 -1
  117. package/dist/organization-management/modules/workspaces/hooks/useWorkspaceMembers.js +1 -2
  118. package/dist/organization-management/modules/workspaces/hooks/useWorkspaceMembers.js.map +1 -1
  119. package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js +11 -12
  120. package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js.map +1 -1
  121. package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js +18 -19
  122. package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
  123. package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +43 -44
  124. package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
  125. package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js +19 -20
  126. package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js.map +1 -1
  127. package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +240 -241
  128. package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
  129. package/dist/organization-management/modules/workspaces/schemas/index.js +6 -7
  130. package/dist/organization-management/modules/workspaces/schemas/index.js.map +1 -1
  131. package/dist/organization-management/providers/OrgConsoleProvider.js +1 -2
  132. package/dist/organization-management/providers/OrgConsoleProvider.js.map +1 -1
  133. package/dist/organization-management/providers/OrgScopedPermissions.js +1 -2
  134. package/dist/organization-management/providers/OrgScopedPermissions.js.map +1 -1
  135. package/dist/organization-management/shared/components/dialog/ConfirmDialog.js +1 -2
  136. package/dist/organization-management/shared/components/dialog/ConfirmDialog.js.map +1 -1
  137. package/dist/organization-management/shared/components/dialog/ConfirmDialogView.js +1 -2
  138. package/dist/organization-management/shared/components/dialog/ConfirmDialogView.js.map +1 -1
  139. package/dist/organization-management/shared/components/feedback/ErrorState.js +1 -2
  140. package/dist/organization-management/shared/components/feedback/ErrorState.js.map +1 -1
  141. package/dist/organization-management/shared/components/feedback/LoadingState.js +1 -2
  142. package/dist/organization-management/shared/components/feedback/LoadingState.js.map +1 -1
  143. package/dist/organization-management/shared/components/input/SearchInput.js +1 -2
  144. package/dist/organization-management/shared/components/input/SearchInput.js.map +1 -1
  145. package/dist/organization-management/shared/components/input/SelectionCheckbox.js +1 -2
  146. package/dist/organization-management/shared/components/input/SelectionCheckbox.js.map +1 -1
  147. package/dist/organization-management/shared/hooks/useDebouncedValue.js +1 -2
  148. package/dist/organization-management/shared/hooks/useDebouncedValue.js.map +1 -1
  149. package/dist/organization-management/shared/hooks/useTr.js +1 -2
  150. package/dist/organization-management/shared/hooks/useTr.js.map +1 -1
  151. package/dist/organization-management/shared/logger.js +1 -2
  152. package/dist/organization-management/shared/logger.js.map +1 -1
  153. package/dist/organization-management/shared/recaptcha.js +1 -2
  154. package/dist/organization-management/shared/recaptcha.js.map +1 -1
  155. package/dist/organization-management/shared/types/graphql.js +1 -2
  156. package/dist/organization-management/shared/types/graphql.js.map +1 -1
  157. package/dist/utils/nativeBridge.js +1 -2
  158. package/package.json +7 -6
  159. package/dist/node_modules/zod/v3/ZodError.js +0 -82
  160. package/dist/node_modules/zod/v3/ZodError.js.map +0 -1
  161. package/dist/node_modules/zod/v3/errors.js +0 -10
  162. package/dist/node_modules/zod/v3/errors.js.map +0 -1
  163. package/dist/node_modules/zod/v3/helpers/errorUtil.js +0 -9
  164. package/dist/node_modules/zod/v3/helpers/errorUtil.js.map +0 -1
  165. package/dist/node_modules/zod/v3/helpers/parseUtil.js +0 -93
  166. package/dist/node_modules/zod/v3/helpers/parseUtil.js.map +0 -1
  167. package/dist/node_modules/zod/v3/helpers/util.js +0 -76
  168. package/dist/node_modules/zod/v3/helpers/util.js.map +0 -1
  169. package/dist/node_modules/zod/v3/locales/en.js +0 -62
  170. package/dist/node_modules/zod/v3/locales/en.js.map +0 -1
  171. package/dist/node_modules/zod/v3/types.js +0 -2441
  172. package/dist/node_modules/zod/v3/types.js.map +0 -1
@@ -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';\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 OrganizationInviteAcceptancePage = lazy(() =>\n import('./modules/members/pages/OrganizationInviteAcceptancePage').then((m) => ({\n default: m.OrganizationInviteAcceptancePage,\n }))\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 {/* Per-invite acceptance page reached from the invite email link\n (`/organizations/invite/:inviteId`). Declared before the dynamic\n `:id` / `:organizationId` blocks so the literal \"invite\" segment\n wins the rank match. */}\n <Route path=\"invite/:inviteId\" element={<OrganizationInviteAcceptancePage />} />\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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"}
1
+ {"version":3,"file":"UserOrganizationRoutes.js","names":["OrganizationCreatePage","lazy","m","OrganizationDetailPage","OrganizationEditPage","WorkspacesListPage","WorkspaceCreatePage","WorkspaceDetailPage","WorkspaceEditPage","MembersListPage","MemberDetailPage","InviteMemberPage","MyInvitesPage","OrganizationInviteAcceptancePage","InvitePeoplePage","OrgScopedRouteLayout","orgId","useParams","paramName","OrgScopedPermissions","Outlet","PageLoadingFallback","UserOrganizationRoutes","Suspense","Routes","Route","OrganizationsListPage","Navigate"],"sources":["../../src/organization-management/UserOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Navigate, Outlet, Route, Routes, useParams } from 'react-router';\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 OrganizationInviteAcceptancePage = lazy(() =>\n import('./modules/members/pages/OrganizationInviteAcceptancePage').then((m) => ({\n default: m.OrganizationInviteAcceptancePage,\n }))\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 {/* Keep legacy/bookmarked /organizations/list links on the list page. */}\n <Route path=\"list\" element={<Navigate to=\"..\" replace />} />\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 {/* Per-invite acceptance page reached from the invite email link\n (`/organizations/invite/:inviteId`). Declared before the dynamic\n `:id` / `:organizationId` blocks so the literal \"invite\" segment\n wins the rank match. */}\n <Route path=\"invite/:inviteId\" element={<OrganizationInviteAcceptancePage />} />\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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"}
@@ -1,4 +1,4 @@
1
- //#region src/organization-management/constants/permissions.ts
1
+ // src/organization-management/constants/permissions.ts
2
2
  var e = {
3
3
  ORG_LIST: "organization:list",
4
4
  ORG_READ: "organization:read",
@@ -16,7 +16,6 @@ var e = {
16
16
  MEMBER_UPDATE: "member:update",
17
17
  MEMBER_REMOVE: "member:delete"
18
18
  };
19
- //#endregion
20
19
  export { e as PERMISSIONS };
21
20
 
22
21
  //# sourceMappingURL=permissions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","names":[],"sources":["../../../src/organization-management/constants/permissions.ts"],"sourcesContent":["/**\n * Organization Management Permission Constants\n *\n * All permission strings for organization management resources.\n * Use with fe-libs usePermissions hook: hasPermission(PERMISSIONS.ORG_CREATE)\n *\n * Resource types:\n * - organization: list, read, create, update, delete\n * - workspace: list, read, create, update, delete\n * - member: list, read, create, update, delete\n */\n\nexport const PERMISSIONS = {\n // Organization resources\n ORG_LIST: 'organization:list',\n ORG_READ: 'organization:read',\n ORG_CREATE: 'organization:create',\n ORG_UPDATE: 'organization:update',\n ORG_DELETE: 'organization:delete',\n\n // Workspace resources\n WORKSPACE_LIST: 'workspace:list',\n WORKSPACE_READ: 'workspace:read',\n WORKSPACE_CREATE: 'workspace:create',\n WORKSPACE_UPDATE: 'workspace:update',\n WORKSPACE_DELETE: 'workspace:delete',\n\n // Member resources\n MEMBER_LIST: 'member:list',\n MEMBER_READ: 'member:read',\n MEMBER_INVITE: 'member:create',\n MEMBER_UPDATE: 'member:update',\n MEMBER_REMOVE: 'member:delete',\n} as const;\n\nexport type OrgPermission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];\n"],"mappings":";AAYA,IAAa,IAAc;CAEzB,UAAU;CACV,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,YAAY;CAGZ,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAGlB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CAChB"}
1
+ {"version":3,"file":"permissions.js","names":["PERMISSIONS"],"sources":["../../../src/organization-management/constants/permissions.ts"],"sourcesContent":["/**\n * Organization Management Permission Constants\n *\n * All permission strings for organization management resources.\n * Use with fe-libs usePermissions hook: hasPermission(PERMISSIONS.ORG_CREATE)\n *\n * Resource types:\n * - organization: list, read, create, update, delete\n * - workspace: list, read, create, update, delete\n * - member: list, read, create, update, delete\n */\n\nexport const PERMISSIONS = {\n // Organization resources\n ORG_LIST: 'organization:list',\n ORG_READ: 'organization:read',\n ORG_CREATE: 'organization:create',\n ORG_UPDATE: 'organization:update',\n ORG_DELETE: 'organization:delete',\n\n // Workspace resources\n WORKSPACE_LIST: 'workspace:list',\n WORKSPACE_READ: 'workspace:read',\n WORKSPACE_CREATE: 'workspace:create',\n WORKSPACE_UPDATE: 'workspace:update',\n WORKSPACE_DELETE: 'workspace:delete',\n\n // Member resources\n MEMBER_LIST: 'member:list',\n MEMBER_READ: 'member:read',\n MEMBER_INVITE: 'member:create',\n MEMBER_UPDATE: 'member:update',\n MEMBER_REMOVE: 'member:delete',\n} as const;\n\nexport type OrgPermission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];\n"],"mappings":";AAYA,IAAaA,IAAc;CAEzB,UAAU;CACV,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,YAAY;CAGZ,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAGlB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;AACjB"}
@@ -1,6 +1,6 @@
1
1
  import { useCallback as e } from "react";
2
2
  import { useEventBus as t } from "@burdenoff/fe-libs/shared/events";
3
- //#region src/organization-management/hooks/useOrganizationEventEmitter.ts
3
+ // src/organization-management/hooks/useOrganizationEventEmitter.ts
4
4
  function n() {
5
5
  let n = t();
6
6
  return { emit: e((e, t = {}) => {
@@ -9,7 +9,6 @@ function n() {
9
9
  } catch {}
10
10
  }, [n]) };
11
11
  }
12
- //#endregion
13
12
  export { n as useOrganizationEventEmitter };
14
13
 
15
14
  //# sourceMappingURL=useOrganizationEventEmitter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOrganizationEventEmitter.js","names":[],"sources":["../../../src/organization-management/hooks/useOrganizationEventEmitter.ts"],"sourcesContent":["/**\n * Thin wrapper over `useEventBus` from fe-libs that emits organization-domain\n * lifecycle events to the host shell's event bus. Per\n * `~/products/infra/infra-specs/microfrontends/47-FRONTEND-EVENT-BUS.md`\n * the MFE never sinks directly to Rybbit/audit — it goes through the\n * shell-owned bus.\n */\nimport { useCallback } from 'react';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport type OrganizationDomainEvent =\n // Resource lifecycle (success)\n | 'organization.created'\n | 'organization.updated'\n | 'organization.deleted'\n | 'organization.invite.created'\n | 'organization.invite.accepted'\n | 'organization.invite.rejected'\n | 'organization.invite.cancelled'\n | 'organization.invite.resent'\n | 'member.added'\n | 'member.removed'\n | 'member.role_changed'\n // Failure / observability events forwarded to OtelBrowserSink + audit\n | 'organization.create_failed'\n | 'organization.update_failed'\n | 'organization.delete_failed'\n | 'organization.invite.create_failed'\n | 'organization.invite.accept_failed'\n | 'organization.invite.reject_failed'\n | 'organization.invite.cancel_failed'\n | 'organization.invite.resend_failed'\n | 'member.add_failed'\n | 'member.remove_failed'\n | 'member.role_change_failed'\n // Module-scoped (not resource-scoped) — uses the plural `organizations.`\n // prefix because it describes a navigation event into the module, not an\n // action on a single Organization entity.\n | 'organizations.page.viewed';\n\nexport interface OrganizationEventPayload {\n organizationId?: string;\n organizationName?: string;\n workspaceId?: string;\n inviteId?: string;\n memberId?: string;\n role?: string;\n count?: number;\n source?: string;\n // Observability fields (no PII per SRE guide §7)\n page?: string;\n durationMs?: number;\n errorCode?: string;\n errorMessage?: string;\n}\n\nexport function useOrganizationEventEmitter(): {\n emit: (event: OrganizationDomainEvent, payload?: OrganizationEventPayload) => void;\n} {\n const bus = useEventBus();\n // Memoize the emit callback so consumers (notably `useOrganizationPageView`)\n // can put it in effect dep arrays without firing on every render.\n const emit = useCallback(\n (event: OrganizationDomainEvent, payload: OrganizationEventPayload = {}) => {\n try {\n type DynamicEmit = (e: string, p: unknown) => void;\n (bus as unknown as { emit: DynamicEmit }).emit(event, payload);\n } catch {\n // Bus failures must not break organization workflows.\n }\n },\n [bus]\n );\n return { emit };\n}\n"],"mappings":";;;AAwDA,SAAgB,IAEd;CACA,IAAM,IAAM,GAAa;AAczB,QAAO,EAAE,MAXI,GACV,GAAgC,IAAoC,EAAE,KAAK;AAC1E,MAAI;AAED,KAAyC,KAAK,GAAO,EAAQ;UACxD;IAIV,CAAC,EAAI,CACN,EACc"}
1
+ {"version":3,"file":"useOrganizationEventEmitter.js","names":["useOrganizationEventEmitter","bus","useEventBus","useCallback","event","payload"],"sources":["../../../src/organization-management/hooks/useOrganizationEventEmitter.ts"],"sourcesContent":["/**\n * Thin wrapper over `useEventBus` from fe-libs that emits organization-domain\n * lifecycle events to the host shell's event bus. Per\n * `~/products/infra/infra-specs/microfrontends/47-FRONTEND-EVENT-BUS.md`\n * the MFE never sinks directly to Rybbit/audit — it goes through the\n * shell-owned bus.\n */\nimport { useCallback } from 'react';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport type OrganizationDomainEvent =\n // Resource lifecycle (success)\n | 'organization.created'\n | 'organization.updated'\n | 'organization.deleted'\n | 'organization.invite.created'\n | 'organization.invite.accepted'\n | 'organization.invite.rejected'\n | 'organization.invite.cancelled'\n | 'organization.invite.resent'\n | 'member.added'\n | 'member.removed'\n | 'member.role_changed'\n // Failure / observability events forwarded to OtelBrowserSink + audit\n | 'organization.create_failed'\n | 'organization.update_failed'\n | 'organization.delete_failed'\n | 'organization.invite.create_failed'\n | 'organization.invite.accept_failed'\n | 'organization.invite.reject_failed'\n | 'organization.invite.cancel_failed'\n | 'organization.invite.resend_failed'\n | 'member.add_failed'\n | 'member.remove_failed'\n | 'member.role_change_failed'\n // Module-scoped (not resource-scoped) — uses the plural `organizations.`\n // prefix because it describes a navigation event into the module, not an\n // action on a single Organization entity.\n | 'organizations.page.viewed';\n\nexport interface OrganizationEventPayload {\n organizationId?: string;\n organizationName?: string;\n workspaceId?: string;\n inviteId?: string;\n memberId?: string;\n role?: string;\n count?: number;\n source?: string;\n // Observability fields (no PII per SRE guide §7)\n page?: string;\n durationMs?: number;\n errorCode?: string;\n errorMessage?: string;\n}\n\nexport function useOrganizationEventEmitter(): {\n emit: (event: OrganizationDomainEvent, payload?: OrganizationEventPayload) => void;\n} {\n const bus = useEventBus();\n // Memoize the emit callback so consumers (notably `useOrganizationPageView`)\n // can put it in effect dep arrays without firing on every render.\n const emit = useCallback(\n (event: OrganizationDomainEvent, payload: OrganizationEventPayload = {}) => {\n try {\n type DynamicEmit = (e: string, p: unknown) => void;\n (bus as unknown as { emit: DynamicEmit }).emit(event, payload);\n } catch {\n // Bus failures must not break organization workflows.\n }\n },\n [bus]\n );\n return { emit };\n}\n"],"mappings":";;;AAwDA,SAAgBA,IAEd;CACA,IAAMC,IAAMC,EAAY;CAcxB,OAAO,EAAE,MAXIC,GACV,GAAgC,IAAoC,CAAC,MAAM;EAC1E,IAAI;GAEF,EAA0C,KAAKC,GAAOC,CAAO;EAC/D,QAAQ,CAER;CACF,GACA,CAACJ,CAAG,CAEG,EAAK;AAChB"}
@@ -1,6 +1,6 @@
1
1
  import { useOrganizationEventEmitter as e } from "./useOrganizationEventEmitter.js";
2
2
  import { useEffect as t } from "react";
3
- //#region src/organization-management/hooks/useOrganizationPageView.ts
3
+ // src/organization-management/hooks/useOrganizationPageView.ts
4
4
  function n(n, r) {
5
5
  let { emit: i } = e(), a = r?.organizationId, o = r?.inviteId, s = r?.workspaceId;
6
6
  t(() => {
@@ -18,7 +18,6 @@ function n(n, r) {
18
18
  s
19
19
  ]);
20
20
  }
21
- //#endregion
22
21
  export { n as useOrganizationPageView };
23
22
 
24
23
  //# sourceMappingURL=useOrganizationPageView.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOrganizationPageView.js","names":[],"sources":["../../../src/organization-management/hooks/useOrganizationPageView.ts"],"sourcesContent":["import { useEffect } from 'react';\n\nimport { useOrganizationEventEmitter } from './useOrganizationEventEmitter';\n\n/**\n * Emits a single `organizations.page.viewed` event when the page mounts (and\n * whenever the page identity changes). Mirrors the groups MFE pattern; the\n * shell-owned event bus forwards this to OtelBrowserSink + audit.\n */\nexport function useOrganizationPageView(\n page: string,\n extra?: { organizationId?: string; inviteId?: string; workspaceId?: string }\n): void {\n const { emit } = useOrganizationEventEmitter();\n const organizationId = extra?.organizationId;\n const inviteId = extra?.inviteId;\n const workspaceId = extra?.workspaceId;\n useEffect(() => {\n emit('organizations.page.viewed', { page, organizationId, inviteId, workspaceId });\n }, [emit, page, organizationId, inviteId, workspaceId]);\n}\n"],"mappings":";;;AASA,SAAgB,EACd,GACA,GACM;CACN,IAAM,EAAE,YAAS,GAA6B,EACxC,IAAiB,GAAO,gBACxB,IAAW,GAAO,UAClB,IAAc,GAAO;AAC3B,SAAgB;AACd,IAAK,6BAA6B;GAAE;GAAM;GAAgB;GAAU;GAAa,CAAC;IACjF;EAAC;EAAM;EAAM;EAAgB;EAAU;EAAY,CAAC"}
1
+ {"version":3,"file":"useOrganizationPageView.js","names":["useOrganizationPageView","useOrganizationEventEmitter","organizationId","extra","inviteId","workspaceId","emit","page"],"sources":["../../../src/organization-management/hooks/useOrganizationPageView.ts"],"sourcesContent":["import { useEffect } from 'react';\n\nimport { useOrganizationEventEmitter } from './useOrganizationEventEmitter';\n\n/**\n * Emits a single `organizations.page.viewed` event when the page mounts (and\n * whenever the page identity changes). Mirrors the groups MFE pattern; the\n * shell-owned event bus forwards this to OtelBrowserSink + audit.\n */\nexport function useOrganizationPageView(\n page: string,\n extra?: { organizationId?: string; inviteId?: string; workspaceId?: string }\n): void {\n const { emit } = useOrganizationEventEmitter();\n const organizationId = extra?.organizationId;\n const inviteId = extra?.inviteId;\n const workspaceId = extra?.workspaceId;\n useEffect(() => {\n emit('organizations.page.viewed', { page, organizationId, inviteId, workspaceId });\n }, [emit, page, organizationId, inviteId, workspaceId]);\n}\n"],"mappings":";;;AASA,SAAgBA,EACd,GACA,GACM;CACN,IAAM,EAAE,YAASC,EAA4B,GACvCC,IAAiBC,GAAO,gBACxBC,IAAWD,GAAO,UAClBE,IAAcF,GAAO;CAC3B,QAAgB;EACd,EAAK,6BAA6B;GAAE;GAAM;GAAgB;GAAU;EAAY,CAAC;CACnF,GAAG;EAACG;EAAMC;EAAML;EAAgBE;EAAUC;CAAW,CAAC;AACxD"}
@@ -5,12 +5,13 @@ import { useCallback as a, useMemo as o, useRef as s, useState as c } from "reac
5
5
  import { CheckCircle2 as l, Download as u, Loader2 as d, Send as f, Upload as p, XCircle as m } from "lucide-react";
6
6
  import { ResponsiveTable as h } from "@burdenoff/fe-libs/ui";
7
7
  import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
8
- //#region src/organization-management/modules/members/components/BulkInviteForm.tsx
8
+ // src/organization-management/modules/members/components/BulkInviteForm.tsx
9
9
  function y({ organizationId: y, inviteMember: b, onCancel: x, loading: S }) {
10
10
  let C = t(), w = s(null), [T, E] = c([]), [D, O] = c(null), [k, A] = c(!1), [j, M] = c(!1), N = a((t) => {
11
11
  let n = new FileReader();
12
12
  n.onload = () => {
13
- E(i(typeof n.result == "string" ? n.result : "", C).map((e) => ({
13
+ let e = typeof n.result == "string" ? n.result : "", r = i(e, C);
14
+ E(r.map((e) => ({
14
15
  ...e,
15
16
  status: { kind: "idle" }
16
17
  }))), O(t.name);
@@ -266,7 +267,6 @@ function y({ organizationId: y, inviteMember: b, onCancel: x, loading: S }) {
266
267
  ]
267
268
  });
268
269
  }
269
- //#endregion
270
270
  export { y as BulkInviteForm };
271
271
 
272
272
  //# sourceMappingURL=BulkInviteForm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BulkInviteForm.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/BulkInviteForm.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\nimport { CheckCircle2, Download, Loader2, Send, Upload, XCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport {\n type BulkInviteRow,\n downloadTemplate,\n formatInviteError,\n parseBulkInviteCsv,\n} from '../utils/bulkInviteCsv';\n\ninterface BulkInviteFormProps {\n organizationId: string;\n inviteMember: (input: {\n organizationId: string;\n email: string;\n message?: string;\n }) => Promise<unknown>;\n onCancel: () => void;\n loading?: boolean;\n}\n\ntype RowStatus =\n | { kind: 'idle' }\n | { kind: 'sending' }\n | { kind: 'invited' }\n | { kind: 'failed'; message: string };\n\ninterface PreviewRow extends BulkInviteRow {\n status: RowStatus;\n}\n\n/**\n * Bulk-invite via CSV upload. Sequentially fires the per-row invite\n * mutation (concurrency 1) so we don't hammer the gateway and so per-row\n * errors are deterministic. Reuses the same mutation as single-invite,\n * which keeps backend behaviour (refetch, OTel events) identical.\n */\nexport function BulkInviteForm({\n organizationId,\n inviteMember,\n onCancel,\n loading,\n}: BulkInviteFormProps) {\n const tr = useTr();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [rows, setRows] = useState<PreviewRow[]>([]);\n const [fileName, setFileName] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [dragActive, setDragActive] = useState(false);\n\n const handleFile = useCallback(\n (file: File) => {\n const reader = new FileReader();\n reader.onload = () => {\n const text = typeof reader.result === 'string' ? reader.result : '';\n const parsed = parseBulkInviteCsv(text, tr);\n setRows(parsed.map((r) => ({ ...r, status: { kind: 'idle' } })));\n setFileName(file.name);\n };\n reader.onerror = () => {\n toast.error(tr('members.bulkInvite.readError', 'Failed to read CSV file.'));\n };\n reader.readAsText(file);\n },\n [tr]\n );\n\n const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) handleFile(file);\n // Reset so re-uploading the same file fires onChange again\n e.target.value = '';\n };\n\n const onDrop = (e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setDragActive(false);\n const file = e.dataTransfer.files?.[0];\n if (file) handleFile(file);\n };\n\n const summary = useMemo(() => {\n const total = rows.length;\n const withErrors = rows.filter((r) => r.errors.length > 0).length;\n const sendable = rows.filter((r) => r.errors.length === 0).length;\n const invited = rows.filter((r) => r.status.kind === 'invited').length;\n const failed = rows.filter((r) => r.status.kind === 'failed').length;\n return { total, withErrors, sendable, invited, failed };\n }, [rows]);\n\n const handleSend = async () => {\n if (summary.sendable === 0 || isSubmitting) return;\n setIsSubmitting(true);\n\n const sendableIndices = rows\n .map((r, idx) => ({ r, idx }))\n .filter(({ r }) => r.errors.length === 0 && r.status.kind !== 'invited');\n\n for (const { idx } of sendableIndices) {\n setRows((prev) =>\n prev.map((row, i) => (i === idx ? { ...row, status: { kind: 'sending' } } : row))\n );\n const row = rows[idx];\n try {\n await inviteMember({\n organizationId,\n email: row.email,\n message: row.message,\n });\n setRows((prev) =>\n prev.map((r, i) => (i === idx ? { ...r, status: { kind: 'invited' } } : r))\n );\n } catch (err) {\n const message = formatInviteError(err, tr);\n setRows((prev) =>\n prev.map((r, i) => (i === idx ? { ...r, status: { kind: 'failed', message } } : r))\n );\n }\n }\n\n setIsSubmitting(false);\n setRows((prev) => {\n const invited = prev.filter((r) => r.status.kind === 'invited').length;\n const failed = prev.filter((r) => r.status.kind === 'failed').length;\n toast.success(\n `${tr('members.bulkInvite.summaryInvited', 'Invited')} ${invited} ${tr('members.bulkInvite.summaryOf', 'of')} ${prev.length}. ${failed} ${tr('members.bulkInvite.summaryFailed', 'failed')}.`\n );\n return prev;\n });\n };\n\n const hasRows = rows.length > 0;\n const canSubmit = summary.sendable > 0 && !isSubmitting && !loading;\n\n const renderRowStatus = (row: PreviewRow) => {\n const hasParseErrors = row.errors.length > 0;\n if (hasParseErrors) {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-error-text)]\">\n <XCircle size={14} aria-hidden=\"true\" />\n {row.errors.join(' ')}\n </span>\n );\n }\n if (row.status.kind === 'sending') {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-text-secondary)]\">\n <Loader2 size={14} className=\"animate-spin\" aria-hidden=\"true\" />\n {tr('members.bulkInvite.statusSending', 'Sending...')}\n </span>\n );\n }\n if (row.status.kind === 'invited') {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-success-text)]\">\n <CheckCircle2 size={14} aria-hidden=\"true\" />\n {tr('members.bulkInvite.statusInvited', 'Invited')}\n </span>\n );\n }\n if (row.status.kind === 'failed') {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-error-text)]\">\n <XCircle size={14} aria-hidden=\"true\" />\n {row.status.message}\n </span>\n );\n }\n return (\n <span className=\"text-[var(--color-text-secondary)]\">\n {tr('members.bulkInvite.statusReady', 'Ready')}\n </span>\n );\n };\n\n const previewColumns: ResponsiveTableColumn<PreviewRow>[] = [\n {\n key: 'rowNumber',\n header: '#',\n priority: 'secondary',\n cell: (row) => (\n <span className=\"text-xs tabular-nums text-[var(--color-text-secondary)]\">\n {row.rowNumber}\n </span>\n ),\n },\n {\n key: 'email',\n header: tr('members.bulkInvite.colEmail', 'Email'),\n priority: 'primary',\n cell: (row) =>\n row.email || (\n <span className=\"italic text-[var(--color-text-muted)]\">\n {tr('members.bulkInvite.empty', '(empty)')}\n </span>\n ),\n },\n {\n key: 'role',\n header: tr('members.bulkInvite.colRole', 'Role'),\n priority: 'secondary',\n cell: (row) =>\n row.role || (\n <span className=\"text-[var(--color-text-muted)]\">\n {tr('members.role.member', 'MEMBER')}\n </span>\n ),\n },\n {\n key: 'status',\n header: tr('members.bulkInvite.colStatus', 'Status'),\n priority: 'secondary',\n cell: (row) => renderRowStatus(row),\n },\n ];\n\n return (\n <div className=\"space-y-5\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.bulkInvite.description',\n 'Upload a CSV with one row per person. Required column: email. Optional: role, message.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={downloadTemplate}\n className=\"inline-flex min-h-10 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <Download size={16} aria-hidden=\"true\" />\n {tr('members.bulkInvite.downloadTemplate', 'Download template')}\n </button>\n </div>\n\n <div\n onDragOver={(e) => {\n e.preventDefault();\n setDragActive(true);\n }}\n onDragLeave={() => setDragActive(false)}\n onDrop={onDrop}\n className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-8 transition-colors ${\n dragActive\n ? 'border-[var(--color-action-primary-bg)] bg-[var(--color-status-info-bgSubtle)]'\n : 'border-[var(--color-border-default)] bg-[var(--color-bg-canvas)]'\n }`}\n >\n <Upload size={28} className=\"text-[var(--color-text-secondary)]\" aria-hidden=\"true\" />\n <div className=\"text-center\">\n <p className=\"text-sm font-medium text-[var(--color-text-primary)]\">\n {fileName\n ? fileName\n : tr('members.bulkInvite.dropHere', 'Drop a CSV file here or click to browse')}\n </p>\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr('members.bulkInvite.csvOnly', 'CSV files only. Max 1000 rows recommended.')}\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".csv,text/csv\"\n onChange={onFileChange}\n className=\"hidden\"\n aria-label={tr('members.bulkInvite.fileInputLabel', 'Choose CSV file')}\n />\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n className=\"inline-flex min-h-10 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-sm text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90\"\n >\n {tr('members.bulkInvite.chooseFile', 'Choose file')}\n </button>\n </div>\n\n {hasRows && (\n <>\n <div className=\"flex flex-wrap gap-4 text-xs text-[var(--color-text-secondary)]\">\n <span>\n {tr('members.bulkInvite.statsTotal', 'Total rows')}: {summary.total}\n </span>\n <span>\n {tr('members.bulkInvite.statsValid', 'Ready')}: {summary.sendable}\n </span>\n {summary.withErrors > 0 && (\n <span className=\"text-[var(--color-status-error-text)]\">\n {tr('members.bulkInvite.statsErrors', 'With errors')}: {summary.withErrors}\n </span>\n )}\n {summary.invited > 0 && (\n <span className=\"text-[var(--color-status-success-text)]\">\n {tr('members.bulkInvite.statsInvited', 'Invited')}: {summary.invited}\n </span>\n )}\n {summary.failed > 0 && (\n <span className=\"text-[var(--color-status-error-text)]\">\n {tr('members.bulkInvite.statsFailed', 'Failed')}: {summary.failed}\n </span>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] p-2\">\n <ResponsiveTable\n columns={previewColumns}\n data={rows}\n rowKey={(row) => `${row.rowNumber}-${row.email}`}\n />\n </div>\n </>\n )}\n\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={!canSubmit}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[200px]\"\n >\n {isSubmitting ? (\n <Loader2 size={16} className=\"animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Send size={16} aria-hidden=\"true\" />\n )}\n {tr('members.bulkInvite.send', 'Send')} {summary.sendable}{' '}\n {tr('members.bulkInvite.sendSuffix', 'invites')}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isSubmitting}\n className=\"rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-2.5 font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[140px]\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BulkInviteForm.js","names":["_Fragment","BulkInviteForm","tr","useTr","fileInputRef","useRef","rows","setRows","useState","fileName","setFileName","isSubmitting","setIsSubmitting","dragActive","setDragActive","handleFile","useCallback","reader","text","parsed","parseBulkInviteCsv","r","file","onFileChange","onDrop","summary","useMemo","handleSend","sendableIndices","prev","i","idx","row","inviteMember","err","message","formatInviteError","invited","failed","toast","hasRows","canSubmit","loading","renderRowStatus","XCircle","Loader2","CheckCircle2","previewColumns","downloadTemplate","Download","Upload","Fragment","ResponsiveTable","Send","onCancel"],"sources":["../../../../../src/organization-management/modules/members/components/BulkInviteForm.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\nimport { CheckCircle2, Download, Loader2, Send, Upload, XCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport {\n type BulkInviteRow,\n downloadTemplate,\n formatInviteError,\n parseBulkInviteCsv,\n} from '../utils/bulkInviteCsv';\n\ninterface BulkInviteFormProps {\n organizationId: string;\n inviteMember: (input: {\n organizationId: string;\n email: string;\n message?: string;\n }) => Promise<unknown>;\n onCancel: () => void;\n loading?: boolean;\n}\n\ntype RowStatus =\n | { kind: 'idle' }\n | { kind: 'sending' }\n | { kind: 'invited' }\n | { kind: 'failed'; message: string };\n\ninterface PreviewRow extends BulkInviteRow {\n status: RowStatus;\n}\n\n/**\n * Bulk-invite via CSV upload. Sequentially fires the per-row invite\n * mutation (concurrency 1) so we don't hammer the gateway and so per-row\n * errors are deterministic. Reuses the same mutation as single-invite,\n * which keeps backend behaviour (refetch, OTel events) identical.\n */\nexport function BulkInviteForm({\n organizationId,\n inviteMember,\n onCancel,\n loading,\n}: BulkInviteFormProps) {\n const tr = useTr();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [rows, setRows] = useState<PreviewRow[]>([]);\n const [fileName, setFileName] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [dragActive, setDragActive] = useState(false);\n\n const handleFile = useCallback(\n (file: File) => {\n const reader = new FileReader();\n reader.onload = () => {\n const text = typeof reader.result === 'string' ? reader.result : '';\n const parsed = parseBulkInviteCsv(text, tr);\n setRows(parsed.map((r) => ({ ...r, status: { kind: 'idle' } })));\n setFileName(file.name);\n };\n reader.onerror = () => {\n toast.error(tr('members.bulkInvite.readError', 'Failed to read CSV file.'));\n };\n reader.readAsText(file);\n },\n [tr]\n );\n\n const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) handleFile(file);\n // Reset so re-uploading the same file fires onChange again\n e.target.value = '';\n };\n\n const onDrop = (e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setDragActive(false);\n const file = e.dataTransfer.files?.[0];\n if (file) handleFile(file);\n };\n\n const summary = useMemo(() => {\n const total = rows.length;\n const withErrors = rows.filter((r) => r.errors.length > 0).length;\n const sendable = rows.filter((r) => r.errors.length === 0).length;\n const invited = rows.filter((r) => r.status.kind === 'invited').length;\n const failed = rows.filter((r) => r.status.kind === 'failed').length;\n return { total, withErrors, sendable, invited, failed };\n }, [rows]);\n\n const handleSend = async () => {\n if (summary.sendable === 0 || isSubmitting) return;\n setIsSubmitting(true);\n\n const sendableIndices = rows\n .map((r, idx) => ({ r, idx }))\n .filter(({ r }) => r.errors.length === 0 && r.status.kind !== 'invited');\n\n for (const { idx } of sendableIndices) {\n setRows((prev) =>\n prev.map((row, i) => (i === idx ? { ...row, status: { kind: 'sending' } } : row))\n );\n const row = rows[idx];\n try {\n await inviteMember({\n organizationId,\n email: row.email,\n message: row.message,\n });\n setRows((prev) =>\n prev.map((r, i) => (i === idx ? { ...r, status: { kind: 'invited' } } : r))\n );\n } catch (err) {\n const message = formatInviteError(err, tr);\n setRows((prev) =>\n prev.map((r, i) => (i === idx ? { ...r, status: { kind: 'failed', message } } : r))\n );\n }\n }\n\n setIsSubmitting(false);\n setRows((prev) => {\n const invited = prev.filter((r) => r.status.kind === 'invited').length;\n const failed = prev.filter((r) => r.status.kind === 'failed').length;\n toast.success(\n `${tr('members.bulkInvite.summaryInvited', 'Invited')} ${invited} ${tr('members.bulkInvite.summaryOf', 'of')} ${prev.length}. ${failed} ${tr('members.bulkInvite.summaryFailed', 'failed')}.`\n );\n return prev;\n });\n };\n\n const hasRows = rows.length > 0;\n const canSubmit = summary.sendable > 0 && !isSubmitting && !loading;\n\n const renderRowStatus = (row: PreviewRow) => {\n const hasParseErrors = row.errors.length > 0;\n if (hasParseErrors) {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-error-text)]\">\n <XCircle size={14} aria-hidden=\"true\" />\n {row.errors.join(' ')}\n </span>\n );\n }\n if (row.status.kind === 'sending') {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-text-secondary)]\">\n <Loader2 size={14} className=\"animate-spin\" aria-hidden=\"true\" />\n {tr('members.bulkInvite.statusSending', 'Sending...')}\n </span>\n );\n }\n if (row.status.kind === 'invited') {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-success-text)]\">\n <CheckCircle2 size={14} aria-hidden=\"true\" />\n {tr('members.bulkInvite.statusInvited', 'Invited')}\n </span>\n );\n }\n if (row.status.kind === 'failed') {\n return (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-error-text)]\">\n <XCircle size={14} aria-hidden=\"true\" />\n {row.status.message}\n </span>\n );\n }\n return (\n <span className=\"text-[var(--color-text-secondary)]\">\n {tr('members.bulkInvite.statusReady', 'Ready')}\n </span>\n );\n };\n\n const previewColumns: ResponsiveTableColumn<PreviewRow>[] = [\n {\n key: 'rowNumber',\n header: '#',\n priority: 'secondary',\n cell: (row) => (\n <span className=\"text-xs tabular-nums text-[var(--color-text-secondary)]\">\n {row.rowNumber}\n </span>\n ),\n },\n {\n key: 'email',\n header: tr('members.bulkInvite.colEmail', 'Email'),\n priority: 'primary',\n cell: (row) =>\n row.email || (\n <span className=\"italic text-[var(--color-text-muted)]\">\n {tr('members.bulkInvite.empty', '(empty)')}\n </span>\n ),\n },\n {\n key: 'role',\n header: tr('members.bulkInvite.colRole', 'Role'),\n priority: 'secondary',\n cell: (row) =>\n row.role || (\n <span className=\"text-[var(--color-text-muted)]\">\n {tr('members.role.member', 'MEMBER')}\n </span>\n ),\n },\n {\n key: 'status',\n header: tr('members.bulkInvite.colStatus', 'Status'),\n priority: 'secondary',\n cell: (row) => renderRowStatus(row),\n },\n ];\n\n return (\n <div className=\"space-y-5\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <p className=\"text-sm text-[var(--color-text-muted)]\">\n {tr(\n 'members.bulkInvite.description',\n 'Upload a CSV with one row per person. Required column: email. Optional: role, message.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={downloadTemplate}\n className=\"inline-flex min-h-10 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <Download size={16} aria-hidden=\"true\" />\n {tr('members.bulkInvite.downloadTemplate', 'Download template')}\n </button>\n </div>\n\n <div\n onDragOver={(e) => {\n e.preventDefault();\n setDragActive(true);\n }}\n onDragLeave={() => setDragActive(false)}\n onDrop={onDrop}\n className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-8 transition-colors ${\n dragActive\n ? 'border-[var(--color-action-primary-bg)] bg-[var(--color-status-info-bgSubtle)]'\n : 'border-[var(--color-border-default)] bg-[var(--color-bg-canvas)]'\n }`}\n >\n <Upload size={28} className=\"text-[var(--color-text-secondary)]\" aria-hidden=\"true\" />\n <div className=\"text-center\">\n <p className=\"text-sm font-medium text-[var(--color-text-primary)]\">\n {fileName\n ? fileName\n : tr('members.bulkInvite.dropHere', 'Drop a CSV file here or click to browse')}\n </p>\n <p className=\"mt-1 text-xs text-[var(--color-text-muted)]\">\n {tr('members.bulkInvite.csvOnly', 'CSV files only. Max 1000 rows recommended.')}\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".csv,text/csv\"\n onChange={onFileChange}\n className=\"hidden\"\n aria-label={tr('members.bulkInvite.fileInputLabel', 'Choose CSV file')}\n />\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n className=\"inline-flex min-h-10 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2 text-sm text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90\"\n >\n {tr('members.bulkInvite.chooseFile', 'Choose file')}\n </button>\n </div>\n\n {hasRows && (\n <>\n <div className=\"flex flex-wrap gap-4 text-xs text-[var(--color-text-secondary)]\">\n <span>\n {tr('members.bulkInvite.statsTotal', 'Total rows')}: {summary.total}\n </span>\n <span>\n {tr('members.bulkInvite.statsValid', 'Ready')}: {summary.sendable}\n </span>\n {summary.withErrors > 0 && (\n <span className=\"text-[var(--color-status-error-text)]\">\n {tr('members.bulkInvite.statsErrors', 'With errors')}: {summary.withErrors}\n </span>\n )}\n {summary.invited > 0 && (\n <span className=\"text-[var(--color-status-success-text)]\">\n {tr('members.bulkInvite.statsInvited', 'Invited')}: {summary.invited}\n </span>\n )}\n {summary.failed > 0 && (\n <span className=\"text-[var(--color-status-error-text)]\">\n {tr('members.bulkInvite.statsFailed', 'Failed')}: {summary.failed}\n </span>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] p-2\">\n <ResponsiveTable\n columns={previewColumns}\n data={rows}\n rowKey={(row) => `${row.rowNumber}-${row.email}`}\n />\n </div>\n </>\n )}\n\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={!canSubmit}\n className=\"inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-6 py-2.5 font-medium text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[200px]\"\n >\n {isSubmitting ? (\n <Loader2 size={16} className=\"animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Send size={16} aria-hidden=\"true\" />\n )}\n {tr('members.bulkInvite.send', 'Send')} {summary.sendable}{' '}\n {tr('members.bulkInvite.sendSuffix', 'invites')}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isSubmitting}\n className=\"rounded-lg border border-[var(--color-border-default)] bg-transparent px-6 py-2.5 font-medium text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] disabled:cursor-not-allowed disabled:opacity-50 sm:min-w-[140px]\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { useTr as e } from "../../../shared/hooks/useTr.js";
2
2
  import { useState as t } from "react";
3
3
  import { Loader2 as n, Mail as r, Send as i } from "lucide-react";
4
4
  import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
- //#region src/organization-management/modules/members/components/InviteMemberForm.tsx
5
+ // src/organization-management/modules/members/components/InviteMemberForm.tsx
6
6
  function s({ organizationId: s, onSubmit: c, onCancel: l, loading: u }) {
7
7
  let d = e(), [f, p] = t({
8
8
  email: "",
@@ -115,7 +115,6 @@ function s({ organizationId: s, onSubmit: c, onCancel: l, loading: u }) {
115
115
  ]
116
116
  });
117
117
  }
118
- //#endregion
119
118
  export { s as InviteMemberForm };
120
119
 
121
120
  //# sourceMappingURL=InviteMemberForm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InviteMemberForm.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/InviteMemberForm.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Loader2, Mail, Send } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface InviteMemberFormProps {\n organizationId: string;\n onSubmit: (data: { organizationId: string; email: string; message?: string }) => Promise<void>;\n onCancel: () => void;\n loading?: boolean;\n}\n\n/**\n * Invite Member Form\n * Email-based form for inviting users to an organization.\n * Role defaults to MEMBER on the backend — admin can change it after the user joins.\n */\nexport function InviteMemberForm({\n organizationId,\n onSubmit,\n onCancel,\n loading,\n}: InviteMemberFormProps) {\n const tr = useTr();\n const [formData, setFormData] = useState({\n email: '',\n message: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n await onSubmit({\n organizationId,\n email: formData.email,\n message: formData.message || undefined,\n });\n setFormData({ email: '', message: '' });\n } catch {\n // Error is surfaced by the parent page via the mutation hook's error state\n }\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Email */}\n <div>\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.emailLabel', 'Email Address')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none\">\n <Mail size={16} className=\"text-[var(--color-text-muted)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={(e) => setFormData({ ...formData, email: e.target.value })}\n aria-label={tr('members.invite.emailLabel', 'Email Address')}\n className=\"\n w-full pl-10 pr-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n \"\n placeholder={tr('members.invite.emailPlaceholder', 'user@example.com')}\n disabled={loading}\n required\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-muted)] mt-1\">\n {tr(\n 'members.invite.emailHelp',\n 'The user will be invited as a Member. You can change their role after they join.'\n )}\n </p>\n </div>\n\n {/* Message (optional) */}\n <div>\n <label\n htmlFor=\"message\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.messageLabel', 'Personal Message')}{' '}\n <span className=\"text-[var(--color-text-muted)]\">\n ({tr('common.optional', 'optional')})\n </span>\n </label>\n <textarea\n id=\"message\"\n value={formData.message}\n onChange={(e) => setFormData({ ...formData, message: e.target.value })}\n aria-label={tr('members.invite.messageLabel', 'Personal Message')}\n className=\"\n w-full px-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n resize-none\n \"\n placeholder={tr(\n 'members.invite.messagePlaceholder',\n 'Add a personal message to the invite...'\n )}\n rows={3}\n maxLength={500}\n disabled={loading}\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"submit\"\n disabled={loading}\n className=\"\n inline-flex items-center gap-2 px-6 py-2.5\n bg-[var(--color-action-primary-bg)]\n text-[var(--color-action-primary-text)]\n rounded-lg\n hover:opacity-90\n \n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[180px]\n \"\n >\n {loading ? <Loader2 size={16} className=\"animate-spin\" /> : <Send size={16} />}\n {tr('members.invite.send', 'Send Invite')}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={loading}\n className=\"\n px-6 py-2.5\n bg-transparent\n text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-bg-subtle)]\n \n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[140px]\n \"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;AAgBA,SAAgB,EAAiB,EAC/B,mBACA,aACA,aACA,cACwB;CACxB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAU,KAAe,EAAS;EACvC,OAAO;EACP,SAAS;EACV,CAAC;AAgBF,QACE,kBAAC,QAAD;EAAM,UAfa,OAAO,MAAuB;AACjD,KAAE,gBAAgB;AAClB,OAAI;AAMF,IALA,MAAM,EAAS;KACb;KACA,OAAO,EAAS;KAChB,SAAS,EAAS,WAAW,KAAA;KAC9B,CAAC,EACF,EAAY;KAAE,OAAO;KAAI,SAAS;KAAI,CAAC;WACjC;;EAMsB,WAAU;YAAxC;GAEE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,SAAD;KACE,SAAQ;KACR,WAAU;eAFZ;MAIG,EAAG,6BAA6B,gBAAgB;MAAE;MACnD,kBAAC,QAAD;OAAM,WAAU;iBAAwC;OAAQ,CAAA;MAC1D;;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAM,MAAM;OAAI,WAAU;OAAmC,CAAA;MACzD,CAAA,EACN,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO,EAAS;MAChB,WAAW,MAAM,EAAY;OAAE,GAAG;OAAU,OAAO,EAAE,OAAO;OAAO,CAAC;MACpE,cAAY,EAAG,6BAA6B,gBAAgB;MAC5D,WAAU;MAUV,aAAa,EAAG,mCAAmC,mBAAmB;MACtE,UAAU;MACV,UAAA;MACA,CAAA,CACE;;IACN,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,4BACA,mFACD;KACC,CAAA;IACA,EAAA,CAAA;GAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IACE,SAAQ;IACR,WAAU;cAFZ;KAIG,EAAG,+BAA+B,mBAAmB;KAAE;KACxD,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAAiD;OAC7C,EAAG,mBAAmB,WAAW;OAAC;OAC/B;;KACD;OACR,kBAAC,YAAD;IACE,IAAG;IACH,OAAO,EAAS;IAChB,WAAW,MAAM,EAAY;KAAE,GAAG;KAAU,SAAS,EAAE,OAAO;KAAO,CAAC;IACtE,cAAY,EAAG,+BAA+B,mBAAmB;IACjE,WAAU;IAWV,aAAa,EACX,qCACA,0CACD;IACD,MAAM;IACN,WAAW;IACX,UAAU;IACV,CAAA,CACE,EAAA,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,WAAU;eAHZ,CAgBG,IAAU,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAC7E,EAAG,uBAAuB,cAAc,CAClC;QAET,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;eAcT,EAAG,iBAAiB,SAAS;KACvB,CAAA,CACL;;GACD"}
1
+ {"version":3,"file":"InviteMemberForm.js","names":["InviteMemberForm","tr","useTr","formData","setFormData","useState","onSubmit","Mail","loading","Loader2","Send","onCancel"],"sources":["../../../../../src/organization-management/modules/members/components/InviteMemberForm.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Loader2, Mail, Send } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface InviteMemberFormProps {\n organizationId: string;\n onSubmit: (data: { organizationId: string; email: string; message?: string }) => Promise<void>;\n onCancel: () => void;\n loading?: boolean;\n}\n\n/**\n * Invite Member Form\n * Email-based form for inviting users to an organization.\n * Role defaults to MEMBER on the backend — admin can change it after the user joins.\n */\nexport function InviteMemberForm({\n organizationId,\n onSubmit,\n onCancel,\n loading,\n}: InviteMemberFormProps) {\n const tr = useTr();\n const [formData, setFormData] = useState({\n email: '',\n message: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n await onSubmit({\n organizationId,\n email: formData.email,\n message: formData.message || undefined,\n });\n setFormData({ email: '', message: '' });\n } catch {\n // Error is surfaced by the parent page via the mutation hook's error state\n }\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Email */}\n <div>\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.emailLabel', 'Email Address')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none\">\n <Mail size={16} className=\"text-[var(--color-text-muted)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={(e) => setFormData({ ...formData, email: e.target.value })}\n aria-label={tr('members.invite.emailLabel', 'Email Address')}\n className=\"\n w-full pl-10 pr-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n \"\n placeholder={tr('members.invite.emailPlaceholder', 'user@example.com')}\n disabled={loading}\n required\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-muted)] mt-1\">\n {tr(\n 'members.invite.emailHelp',\n 'The user will be invited as a Member. You can change their role after they join.'\n )}\n </p>\n </div>\n\n {/* Message (optional) */}\n <div>\n <label\n htmlFor=\"message\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.messageLabel', 'Personal Message')}{' '}\n <span className=\"text-[var(--color-text-muted)]\">\n ({tr('common.optional', 'optional')})\n </span>\n </label>\n <textarea\n id=\"message\"\n value={formData.message}\n onChange={(e) => setFormData({ ...formData, message: e.target.value })}\n aria-label={tr('members.invite.messageLabel', 'Personal Message')}\n className=\"\n w-full px-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n resize-none\n \"\n placeholder={tr(\n 'members.invite.messagePlaceholder',\n 'Add a personal message to the invite...'\n )}\n rows={3}\n maxLength={500}\n disabled={loading}\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"submit\"\n disabled={loading}\n className=\"\n inline-flex items-center gap-2 px-6 py-2.5\n bg-[var(--color-action-primary-bg)]\n text-[var(--color-action-primary-text)]\n rounded-lg\n hover:opacity-90\n \n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[180px]\n \"\n >\n {loading ? <Loader2 size={16} className=\"animate-spin\" /> : <Send size={16} />}\n {tr('members.invite.send', 'Send Invite')}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={loading}\n className=\"\n px-6 py-2.5\n bg-transparent\n text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-bg-subtle)]\n \n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[140px]\n \"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;AAgBA,SAAgBA,EAAiB,EAC/B,mBACA,aACA,aACA,cACwB;CACxB,IAAMC,IAAKC,EAAM,GACX,CAACC,GAAUC,KAAeC,EAAS;EACvC,OAAO;EACP,SAAS;CACX,CAAC;CAgBD,OACE,kBAAC,QAAD;EAAM,UAAU,OAfU,MAAuB;GACjD,EAAE,eAAe;GACjB,IAAI;IAMF,AALA,MAAMC,EAAS;KACb;KACA,OAAOH,EAAS;KAChB,SAASA,EAAS,WAAW,KAAA;IAC/B,CAAC,GACDC,EAAY;KAAE,OAAO;KAAI,SAAS;IAAG,CAAC;GACxC,QAAQ,CAER;EACF;EAGgC,WAAU;EAAxC,UAAA;GAEE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,SAAD;KACE,SAAQ;KACR,WAAU;KAFZ,UAAA;MAIGH,EAAG,6BAA6B,eAAe;MAAG;MACnD,kBAAC,QAAD;OAAM,WAAU;OAAwC,UAAA;MAAO,CAAA;KAC1D;;IACP,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;MACb,UAAA,kBAACM,GAAD;OAAM,MAAM;OAAI,WAAU;MAAkC,CAAA;KACzD,CAAA,GACL,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAOJ,EAAS;MAChB,WAAW,MAAMC,EAAY;OAAE,GAAGD;OAAU,OAAO,EAAE,OAAO;MAAM,CAAC;MACnE,cAAYF,EAAG,6BAA6B,eAAe;MAC3D,WAAU;MAUV,aAAaA,EAAG,mCAAmC,kBAAkB;MACrE,UAAUO;MACV,UAAA;KACD,CAAA,CACE;;IACL,kBAAC,KAAD;KAAG,WAAU;KACV,UAAAP,EACC,4BACA,kFACF;IACC,CAAA;GACA,EAAA,CAAA;GAGL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IACE,SAAQ;IACR,WAAU;IAFZ,UAAA;KAIGA,EAAG,+BAA+B,kBAAkB;KAAG;KACxD,kBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA;OAAiD;OAC7CA,EAAG,mBAAmB,UAAU;OAAE;MAChC;;IACD;GACP,CAAA,GAAA,kBAAC,YAAD;IACE,IAAG;IACH,OAAOE,EAAS;IAChB,WAAW,MAAMC,EAAY;KAAE,GAAGD;KAAU,SAAS,EAAE,OAAO;IAAM,CAAC;IACrE,cAAYF,EAAG,+BAA+B,kBAAkB;IAChE,WAAU;IAWV,aAAaA,EACX,qCACA,yCACF;IACA,MAAM;IACN,WAAW;IACX,UAAUO;GACX,CAAA,CACE,EAAA,CAAA;GAGL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,UAAD;KACE,MAAK;KACL,UAAUA;KACV,WAAU;KAHZ,UAAA,CAgBGA,IAAU,kBAACC,GAAD;MAAS,MAAM;MAAI,WAAU;KAAgB,CAAA,IAAI,kBAACC,GAAD,EAAM,MAAM,GAAK,CAAA,GAC5ET,EAAG,uBAAuB,aAAa,CAClC;IAER,CAAA,GAAA,kBAAC,UAAD;KACE,MAAK;KACL,SAASU;KACT,UAAUH;KACV,WAAU;KAcT,UAAAP,EAAG,iBAAiB,QAAQ;IACvB,CAAA,CACL;;EACD;;AAEV"}
@@ -2,7 +2,7 @@ import { useCallback as e } from "react";
2
2
  import { AlertTriangle as t, Ban as n, Check as r, Clock as i, X as a } from "lucide-react";
3
3
  import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
4
  import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
- //#region src/organization-management/modules/members/components/InviteStatusBadge.tsx
5
+ // src/organization-management/modules/members/components/InviteStatusBadge.tsx
6
6
  var l = {
7
7
  PENDING: {
8
8
  labelKey: "members.inviteStatus.pending",
@@ -49,7 +49,6 @@ function u({ status: t }) {
49
49
  }), r(i.labelKey, i.defaultLabel)]
50
50
  });
51
51
  }
52
- //#endregion
53
52
  export { u as InviteStatusBadge };
54
53
 
55
54
  //# sourceMappingURL=InviteStatusBadge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InviteStatusBadge.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/InviteStatusBadge.tsx"],"sourcesContent":["import { Clock, Check, X, AlertTriangle, Ban } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface InviteStatusBadgeProps {\n status: string;\n}\n\nconst STATUS_CONFIG: Record<\n string,\n { labelKey: string; defaultLabel: string; className: string; icon: typeof Clock; pulse?: boolean }\n> = {\n PENDING: {\n labelKey: 'members.inviteStatus.pending',\n defaultLabel: 'Pending',\n className:\n 'bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)] border border-[var(--color-status-warning-border)]',\n icon: Clock,\n pulse: true,\n },\n ACCEPTED: {\n labelKey: 'members.inviteStatus.accepted',\n defaultLabel: 'Accepted',\n className:\n 'bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)] border border-[var(--color-status-success-border)]',\n icon: Check,\n },\n REJECTED: {\n labelKey: 'members.inviteStatus.rejected',\n defaultLabel: 'Rejected',\n className:\n 'bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)] border border-[var(--color-status-error-border)]',\n icon: X,\n },\n EXPIRED: {\n labelKey: 'members.inviteStatus.expired',\n defaultLabel: 'Expired',\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border border-[var(--color-border-default)]',\n icon: AlertTriangle,\n },\n CANCELLED: {\n labelKey: 'members.inviteStatus.cancelled',\n defaultLabel: 'Cancelled',\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border border-[var(--color-border-default)]',\n icon: Ban,\n },\n};\n\nexport function InviteStatusBadge({ status }: InviteStatusBadgeProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.PENDING;\n const Icon = config.icon;\n\n return (\n <span\n className={`inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium ${config.className}`}\n >\n <Icon size={12} className={config.pulse ? 'motion-safe:animate-vc-glow-pulse' : ''} />\n {tr(config.labelKey, config.defaultLabel)}\n </span>\n );\n}\n"],"mappings":";;;;;AAQA,IAAM,IAGF;CACF,SAAS;EACP,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAM;EACN,OAAO;EACR;CACD,UAAU;EACR,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAM;EACP;CACD,UAAU;EACR,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAM;EACP;CACD,SAAS;EACP,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAM;EACP;CACD,WAAW;EACT,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAM;EACP;CACF;AAED,SAAgB,EAAkB,EAAE,aAAkC;CACpE,IAAM,EAAE,SAAM,GAAS,EACjB,IAAK,GACR,GAAa,MAAqB;EACjC,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEzC,CAAC,EAAE,CACJ,EACK,IAAS,EAAc,MAAW,EAAc,SAChD,IAAO,EAAO;AAEpB,QACE,kBAAC,QAAD;EACE,WAAW,iFAAiF,EAAO;YADrG,CAGE,kBAAC,GAAD;GAAM,MAAM;GAAI,WAAW,EAAO,QAAQ,sCAAsC;GAAM,CAAA,EACrF,EAAG,EAAO,UAAU,EAAO,aAAa,CACpC"}
1
+ {"version":3,"file":"InviteStatusBadge.js","names":["STATUS_CONFIG","Clock","Check","X","AlertTriangle","Ban","InviteStatusBadge","useI18n","tr","useCallback","translated","t","key","fallback","config","status","Icon"],"sources":["../../../../../src/organization-management/modules/members/components/InviteStatusBadge.tsx"],"sourcesContent":["import { Clock, Check, X, AlertTriangle, Ban } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface InviteStatusBadgeProps {\n status: string;\n}\n\nconst STATUS_CONFIG: Record<\n string,\n { labelKey: string; defaultLabel: string; className: string; icon: typeof Clock; pulse?: boolean }\n> = {\n PENDING: {\n labelKey: 'members.inviteStatus.pending',\n defaultLabel: 'Pending',\n className:\n 'bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)] border border-[var(--color-status-warning-border)]',\n icon: Clock,\n pulse: true,\n },\n ACCEPTED: {\n labelKey: 'members.inviteStatus.accepted',\n defaultLabel: 'Accepted',\n className:\n 'bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)] border border-[var(--color-status-success-border)]',\n icon: Check,\n },\n REJECTED: {\n labelKey: 'members.inviteStatus.rejected',\n defaultLabel: 'Rejected',\n className:\n 'bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)] border border-[var(--color-status-error-border)]',\n icon: X,\n },\n EXPIRED: {\n labelKey: 'members.inviteStatus.expired',\n defaultLabel: 'Expired',\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border border-[var(--color-border-default)]',\n icon: AlertTriangle,\n },\n CANCELLED: {\n labelKey: 'members.inviteStatus.cancelled',\n defaultLabel: 'Cancelled',\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border border-[var(--color-border-default)]',\n icon: Ban,\n },\n};\n\nexport function InviteStatusBadge({ status }: InviteStatusBadgeProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.PENDING;\n const Icon = config.icon;\n\n return (\n <span\n className={`inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium ${config.className}`}\n >\n <Icon size={12} className={config.pulse ? 'motion-safe:animate-vc-glow-pulse' : ''} />\n {tr(config.labelKey, config.defaultLabel)}\n </span>\n );\n}\n"],"mappings":";;;;;AAQA,IAAMA,IAGF;CACF,SAAS;EACP,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAMC;EACN,OAAO;CACT;CACA,UAAU;EACR,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAMC;CACR;CACA,UAAU;EACR,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAMC;CACR;CACA,SAAS;EACP,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAMC;CACR;CACA,WAAW;EACT,UAAU;EACV,cAAc;EACd,WACE;EACF,MAAMC;CACR;AACF;AAEA,SAAgBC,EAAkB,EAAE,aAAkC;CACpE,IAAM,EAAE,SAAMC,EAAQ,GAChBC,IAAKC,GACR,GAAa,MAAqB;EACjC,IAAMC,IAAaC,EAAEC,CAAG;EACxB,OAAOF,MAAeE,IAAMC,IAAWH;CACzC,GACA,CAACC,CAAC,CACJ,GACMG,IAASd,EAAce,MAAWf,EAAc,SAChDgB,IAAOF,EAAO;CAEpB,OACE,kBAAC,QAAD;EACE,WAAW,iFAAiFA,EAAO;EADrG,UAAA,CAGE,kBAACE,GAAD;GAAM,MAAM;GAAI,WAAWF,EAAO,QAAQ,sCAAsC;EAAK,CAAA,GACpFN,EAAGM,EAAO,UAAUA,EAAO,YAAY,CACpC;;AAEV"}
@@ -2,7 +2,7 @@ import { useCallback as e } from "react";
2
2
  import { Eye as t, Shield as n, UserCheck as r } from "lucide-react";
3
3
  import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
4
  import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
- //#region src/organization-management/modules/members/components/MemberRoleBadge.tsx
5
+ // src/organization-management/modules/members/components/MemberRoleBadge.tsx
6
6
  function s({ role: s, showIcon: c = !0 }) {
7
7
  let { t: l } = o(), u = e((e, t) => {
8
8
  let n = l(e);
@@ -50,7 +50,6 @@ function s({ role: s, showIcon: c = !0 }) {
50
50
  children: [c && /* @__PURE__ */ i(f, { size: 14 }), d.label]
51
51
  });
52
52
  }
53
- //#endregion
54
53
  export { s as MemberRoleBadge };
55
54
 
56
55
  //# sourceMappingURL=MemberRoleBadge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MemberRoleBadge.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/MemberRoleBadge.tsx"],"sourcesContent":["import { Shield, UserCheck, Eye } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface MemberRoleBadgeProps {\n role: string;\n showIcon?: boolean;\n}\n\n/**\n * MemberRoleBadge\n * Displays a role badge for a member with appropriate styling\n */\nexport function MemberRoleBadge({ role, showIcon = true }: MemberRoleBadgeProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const getRoleConfig = () => {\n const roleUpper = role.toUpperCase();\n\n switch (roleUpper) {\n case 'SUPER_ADMIN':\n return {\n label: tr('members.role.superAdmin', 'Super Admin'),\n icon: Shield,\n className:\n 'bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)] border-[var(--color-status-error-border)]',\n };\n case 'TENANT_ADMIN':\n case 'ORG_ADMIN':\n case 'WORKSPACE_ADMIN':\n return {\n label:\n roleUpper === 'TENANT_ADMIN'\n ? tr('members.role.tenantAdmin', 'Tenant Admin')\n : roleUpper === 'ORG_ADMIN'\n ? tr('members.role.orgAdmin', 'Org Admin')\n : tr('members.role.workspaceAdmin', 'Workspace Admin'),\n icon: Shield,\n className:\n 'bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)] border-[var(--color-status-warning-border)]',\n };\n case 'MEMBER':\n return {\n label: tr('members.role.member', 'Member'),\n icon: UserCheck,\n className:\n 'bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)] border-[var(--color-status-info-border)]',\n };\n case 'GUEST':\n return {\n label: tr('members.role.guest', 'Guest'),\n icon: Eye,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n default:\n return {\n label: role,\n icon: UserCheck,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n }\n };\n\n const config = getRoleConfig();\n const Icon = config.icon;\n\n return (\n <span\n className={`\n inline-flex items-center gap-1.5 px-2.5 py-1\n text-xs font-medium rounded-md\n border\n transition-colors duration-200\n ${config.className}\n `}\n >\n {showIcon && <Icon size={14} />}\n {config.label}\n </span>\n );\n}\n"],"mappings":";;;;;AAaA,SAAgB,EAAgB,EAAE,SAAM,cAAW,MAA8B;CAC/E,IAAM,EAAE,SAAM,GAAS,EACjB,IAAK,GACR,GAAa,MAAqB;EACjC,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEzC,CAAC,EAAE,CACJ,EAkDK,WAjDsB;EAC1B,IAAM,IAAY,EAAK,aAAa;AAEpC,UAAQ,GAAR;GACE,KAAK,cACH,QAAO;IACL,OAAO,EAAG,2BAA2B,cAAc;IACnD,MAAM;IACN,WACE;IACH;GACH,KAAK;GACL,KAAK;GACL,KAAK,kBACH,QAAO;IACL,OACE,MAAc,iBACV,EAAG,4BAA4B,eAAe,GAC9C,MAAc,cACZ,EAAG,yBAAyB,YAAY,GACxC,EAAG,+BAA+B,kBAAkB;IAC5D,MAAM;IACN,WACE;IACH;GACH,KAAK,SACH,QAAO;IACL,OAAO,EAAG,uBAAuB,SAAS;IAC1C,MAAM;IACN,WACE;IACH;GACH,KAAK,QACH,QAAO;IACL,OAAO,EAAG,sBAAsB,QAAQ;IACxC,MAAM;IACN,WACE;IACH;GACH,QACE,QAAO;IACL,OAAO;IACP,MAAM;IACN,WACE;IACH;;KAIuB,EACxB,IAAO,EAAO;AAEpB,QACE,kBAAC,QAAD;EACE,WAAW;;;;;UAKP,EAAO,UAAU;;YANvB,CASG,KAAY,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAC9B,EAAO,MACH"}
1
+ {"version":3,"file":"MemberRoleBadge.js","names":["MemberRoleBadge","useI18n","tr","useCallback","translated","t","key","fallback","config","roleUpper","role","Shield","UserCheck","Eye","Icon","showIcon"],"sources":["../../../../../src/organization-management/modules/members/components/MemberRoleBadge.tsx"],"sourcesContent":["import { Shield, UserCheck, Eye } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface MemberRoleBadgeProps {\n role: string;\n showIcon?: boolean;\n}\n\n/**\n * MemberRoleBadge\n * Displays a role badge for a member with appropriate styling\n */\nexport function MemberRoleBadge({ role, showIcon = true }: MemberRoleBadgeProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n const getRoleConfig = () => {\n const roleUpper = role.toUpperCase();\n\n switch (roleUpper) {\n case 'SUPER_ADMIN':\n return {\n label: tr('members.role.superAdmin', 'Super Admin'),\n icon: Shield,\n className:\n 'bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)] border-[var(--color-status-error-border)]',\n };\n case 'TENANT_ADMIN':\n case 'ORG_ADMIN':\n case 'WORKSPACE_ADMIN':\n return {\n label:\n roleUpper === 'TENANT_ADMIN'\n ? tr('members.role.tenantAdmin', 'Tenant Admin')\n : roleUpper === 'ORG_ADMIN'\n ? tr('members.role.orgAdmin', 'Org Admin')\n : tr('members.role.workspaceAdmin', 'Workspace Admin'),\n icon: Shield,\n className:\n 'bg-[var(--color-status-warning-bgSubtle)] text-[var(--color-status-warning-text)] border-[var(--color-status-warning-border)]',\n };\n case 'MEMBER':\n return {\n label: tr('members.role.member', 'Member'),\n icon: UserCheck,\n className:\n 'bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)] border-[var(--color-status-info-border)]',\n };\n case 'GUEST':\n return {\n label: tr('members.role.guest', 'Guest'),\n icon: Eye,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n default:\n return {\n label: role,\n icon: UserCheck,\n className:\n 'bg-[var(--color-bg-muted)] text-[var(--color-text-secondary)] border-[var(--color-border-default)]',\n };\n }\n };\n\n const config = getRoleConfig();\n const Icon = config.icon;\n\n return (\n <span\n className={`\n inline-flex items-center gap-1.5 px-2.5 py-1\n text-xs font-medium rounded-md\n border\n transition-colors duration-200\n ${config.className}\n `}\n >\n {showIcon && <Icon size={14} />}\n {config.label}\n </span>\n );\n}\n"],"mappings":";;;;;AAaA,SAAgBA,EAAgB,EAAE,SAAM,cAAW,MAA8B;CAC/E,IAAM,EAAE,SAAMC,EAAQ,GAChBC,IAAKC,GACR,GAAa,MAAqB;EACjC,IAAMC,IAAaC,EAAEC,CAAG;EACxB,OAAOF,MAAeE,IAAMC,IAAWH;CACzC,GACA,CAACC,CAAC,CACJ,GAkDMG,WAjDsB;EAC1B,IAAMC,IAAYC,EAAK,YAAY;EAEnC,QAAQD,GAAR;GACE,KAAK,eACH,OAAO;IACL,OAAOP,EAAG,2BAA2B,aAAa;IAClD,MAAMS;IACN,WACE;GACJ;GACF,KAAK;GACL,KAAK;GACL,KAAK,mBACH,OAAO;IACL,OACEF,MAAc,iBACVP,EAAG,4BAA4B,cAAc,IAC7CO,MAAc,cACZP,EAAG,yBAAyB,WAAW,IACvCA,EAAG,+BAA+B,iBAAiB;IAC3D,MAAMS;IACN,WACE;GACJ;GACF,KAAK,UACH,OAAO;IACL,OAAOT,EAAG,uBAAuB,QAAQ;IACzC,MAAMU;IACN,WACE;GACJ;GACF,KAAK,SACH,OAAO;IACL,OAAOV,EAAG,sBAAsB,OAAO;IACvC,MAAMW;IACN,WACE;GACJ;GACF,SACE,OAAO;IACL,OAAOH;IACP,MAAME;IACN,WACE;GACJ;EACJ;CACF,EAEe,CAAc,GACvBE,IAAON,EAAO;CAEpB,OACE,kBAAC,QAAD;EACE,WAAW;;;;;UAKPA,EAAO,UAAU;;EANvB,UAAA,CASGO,KAAY,kBAACD,GAAD,EAAM,MAAM,GAAK,CAAA,GAC7BN,EAAO,KACJ;;AAEV"}
@@ -1,10 +1,10 @@
1
- import { useTr as e } from "../../../shared/hooks/useTr.js";
2
- import { MemberRoleBadge as t } from "./MemberRoleBadge.js";
1
+ import { MemberRoleBadge as e } from "./MemberRoleBadge.js";
2
+ import { useTr as t } from "../../../shared/hooks/useTr.js";
3
3
  import { Building2 as n, Check as r, Clock as i, Loader2 as a, X as o } from "lucide-react";
4
4
  import { jsx as s, jsxs as c } from "react/jsx-runtime";
5
- //#region src/organization-management/modules/members/components/MyInviteCard.tsx
5
+ // src/organization-management/modules/members/components/MyInviteCard.tsx
6
6
  function l({ invite: l, onAccept: u, onReject: d, accepting: f, rejecting: p }) {
7
- let m = e(), h = Math.max(0, Math.ceil((new Date(l.expiresAt).getTime() - Date.now()) / (1e3 * 60 * 60 * 24)));
7
+ let m = t(), h = Math.max(0, Math.ceil((new Date(l.expiresAt).getTime() - Date.now()) / 864e5));
8
8
  return /* @__PURE__ */ s("div", {
9
9
  className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 transition-colors hover:border-[var(--color-border-focus)]",
10
10
  children: /* @__PURE__ */ c("div", {
@@ -26,7 +26,7 @@ function l({ invite: l, onAccept: u, onReject: d, accepting: f, rejecting: p })
26
26
  }),
27
27
  /* @__PURE__ */ c("div", {
28
28
  className: "flex items-center gap-2 mt-1",
29
- children: [/* @__PURE__ */ s(t, { role: l.role }), /* @__PURE__ */ c("span", {
29
+ children: [/* @__PURE__ */ s(e, { role: l.role }), /* @__PURE__ */ c("span", {
30
30
  className: "text-xs text-[var(--color-text-muted)] flex items-center gap-1",
31
31
  children: [/* @__PURE__ */ s(i, { size: 12 }), h > 0 ? `${m("members.myInvites.expiresIn", "Expires in")} ${h}d` : m("members.myInvites.expiringSoon", "Expiring soon")]
32
32
  })]
@@ -66,7 +66,6 @@ function l({ invite: l, onAccept: u, onReject: d, accepting: f, rejecting: p })
66
66
  })
67
67
  });
68
68
  }
69
- //#endregion
70
69
  export { l as MyInviteCard };
71
70
 
72
71
  //# sourceMappingURL=MyInviteCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MyInviteCard.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/MyInviteCard.tsx"],"sourcesContent":["import { Check, X, Building2, Clock, Loader2 } from 'lucide-react';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface MyInviteCardProps {\n invite: {\n id: string;\n email: string;\n role: string;\n expiresAt: string;\n message?: string | null;\n createdAt: string;\n organization?: {\n id: string;\n name: string;\n slug: string;\n } | null;\n };\n onAccept: (inviteId: string) => Promise<void>;\n onReject: (inviteId: string) => Promise<void>;\n accepting?: boolean;\n rejecting?: boolean;\n}\n\nexport function MyInviteCard({\n invite,\n onAccept,\n onReject,\n accepting,\n rejecting,\n}: MyInviteCardProps) {\n const tr = useTr();\n const daysUntilExpiry = Math.max(\n 0,\n Math.ceil((new Date(invite.expiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24))\n );\n\n return (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 transition-colors hover:border-[var(--color-border-focus)]\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n {/* Left: Org info */}\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"p-2 rounded-lg bg-[var(--color-bg-subtle)]\">\n <Building2 size={20} className=\"text-[var(--color-text-secondary)]\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] truncate\">\n {invite.organization?.name ?? tr('common.organization', 'Organization')}\n </h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <MemberRoleBadge role={invite.role} />\n <span className=\"text-xs text-[var(--color-text-muted)] flex items-center gap-1\">\n <Clock size={12} />\n {daysUntilExpiry > 0\n ? `${tr('members.myInvites.expiresIn', 'Expires in')} ${daysUntilExpiry}d`\n : tr('members.myInvites.expiringSoon', 'Expiring soon')}\n </span>\n </div>\n {invite.message && (\n <p className=\"mt-2 text-sm text-[var(--color-text-secondary)] italic\">\n &ldquo;{invite.message}&rdquo;\n </p>\n )}\n </div>\n </div>\n\n {/* Right: Actions */}\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button\n type=\"button\"\n onClick={() => onAccept(invite.id)}\n disabled={accepting || rejecting}\n className=\"\n inline-flex items-center gap-1.5 px-4 py-2\n bg-[var(--color-status-success-bg)] text-[var(--color-text-inverse)] rounded-lg\n hover:opacity-90\n \n transition-all duration-200\n text-sm font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n \"\n >\n {accepting ? <Loader2 size={14} className=\"animate-spin\" /> : <Check size={14} />}\n {tr('members.myInvites.accept', 'Accept')}\n </button>\n <button\n type=\"button\"\n onClick={() => onReject(invite.id)}\n disabled={accepting || rejecting}\n className=\"\n inline-flex items-center gap-1.5 px-4 py-2\n bg-transparent text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-status-error-bgSubtle)] hover:border-[var(--color-status-error-border)] hover:text-[var(--color-status-error-text)]\n \n transition-all duration-200\n text-sm font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n \"\n >\n {rejecting ? <Loader2 size={14} className=\"animate-spin\" /> : <X size={14} />}\n {tr('members.myInvites.decline', 'Decline')}\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAwBA,SAAgB,EAAa,EAC3B,WACA,aACA,aACA,cACA,gBACoB;CACpB,IAAM,IAAK,GAAO,EACZ,IAAkB,KAAK,IAC3B,GACA,KAAK,MAAM,IAAI,KAAK,EAAO,UAAU,CAAC,SAAS,GAAG,KAAK,KAAK,KAAK,MAAO,KAAK,KAAK,IAAI,CACvF;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAW,MAAM;MAAI,WAAU;MAAuC,CAAA;KAClE,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAO,cAAc,QAAQ,EAAG,uBAAuB,eAAe;OACpE,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA,EACtC,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAClB,IAAkB,IACf,GAAG,EAAG,+BAA+B,aAAa,CAAC,GAAG,EAAgB,KACtE,EAAG,kCAAkC,gBAAgB,CACpD;UACH;;MACL,EAAO,WACN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAsE;QAC5D,EAAO;QAAQ;QACrB;;MAEF;OACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,EAAO,GAAG;KAClC,UAAU,KAAa;KACvB,WAAU;eAJZ,CAcG,IAAY,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAChF,EAAG,4BAA4B,SAAS,CAClC;QACT,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,EAAO,GAAG;KAClC,UAAU,KAAa;KACvB,WAAU;eAJZ,CAgBG,IAAY,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA,EAC5E,EAAG,6BAA6B,UAAU,CACpC;OACL;MACF;;EACF,CAAA"}
1
+ {"version":3,"file":"MyInviteCard.js","names":["MyInviteCard","tr","useTr","daysUntilExpiry","invite","Building2","MemberRoleBadge","Clock","onAccept","accepting","rejecting","Loader2","Check","onReject","X"],"sources":["../../../../../src/organization-management/modules/members/components/MyInviteCard.tsx"],"sourcesContent":["import { Check, X, Building2, Clock, Loader2 } from 'lucide-react';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface MyInviteCardProps {\n invite: {\n id: string;\n email: string;\n role: string;\n expiresAt: string;\n message?: string | null;\n createdAt: string;\n organization?: {\n id: string;\n name: string;\n slug: string;\n } | null;\n };\n onAccept: (inviteId: string) => Promise<void>;\n onReject: (inviteId: string) => Promise<void>;\n accepting?: boolean;\n rejecting?: boolean;\n}\n\nexport function MyInviteCard({\n invite,\n onAccept,\n onReject,\n accepting,\n rejecting,\n}: MyInviteCardProps) {\n const tr = useTr();\n const daysUntilExpiry = Math.max(\n 0,\n Math.ceil((new Date(invite.expiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24))\n );\n\n return (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 transition-colors hover:border-[var(--color-border-focus)]\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n {/* Left: Org info */}\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"p-2 rounded-lg bg-[var(--color-bg-subtle)]\">\n <Building2 size={20} className=\"text-[var(--color-text-secondary)]\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] truncate\">\n {invite.organization?.name ?? tr('common.organization', 'Organization')}\n </h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <MemberRoleBadge role={invite.role} />\n <span className=\"text-xs text-[var(--color-text-muted)] flex items-center gap-1\">\n <Clock size={12} />\n {daysUntilExpiry > 0\n ? `${tr('members.myInvites.expiresIn', 'Expires in')} ${daysUntilExpiry}d`\n : tr('members.myInvites.expiringSoon', 'Expiring soon')}\n </span>\n </div>\n {invite.message && (\n <p className=\"mt-2 text-sm text-[var(--color-text-secondary)] italic\">\n &ldquo;{invite.message}&rdquo;\n </p>\n )}\n </div>\n </div>\n\n {/* Right: Actions */}\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button\n type=\"button\"\n onClick={() => onAccept(invite.id)}\n disabled={accepting || rejecting}\n className=\"\n inline-flex items-center gap-1.5 px-4 py-2\n bg-[var(--color-status-success-bg)] text-[var(--color-text-inverse)] rounded-lg\n hover:opacity-90\n \n transition-all duration-200\n text-sm font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n \"\n >\n {accepting ? <Loader2 size={14} className=\"animate-spin\" /> : <Check size={14} />}\n {tr('members.myInvites.accept', 'Accept')}\n </button>\n <button\n type=\"button\"\n onClick={() => onReject(invite.id)}\n disabled={accepting || rejecting}\n className=\"\n inline-flex items-center gap-1.5 px-4 py-2\n bg-transparent text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-status-error-bgSubtle)] hover:border-[var(--color-status-error-border)] hover:text-[var(--color-status-error-text)]\n \n transition-all duration-200\n text-sm font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n \"\n >\n {rejecting ? <Loader2 size={14} className=\"animate-spin\" /> : <X size={14} />}\n {tr('members.myInvites.decline', 'Decline')}\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAwBA,SAAgBA,EAAa,EAC3B,WACA,aACA,aACA,cACA,gBACoB;CACpB,IAAMC,IAAKC,EAAM,GACXC,IAAkB,KAAK,IAC3B,GACA,KAAK,MAAM,IAAI,KAAKC,EAAO,SAAS,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI,KAAM,KAAoB,CACvF;CAEA,OACE,kBAAC,OAAD;EAAK,WAAU;EACb,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CAEE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KACb,UAAA,kBAACC,GAAD;MAAW,MAAM;MAAI,WAAU;KAAsC,CAAA;IAClE,CAAA,GACL,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,kBAAC,MAAD;OAAI,WAAU;OACX,UAAAD,EAAO,cAAc,QAAQH,EAAG,uBAAuB,cAAc;MACpE,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,kBAACK,GAAD,EAAiB,MAAMF,EAAO,KAAO,CAAA,GACrC,kBAAC,QAAD;QAAM,WAAU;QAAhB,UAAA,CACE,kBAACG,GAAD,EAAO,MAAM,GAAK,CAAA,GACjBJ,IAAkB,IACf,GAAGF,EAAG,+BAA+B,YAAY,EAAE,GAAGE,EAAgB,KACtEF,EAAG,kCAAkC,eAAe,CACpD;OACH,CAAA,CAAA;;MACJG,EAAO,WACN,kBAAC,KAAD;OAAG,WAAU;OAAb,UAAA;QAAsE;QAC5DA,EAAO;QAAQ;OACtB;;KAEF;IACF,CAAA,CAAA;GAGL,CAAA,GAAA,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAeI,EAASJ,EAAO,EAAE;KACjC,UAAUK,KAAaC;KACvB,WAAU;KAJZ,UAAA,CAcGD,IAAY,kBAACE,GAAD;MAAS,MAAM;MAAI,WAAU;KAAgB,CAAA,IAAI,kBAACC,GAAD,EAAO,MAAM,GAAK,CAAA,GAC/EX,EAAG,4BAA4B,QAAQ,CAClC;IACR,CAAA,GAAA,kBAAC,UAAD;KACE,MAAK;KACL,eAAeY,EAAST,EAAO,EAAE;KACjC,UAAUK,KAAaC;KACvB,WAAU;KAJZ,UAAA,CAgBGA,IAAY,kBAACC,GAAD;MAAS,MAAM;MAAI,WAAU;KAAgB,CAAA,IAAI,kBAACG,GAAD,EAAG,MAAM,GAAK,CAAA,GAC3Eb,EAAG,6BAA6B,SAAS,CACpC;IACL,CAAA,CAAA;GACF,CAAA,CAAA;;CACF,CAAA;AAET"}
@@ -1,16 +1,16 @@
1
1
  import { toast as e } from "../../../../node_modules/sonner/dist/index.js";
2
2
  import { useCancelInvite as t, useOrganizationInvites as n, useResendInvite 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 { InviteStatusBadge as o } from "./InviteStatusBadge.js";
6
- import { MemberRoleBadge as s } from "./MemberRoleBadge.js";
3
+ import { InviteStatusBadge as i } from "./InviteStatusBadge.js";
4
+ import { MemberRoleBadge as a } from "./MemberRoleBadge.js";
5
+ import { InviteStatus as o } from "../../../../generated/global-types.js";
6
+ import { useTr as s } from "../../../shared/hooks/useTr.js";
7
7
  import { useState as c } from "react";
8
8
  import { AlertCircle as l, Loader2 as u, RotateCw as d, X as f } from "lucide-react";
9
9
  import { ResponsiveTable as p } from "@burdenoff/fe-libs/ui";
10
10
  import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
11
- //#region src/organization-management/modules/members/components/PendingInvitesList.tsx
11
+ // src/organization-management/modules/members/components/PendingInvitesList.tsx
12
12
  function _({ organizationId: _ }) {
13
- let v = a(), { invites: y, loading: b, error: x, total: S } = n(_, { status: i.Pending }, 50), { cancelInvite: C, loading: w } = t(), { resendInvite: T, loading: E } = r(), [D, O] = c(null);
13
+ let v = s(), { invites: y, loading: b, error: x, total: S } = n(_, { status: o.Pending }, 50), { cancelInvite: C, loading: w } = t(), { resendInvite: T, loading: E } = r(), [D, O] = c(null);
14
14
  if (b && y.length === 0) return /* @__PURE__ */ h("div", {
15
15
  className: "flex items-center justify-center py-8",
16
16
  children: /* @__PURE__ */ h(u, {
@@ -57,12 +57,12 @@ function _({ organizationId: _ }) {
57
57
  {
58
58
  key: "role",
59
59
  header: v("members.list.column.role", "Role"),
60
- cell: (e) => /* @__PURE__ */ h(s, { role: e.role })
60
+ cell: (e) => /* @__PURE__ */ h(a, { role: e.role })
61
61
  },
62
62
  {
63
63
  key: "status",
64
64
  header: v("common.status", "Status"),
65
- cell: (e) => /* @__PURE__ */ h(o, { status: e.status })
65
+ cell: (e) => /* @__PURE__ */ h(i, { status: e.status })
66
66
  },
67
67
  {
68
68
  key: "expires",
@@ -117,7 +117,6 @@ function _({ organizationId: _ }) {
117
117
  })
118
118
  ] });
119
119
  }
120
- //#endregion
121
120
  export { _ as PendingInvitesList };
122
121
 
123
122
  //# sourceMappingURL=PendingInvitesList.js.map